mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-08-26 17:31:34 +00:00
feat(satpass): GUI safety controls panel — armed-state banner, dry-run toggle, rate cap
Tracking panel gains both new adapter-config keys:
- dry_run toggle (sky-blue accent, visually distinct from enabled)
- max_broadcasts_per_hour number input (1–60 range)
Armed-state banner at top of panel:
- OFF (grey) when disabled
- DRY RUN (sky-blue) when enabled + dry_run
- ⚠ LIVE (amber, pulsing) when enabled + !dry_run
Load/save through adapter-config API per 199929f pattern.
NORAD IDs helper updated to reflect opt-in semantics.
Bundle hash: index-DPN58SF4.js → index-Di1mw816.js
index-CB06j1ej.css → index-WwNJt5S-.css
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e903444356
commit
17808c3d65
4 changed files with 90 additions and 43 deletions
|
|
@ -78,6 +78,8 @@ interface SatpassConfig {
|
||||||
observers: string[]
|
observers: string[]
|
||||||
min_elevation: number
|
min_elevation: number
|
||||||
norad_ids: string[]
|
norad_ids: string[]
|
||||||
|
max_broadcasts_per_hour: number
|
||||||
|
dry_run: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// SWPC adapter config shape
|
// SWPC adapter config shape
|
||||||
|
|
@ -315,6 +317,8 @@ export default function Environment() {
|
||||||
observers: [],
|
observers: [],
|
||||||
min_elevation: 30,
|
min_elevation: 30,
|
||||||
norad_ids: [],
|
norad_ids: [],
|
||||||
|
max_broadcasts_per_hour: 4,
|
||||||
|
dry_run: true,
|
||||||
})
|
})
|
||||||
const [satpassOriginal, setSatpassOriginal] = useState<string>("")
|
const [satpassOriginal, setSatpassOriginal] = useState<string>("")
|
||||||
|
|
||||||
|
|
@ -459,6 +463,8 @@ export default function Environment() {
|
||||||
observers: satpassData.observers?.value ?? [],
|
observers: satpassData.observers?.value ?? [],
|
||||||
min_elevation: satpassData.min_elevation?.value ?? 30,
|
min_elevation: satpassData.min_elevation?.value ?? 30,
|
||||||
norad_ids: satpassData.norad_ids?.value ?? [],
|
norad_ids: satpassData.norad_ids?.value ?? [],
|
||||||
|
max_broadcasts_per_hour: satpassData.max_broadcasts_per_hour?.value ?? 4,
|
||||||
|
dry_run: satpassData.dry_run?.value ?? true,
|
||||||
}
|
}
|
||||||
setSatpassConfig(cfg)
|
setSatpassConfig(cfg)
|
||||||
setSatpassOriginal(JSON.stringify(cfg))
|
setSatpassOriginal(JSON.stringify(cfg))
|
||||||
|
|
@ -659,6 +665,12 @@ const save = async () => {
|
||||||
if (JSON.stringify(satpassConfig.norad_ids) !== JSON.stringify(orig.norad_ids)) {
|
if (JSON.stringify(satpassConfig.norad_ids) !== JSON.stringify(orig.norad_ids)) {
|
||||||
await saveAdapterConfig("satpass", "norad_ids", satpassConfig.norad_ids)
|
await saveAdapterConfig("satpass", "norad_ids", satpassConfig.norad_ids)
|
||||||
}
|
}
|
||||||
|
if (satpassConfig.max_broadcasts_per_hour !== orig.max_broadcasts_per_hour) {
|
||||||
|
await saveAdapterConfig("satpass", "max_broadcasts_per_hour", satpassConfig.max_broadcasts_per_hour)
|
||||||
|
}
|
||||||
|
if (satpassConfig.dry_run !== orig.dry_run) {
|
||||||
|
await saveAdapterConfig("satpass", "dry_run", satpassConfig.dry_run)
|
||||||
|
}
|
||||||
setSatpassOriginal(JSON.stringify(satpassConfig))
|
setSatpassOriginal(JSON.stringify(satpassConfig))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1088,8 +1100,43 @@ const save = async () => {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</>)
|
</>)
|
||||||
case 'satpass': return (
|
case 'satpass': {
|
||||||
|
const armedState = !satpassConfig.enabled
|
||||||
|
? { label: 'OFF', color: 'text-[#777] bg-[#1a1a1a]', desc: '' }
|
||||||
|
: satpassConfig.dry_run
|
||||||
|
? { label: 'DRY RUN', color: 'text-sky-400 bg-sky-400/10 border border-sky-400/30', desc: ' — logging only, nothing transmits' }
|
||||||
|
: { label: '⚠ LIVE', color: 'text-amber-400 bg-amber-500/20 border-2 border-amber-500 font-bold animate-pulse', desc: ' — transmitting to mesh' }
|
||||||
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
{/* Armed-state banner */}
|
||||||
|
<div className={`px-4 py-2.5 text-sm rounded ${armedState.color}`}>
|
||||||
|
<span className="font-semibold">{armedState.label}</span>
|
||||||
|
{armedState.desc && <span className="font-normal">{armedState.desc}</span>}
|
||||||
|
</div>
|
||||||
|
{/* Safety controls */}
|
||||||
|
<div>
|
||||||
|
<div className="text-[10px] font-sans font-medium uppercase tracking-widest text-[#666] mb-3">
|
||||||
|
Safety Controls
|
||||||
|
</div>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<span className="text-sm text-[#e0e0e0]">Dry run — log instead of transmit</span>
|
||||||
|
<p className="text-xs text-[#666]">When enabled, passes are logged but never broadcast to mesh</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => setSatpassConfig({ ...satpassConfig, dry_run: !satpassConfig.dry_run })}
|
||||||
|
className={`relative w-10 h-5 rounded-full transition-colors ${satpassConfig.dry_run ? 'bg-sky-500' : 'bg-[#333]'}`}
|
||||||
|
>
|
||||||
|
<span className={`absolute top-0.5 left-0.5 w-4 h-4 rounded-full bg-white transition-transform ${satpassConfig.dry_run ? 'translate-x-5' : ''}`} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<NumberInput label="Max broadcasts / hour" value={satpassConfig.max_broadcasts_per_hour}
|
||||||
|
onChange={(v) => setSatpassConfig({ ...satpassConfig, max_broadcasts_per_hour: v })}
|
||||||
|
min={1} max={60} helper="Rate cap — broadcasts exceeding this limit are dropped" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* Pass filters */}
|
||||||
<div>
|
<div>
|
||||||
<div className="text-[10px] font-sans font-medium uppercase tracking-widest text-[#666] mb-3">
|
<div className="text-[10px] font-sans font-medium uppercase tracking-widest text-[#666] mb-3">
|
||||||
Pass Filters
|
Pass Filters
|
||||||
|
|
@ -1105,9 +1152,9 @@ const save = async () => {
|
||||||
helper="Observer names to include (empty = all)" />
|
helper="Observer names to include (empty = all)" />
|
||||||
<ListInput label="NORAD IDs" value={satpassConfig.norad_ids}
|
<ListInput label="NORAD IDs" value={satpassConfig.norad_ids}
|
||||||
onChange={(v) => setSatpassConfig({ ...satpassConfig, norad_ids: v })}
|
onChange={(v) => setSatpassConfig({ ...satpassConfig, norad_ids: v })}
|
||||||
helper="NORAD catalog IDs to track (empty = all)" />
|
helper="NORAD catalog IDs to broadcast (empty = broadcast nothing, opt-in only)" />
|
||||||
</div>
|
</div>
|
||||||
)
|
)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -8,8 +8,8 @@
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
<script type="module" crossorigin src="/assets/index-DPN58SF4.js"></script>
|
<script type="module" crossorigin src="/assets/index-Di1mw816.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-CB06j1ej.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-WwNJt5S-.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue