fix: widen Settings modal to two columns; env cleanup

- SettingsModal: widen to sm:max-w-3xl with max-h-[90vh] scroll and a
  two-column grid so it no longer overflows the viewport. Left column =
  status/service checks + canvas prefs; right column groups all auto-sync
  config (Zigbee, Z-Wave, Proxmox).
- .env.example: drop ZIGBEE_/ZWAVE_ SYNC_ENABLED + SYNC_INTERVAL — the
  auto-sync activation is configured in the Settings modal (persisted to
  scan_config.json), same as Proxmox; connection config stays env-only.

ha-relevant: no
This commit is contained in:
Pouzor
2026-07-10 15:45:46 +02:00
parent b99450db2f
commit d3728f4108
2 changed files with 88 additions and 86 deletions
-4
View File
@@ -71,8 +71,6 @@ MCP_SERVICE_KEY=svc_changeme
# ZIGBEE_BASE_TOPIC=zigbee2mqtt # ZIGBEE_BASE_TOPIC=zigbee2mqtt
# ZIGBEE_MQTT_TLS=false # true for TLS brokers (typically port 8883) # ZIGBEE_MQTT_TLS=false # true for TLS brokers (typically port 8883)
# ZIGBEE_MQTT_TLS_INSECURE=false # skip cert verify (self-signed only; requires TLS) # ZIGBEE_MQTT_TLS_INSECURE=false # skip cert verify (self-signed only; requires TLS)
# ZIGBEE_SYNC_ENABLED=false # turn scheduled auto-sync on
# ZIGBEE_SYNC_INTERVAL=3600 # seconds between syncs (min 300)
# Z-Wave JS UI (zwavejs2mqtt) auto-sync — same MQTT secret/env rules as Zigbee. # Z-Wave JS UI (zwavejs2mqtt) auto-sync — same MQTT secret/env rules as Zigbee.
# ZWAVE_MQTT_HOST=192.168.1.20 # ZWAVE_MQTT_HOST=192.168.1.20
@@ -83,5 +81,3 @@ MCP_SERVICE_KEY=svc_changeme
# ZWAVE_GATEWAY_NAME=zwavejs2mqtt # ZWAVE_GATEWAY_NAME=zwavejs2mqtt
# ZWAVE_MQTT_TLS=false # true for TLS brokers (typically port 8883) # ZWAVE_MQTT_TLS=false # true for TLS brokers (typically port 8883)
# ZWAVE_MQTT_TLS_INSECURE=false # skip cert verify (self-signed only; requires TLS) # ZWAVE_MQTT_TLS_INSECURE=false # skip cert verify (self-signed only; requires TLS)
# ZWAVE_SYNC_ENABLED=false # turn scheduled auto-sync on
# ZWAVE_SYNC_INTERVAL=3600 # seconds between syncs (min 300)
@@ -249,12 +249,14 @@ export function SettingsModal({ open, onClose }: SettingsModalProps) {
return ( return (
<Dialog open={open} onOpenChange={(v) => !v && onClose()}> <Dialog open={open} onOpenChange={(v) => !v && onClose()}>
<DialogContent className="bg-[#161b22] border-border max-w-md"> <DialogContent className="bg-[#161b22] border-border max-w-[calc(100%-2rem)] sm:max-w-3xl max-h-[90vh] overflow-y-auto">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-foreground">Settings</DialogTitle> <DialogTitle className="text-foreground">Settings</DialogTitle>
</DialogHeader> </DialogHeader>
<div className="space-y-5 py-2"> <div className="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-5 py-2">
{/* Left column */}
<div className="space-y-5">
{/* Status checker */} {/* Status checker */}
{!STANDALONE && ( {!STANDALONE && (
<div className="space-y-1.5"> <div className="space-y-1.5">
@@ -306,6 +308,90 @@ export function SettingsModal({ open, onClose }: SettingsModalProps) {
</div> </div>
)} )}
{/* Canvas */}
<div className="pt-3 border-t border-border space-y-3">
<span className="text-xs font-medium text-muted-foreground uppercase tracking-wider">Canvas</span>
<label className="flex items-center justify-between gap-2 cursor-pointer">
<span className="text-xs text-foreground">Snap to nodes</span>
<input
type="checkbox"
checked={alignment.enabled}
onChange={(e) => updateAlignment({ enabled: e.target.checked })}
className="cursor-pointer accent-[#00d4ff]"
aria-label="Toggle alignment guides"
/>
</label>
<label className="flex items-center justify-between gap-2 cursor-pointer">
<span className="text-xs text-foreground">Hide IP addresses</span>
<input
type="checkbox"
checked={hideIp}
onChange={(e) => setHideIp(e.target.checked)}
className="cursor-pointer accent-[#00d4ff]"
aria-label="Toggle IP address masking"
/>
</label>
<div className={alignment.enabled ? 'space-y-1.5' : 'space-y-1.5 opacity-50 pointer-events-none'}>
<label className="text-xs text-muted-foreground">Snap distance</label>
<div className="flex items-center gap-2">
<input
type="range"
min={2}
max={16}
step={1}
value={alignment.threshold}
onChange={(e) => updateAlignment({ threshold: Number(e.target.value) })}
className="flex-1 cursor-pointer accent-[#00d4ff]"
aria-label="Alignment snap threshold"
/>
<span className="font-mono text-[11px] text-foreground w-8 text-right">{alignment.threshold}px</span>
</div>
<p className="text-[10px] text-muted-foreground leading-tight">
Distance at which dragged nodes snap to neighbours. Hold Alt while dragging to disable.
</p>
</div>
</div>
</div>
{/* Right column */}
<div className="space-y-5">
{/* Zigbee auto-sync */}
{!STANDALONE && zbConfig && (
<MeshAutoSync
title="Zigbee auto-sync"
accent="#39d353"
hostConfigured={zbConfig.host_configured}
envHostVar="ZIGBEE_MQTT_HOST"
enabled={zbSyncEnabled}
onEnabledChange={setZbSyncEnabled}
interval={zbInterval}
onIntervalChange={setZbInterval}
description="Re-imports the Zigbee mesh into the pending inventory. Min 300s (5 min)."
syncing={zbSyncing}
onSyncNow={handleZbSyncNow}
/>
)}
{/* Z-Wave auto-sync */}
{!STANDALONE && zwConfig && (
<MeshAutoSync
title="Z-Wave auto-sync"
accent="#a855f7"
hostConfigured={zwConfig.host_configured}
envHostVar="ZWAVE_MQTT_HOST"
enabled={zwSyncEnabled}
onEnabledChange={setZwSyncEnabled}
interval={zwInterval}
onIntervalChange={setZwInterval}
description="Re-imports the Z-Wave network into the pending inventory. Min 300s (5 min)."
syncing={zwSyncing}
onSyncNow={handleZwSyncNow}
/>
)}
{/* Proxmox auto-sync */} {/* Proxmox auto-sync */}
{!STANDALONE && pmConfig && ( {!STANDALONE && pmConfig && (
<div className="pt-3 border-t border-border space-y-2"> <div className="pt-3 border-t border-border space-y-2">
@@ -368,86 +454,6 @@ export function SettingsModal({ open, onClose }: SettingsModalProps) {
)} )}
</div> </div>
)} )}
{/* Zigbee auto-sync */}
{!STANDALONE && zbConfig && (
<MeshAutoSync
title="Zigbee auto-sync"
accent="#39d353"
hostConfigured={zbConfig.host_configured}
envHostVar="ZIGBEE_MQTT_HOST"
enabled={zbSyncEnabled}
onEnabledChange={setZbSyncEnabled}
interval={zbInterval}
onIntervalChange={setZbInterval}
description="Re-imports the Zigbee mesh into the pending inventory. Min 300s (5 min)."
syncing={zbSyncing}
onSyncNow={handleZbSyncNow}
/>
)}
{/* Z-Wave auto-sync */}
{!STANDALONE && zwConfig && (
<MeshAutoSync
title="Z-Wave auto-sync"
accent="#a855f7"
hostConfigured={zwConfig.host_configured}
envHostVar="ZWAVE_MQTT_HOST"
enabled={zwSyncEnabled}
onEnabledChange={setZwSyncEnabled}
interval={zwInterval}
onIntervalChange={setZwInterval}
description="Re-imports the Z-Wave network into the pending inventory. Min 300s (5 min)."
syncing={zwSyncing}
onSyncNow={handleZwSyncNow}
/>
)}
{/* Canvas */}
<div className="pt-3 border-t border-border space-y-3">
<span className="text-xs font-medium text-muted-foreground uppercase tracking-wider">Canvas</span>
<label className="flex items-center justify-between gap-2 cursor-pointer">
<span className="text-xs text-foreground">Snap to nodes</span>
<input
type="checkbox"
checked={alignment.enabled}
onChange={(e) => updateAlignment({ enabled: e.target.checked })}
className="cursor-pointer accent-[#00d4ff]"
aria-label="Toggle alignment guides"
/>
</label>
<label className="flex items-center justify-between gap-2 cursor-pointer">
<span className="text-xs text-foreground">Hide IP addresses</span>
<input
type="checkbox"
checked={hideIp}
onChange={(e) => setHideIp(e.target.checked)}
className="cursor-pointer accent-[#00d4ff]"
aria-label="Toggle IP address masking"
/>
</label>
<div className={alignment.enabled ? 'space-y-1.5' : 'space-y-1.5 opacity-50 pointer-events-none'}>
<label className="text-xs text-muted-foreground">Snap distance</label>
<div className="flex items-center gap-2">
<input
type="range"
min={2}
max={16}
step={1}
value={alignment.threshold}
onChange={(e) => updateAlignment({ threshold: Number(e.target.value) })}
className="flex-1 cursor-pointer accent-[#00d4ff]"
aria-label="Alignment snap threshold"
/>
<span className="font-mono text-[11px] text-foreground w-8 text-right">{alignment.threshold}px</span>
</div>
<p className="text-[10px] text-muted-foreground leading-tight">
Distance at which dragged nodes snap to neighbours. Hold Alt while dragging to disable.
</p>
</div>
</div> </div>
</div> </div>