fix(web): explicit keep-alive Off/Keep toggle + roomier custom display form
Two UX fixes on the Virtual displays Configuration card: - Keep-alive is no longer implicitly "on" by typing in the seconds field. It's an explicit two-button toggle — **Off** (tear down at disconnect) vs. **Keep for** [N] seconds — and the seconds input only appears when "Keep for" is selected. The duration is remembered across toggles, and the help text explains both modes. - Opened up the cramped custom form: the fields container is `space-y-6` with more padding (`p-5`, rounded-lg), each option group is `space-y-2.5`, and the Save button sits below a divider — so it reads as sections with room instead of a pressed stack. web tsc + vite build + biome-lint green; deployed on .21. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -147,10 +147,11 @@ const DisplayForm: FC<{
|
||||
};
|
||||
|
||||
const ka = customFields.keep_alive;
|
||||
const secondsValue = ka.mode === "duration" ? ka.seconds : 300;
|
||||
// The duration value, remembered across the Off/Keep toggle so switching back restores it.
|
||||
const [keepSecs, setKeepSecs] = useState(ka.mode === "duration" ? ka.seconds : 300);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-6">
|
||||
{/* One-click presets — a 2-up grid so each has room to breathe */}
|
||||
<div className="space-y-3">
|
||||
<Label className="text-base font-semibold">{m.display_preset()}</Label>
|
||||
@@ -208,10 +209,10 @@ const DisplayForm: FC<{
|
||||
|
||||
{/* Custom: every option by hand */}
|
||||
{isCustom && (
|
||||
<div className="space-y-4 rounded-md border p-4">
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-6 rounded-lg border p-5">
|
||||
<div className="space-y-2.5">
|
||||
<Label>{m.display_keep_alive()}</Label>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant={ka.mode === "off" ? "default" : "outline"}
|
||||
@@ -220,25 +221,35 @@ const DisplayForm: FC<{
|
||||
>
|
||||
{m.display_keep_alive_off()}
|
||||
</Button>
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
className="w-24"
|
||||
value={secondsValue}
|
||||
<Button
|
||||
size="sm"
|
||||
variant={ka.mode === "duration" ? "default" : "outline"}
|
||||
disabled={busy}
|
||||
onChange={(e) =>
|
||||
setDraft({
|
||||
...draft,
|
||||
keep_alive: {
|
||||
mode: "duration",
|
||||
seconds: Math.max(0, Number(e.target.value) || 0),
|
||||
},
|
||||
})
|
||||
onClick={() =>
|
||||
setDraft({ ...draft, keep_alive: { mode: "duration", seconds: keepSecs } })
|
||||
}
|
||||
/>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{m.display_keep_alive_seconds()}
|
||||
</span>
|
||||
>
|
||||
{m.display_keep_alive_keep()}
|
||||
</Button>
|
||||
{ka.mode === "duration" && (
|
||||
<div className="flex items-center gap-2">
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
className="w-24"
|
||||
value={ka.seconds}
|
||||
disabled={busy}
|
||||
onChange={(e) => {
|
||||
const n = Math.max(0, Number(e.target.value) || 0);
|
||||
setKeepSecs(n);
|
||||
setDraft({ ...draft, keep_alive: { mode: "duration", seconds: n } });
|
||||
}}
|
||||
/>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{m.display_keep_alive_seconds()}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">{m.display_keep_alive_help()}</p>
|
||||
</div>
|
||||
@@ -285,7 +296,7 @@ const DisplayForm: FC<{
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-2.5">
|
||||
<Label htmlFor="disp-max">{m.display_max()}</Label>
|
||||
<Input
|
||||
id="disp-max"
|
||||
@@ -304,9 +315,11 @@ const DisplayForm: FC<{
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button onClick={() => apply(draft)} disabled={busy}>
|
||||
{m.display_save()}
|
||||
</Button>
|
||||
<div className="border-t pt-4">
|
||||
<Button onClick={() => apply(draft)} disabled={busy}>
|
||||
{m.display_save()}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -337,7 +350,7 @@ const Choice: FC<{
|
||||
disabled: boolean;
|
||||
onPick: (v: string) => void;
|
||||
}> = ({ label, help, value, options, labels, disabled, onPick }) => (
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-2.5">
|
||||
<Label>{label}</Label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{options.map((o) => (
|
||||
|
||||
Reference in New Issue
Block a user