mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-07 23:19:14 +00:00
feat(editors): три диалога настроек собраны в карточки-группы
«Общие настройки», «Пространство» и «Устройство на плане» были плоскими столбцами. В «Общих» девять подписей-заголовков шли вперемешку с рядами и абзацами; в «Устройстве» на 760 строк формы не было ни одного деления, и «Название» лежало в одном списке с «Дополнительными действиями». Теперь это карточки общего набора: семь в «Общих», четыре в «Пространстве», пять в «Устройстве». Набор взят как есть — он поднят в #594 и доказан на диалоге комнаты и редакторе панели (#597). Шесть пояснений переехали под «?» к своему заголовку, подзаголовку или строке; текст не переписан. Два сообщения о СОСТОЯНИИ остались на виду — решение владельца: `gs.sun_missing` и `marker.run_target_gone` говорят, что настройка сломана прямо сейчас, а второе ещё и называет пропавшую цель подстановкой, которой в статической подсказке взяться неоткуда. `gs.hint` при этом переехал из ленивого support-словаря в основной: «?» читает текст через `_t`, а не `supportT`. Тест мёртвых ключей сливает оба словаря, так что копия в support удалена во всех четырёх локалях. Классы-опоры сохранены намеренно: `.srcrow`, `.dispsection`, `.gsrow`, `.colorrow`, `.namein`, `.areasel`, `.tempin` остались на своих элементах, а карточки добавились вокруг. За эти имена держатся одиннадцать смоков, и потеря любого превратила бы ревью в чтение переписанных свидетелей. Изменён ровно один смок — `smoke_general_settings`, и изменён в сторону строгости: вместо одного списка подписей он снимает теперь два (заголовки карточек и подзаголовки внутри), требует непустоты каждой карточки, проверяет что радиус свечения лежит ВНУТРИ карточки свечения (подпись такой границей не была), что четыре переехавших пояснения доступны кнопкой и что их абзацев в форме не осталось. `src/houseplan-onboarding-runtime.ts` затронут двумя строками: он второй потребитель переименованных ключей `space.hide_*_tip` → `space.hide_*.help`. Один текст — один ключ; дублировать его ради неприкосновенности файла хуже. Потолок стартового графа поднят 291 400 → 292 500. Выросли СТРОКИ, а не код: 12 новых ключей в четырёх локалях плюс переехавший `gs.hint`. Разметка диалогов как была в ленивом редакторском графе, так там и осталась. Свидетели: `state-callout-hidden-under-help` прячет сообщение о состоянии под «?» и краснит новый блок `smoke_tap_run`; `dialog-card-loses-its-heading` обнуляет заголовок карточки и краснит `smoke_general_settings` — карточка без заголовка это снова плоский список, только с рамкой. Issue: #598 User-Visible: yes
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
/**
|
||||
* Диалог «Общие настройки» (#592: вынесен из houseplan-editor-runtime.ts как есть).
|
||||
* Диалог «Общие настройки».
|
||||
*
|
||||
* Тело перенесено побайтово: тип `this` объявлен параметром, поэтому ни одна
|
||||
* строка разметки, ни один обработчик и ни один якорь мутанта не переписаны.
|
||||
* Состояние остаётся на хосте — модуль только рисует (#592, К2).
|
||||
* #592 вынес его сюда побайтово; #598 собрал девять подписей-заголовков в семь
|
||||
* карточек общего набора. Состояние по-прежнему живёт на хосте — модуль только
|
||||
* рисует, и каждый контрол пишет в тот же ключ черновика, что и раньше (#598 К1).
|
||||
*
|
||||
* Классы `.srcrow`, `.dispsection`, `.gsrow`, `.colorrow`, `.namein`, `.areasel`,
|
||||
* `.tempin` сохранены намеренно: за них держатся одиннадцать смоков, и потеря
|
||||
* любого превратила бы ревью в чтение переписанных свидетелей (#598 К6).
|
||||
*/
|
||||
import { html, nothing, type TemplateResult } from 'lit';
|
||||
|
||||
import { formCard, segmented } from './form-kit';
|
||||
import { langOf } from '../i18n';
|
||||
import { supportT } from '../i18n/support';
|
||||
import { DEFAULT_FILL_COLORS } from '../logic';
|
||||
@@ -20,147 +25,173 @@ import type { HouseplanEditorRuntime } from '../houseplan-editor-runtime';
|
||||
export function renderGeneralSettingsDialog(this: HouseplanEditorRuntime): TemplateResult {
|
||||
return html`<hp-dialog .hass=${this.host.hass} data-kind="settings" .title=${this.host._t('gs.title')} icon="mdi:cog-outline" wide
|
||||
@hp-close=${() => (this.host._settingsDialog = null)}>
|
||||
<div class="body">
|
||||
<div class="rhint">${supportT(
|
||||
langOf(this.host.hass, this.host._config?.language), 'gs.hint',
|
||||
)}</div>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(this.host._settingsDialog!.showRoomTooltip, (v) =>
|
||||
(this.host._settingsDialog = { ...this.host._settingsDialog!, showRoomTooltip: v }))}
|
||||
<span>${supportT(
|
||||
langOf(this.host.hass, this.host._config?.language), 'gs.show_room_tooltip',
|
||||
)}</span>
|
||||
</label>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(this.host._settingsDialog!.radarShowLive, (v) =>
|
||||
(this.host._settingsDialog = { ...this.host._settingsDialog!, radarShowLive: v }))}
|
||||
<span>${this.host._t('gs.radar_show_live')}</span>
|
||||
</label>
|
||||
<div class="rhint">${this.host._t('gs.radar_show_live_hint')}</div>
|
||||
<hp-zigbee-topology-settings .hass=${this.host.hass} .value=${this.host._settingsDialog!.zigbeeTopology} .savedEnabled=${zigbeeTopologySettingsOf(this.host._settings).enabled} .devices=${this.host._devices} .registry=${this.host._haRegistry} @hp-topology-settings-change=${(event: CustomEvent<ZigbeeTopologySettings>) => (this.host._settingsDialog = { ...this.host._settingsDialog!, zigbeeTopology: event.detail })}></hp-zigbee-topology-settings>
|
||||
<label class="dispsection">${this.host._t('gs.light_group')}</label>
|
||||
${this._renderColorRow('light_on', 'gs.light_on')}
|
||||
${this._renderColorRow('light_off', 'gs.light_off')}
|
||||
${this._renderColorRow('light_none', 'gs.light_none')}
|
||||
<label class="dispsection">${this.host._t('gs.temp_group')}</label>
|
||||
${this._renderColorRow('temp_cold', 'gs.temp_cold')}
|
||||
${this._renderColorRow('temp_ok', 'gs.temp_ok')}
|
||||
${this._renderColorRow('temp_hot', 'gs.temp_hot')}
|
||||
<label class="dispsection">${this.host._t('gs.lqi_group')}</label>
|
||||
${this._renderColorRow('lqi_low', 'gs.lqi_low')}
|
||||
${this._renderColorRow('lqi_high', 'gs.lqi_high')}
|
||||
<label class="dispsection">${this.host._t('gs.glow_group')}</label>
|
||||
${this._renderColorRow('glow_base', 'gs.glow_base')}
|
||||
${this._renderColorRow('glow_light', 'gs.glow_light')}
|
||||
<div class="colorrow gsrow">
|
||||
<span class="gsl help-inline-label"><label for="gs-glow-radius">${this.host._t('gs.glow_radius')}</label>
|
||||
${this._help('gs.glow_radius.help')}</span>
|
||||
<input id="gs-glow-radius" type="number" class="tempin" min="0.5" step="0.5"
|
||||
.value=${String(this.host._settingsDialog!.glowRadius)}
|
||||
@input=${(e: Event) => {
|
||||
const v = strictNumber((e.target as HTMLInputElement).value);
|
||||
if (v != null && v > 0)
|
||||
this.host._settingsDialog = { ...this.host._settingsDialog!, glowRadius: v };
|
||||
}} />
|
||||
<span class="opl">${this.host._imperial ? this.host._t('gs.unit_ft') : this.host._t('gs.unit_m')}</span>
|
||||
</div>
|
||||
<label class="dispsection">${this.host._t('gs.wall_group')}</label>
|
||||
${this._renderColorRow('wall_fill', 'gs.wall_fill')}
|
||||
<label class="dispsection">${this.host._t('gs.bg_group')}</label>
|
||||
<div class="colorrow gsrow">
|
||||
<span class="gsl help-inline-label"><label for="gs-bg-mode">${this.host._t('gs.bg_mode')}</label>
|
||||
${this._help('gs.bg_mode.help')}</span>
|
||||
<select id="gs-bg-mode" class="areasel"
|
||||
@change=${(e: Event) =>
|
||||
(this.host._settingsDialog = { ...this.host._settingsDialog!, bgMode: (e.target as HTMLSelectElement).value === 'daynight' ? 'daynight' : 'static' })}>
|
||||
<option value="static" ?selected=${this.host._settingsDialog!.bgMode === 'static'}>${this.host._t('gs.bg_static')}</option>
|
||||
<option value="daynight" ?selected=${this.host._settingsDialog!.bgMode === 'daynight'}>${this.host._t('gs.bg_daynight')}</option>
|
||||
</select>
|
||||
</div>
|
||||
${this.host._settingsDialog!.bgMode === 'static'
|
||||
? html`<div class="colorrow gsrow">
|
||||
<hp-color-opacity .label=${this.host._t('gs.bg_color')}
|
||||
.pickerLabels=${this.host._colorPickerLabels}
|
||||
.color=${this.host._settingsDialog!.bgColor || this.host._stageBgHex()}
|
||||
.opacity=${1} .showOpacity=${false}
|
||||
@hp-color-opacity-change=${(e: CustomEvent<{ color: string }>) => {
|
||||
this.host._settingsDialog = { ...this.host._settingsDialog!, bgColor: e.detail.color };
|
||||
}}></hp-color-opacity>
|
||||
${this.host._settingsDialog!.bgColor
|
||||
? html`<button class="btn ghost" @click=${() =>
|
||||
(this.host._settingsDialog = { ...this.host._settingsDialog!, bgColor: null })}>${this.host._t('gs.bg_default')}</button>`
|
||||
: html`<span class="opl">${this.host._t('gs.bg_theme')}</span>`}
|
||||
</div>`
|
||||
: nothing}
|
||||
<label class="dispsection">${this.host._t('gs.sun_group')}</label>
|
||||
${!sunStateOf(this.host.hass)
|
||||
? html`<div class="rhint">${this.host._t('gs.sun_missing')}</div>`
|
||||
: nothing}
|
||||
<div class="sunrow">
|
||||
${this.host._renderCompass()}
|
||||
<div class="suncol">
|
||||
<div class="helpfieldlabel compact">
|
||||
<label for="gs-north">${this.host._t('gs.north')}</label>
|
||||
${this._help('gs.north.help')}
|
||||
</div>
|
||||
<div class="colorrow">
|
||||
<input id="gs-north" class="namein tempin" type="number" min="0" max="359" step="1"
|
||||
placeholder=${this.host._t('gs.north_ph')}
|
||||
.value=${this.host._settingsDialog!.northDeg === null ? '' : String(this.host._settingsDialog!.northDeg)}
|
||||
<div class="body hpf-form">
|
||||
${formCard({
|
||||
title: this.host._t('gs.card_display'),
|
||||
body: html`
|
||||
<label class="srcrow">
|
||||
${this._boolInput(this.host._settingsDialog!.showRoomTooltip, (v) =>
|
||||
(this.host._settingsDialog = { ...this.host._settingsDialog!, showRoomTooltip: v }))}
|
||||
<span>${supportT(
|
||||
langOf(this.host.hass, this.host._config?.language), 'gs.show_room_tooltip',
|
||||
)}</span>
|
||||
</label>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(this.host._settingsDialog!.radarShowLive, (v) =>
|
||||
(this.host._settingsDialog = { ...this.host._settingsDialog!, radarShowLive: v }))}
|
||||
<span>${this.host._t('gs.radar_show_live')}</span>
|
||||
${this._help('gs.radar_show_live.help')}
|
||||
</label>`,
|
||||
})}
|
||||
${formCard({
|
||||
title: this.host._t('gs.card_zigbee'),
|
||||
body: html`<hp-zigbee-topology-settings .hass=${this.host.hass} .value=${this.host._settingsDialog!.zigbeeTopology} .savedEnabled=${zigbeeTopologySettingsOf(this.host._settings).enabled} .devices=${this.host._devices} .registry=${this.host._haRegistry} @hp-topology-settings-change=${(event: CustomEvent<ZigbeeTopologySettings>) => (this.host._settingsDialog = { ...this.host._settingsDialog!, zigbeeTopology: event.detail })}></hp-zigbee-topology-settings>`,
|
||||
})}
|
||||
${formCard({
|
||||
title: this.host._t('gs.card_fills'),
|
||||
help: this._help('gs.card_fills.help'),
|
||||
body: html`
|
||||
<label class="dispsection">${this.host._t('gs.light_group')}</label>
|
||||
${this._renderColorRow('light_on', 'gs.light_on')}
|
||||
${this._renderColorRow('light_off', 'gs.light_off')}
|
||||
${this._renderColorRow('light_none', 'gs.light_none')}
|
||||
<label class="dispsection">${this.host._t('gs.temp_group')}</label>
|
||||
${this._renderColorRow('temp_cold', 'gs.temp_cold')}
|
||||
${this._renderColorRow('temp_ok', 'gs.temp_ok')}
|
||||
${this._renderColorRow('temp_hot', 'gs.temp_hot')}
|
||||
<label class="dispsection">${this.host._t('gs.lqi_group')}</label>
|
||||
${this._renderColorRow('lqi_low', 'gs.lqi_low')}
|
||||
${this._renderColorRow('lqi_high', 'gs.lqi_high')}`,
|
||||
})}
|
||||
${formCard({
|
||||
title: this.host._t('gs.glow_group'),
|
||||
body: html`
|
||||
${this._renderColorRow('glow_base', 'gs.glow_base')}
|
||||
${this._renderColorRow('glow_light', 'gs.glow_light')}
|
||||
<div class="colorrow gsrow">
|
||||
<span class="gsl help-inline-label"><label for="gs-glow-radius">${this.host._t('gs.glow_radius')}</label>
|
||||
${this._help('gs.glow_radius.help')}</span>
|
||||
<input id="gs-glow-radius" type="number" class="tempin" min="0.5" step="0.5"
|
||||
.value=${String(this.host._settingsDialog!.glowRadius)}
|
||||
@input=${(e: Event) => {
|
||||
const raw = (e.target as HTMLInputElement).value.trim();
|
||||
const n = raw === '' ? null : Math.round(Number(raw));
|
||||
this.host._settingsDialog = {
|
||||
...this.host._settingsDialog!,
|
||||
northDeg: n !== null && Number.isFinite(n) ? Math.min(359, Math.max(0, n)) : null,
|
||||
};
|
||||
const v = strictNumber((e.target as HTMLInputElement).value);
|
||||
if (v != null && v > 0)
|
||||
this.host._settingsDialog = { ...this.host._settingsDialog!, glowRadius: v };
|
||||
}} />
|
||||
${this.host._settingsDialog!.northDeg !== null
|
||||
? html`<button class="btn ghost" @click=${() =>
|
||||
(this.host._settingsDialog = { ...this.host._settingsDialog!, northDeg: null })}>${this.host._t('gs.north_clear')}</button>`
|
||||
: nothing}
|
||||
<span class="opl">${this.host._imperial ? this.host._t('gs.unit_ft') : this.host._t('gs.unit_m')}</span>
|
||||
</div>`,
|
||||
})}
|
||||
${formCard({
|
||||
title: this.host._t('gs.card_plan'),
|
||||
body: html`
|
||||
<label class="dispsection">${this.host._t('gs.wall_group')}</label>
|
||||
${this._renderColorRow('wall_fill', 'gs.wall_fill')}
|
||||
<label class="dispsection">${this.host._t('gs.bg_group')}</label>
|
||||
<div class="colorrow gsrow">
|
||||
<span class="gsl help-inline-label"><label for="gs-bg-mode">${this.host._t('gs.bg_mode')}</label>
|
||||
${this._help('gs.bg_mode.help')}</span>
|
||||
${segmented({
|
||||
name: 'gs-bg-mode',
|
||||
value: this.host._settingsDialog!.bgMode === 'daynight' ? 'daynight' : 'static',
|
||||
ariaLabel: this.host._t('gs.bg_mode'),
|
||||
options: [
|
||||
{ value: 'static', label: this.host._t('gs.bg_static') },
|
||||
{ value: 'daynight', label: this.host._t('gs.bg_daynight') },
|
||||
],
|
||||
onChange: (value) =>
|
||||
(this.host._settingsDialog = { ...this.host._settingsDialog!, bgMode: value === 'daynight' ? 'daynight' : 'static' }),
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(this.host._settingsDialog!.sunRays, (v) =>
|
||||
(this.host._settingsDialog = { ...this.host._settingsDialog!, sunRays: v }))}
|
||||
<span>${this.host._t('gs.sun_rays')}</span>
|
||||
</label>
|
||||
${renderSunRayOriginSelect(this.host._settingsDialog!.sunRayOrigin, (key) => this.host._t(key), (sunRayOrigin) => (this.host._settingsDialog = { ...this.host._settingsDialog!, sunRayOrigin }))}
|
||||
${this.host._canEdit ? html`
|
||||
<label class="dispsection">${this.host._t('gs.backup_group')}</label>
|
||||
<div class="rhint">${this.host._t('gs.backup_hint')}</div>
|
||||
<div class="backupactions">
|
||||
<button class="btn ghost" @click=${() => this._openBackupExport()}>
|
||||
<ha-icon icon="mdi:download"></ha-icon>${this.host._t('backup.export_open')}
|
||||
</button>
|
||||
<span class="backupupload">
|
||||
<button class="btn ghost" type="button" @click=${(e: Event) =>
|
||||
((e.currentTarget as HTMLElement).nextElementSibling as HTMLInputElement | null)?.click()}>
|
||||
<ha-icon icon="mdi:upload"></ha-icon>${this.host._t('backup.import_open')}
|
||||
${this.host._settingsDialog!.bgMode === 'static'
|
||||
? html`<div class="colorrow gsrow">
|
||||
<hp-color-opacity .label=${this.host._t('gs.bg_color')}
|
||||
.pickerLabels=${this.host._colorPickerLabels}
|
||||
.color=${this.host._settingsDialog!.bgColor || this.host._stageBgHex()}
|
||||
.opacity=${1} .showOpacity=${false}
|
||||
@hp-color-opacity-change=${(e: CustomEvent<{ color: string }>) => {
|
||||
this.host._settingsDialog = { ...this.host._settingsDialog!, bgColor: e.detail.color };
|
||||
}}></hp-color-opacity>
|
||||
${this.host._settingsDialog!.bgColor
|
||||
? html`<button class="btn ghost" @click=${() =>
|
||||
(this.host._settingsDialog = { ...this.host._settingsDialog!, bgColor: null })}>${this.host._t('gs.bg_default')}</button>`
|
||||
: html`<span class="opl">${this.host._t('gs.bg_theme')}</span>`}
|
||||
</div>`
|
||||
: nothing}`,
|
||||
})}
|
||||
${formCard({
|
||||
title: this.host._t('gs.sun_group'),
|
||||
body: html`
|
||||
${!sunStateOf(this.host.hass)
|
||||
? html`<div class="rhint">${this.host._t('gs.sun_missing')}</div>`
|
||||
: nothing}
|
||||
<div class="sunrow">
|
||||
${this.host._renderCompass()}
|
||||
<div class="suncol">
|
||||
<div class="helpfieldlabel compact">
|
||||
<label for="gs-north">${this.host._t('gs.north')}</label>
|
||||
${this._help('gs.north.help')}
|
||||
</div>
|
||||
<div class="colorrow">
|
||||
<input id="gs-north" class="namein tempin" type="number" min="0" max="359" step="1"
|
||||
placeholder=${this.host._t('gs.north_ph')}
|
||||
.value=${this.host._settingsDialog!.northDeg === null ? '' : String(this.host._settingsDialog!.northDeg)}
|
||||
@input=${(e: Event) => {
|
||||
const raw = (e.target as HTMLInputElement).value.trim();
|
||||
const n = raw === '' ? null : Math.round(Number(raw));
|
||||
this.host._settingsDialog = {
|
||||
...this.host._settingsDialog!,
|
||||
northDeg: n !== null && Number.isFinite(n) ? Math.min(359, Math.max(0, n)) : null,
|
||||
};
|
||||
}} />
|
||||
${this.host._settingsDialog!.northDeg !== null
|
||||
? html`<button class="btn ghost" @click=${() =>
|
||||
(this.host._settingsDialog = { ...this.host._settingsDialog!, northDeg: null })}>${this.host._t('gs.north_clear')}</button>`
|
||||
: nothing}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(this.host._settingsDialog!.sunRays, (v) =>
|
||||
(this.host._settingsDialog = { ...this.host._settingsDialog!, sunRays: v }))}
|
||||
<span>${this.host._t('gs.sun_rays')}</span>
|
||||
</label>
|
||||
${renderSunRayOriginSelect(this.host._settingsDialog!.sunRayOrigin, (key) => this.host._t(key), (sunRayOrigin) => (this.host._settingsDialog = { ...this.host._settingsDialog!, sunRayOrigin }))}`,
|
||||
})}
|
||||
${formCard({
|
||||
title: this.host._t('gs.card_data'),
|
||||
body: html`
|
||||
${this.host._canEdit ? html`
|
||||
<label class="dispsection">${this.host._t('gs.backup_group')}${this._help('gs.backup_group.help')}</label>
|
||||
<div class="backupactions">
|
||||
<button class="btn ghost" @click=${() => this._openBackupExport()}>
|
||||
<ha-icon icon="mdi:download"></ha-icon>${this.host._t('backup.export_open')}
|
||||
</button>
|
||||
<span class="backupupload">
|
||||
<button class="btn ghost" type="button" @click=${(e: Event) =>
|
||||
((e.currentTarget as HTMLElement).nextElementSibling as HTMLInputElement | null)?.click()}>
|
||||
<ha-icon icon="mdi:upload"></ha-icon>${this.host._t('backup.import_open')}
|
||||
</button>
|
||||
<input type="file" accept="application/json,.json" @change=${(event: Event) => this._pickBackupImport(event)} />
|
||||
</span>
|
||||
${this.host._canOptimizeUndo && this.host._undoKind === 'import' ? html`
|
||||
<button class="btn ghost" @click=${() => this._undoPlanOptimization()}
|
||||
?disabled=${this.host._optimizeUndoBusy}>
|
||||
<ha-icon icon="mdi:undo-variant"></ha-icon>${this.host._t('backup.undo_import')}
|
||||
</button>` : nothing}
|
||||
</div>` : nothing}
|
||||
<label class="dispsection">${this.host._t('gs.grid_group')}${this._help('gs.grid_group.help')}</label>
|
||||
<div class="colorrow gsrow">
|
||||
<button class="btn ghost alignall" @click=${() => this._openAlignDialog()}>
|
||||
<ha-icon icon="mdi:broom"></ha-icon>${this.host._t('gs.align_all')}
|
||||
</button>
|
||||
<input type="file" accept="application/json,.json" @change=${(event: Event) => this._pickBackupImport(event)} />
|
||||
</span>
|
||||
${this.host._canOptimizeUndo && this.host._undoKind === 'import' ? html`
|
||||
<button class="btn ghost" @click=${() => this._undoPlanOptimization()}
|
||||
</div>
|
||||
${this.host._canOptimizeUndo && this.host._undoKind !== 'import' ? html`<div class="colorrow gsrow">
|
||||
<button class="btn ghost alignall" @click=${() => this._undoPlanOptimization()}
|
||||
?disabled=${this.host._optimizeUndoBusy}>
|
||||
<ha-icon icon="mdi:undo-variant"></ha-icon>${this.host._t('backup.undo_import')}
|
||||
</button>` : nothing}
|
||||
</div>` : nothing}
|
||||
<label class="dispsection">${this.host._t('gs.grid_group')}</label>
|
||||
<div class="rhint">${this.host._t('gs.grid_hint')}</div>
|
||||
<div class="colorrow gsrow">
|
||||
<button class="btn ghost alignall" @click=${() => this._openAlignDialog()}>
|
||||
<ha-icon icon="mdi:broom"></ha-icon>${this.host._t('gs.align_all')}
|
||||
</button>
|
||||
</div>
|
||||
${this.host._canOptimizeUndo && this.host._undoKind !== 'import' ? html`<div class="colorrow gsrow">
|
||||
<button class="btn ghost alignall" @click=${() => this._undoPlanOptimization()}
|
||||
?disabled=${this.host._optimizeUndoBusy}>
|
||||
<ha-icon icon="mdi:undo-variant"></ha-icon>${this.host._t('gs.optimize_undo')}
|
||||
</button>
|
||||
</div>` : nothing}
|
||||
<ha-icon icon="mdi:undo-variant"></ha-icon>${this.host._t('gs.optimize_undo')}
|
||||
</button>
|
||||
</div>` : nothing}`,
|
||||
})}
|
||||
</div>
|
||||
<div class="row" slot="footer">
|
||||
<button class="btn ghost" @click=${() =>
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
*/
|
||||
import { html, nothing, type TemplateResult } from 'lit';
|
||||
|
||||
import { formCard } from './form-kit';
|
||||
|
||||
import { resolveDevicePresentation } from '../device-presentation';
|
||||
import { toggleEntityCandidates } from '../device-toggle';
|
||||
import { recommendedValueBadgeSource, valueBadgeCandidates, valueBadgeSourceFromKey, valueBadgeSourceKey } from '../device-value-badge';
|
||||
@@ -131,7 +133,10 @@ export function renderMarkerDialog(this: HouseplanEditorRuntime): TemplateResult
|
||||
return html`<hp-dialog id="marker-dialog" .hass=${this.host.hass} data-kind="marker"
|
||||
.title=${d.devId ? this.host._t('info.device_header') : this.host._t('marker.new_device')}
|
||||
icon="mdi:shape-plus" wide @hp-close=${() => this._closeMarkerDialog()}>
|
||||
<div class="body">
|
||||
<div class="body hpf-form">
|
||||
${formCard({
|
||||
title: this.host._t('marker.card_basics'),
|
||||
body: html`
|
||||
${bindingStatus?.kind === 'ha_disabled'
|
||||
? html`<div class="habindingbanner" role="status">
|
||||
<ha-icon icon="mdi:power-plug-off-outline"></ha-icon>
|
||||
@@ -260,7 +265,11 @@ export function renderMarkerDialog(this: HouseplanEditorRuntime): TemplateResult
|
||||
</select>
|
||||
|
||||
${this._renderRadarSection(d, previewDevice)}
|
||||
${this._renderVacSection(d)}
|
||||
${this._renderVacSection(d)}`,
|
||||
})}
|
||||
${formCard({
|
||||
title: this.host._t('marker.card_tap'),
|
||||
body: html`
|
||||
|
||||
<label>${this.host._t('marker.tap_label')}</label>
|
||||
<select id="marker-tap-action" class="areasel"
|
||||
@@ -388,7 +397,11 @@ export function renderMarkerDialog(this: HouseplanEditorRuntime): TemplateResult
|
||||
</button>`)}
|
||||
</div>`
|
||||
: nothing}
|
||||
|
||||
`,
|
||||
})}
|
||||
${formCard({
|
||||
title: this.host._t('marker.card_light'),
|
||||
body: html`
|
||||
${this.host._bindingHasClimate(d.binding)
|
||||
? html`<label class="srcrow climrow" title=${this.host._t('marker.use_climate_temp_tip')}>
|
||||
${this._boolInput(d.useClimateTemp, (v) => (this.host._markerDialog = { ...d, useClimateTemp: v }))}
|
||||
@@ -502,7 +515,11 @@ export function renderMarkerDialog(this: HouseplanEditorRuntime): TemplateResult
|
||||
<ha-icon icon="mdi:information-outline"></ha-icon>${glowDisabledHint}
|
||||
</p>`
|
||||
: nothing}
|
||||
|
||||
`,
|
||||
})}
|
||||
${formCard({
|
||||
title: this.host._t('marker.card_appearance'),
|
||||
body: html`
|
||||
<label>${this.host._t('marker.icon_label')}</label>
|
||||
${customElements.get('ha-icon-picker')
|
||||
// Feed the effective icon to HA's picker so its field renders both
|
||||
@@ -691,7 +708,11 @@ export function renderMarkerDialog(this: HouseplanEditorRuntime): TemplateResult
|
||||
line up with a wall that is not on a 10-degree grid. */}
|
||||
${this._rangeInput(0, 355, 5, d.angle, (n) => (this.host._markerDialog = { ...d, angle: n }))}
|
||||
<span class="opv">${d.angle}°</span>
|
||||
</div>
|
||||
</div>`,
|
||||
})}
|
||||
${formCard({
|
||||
title: this.host._t('marker.card_details'),
|
||||
body: html`
|
||||
|
||||
<label>${this.host._t('marker.model_label')}</label>
|
||||
<input class="namein" type="text" placeholder=${this.host._t('marker.model_ph')}
|
||||
@@ -724,7 +745,8 @@ export function renderMarkerDialog(this: HouseplanEditorRuntime): TemplateResult
|
||||
@change=${(e: Event) => this._pickMarkerFiles(e)} />
|
||||
</span>
|
||||
</div>
|
||||
${this._renderRadarSection(d, previewDevice, 'additional')}
|
||||
${this._renderRadarSection(d, previewDevice, 'additional')}`,
|
||||
})}
|
||||
</div>
|
||||
<div class="row markerfooter" slot="footer">
|
||||
<div class="markeractions">
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
*/
|
||||
import { html, nothing, type TemplateResult } from 'lit';
|
||||
|
||||
import { formCard, segmented } from './form-kit';
|
||||
|
||||
import { gridCellFieldToCm, gridCellFieldValue } from '../grid-scale';
|
||||
import { DEFAULT_CUSTOM_FILL, SPACE_FILL_UI_MODES, stageBgOf } from '../logic';
|
||||
import { openSpaceCopyDialog, renderSpaceCopyDialog } from '../space-copy-runtime';
|
||||
@@ -37,7 +39,10 @@ export function renderSpaceSettingsDialog(this: HouseplanEditorRuntime): Templat
|
||||
return html`<hp-dialog .hass=${this.host.hass} data-kind="space"
|
||||
.title=${`${d.mode === 'create' ? this.host._t('space.new') : this.host._t('space.header')}${progress ? ` · ${progress}` : ''}`}
|
||||
icon="mdi:floor-plan" wide @hp-close=${close}>
|
||||
<div class="body">
|
||||
<div class="body hpf-form">
|
||||
${formCard({
|
||||
title: this.host._t('space.card_basics'),
|
||||
body: html`
|
||||
<label>${this.host._t('space.title_label')}</label>
|
||||
<input class="namein" type="text" placeholder=${this.host._t('space.title_ph')}
|
||||
.value=${d.title}
|
||||
@@ -100,9 +105,11 @@ export function renderSpaceSettingsDialog(this: HouseplanEditorRuntime): Templat
|
||||
<span class="opl">${this.host._t(
|
||||
this.host._imperial ? 'space.scale_unit_imperial' : 'space.scale_unit',
|
||||
)}</span>
|
||||
</div>
|
||||
|
||||
<label class="dispsection">${this.host._t('space.display_section')}</label>
|
||||
</div>`,
|
||||
})}
|
||||
${formCard({
|
||||
title: this.host._t('space.display_section'),
|
||||
body: html`
|
||||
<label class="srcrow">
|
||||
${this._boolInput(d.showBorders, (v) => (this.host._spaceDialog = touchSpaceDisplay(d, 'showBorders', v)))}
|
||||
<span>${this.host._t('space.show_borders')}</span>
|
||||
@@ -140,14 +147,76 @@ export function renderSpaceSettingsDialog(this: HouseplanEditorRuntime): Templat
|
||||
<label class="srcrow">
|
||||
${this._boolInput(d.hideDecor, (v) => (this.host._spaceDialog = { ...d, hideDecor: v }))}
|
||||
<span>${this.host._t('space.hide_decor')}</span>
|
||||
${this._help('space.hide_decor.help')}
|
||||
</label>
|
||||
<div class="rhint">${this.host._t('space.hide_decor_tip')}</div>
|
||||
|
||||
<label class="srcrow">
|
||||
${this._boolInput(d.hideOpenings, (v) => (this.host._spaceDialog = { ...d, hideOpenings: v }))}
|
||||
<span>${this.host._t('space.hide_openings')}</span>
|
||||
${this._help('space.hide_openings.help')}
|
||||
</label>
|
||||
<div class="rhint">${this.host._t('space.hide_openings_tip')}</div>
|
||||
<label class="dispsection">${this.host._t('space.roomcard_section')}</label>
|
||||
|
||||
<div class="colorrow">
|
||||
<hp-color-opacity .label=${this.host._t('space.room_color')}
|
||||
.opacityLabel=${this.host._t('space.opacity')}
|
||||
.pickerLabels=${this.host._colorPickerLabels}
|
||||
.color=${d.roomColor} .opacity=${d.roomOpacity} .showOpacity=${true}
|
||||
@hp-color-opacity-change=${(e: CustomEvent<{ color: string; opacity: number }>) => {
|
||||
this.host._spaceDialog = {
|
||||
...d, roomColor: e.detail.color, roomOpacity: e.detail.opacity,
|
||||
};
|
||||
}}></hp-color-opacity>
|
||||
</div>
|
||||
<div class="helpfieldlabel">
|
||||
<span>${this.host._t('space.fill_label')}</span>
|
||||
${this._help('space.fill_mode.help')}
|
||||
</div>
|
||||
${SPACE_FILL_UI_MODES.map((v) => [v, 'fill.' + v] as const).map(
|
||||
([v, k]) => html`<label class="srcrow">
|
||||
<input type="radio" name="fillmode" .checked=${d.fillMode === v}
|
||||
@change=${() => (this.host._spaceDialog = { ...d, fillMode: v as any })} />
|
||||
<span>${this.host._t(k as any)}</span>
|
||||
${v === 'temp' && d.fillMode === 'temp'
|
||||
? html`<span class="temprange">
|
||||
<input class="namein tempin" type="number" step="0.5" .value=${String(d.tempMin)}
|
||||
@input=${(e: Event) => {
|
||||
const n = strictNumber((e.target as HTMLInputElement).value);
|
||||
if (n != null) this.host._spaceDialog = { ...d, tempMin: n };
|
||||
}} />
|
||||
–
|
||||
<input class="namein tempin" type="number" step="0.5" .value=${String(d.tempMax)}
|
||||
@input=${(e: Event) => {
|
||||
const n = strictNumber((e.target as HTMLInputElement).value);
|
||||
if (n != null) this.host._spaceDialog = { ...d, tempMax: n };
|
||||
}} />
|
||||
°C
|
||||
</span>`
|
||||
: nothing}
|
||||
</label>
|
||||
${v === 'custom' && d.fillMode === 'custom'
|
||||
? html`<div class="colorrow gsrow">
|
||||
<span class="gsl">${this.host._t('space.custom_fill')}</span>
|
||||
<hp-color-opacity
|
||||
.label=${this.host._t('space.custom_fill')}
|
||||
.opacityLabel=${this.host._t('space.opacity')}
|
||||
.pickerLabels=${this.host._colorPickerLabels}
|
||||
.color=${(d.customFill || DEFAULT_CUSTOM_FILL).c}
|
||||
.opacity=${(d.customFill || DEFAULT_CUSTOM_FILL).a}
|
||||
@hp-color-opacity-change=${(e: CustomEvent<{ color: string; opacity: number }>) => {
|
||||
this.host._spaceDialog = { ...d, customFill: { c: e.detail.color, a: e.detail.opacity } };
|
||||
}}></hp-color-opacity>
|
||||
${d.customFill
|
||||
? html`<button class="btn ghost" type="button"
|
||||
@click=${() => (this.host._spaceDialog = { ...d, customFill: null })}>
|
||||
${this.host._t('btn.reset')}</button>`
|
||||
: nothing}
|
||||
</div>`
|
||||
: nothing}`,
|
||||
)}`,
|
||||
})}
|
||||
${formCard({
|
||||
title: this.host._t('space.roomcard_section'),
|
||||
body: html`
|
||||
${([['labelTemp', 'space.label_temp'], ['labelHum', 'space.label_hum'],
|
||||
['labelLqi', 'space.label_lqi'], ['labelLight', 'space.label_light']] as const).map(
|
||||
([f, k]) => html`<label class="srcrow">
|
||||
@@ -160,18 +229,11 @@ export function renderSpaceSettingsDialog(this: HouseplanEditorRuntime): Templat
|
||||
${this._rangeInput(50, 300, 5, Math.round(d.cardFontScale * 100), (n) => (this.host._spaceDialog = { ...d, cardFontScale: n / 100 }))}
|
||||
<span class="opv">${Math.round(d.cardFontScale * 100)}%</span>
|
||||
</div>
|
||||
${this.host._renderCardPreview(d.cardFontScale, 1, 1)}
|
||||
<div class="colorrow">
|
||||
<hp-color-opacity .label=${this.host._t('space.room_color')}
|
||||
.opacityLabel=${this.host._t('space.opacity')}
|
||||
.pickerLabels=${this.host._colorPickerLabels}
|
||||
.color=${d.roomColor} .opacity=${d.roomOpacity} .showOpacity=${true}
|
||||
@hp-color-opacity-change=${(e: CustomEvent<{ color: string; opacity: number }>) => {
|
||||
this.host._spaceDialog = {
|
||||
...d, roomColor: e.detail.color, roomOpacity: e.detail.opacity,
|
||||
};
|
||||
}}></hp-color-opacity>
|
||||
</div>
|
||||
${this.host._renderCardPreview(d.cardFontScale, 1, 1)}`,
|
||||
})}
|
||||
${formCard({
|
||||
title: this.host._t('space.card_sun'),
|
||||
body: html`
|
||||
<div class="helpfieldlabel">
|
||||
<label for="space-bg-mode">${this.host._t('space.bg_mode')}</label>
|
||||
${this._help('space.bg_mode.help')}
|
||||
@@ -229,58 +291,13 @@ export function renderSpaceSettingsDialog(this: HouseplanEditorRuntime): Templat
|
||||
<option value="1" ?selected=${d.sunRays === true}>${this.host._t('space.sun_on')}</option>
|
||||
<option value="0" ?selected=${d.sunRays === false}>${this.host._t('space.sun_off')}</option>
|
||||
</select>
|
||||
<div class="helpfieldlabel">
|
||||
<span>${this.host._t('space.fill_label')}</span>
|
||||
${this._help('space.fill_mode.help')}
|
||||
</div>
|
||||
${SPACE_FILL_UI_MODES.map((v) => [v, 'fill.' + v] as const).map(
|
||||
([v, k]) => html`<label class="srcrow">
|
||||
<input type="radio" name="fillmode" .checked=${d.fillMode === v}
|
||||
@change=${() => (this.host._spaceDialog = { ...d, fillMode: v as any })} />
|
||||
<span>${this.host._t(k as any)}</span>
|
||||
${v === 'temp' && d.fillMode === 'temp'
|
||||
? html`<span class="temprange">
|
||||
<input class="namein tempin" type="number" step="0.5" .value=${String(d.tempMin)}
|
||||
@input=${(e: Event) => {
|
||||
const n = strictNumber((e.target as HTMLInputElement).value);
|
||||
if (n != null) this.host._spaceDialog = { ...d, tempMin: n };
|
||||
}} />
|
||||
–
|
||||
<input class="namein tempin" type="number" step="0.5" .value=${String(d.tempMax)}
|
||||
@input=${(e: Event) => {
|
||||
const n = strictNumber((e.target as HTMLInputElement).value);
|
||||
if (n != null) this.host._spaceDialog = { ...d, tempMax: n };
|
||||
}} />
|
||||
°C
|
||||
</span>`
|
||||
: nothing}
|
||||
</label>
|
||||
${v === 'custom' && d.fillMode === 'custom'
|
||||
? html`<div class="colorrow gsrow">
|
||||
<span class="gsl">${this.host._t('space.custom_fill')}</span>
|
||||
<hp-color-opacity
|
||||
.label=${this.host._t('space.custom_fill')}
|
||||
.opacityLabel=${this.host._t('space.opacity')}
|
||||
.pickerLabels=${this.host._colorPickerLabels}
|
||||
.color=${(d.customFill || DEFAULT_CUSTOM_FILL).c}
|
||||
.opacity=${(d.customFill || DEFAULT_CUSTOM_FILL).a}
|
||||
@hp-color-opacity-change=${(e: CustomEvent<{ color: string; opacity: number }>) => {
|
||||
this.host._spaceDialog = { ...d, customFill: { c: e.detail.color, a: e.detail.opacity } };
|
||||
}}></hp-color-opacity>
|
||||
${d.customFill
|
||||
? html`<button class="btn ghost" type="button"
|
||||
@click=${() => (this.host._spaceDialog = { ...d, customFill: null })}>
|
||||
${this.host._t('btn.reset')}</button>`
|
||||
: nothing}
|
||||
</div>`
|
||||
: nothing}`,
|
||||
)}
|
||||
<label class="srcrow">
|
||||
${this._boolInput(d.glowEnabled, (checked) => {
|
||||
this.host._spaceDialog = { ...d, glowEnabled: checked };
|
||||
})}
|
||||
<span>${this.host._t('space.glow_enabled')}</span>
|
||||
</label>
|
||||
</label>`,
|
||||
})}
|
||||
${d.deleteBlockers
|
||||
? html`<div class="backuperror" role="alert">${this.host._t('space.delete_blocked', {
|
||||
n: String(d.deleteBlockers),
|
||||
|
||||
@@ -745,14 +745,14 @@ export class HouseplanOnboardingRuntime {
|
||||
})}
|
||||
<span>${this.host._t('space.hide_decor')}</span>
|
||||
</label>
|
||||
<div class="rhint">${this.host._t('space.hide_decor_tip')}</div>
|
||||
<div class="rhint">${this.host._t('space.hide_decor.help')}</div>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(dialog.hideOpenings, (value) => {
|
||||
this.host._spaceDialog = { ...dialog, hideOpenings: value };
|
||||
})}
|
||||
<span>${this.host._t('space.hide_openings')}</span>
|
||||
</label>
|
||||
<div class="rhint">${this.host._t('space.hide_openings_tip')}</div>
|
||||
<div class="rhint">${this.host._t('space.hide_openings.help')}</div>
|
||||
<label class="dispsection">${this.host._t('space.roomcard_section')}</label>
|
||||
${([
|
||||
['labelTemp', 'space.label_temp'], ['labelHum', 'space.label_hum'],
|
||||
|
||||
+25
-6
@@ -469,7 +469,6 @@
|
||||
"gs.reset": "Auf Standard zurücksetzen",
|
||||
"gs.saved": "Allgemeine Einstellungen gespeichert",
|
||||
"gs.radar_show_live": "Live-Präsenz auf dem Plan anzeigen",
|
||||
"gs.radar_show_live_hint": "Blendet alle Live-Radarziele ein oder aus. Eine Beobachtungsaufzeichnung wird dadurch weder gestartet noch beendet.",
|
||||
"radar.title": "Präsenz auf dem Plan",
|
||||
"radar.help": "Verbinde diese Markierung mit exakten Home-Assistant-Radarentitäten und wähle Position sowie Koordinatenprofil. Live-Positionen werden auf den ausgewählten Raum begrenzt.",
|
||||
"radar.help.aria": "Hilfe: Einstellungen des Präsenzradars",
|
||||
@@ -576,9 +575,7 @@
|
||||
"radar.report_age": "vor {seconds} s",
|
||||
"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",
|
||||
@@ -1032,7 +1029,6 @@
|
||||
"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.",
|
||||
@@ -1254,7 +1250,6 @@
|
||||
"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",
|
||||
@@ -1384,5 +1379,29 @@
|
||||
"backdrop.keep_original": "Original behalten",
|
||||
"backdrop.too_large_title": "Bild ist zu groß",
|
||||
"backdrop.too_large_body": "Dieses Bild überschreitet die Anzeigegrenzen des Browsers ({w}×{h}, Limit {limit} px pro Seite). Bitte verkleinern Sie es in einem Desktop-Editor und laden Sie es erneut hoch.",
|
||||
"backdrop.downscale_failed": "Die verkleinerte Kopie konnte nicht erstellt werden. Der aktuelle Plan wurde nicht geändert."
|
||||
"backdrop.downscale_failed": "Die verkleinerte Kopie konnte nicht erstellt werden. Der aktuelle Plan wurde nicht geändert.",
|
||||
"gs.card_display": "Anzeige",
|
||||
"gs.card_zigbee": "Zigbee-Verbindungen",
|
||||
"gs.card_fills": "Füllfarben der Räume",
|
||||
"gs.card_plan": "Plan",
|
||||
"gs.card_data": "Daten",
|
||||
"gs.radar_show_live.help.aria": "Über die Live-Radaranwesenheit",
|
||||
"gs.card_fills.help.aria": "Über die Füllfarben der Räume",
|
||||
"gs.backup_group.help.aria": "Über Sicherung und Übertragung",
|
||||
"gs.grid_group.help.aria": "Über die Planwartung",
|
||||
"gs.radar_show_live.help": "Blendet alle Live-Radarziele ein oder aus. Eine Beobachtungsaufzeichnung wird dadurch weder gestartet noch beendet.",
|
||||
"gs.backup_group.help": "Laden Sie ein tragbares JSON-Backup herunter oder sehen Sie eine Vorschau und importieren Sie ein Backup, das mit House Plan erstellt wurde.",
|
||||
"gs.grid_group.help": "Aktualisiert Datenmodelle, richtet Planelemente am Raster aus und fasst redundante Wandfragmente zusammen. Ein exakter Bericht wird angezeigt, bevor etwas gespeichert wird.",
|
||||
"gs.card_fills.help": "Füllfarben gelten für jeden Bereich; jede Farbe hat ihre eigene Deckkraft. Der Füllmodus wird in den Einstellungen des jeweiligen Bereichs festgelegt.",
|
||||
"space.card_basics": "Allgemein",
|
||||
"space.card_sun": "Sonne und Licht",
|
||||
"space.hide_decor.help.aria": "Über das Ausblenden der Dekorschicht",
|
||||
"space.hide_openings.help.aria": "Über das Ausblenden der Öffnungen",
|
||||
"space.hide_decor.help": "Linien, Formen, Beschriftungen und Möbel bleiben an ihrem Platz — im Hintergrundeditor sichtbar, aber nicht auf dem Plan.",
|
||||
"space.hide_openings.help": "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.",
|
||||
"marker.card_basics": "Allgemein",
|
||||
"marker.card_tap": "Tippaktion",
|
||||
"marker.card_light": "Licht und Schein",
|
||||
"marker.card_appearance": "Aussehen",
|
||||
"marker.card_details": "Details"
|
||||
}
|
||||
|
||||
+25
-6
@@ -469,7 +469,6 @@
|
||||
"gs.reset": "Reset to defaults",
|
||||
"gs.saved": "General settings saved",
|
||||
"gs.radar_show_live": "Show live presence on the plan",
|
||||
"gs.radar_show_live_hint": "Hides or shows all live radar targets. This does not start or stop observation recording.",
|
||||
"radar.title": "Presence on the plan",
|
||||
"radar.help": "Connect this marker to exact Home Assistant radar entities, place the sensor and choose its coordinate profile. Live positions are clipped to the selected room.",
|
||||
"radar.help.aria": "Help: presence radar settings",
|
||||
@@ -576,9 +575,7 @@
|
||||
"radar.report_age": "{seconds} s ago",
|
||||
"space.show_lqi": "Show zigbee signal (LQI) next to devices",
|
||||
"space.hide_decor": "Hide the decorative layer",
|
||||
"space.hide_decor_tip": "Lines, shapes, labels and furniture stay where they are — visible in the backdrop editor, not on the plan.",
|
||||
"space.hide_openings": "Hide openings",
|
||||
"space.hide_openings_tip": "Door, window and gate symbols are not drawn, but the openings keep working: light passes through, the sun comes in at a window, contact sensors still open them. The plan editor always shows them.",
|
||||
"gs.light_none": "No light sources",
|
||||
"mode.plan": "Plan editor",
|
||||
"mode.devices": "Device editor",
|
||||
@@ -1032,7 +1029,6 @@
|
||||
"canvas.show_far": "Show",
|
||||
"canvas.home_tip": "The plan is over there — click to fit it",
|
||||
"gs.grid_group": "Plan maintenance",
|
||||
"gs.grid_hint": "Updates data models, aligns plan elements to the grid and merges redundant wall fragments. An exact report is shown before anything is stored.",
|
||||
"gs.align_all": "Optimize plans",
|
||||
"gs.align_title": "Optimize plans",
|
||||
"gs.align_none": "All plans already use the current optimized data model.",
|
||||
@@ -1254,7 +1250,6 @@
|
||||
"vac.residual_message": "The matched rooms disagree by up to {error}. Apply this approximate calibration, refine it manually, or cancel without changing the saved setup.",
|
||||
"vac.apply_proposal": "Apply",
|
||||
"gs.backup_group": "Backup and transfer",
|
||||
"gs.backup_hint": "Download a portable JSON backup or preview and import a backup made by House Plan.",
|
||||
"backup.export_open": "Export",
|
||||
"backup.import_open": "Import",
|
||||
"backup.export_title": "Export House Plan",
|
||||
@@ -1384,5 +1379,29 @@
|
||||
"backdrop.keep_original": "Keep the original",
|
||||
"backdrop.too_large_title": "Image is too large",
|
||||
"backdrop.too_large_body": "This image exceeds what browsers can display ({w}×{h}, limit {limit} px per side). Please reduce it in a desktop editor and upload again.",
|
||||
"backdrop.downscale_failed": "Could not create the reduced copy. The original plan was not changed."
|
||||
"backdrop.downscale_failed": "Could not create the reduced copy. The original plan was not changed.",
|
||||
"gs.card_display": "Display",
|
||||
"gs.card_zigbee": "Zigbee links",
|
||||
"gs.card_fills": "Room fill colors",
|
||||
"gs.card_plan": "Plan",
|
||||
"gs.card_data": "Data",
|
||||
"gs.radar_show_live.help.aria": "About live radar presence",
|
||||
"gs.card_fills.help.aria": "About room fill colors",
|
||||
"gs.backup_group.help.aria": "About backup and transfer",
|
||||
"gs.grid_group.help.aria": "About plan maintenance",
|
||||
"gs.radar_show_live.help": "Hides or shows all live radar targets. This does not start or stop observation recording.",
|
||||
"gs.backup_group.help": "Download a portable JSON backup or preview and import a backup made by House Plan.",
|
||||
"gs.grid_group.help": "Updates data models, aligns plan elements to the grid and merges redundant wall fragments. An exact report is shown before anything is stored.",
|
||||
"gs.card_fills.help": "Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.",
|
||||
"space.card_basics": "Basics",
|
||||
"space.card_sun": "Sun & light",
|
||||
"space.hide_decor.help.aria": "About hiding the decorative layer",
|
||||
"space.hide_openings.help.aria": "About hiding openings",
|
||||
"space.hide_decor.help": "Lines, shapes, labels and furniture stay where they are — visible in the backdrop editor, not on the plan.",
|
||||
"space.hide_openings.help": "Door, window and gate symbols are not drawn, but the openings keep working: light passes through, the sun comes in at a window, contact sensors still open them. The plan editor always shows them.",
|
||||
"marker.card_basics": "Basics",
|
||||
"marker.card_tap": "Tap action",
|
||||
"marker.card_light": "Light and glow",
|
||||
"marker.card_appearance": "Appearance",
|
||||
"marker.card_details": "Details"
|
||||
}
|
||||
|
||||
+25
-6
@@ -469,7 +469,6 @@
|
||||
"gs.reset": "Réinitialiser par défaut",
|
||||
"gs.saved": "Paramètres généraux enregistrés",
|
||||
"gs.radar_show_live": "Afficher la présence en direct sur le plan",
|
||||
"gs.radar_show_live_hint": "Masque ou affiche toutes les cibles radar en direct. Cela ne démarre ni n’arrête l’enregistrement des observations.",
|
||||
"radar.title": "Présence sur le plan",
|
||||
"radar.help": "Reliez ce marqueur aux entités radar exactes de Home Assistant, placez le capteur et choisissez son profil de coordonnées. Les positions en direct sont limitées à la pièce sélectionnée.",
|
||||
"radar.help.aria": "Aide : paramètres du radar de présence",
|
||||
@@ -576,9 +575,7 @@
|
||||
"radar.report_age": "il y a {seconds} s",
|
||||
"space.show_lqi": "Afficher le signal Zigbee (LQI) à côté des appareils",
|
||||
"space.hide_decor": "Masquer la couche décorative",
|
||||
"space.hide_decor_tip": "Les lignes, formes, étiquettes et meubles restent à leur place — visibles dans l’éditeur d’arrière-plan, mais pas sur le plan.",
|
||||
"space.hide_openings": "Masquer les ouvertures",
|
||||
"space.hide_openings_tip": "Les symboles de portes, fenêtres et portails ne sont pas dessinés, mais les ouvertures continuent de fonctionner : la lumière les traverse, le soleil entre par les fenêtres et les capteurs de contact continuent de les ouvrir. L’éditeur de plan les affiche toujours.",
|
||||
"gs.light_none": "Aucune source lumineuse",
|
||||
"mode.plan": "Éditeur de plan",
|
||||
"mode.devices": "Éditeur d’appareils",
|
||||
@@ -1032,7 +1029,6 @@
|
||||
"canvas.show_far": "Afficher",
|
||||
"canvas.home_tip": "Le plan est là-bas — cliquez pour l’ajuster à l’écran",
|
||||
"gs.grid_group": "Maintenance du plan",
|
||||
"gs.grid_hint": "Met à jour les modèles de données, aligne les éléments du plan sur la grille et fusionne les fragments de murs redondants. Un rapport précis est affiché avant tout enregistrement.",
|
||||
"gs.align_all": "Optimiser les plans",
|
||||
"gs.align_title": "Optimiser les plans",
|
||||
"gs.align_none": "Tous les plans utilisent déjà le modèle de données optimisé actuel.",
|
||||
@@ -1254,7 +1250,6 @@
|
||||
"vac.residual_message": "Les pièces correspondantes présentent un écart maximal de {error}. Appliquez cette calibration approximative, affinez-la manuellement ou annulez sans modifier la configuration enregistrée.",
|
||||
"vac.apply_proposal": "Appliquer",
|
||||
"gs.backup_group": "Sauvegarde et transfert",
|
||||
"gs.backup_hint": "Téléchargez une sauvegarde JSON portable ou prévisualisez et importez une sauvegarde créée par House Plan.",
|
||||
"backup.export_open": "Exporter",
|
||||
"backup.import_open": "Importer",
|
||||
"backup.export_title": "Exporter House Plan",
|
||||
@@ -1384,5 +1379,29 @@
|
||||
"backdrop.keep_original": "Conserver l'original",
|
||||
"backdrop.too_large_title": "Image trop grande",
|
||||
"backdrop.too_large_body": "Cette image dépasse ce que les navigateurs peuvent afficher ({w}×{h}, limite {limit} px par côté). Réduisez-la dans un éditeur de bureau puis téléversez-la à nouveau.",
|
||||
"backdrop.downscale_failed": "Impossible de créer la copie réduite. Le plan actuel n'a pas été modifié."
|
||||
"backdrop.downscale_failed": "Impossible de créer la copie réduite. Le plan actuel n'a pas été modifié.",
|
||||
"gs.card_display": "Affichage",
|
||||
"gs.card_zigbee": "Liens Zigbee",
|
||||
"gs.card_fills": "Couleurs de remplissage des pièces",
|
||||
"gs.card_plan": "Plan",
|
||||
"gs.card_data": "Données",
|
||||
"gs.radar_show_live.help.aria": "À propos de la présence radar en direct",
|
||||
"gs.card_fills.help.aria": "À propos des couleurs de remplissage",
|
||||
"gs.backup_group.help.aria": "À propos de la sauvegarde et du transfert",
|
||||
"gs.grid_group.help.aria": "À propos de la maintenance du plan",
|
||||
"gs.radar_show_live.help": "Masque ou affiche toutes les cibles radar en direct. Cela ne démarre ni n’arrête l’enregistrement des observations.",
|
||||
"gs.backup_group.help": "Téléchargez une sauvegarde JSON portable ou prévisualisez et importez une sauvegarde créée par House Plan.",
|
||||
"gs.grid_group.help": "Met à jour les modèles de données, aligne les éléments du plan sur la grille et fusionne les fragments de murs redondants. Un rapport précis est affiché avant tout enregistrement.",
|
||||
"gs.card_fills.help": "Les couleurs de remplissage s’appliquent à tous les espaces ; chaque couleur possède sa propre opacité. Le mode de remplissage utilisé par un espace se règle dans la boîte de dialogue de cet espace.",
|
||||
"space.card_basics": "Général",
|
||||
"space.card_sun": "Soleil et lumière",
|
||||
"space.hide_decor.help.aria": "À propos du masquage de la couche décorative",
|
||||
"space.hide_openings.help.aria": "À propos du masquage des ouvertures",
|
||||
"space.hide_decor.help": "Les lignes, formes, étiquettes et meubles restent à leur place — visibles dans l’éditeur d’arrière-plan, mais pas sur le plan.",
|
||||
"space.hide_openings.help": "Les symboles de portes, fenêtres et portails ne sont pas dessinés, mais les ouvertures continuent de fonctionner : la lumière les traverse, le soleil entre par les fenêtres et les capteurs de contact continuent de les ouvrir. L’éditeur de plan les affiche toujours.",
|
||||
"marker.card_basics": "Général",
|
||||
"marker.card_tap": "Action au toucher",
|
||||
"marker.card_light": "Lumière et halo",
|
||||
"marker.card_appearance": "Apparence",
|
||||
"marker.card_details": "Détails"
|
||||
}
|
||||
|
||||
+25
-6
@@ -469,7 +469,6 @@
|
||||
"gs.reset": "Сбросить к умолчаниям",
|
||||
"gs.saved": "Общие настройки сохранены",
|
||||
"gs.radar_show_live": "Показывать текущее присутствие на плане",
|
||||
"gs.radar_show_live_hint": "Скрывает или показывает все живые цели радаров. Это не запускает и не останавливает запись наблюдений.",
|
||||
"radar.title": "Присутствие на плане",
|
||||
"radar.help": "Свяжите маркер с точными сущностями радара Home Assistant, укажите положение датчика и профиль координат. Живые позиции обрезаются по выбранной комнате.",
|
||||
"radar.help.aria": "Подсказка: настройки радара присутствия",
|
||||
@@ -576,9 +575,7 @@
|
||||
"radar.report_age": "{seconds} с назад",
|
||||
"space.show_lqi": "Показывать зигби-сигнал (LQI) у устройств",
|
||||
"space.hide_decor": "Скрыть декоративный слой",
|
||||
"space.hide_decor_tip": "Линии, фигуры, надписи и мебель остаются на месте — их видно в редакторе подложки, но не на плане.",
|
||||
"space.hide_openings": "Скрыть проёмы",
|
||||
"space.hide_openings_tip": "Двери, окна и ворота не рисуются, но продолжают работать: свет проходит, солнце светит в окна, датчики открытия срабатывают. В редакторе плана проёмы видно всегда.",
|
||||
"gs.light_none": "Нет источников света",
|
||||
"mode.plan": "Редактор плана",
|
||||
"mode.devices": "Редактор устройств",
|
||||
@@ -1032,7 +1029,6 @@
|
||||
"canvas.show_far": "Показать",
|
||||
"canvas.home_tip": "План там — нажмите, чтобы вписать",
|
||||
"gs.grid_group": "Обслуживание планов",
|
||||
"gs.grid_hint": "Обновляет модели данных, выравнивает элементы по сетке и объединяет лишние фрагменты стен. Перед записью будет показан точный отчёт.",
|
||||
"gs.align_all": "Оптимизировать планы",
|
||||
"gs.align_title": "Оптимизировать планы",
|
||||
"gs.align_none": "Все планы уже используют актуальную и оптимальную модель данных.",
|
||||
@@ -1254,7 +1250,6 @@
|
||||
"vac.residual_message": "Совпавшие комнаты расходятся максимум на {error}. Примените приблизительную калибровку, подгоните её вручную либо отмените без изменения сохранённых настроек.",
|
||||
"vac.apply_proposal": "Применить",
|
||||
"gs.backup_group": "Резервная копия и перенос",
|
||||
"gs.backup_hint": "Скачайте переносимую JSON-копию или проверьте и импортируйте копию, созданную House Plan.",
|
||||
"backup.export_open": "Экспорт",
|
||||
"backup.import_open": "Импорт",
|
||||
"backup.export_title": "Экспорт House Plan",
|
||||
@@ -1384,5 +1379,29 @@
|
||||
"backdrop.keep_original": "Оставить оригинал",
|
||||
"backdrop.too_large_title": "Изображение слишком большое",
|
||||
"backdrop.too_large_body": "Изображение превышает возможности браузера ({w}×{h}, предел {limit} px по стороне). Уменьшите его в редакторе на компьютере и загрузите снова.",
|
||||
"backdrop.downscale_failed": "Не удалось создать уменьшенную копию. Текущий план не изменён."
|
||||
"backdrop.downscale_failed": "Не удалось создать уменьшенную копию. Текущий план не изменён.",
|
||||
"gs.card_display": "Отображение",
|
||||
"gs.card_zigbee": "Связи Zigbee",
|
||||
"gs.card_fills": "Цвета заливки комнат",
|
||||
"gs.card_plan": "План",
|
||||
"gs.card_data": "Данные",
|
||||
"gs.radar_show_live.help.aria": "О живом присутствии радаров",
|
||||
"gs.card_fills.help.aria": "О цветах заливки комнат",
|
||||
"gs.backup_group.help.aria": "О резервной копии и переносе",
|
||||
"gs.grid_group.help.aria": "Об обслуживании плана",
|
||||
"gs.radar_show_live.help": "Скрывает или показывает все живые цели радаров. Это не запускает и не останавливает запись наблюдений.",
|
||||
"gs.backup_group.help": "Скачайте переносимую JSON-копию или проверьте и импортируйте копию, созданную House Plan.",
|
||||
"gs.grid_group.help": "Обновляет модели данных, выравнивает элементы по сетке и объединяет лишние фрагменты стен. Перед записью будет показан точный отчёт.",
|
||||
"gs.card_fills.help": "Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.",
|
||||
"space.card_basics": "Основное",
|
||||
"space.card_sun": "Солнце и свет",
|
||||
"space.hide_decor.help.aria": "О скрытии декоративного слоя",
|
||||
"space.hide_openings.help.aria": "О скрытии проёмов",
|
||||
"space.hide_decor.help": "Линии, фигуры, надписи и мебель остаются на месте — их видно в редакторе подложки, но не на плане.",
|
||||
"space.hide_openings.help": "Двери, окна и ворота не рисуются, но продолжают работать: свет проходит, солнце светит в окна, датчики открытия срабатывают. В редакторе плана проёмы видно всегда.",
|
||||
"marker.card_basics": "Основное",
|
||||
"marker.card_tap": "Действие по нажатию",
|
||||
"marker.card_light": "Свет и свечение",
|
||||
"marker.card_appearance": "Внешний вид",
|
||||
"marker.card_details": "Подробности"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"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.show_room_tooltip": "Rauminformationen beim Darüberfahren anzeigen",
|
||||
"support.about_group": "Über die Karte",
|
||||
"support.guide_group": "Dokumentation",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"gs.hint": "Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.",
|
||||
"gs.show_room_tooltip": "Show the room information window on hover",
|
||||
"support.about_group": "About the card",
|
||||
"support.guide_group": "Documentation",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"gs.hint": "Les couleurs de remplissage s’appliquent à tous les espaces ; chaque couleur possède sa propre opacité. Le mode de remplissage utilisé par un espace se règle dans la boîte de dialogue de cet espace.",
|
||||
"gs.show_room_tooltip": "Afficher les informations de la pièce au survol",
|
||||
"support.about_group": "À propos de la carte",
|
||||
"support.guide_group": "Documentation d’aide",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"gs.hint": "Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.",
|
||||
"gs.show_room_tooltip": "Показывать окно с информацией при наведении на комнату",
|
||||
"support.about_group": "О карточке",
|
||||
"support.guide_group": "Документация",
|
||||
|
||||
Reference in New Issue
Block a user