v0.8.17
This commit is contained in:
+2
-1
@@ -437,7 +437,8 @@
|
||||
<div class="profile-grid"><strong data-i18n="mode.cool">Cooling</strong><label><span data-i18n="preset.comfort">Comfort</span><input type="text" inputmode="decimal" step="0.5" name="cool_comfort_setpoint" value="23"></label><label><span data-i18n="preset.sleep">Sleep</span><input type="text" inputmode="decimal" step="0.5" name="cool_sleep_setpoint" value="24.5"></label><label><span data-i18n="preset.away">Away</span><input type="text" inputmode="decimal" step="0.5" name="cool_away_setpoint" value="27"></label></div>
|
||||
<div class="profile-grid"><strong data-i18n="mode.heat">Heating</strong><label><span data-i18n="preset.comfort">Comfort</span><input type="text" inputmode="decimal" step="0.5" name="heat_comfort_setpoint" value="21"></label><label><span data-i18n="preset.sleep">Sleep</span><input type="text" inputmode="decimal" step="0.5" name="heat_sleep_setpoint" value="19"></label><label><span data-i18n="preset.away">Away</span><input type="text" inputmode="decimal" step="0.5" name="heat_away_setpoint" value="17"></label></div>
|
||||
</details>
|
||||
<div class="two"><label><span data-i18n="zones.hysteresis">Hysteresis °C</span><input type="text" inputmode="decimal" step="0.1" min="0.1" max="5" name="hysteresis" value="0.6"></label><label><span data-i18n="zones.source">Temperature strategy</span><select name="sensor_source"><option value="combined" selected data-i18n="zones.combinedSensor">GREE + room sensor</option><option value="device" data-i18n="zones.greeSensor">GREE only</option><option value="home_assistant" data-i18n="zones.externalSensor">Room sensor only</option></select></label></div>
|
||||
<div class="two"><div class="hysteresis-config"><label id="commonHysteresisField"><span data-i18n="zones.hysteresis">Hysteresis °C</span><input type="text" inputmode="decimal" step="0.1" min="0.1" max="5" name="hysteresis" value="0.6"></label><label class="check"><input type="checkbox" name="separate_hysteresis"> <span data-i18n="zones.separateHysteresis">Set heating and cooling hysteresis separately</span></label></div><label><span data-i18n="zones.source">Temperature strategy</span><select name="sensor_source"><option value="combined" selected data-i18n="zones.combinedSensor">GREE + room sensor</option><option value="device" data-i18n="zones.greeSensor">GREE only</option><option value="home_assistant" data-i18n="zones.externalSensor">Room sensor only</option></select></label></div>
|
||||
<div id="separateHysteresisFields" class="two" hidden><label><span data-i18n="zones.coolHysteresis">Cooling hysteresis °C</span><input type="text" inputmode="decimal" step="0.1" min="0.1" max="5" name="cool_hysteresis" value="0.6"></label><label><span data-i18n="zones.heatHysteresis">Heating hysteresis °C</span><input type="text" inputmode="decimal" step="0.1" min="0.1" max="5" name="heat_hysteresis" value="0.6"></label></div>
|
||||
<div id="externalSensorFields">
|
||||
<label><span data-i18n="zones.roomSensorEntity">Room sensor entity_id</span><input name="ha_entity_id" placeholder="sensor.living_room_temperature"></label>
|
||||
<div class="two"><label><span data-i18n="zones.roomSensorWeight">Room sensor weight %</span><input type="number" step="5" min="0" max="100" name="external_sensor_weight_percent" value="40"></label><label><span data-i18n="zones.maxDifference">Max. sensor difference °C</span><input type="text" inputmode="decimal" step="0.1" min="0.1" max="20" name="max_sensor_difference" value="3.0"></label></div>
|
||||
|
||||
@@ -47,6 +47,12 @@ const tr = (key, params = {}) => {
|
||||
return String(template).replace(/\{([a-zA-Z0-9_]+)\}/g, (_, name) => params[name] ?? `{${name}}`);
|
||||
};
|
||||
const fmtTemp = value => value !== null && value !== undefined && value !== '' && Number.isFinite(Number(value)) ? `${Number(value).toFixed(1)}°C` : '--';
|
||||
const zoneHysteresisForMode = (zone, mode) => {
|
||||
const common = Number(zone?.hysteresis ?? 0.6);
|
||||
if (!zone?.separate_hysteresis) return Number.isFinite(common) ? common : 0.6;
|
||||
const value = Number(mode === 'heat' ? zone?.heat_hysteresis : zone?.cool_hysteresis);
|
||||
return Number.isFinite(value) ? value : (Number.isFinite(common) ? common : 0.6);
|
||||
};
|
||||
const historyNumber = value => value === null || value === undefined || value === '' ? NaN : Number(value);
|
||||
const modeLabel = mode => tr(`mode.${mode}`) === `mode.${mode}` ? mode : tr(`mode.${mode}`);
|
||||
const houseModeLabel = mode => mode === 'off' ? tr('house.noControl') : modeLabel(mode);
|
||||
|
||||
+1
-1
@@ -175,7 +175,7 @@ function buildZoneSimulation(zone, planZone) {
|
||||
const current = simulationNumeric(planZone?.current_temperature ?? zone?.current_temperature ?? zone?.device_temperature ?? device?.current_temperature);
|
||||
const target = mode === 'off' ? null : simulationNumeric(planZone?.target_temperature ?? zone?.effective_setpoint ?? zone?.manual_setpoint ?? zone?.setpoint);
|
||||
const outdoor = simulationNumeric(app.outdoorTemperature ?? app.controlPlan?.outdoor_temperature);
|
||||
const hysteresis = Math.max(simulationNumeric(zone?.hysteresis) || 0.4, 0.1);
|
||||
const hysteresis = Math.max(simulationNumeric(zoneHysteresisForMode(zone, mode)) || 0.4, 0.1);
|
||||
const half = hysteresis / 2;
|
||||
let status = 'waiting';
|
||||
let demand = false;
|
||||
|
||||
+3
-3
@@ -32,7 +32,7 @@ function deviceCard(device, detailed = false) {
|
||||
</div>
|
||||
<div class="temperature-control">
|
||||
<button data-action="temperature" data-delta="-1" data-device="${esc(device.id)}"${locked}>−</button>
|
||||
<div class="target-temp">${Number(device.target_temperature).toFixed(1)}<small>°C</small></div>
|
||||
<div class="target-temp editable-target" data-temperature-kind="device" data-id="${esc(device.id)}" data-value="${Number(device.target_temperature)}" data-editable="${zoneLocked ? 'false' : 'true'}" tabindex="${zoneLocked ? '-1' : '0'}" role="button" title="${esc(tr('common.clickTemperatureToEdit'))}">${Number(device.target_temperature).toFixed(1)}<small>°C</small></div>
|
||||
<button data-action="temperature" data-delta="1" data-device="${esc(device.id)}"${locked}>+</button>
|
||||
</div>
|
||||
<div class="current-line">${esc(tr('devices.currentTemperature'))}: <strong>${fmtTemp(device.current_temperature)}</strong>${device.outdoor_temperature == null ? '' : ` · ${esc(tr('devices.outdoor'))} ${fmtTemp(device.outdoor_temperature)}`}</div>
|
||||
@@ -168,7 +168,7 @@ function zoneCard(zone, detailed = true) {
|
||||
<div class="zone-config-grid">
|
||||
<div><small>${esc(tr('zones.modePolicy'))}</small><strong>${esc(policy)}</strong></div>
|
||||
<div><small>${esc(tr('zones.source'))}</small><strong>${esc(zoneStrategyLabel(zone))}</strong></div>
|
||||
<div><small>${esc(tr('zones.hysteresis'))}</small><strong>${Number(zone.hysteresis ?? 0.6).toFixed(1)}°C</strong></div>
|
||||
<div><small>${esc(tr('zones.hysteresis'))}</small><strong>${zone.separate_hysteresis ? `${esc(tr('mode.cool'))} ${Number(zone.cool_hysteresis ?? zone.hysteresis ?? 0.6).toFixed(1)}°C · ${esc(tr('mode.heat'))} ${Number(zone.heat_hysteresis ?? zone.hysteresis ?? 0.6).toFixed(1)}°C` : `${Number(zone.hysteresis ?? 0.6).toFixed(1)}°C`}</strong></div>
|
||||
<div><small>${esc(tr('zones.smartFan'))}</small><strong>${esc(tr(zone.smart_fan === false ? 'common.off' : 'common.on'))}</strong></div>
|
||||
</div>
|
||||
<div class="sensor-detail">${esc(sensorDetails)}</div>
|
||||
@@ -179,7 +179,7 @@ function zoneCard(zone, detailed = true) {
|
||||
|
||||
return `<article class="list-card zone-thermostat quick-control-card quick-thermostat-control ${zone.demand ? 'demanding' : ''} ${zone.enabled ? '' : 'zone-disabled'} ${deviceManualOverride ? 'manual-takeover' : ''}" data-zone-card="${esc(zone.id)}">
|
||||
<div class="list-card-head"><div><h3>${esc(zone.name)}</h3><p>${esc(device?.name || tr('common.noDevice'))} · ${esc(zonePresetLabel(displayPreset))}</p></div><div class="zone-quick-actions">${localThermostatPowerControl}${temporaryThermostatControl}</div></div>
|
||||
<div class="thermostat-main"><div><small>${esc(tr('zones.measurement'))}</small><strong>${fmtTemp(roomTemperature)}</strong></div><div class="temperature-control compact"><button data-action="zone-temperature" data-id="${esc(zone.id)}" data-delta="-0.5">−</button><div class="target-temp compact">${Number.isFinite(target)?target.toFixed(1):'--'}<small>°C</small></div><button data-action="zone-temperature" data-id="${esc(zone.id)}" data-delta="0.5">+</button></div><div><small>${esc(tr('zones.deviceTarget'))}</small><strong>${fmtTemp(zone.device_setpoint)}</strong></div></div>
|
||||
<div class="thermostat-main"><div><small>${esc(tr('zones.measurement'))}</small><strong>${fmtTemp(roomTemperature)}</strong></div><div class="temperature-control compact"><button data-action="zone-temperature" data-id="${esc(zone.id)}" data-delta="-0.5">−</button><div class="target-temp compact editable-target" data-temperature-kind="zone" data-id="${esc(zone.id)}" data-value="${Number.isFinite(target) ? target : ''}" tabindex="0" role="button" title="${esc(tr('common.clickTemperatureToEdit'))}">${Number.isFinite(target)?target.toFixed(1):'--'}<small>°C</small></div><button data-action="zone-temperature" data-id="${esc(zone.id)}" data-delta="0.5">+</button></div><div><small>${esc(tr('zones.deviceTarget'))}</small><strong>${fmtTemp(zone.device_setpoint)}</strong></div></div>
|
||||
<div class="preset-row quick-control-row quick-control-row-4">
|
||||
${['auto','comfort','sleep','away'].map(preset=>`<button class="${manual===preset?'active':''}" data-action="zone-preset" data-id="${esc(zone.id)}" data-value="${preset}">${esc(preset==='sleep'?tr('zones.sleepNow'):zonePresetLabel(preset))}</button>`).join('')}
|
||||
</div>
|
||||
|
||||
+18
-3
@@ -1,5 +1,16 @@
|
||||
|
||||
document.addEventListener('keydown', event => {
|
||||
const target = event.target.closest?.('[data-temperature-kind]');
|
||||
if (!target || event.target.matches('input')) return;
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault();
|
||||
beginInlineTemperatureEdit(target);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('click', async event => {
|
||||
const inlineTarget = event.target.closest('[data-temperature-kind]');
|
||||
if (inlineTarget && !event.target.closest('button')) { beginInlineTemperatureEdit(inlineTarget); return; }
|
||||
const button = event.target.closest('button'); if (!button) return;
|
||||
if (button.dataset.settingsTab) { setSettingsTab(button.dataset.settingsTab); return; }
|
||||
if (button.dataset.debugFilter) { app.debugFilter = button.dataset.debugFilter; renderDebugOverlay(); return; }
|
||||
@@ -13,8 +24,9 @@ document.addEventListener('click', async event => {
|
||||
const form = document.getElementById(button.dataset.open.replace('Dialog','Form'));
|
||||
if (form) form.reset();
|
||||
if (button.dataset.open === 'zoneDialog') {
|
||||
if (form) { form.dataset.editingZoneId = ''; if (form.elements.id) form.elements.id.value = ''; }
|
||||
if (form) { form.dataset.editingZoneId = ''; form.dataset.separateHysteresisInitialized = 'false'; if (form.elements.id) form.elements.id.value = ''; }
|
||||
updateZoneSensorFields();
|
||||
updateZoneHysteresisFields();
|
||||
}
|
||||
if (button.dataset.open === 'groupDialog') {
|
||||
if (form?.elements.id) form.elements.id.value = '';
|
||||
@@ -171,6 +183,7 @@ $('#themeSelect')?.addEventListener('change', event => setTheme(event.target.val
|
||||
$('#logLevelFilter')?.addEventListener('change', loadLogs); $('#logCategoryFilter')?.addEventListener('change', loadLogs);
|
||||
$('#settingsForm [name=notifications_provider]')?.addEventListener('change', updateNotificationFields);
|
||||
$('#zoneForm [name=sensor_source]').addEventListener('change', updateZoneSensorFields);
|
||||
$('#zoneForm [name=separate_hysteresis]')?.addEventListener('change', () => updateZoneHysteresisFields({syncFromCommon:true}));
|
||||
$('#scheduleForm [name=preset]').addEventListener('change', updateSchedulePresetField);
|
||||
$('#automationForm [name=action_target_kind]')?.addEventListener('change', updateAutomationTargetFields);
|
||||
$('#simulationScope')?.addEventListener('change', event => { app.simulationScope=event.target.value; app.simulationTarget='all'; renderSimulationPage(); updateSimulationUrl(); });
|
||||
@@ -198,7 +211,7 @@ $('#copyZoneSettings')?.addEventListener('click',()=>{
|
||||
ha_entity_id:f.elements.ha_entity_id.value,
|
||||
enabled:f.elements.enabled.checked,
|
||||
};
|
||||
const copyFields=['setpoint','cool_comfort_setpoint','cool_sleep_setpoint','cool_away_setpoint','heat_comfort_setpoint','heat_sleep_setpoint','heat_away_setpoint','hysteresis','min_on_seconds','min_off_seconds','min_adjust_seconds','standby_offset_c','external_sensor_weight_percent','max_sensor_difference','sensor_stale_after_seconds'];
|
||||
const copyFields=['setpoint','cool_comfort_setpoint','cool_sleep_setpoint','cool_away_setpoint','heat_comfort_setpoint','heat_sleep_setpoint','heat_away_setpoint','hysteresis','cool_hysteresis','heat_hysteresis','min_on_seconds','min_off_seconds','min_adjust_seconds','standby_offset_c','external_sensor_weight_percent','max_sensor_difference','sensor_stale_after_seconds'];
|
||||
copyFields.forEach(name=>{
|
||||
const input=f.elements[name];
|
||||
if(!input)return;
|
||||
@@ -206,6 +219,8 @@ $('#copyZoneSettings')?.addEventListener('click',()=>{
|
||||
if(value!==undefined&&value!==null)input.value=String(value);
|
||||
});
|
||||
f.elements.smart_fan.checked=source.smart_fan!==false;
|
||||
f.elements.separate_hysteresis.checked=source.separate_hysteresis===true;
|
||||
updateZoneHysteresisFields();
|
||||
if(f.elements.mode_policy) f.elements.mode_policy.value=source.inherit_house_mode===false?(source.mode||'cool'):'house';
|
||||
|
||||
f.elements.id.value=protectedValues.id;
|
||||
@@ -278,7 +293,7 @@ $('#zoneForm').addEventListener('submit', async event => {
|
||||
mode:raw.mode_policy==='house'?'cool':raw.mode_policy,inherit_house_mode:raw.mode_policy==='house',setpoint:parseDecimal(raw.setpoint),
|
||||
cool_comfort_setpoint:parseDecimal(raw.cool_comfort_setpoint),cool_sleep_setpoint:parseDecimal(raw.cool_sleep_setpoint),cool_away_setpoint:parseDecimal(raw.cool_away_setpoint),
|
||||
heat_comfort_setpoint:parseDecimal(raw.heat_comfort_setpoint),heat_sleep_setpoint:parseDecimal(raw.heat_sleep_setpoint),heat_away_setpoint:parseDecimal(raw.heat_away_setpoint),
|
||||
hysteresis:parseDecimal(raw.hysteresis),min_on_seconds:Number(raw.min_on_seconds),min_off_seconds:Number(raw.min_off_seconds),
|
||||
hysteresis:parseDecimal(raw.hysteresis),separate_hysteresis:form.separate_hysteresis.checked,cool_hysteresis:parseDecimal(raw.cool_hysteresis),heat_hysteresis:parseDecimal(raw.heat_hysteresis),min_on_seconds:Number(raw.min_on_seconds),min_off_seconds:Number(raw.min_off_seconds),
|
||||
min_adjust_seconds:Number(raw.min_adjust_seconds),standby_offset_c:parseDecimal(raw.standby_offset_c),smart_fan:form.smart_fan.checked,
|
||||
sensor_source:raw.sensor_source,ha_entity_id:raw.ha_entity_id||null,external_sensor_weight:Number(raw.external_sensor_weight_percent)/100,max_sensor_difference:parseDecimal(raw.max_sensor_difference),sensor_stale_after_seconds:Number(raw.sensor_stale_after_seconds||300),revision:raw.revision?Number(raw.revision):null
|
||||
};
|
||||
|
||||
+7
-3
@@ -133,11 +133,15 @@ function validateForm(form) {
|
||||
showFieldError(entity, entity?.value?.trim() ? tr('validation.entityId') : tr('validation.required'));
|
||||
valid = false;
|
||||
}
|
||||
[
|
||||
const zoneDecimalFields = [
|
||||
['cool_comfort_setpoint',8,30],['cool_sleep_setpoint',8,30],['cool_away_setpoint',8,30],
|
||||
['heat_comfort_setpoint',8,30],['heat_sleep_setpoint',8,30],['heat_away_setpoint',8,30],
|
||||
['hysteresis',0.1,5],['max_sensor_difference',0.1,20],['standby_offset_c',0.5,8]
|
||||
].forEach(([name,min,max]) => { if (!validateDecimalField(form,name,min,max)) valid = false; });
|
||||
['max_sensor_difference',0.1,20],['standby_offset_c',0.5,8],
|
||||
...(form.elements.separate_hysteresis?.checked
|
||||
? [['cool_hysteresis',0.1,5],['heat_hysteresis',0.1,5]]
|
||||
: [['hysteresis',0.1,5]])
|
||||
];
|
||||
zoneDecimalFields.forEach(([name,min,max]) => { if (!validateDecimalField(form,name,min,max)) valid = false; });
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
|
||||
+68
-3
@@ -69,8 +69,9 @@ async function sendZoneControl(id, patch) {
|
||||
}
|
||||
}
|
||||
|
||||
function queueZoneTemperature(zone, value) {
|
||||
const next = Math.round(clamp(value, 8, 30) * 2) / 2;
|
||||
function queueZoneTemperature(zone, value, {snapToHalf=true}={}) {
|
||||
const clamped = clamp(value, 8, 30);
|
||||
const next = snapToHalf ? Math.round(clamped * 2) / 2 : Math.round(clamped * 10) / 10;
|
||||
zone.manual_setpoint = next;
|
||||
zone.setpoint = next;
|
||||
zone.effective_setpoint = next;
|
||||
@@ -79,6 +80,49 @@ function queueZoneTemperature(zone, value) {
|
||||
app.zoneTemperatureTimers[zone.id] = setTimeout(() => sendZoneControl(zone.id, {setpoint: next}), 160);
|
||||
}
|
||||
|
||||
function beginInlineTemperatureEdit(target) {
|
||||
if (!target || target.querySelector('input') || target.dataset.editable === 'false') return;
|
||||
const kind = target.dataset.temperatureKind;
|
||||
const id = target.dataset.id;
|
||||
const value = parseDecimal(target.dataset.value);
|
||||
if (!Number.isFinite(value) || !kind || !id) return;
|
||||
const decimals = kind === 'zone' ? 1 : 0;
|
||||
const input = document.createElement('input');
|
||||
input.className = 'inline-temperature-input';
|
||||
input.type = 'text';
|
||||
input.inputMode = 'decimal';
|
||||
input.value = value.toFixed(decimals);
|
||||
input.setAttribute('aria-label', tr('common.targetTemperature'));
|
||||
target.classList.add('editing');
|
||||
target.replaceChildren(input);
|
||||
input.focus();
|
||||
input.select();
|
||||
|
||||
let finished = false;
|
||||
const finish = async commit => {
|
||||
if (finished) return;
|
||||
finished = true;
|
||||
const parsed = parseDecimal(input.value);
|
||||
if (!commit || !Number.isFinite(parsed) || parsed < 8 || parsed > 30) {
|
||||
if (commit && (!Number.isFinite(parsed) || parsed < 8 || parsed > 30)) toast(tr('validation.range', {min:8, max:30}), true);
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
if (kind === 'zone') {
|
||||
const zone = app.zones.find(item => item.id === id);
|
||||
if (zone) queueZoneTemperature(zone, parsed, {snapToHalf:false});
|
||||
return;
|
||||
}
|
||||
const device = app.devices.find(item => item.id === id);
|
||||
if (device) await sendDeviceCommand(id, {target_temperature:Math.round(clamp(parsed, 8, 30))});
|
||||
};
|
||||
input.addEventListener('keydown', event => {
|
||||
if (event.key === 'Enter') { event.preventDefault(); input.blur(); }
|
||||
if (event.key === 'Escape') { event.preventDefault(); finished = true; renderAll(); }
|
||||
});
|
||||
input.addEventListener('blur', () => finish(true), {once:true});
|
||||
}
|
||||
|
||||
function showDiscoveryNames(ids) {
|
||||
const wanted = new Set(Array.isArray(ids) ? ids : []);
|
||||
const devices = app.devices.filter(device => wanted.has(device.id));
|
||||
@@ -115,6 +159,22 @@ function openDialog(id) {
|
||||
}
|
||||
}
|
||||
|
||||
function updateZoneHysteresisFields({syncFromCommon=false}={}) {
|
||||
const form = $('#zoneForm');
|
||||
if (!form) return;
|
||||
const separate = !!form.separate_hysteresis?.checked;
|
||||
$('#commonHysteresisField').hidden = separate;
|
||||
$('#separateHysteresisFields').hidden = !separate;
|
||||
if (separate && syncFromCommon && form.dataset.separateHysteresisInitialized !== 'true') {
|
||||
const common = parseDecimal(form.hysteresis.value);
|
||||
if (Number.isFinite(common)) {
|
||||
form.cool_hysteresis.value = String(common);
|
||||
form.heat_hysteresis.value = String(common);
|
||||
}
|
||||
form.dataset.separateHysteresisInitialized = 'true';
|
||||
}
|
||||
}
|
||||
|
||||
function updateZoneSensorFields() {
|
||||
const form = $('#zoneForm');
|
||||
if (!form) return;
|
||||
@@ -167,7 +227,12 @@ function populateZone(id) {
|
||||
form.min_adjust_seconds.value = Number(item.min_adjust_seconds ?? 120);
|
||||
form.standby_offset_c.value = Number(item.standby_offset_c ?? 2);
|
||||
form.smart_fan.checked = item.smart_fan !== false;
|
||||
form.enabled.checked = item.enabled; updateZoneSensorFields(); openDialog('zoneDialog');
|
||||
form.separate_hysteresis.checked = item.separate_hysteresis === true;
|
||||
const commonHysteresis = Number(item.hysteresis ?? 0.6);
|
||||
form.cool_hysteresis.value = Number(item.separate_hysteresis ? (item.cool_hysteresis ?? commonHysteresis) : commonHysteresis);
|
||||
form.heat_hysteresis.value = Number(item.separate_hysteresis ? (item.heat_hysteresis ?? commonHysteresis) : commonHysteresis);
|
||||
form.dataset.separateHysteresisInitialized = item.separate_hysteresis === true ? 'true' : 'false';
|
||||
form.enabled.checked = item.enabled; updateZoneSensorFields(); updateZoneHysteresisFields(); openDialog('zoneDialog');
|
||||
}
|
||||
|
||||
const dateTimeLocalValue = value => {
|
||||
|
||||
@@ -141,6 +141,13 @@ h3 { margin-bottom: 10px; }
|
||||
.temperature-control button { width: 42px; height: 42px; padding: 0; border: 1px solid var(--line); border-radius: 50%; background: transparent; font-size: 22px; }
|
||||
.target-temp { min-width: 106px; text-align: center; font-size: 48px; font-weight: 300; letter-spacing: -.06em; }
|
||||
.target-temp small { margin-left: 3px; color: var(--muted); font-size: 15px; font-weight: 650; }
|
||||
.editable-target[data-editable="true"], .editable-target:not([data-editable]) { cursor: text; border-radius: 10px; transition: background .15s ease, box-shadow .15s ease; }
|
||||
.editable-target[data-editable="true"]:hover, .editable-target[data-editable="true"]:focus-visible, .editable-target:not([data-editable]):hover, .editable-target:not([data-editable]):focus-visible { background: var(--surface-2); box-shadow: 0 0 0 1px var(--line); outline: none; }
|
||||
.editable-target.editing { min-width: 92px; }
|
||||
.inline-temperature-input { width: 92px; max-width: 100%; padding: 3px 6px; border: 1px solid var(--line); border-radius: 9px; background: var(--surface); color: var(--text); font: inherit; font-size: .72em; font-weight: 500; text-align: center; letter-spacing: 0; outline: none; }
|
||||
.inline-temperature-input:focus { border-color: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 20%, transparent); }
|
||||
.hysteresis-config { display: grid; gap: 10px; }
|
||||
.hysteresis-config .check { margin-top: 0; }
|
||||
.current-line { display: flex; justify-content: center; gap: 6px; margin: -10px 0 18px; color: var(--muted); font-size: 12px; }
|
||||
.current-line strong { color: var(--text); }
|
||||
.mode-row, .fan-row { display: flex; gap: 7px; overflow-x: auto; padding: 2px 0 10px; scrollbar-width: none; }
|
||||
|
||||
Reference in New Issue
Block a user