feat(web): full virtual-display config surface — one-click presets + every axis editable

The Virtual displays card previously only exposed keep_alive/topology/max_displays as
editable custom fields; conflict/identity/layout (enforced since Stages 3-5) had no
controls, and the presets weren't surfaced as one-click options. Rework the card so the
whole policy is configurable WITHOUT any client connected:

- Presets front-and-center: each of the five (default/shared-desktop/hotdesk/workstation/
  gaming-rig) is a one-click row showing its story AND what it sets (keep-alive · topology ·
  conflict · identity badges), highlighting the active one. A click applies it immediately.
  gaming-rig stays disabled + "coming soon" (keep_alive: forever isn't cross-platform yet).
- Custom mode reveals EVERY axis editably — keep-alive, topology, conflict, identity, layout,
  max-displays — seeded from the current effective behavior, with a Save button. A reusable
  `Choice` button-group + a tolerant `tr()` label lookup keep it tidy.
- The live-display list + multi-monitor arrangement table stay below (they need a live
  session); the settings above work standalone.
- en+de i18n for the new controls; refreshed the effective-preview row to show all axes.

web tsc + vite build + biome-lint green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-05 14:38:40 +00:00
parent 62e0367f4b
commit 8986667b78
3 changed files with 342 additions and 170 deletions
+20
View File
@@ -80,6 +80,26 @@
"display_arrange": "Anzeigen anordnen",
"display_arrange_help": "Legen Sie fest, wo jede gestreamte Anzeige auf dem Desktop sitzt (in Pixeln). Beim Speichern wird auf ein manuelles Layout umgeschaltet; es greift ab der nächsten Verbindung.",
"display_arrange_save": "Anordnung speichern",
"display_custom_desc": "Jede Option selbst festlegen.",
"display_preset_current": "Aktiv",
"display_preset_soon": "in Kürze",
"display_keep_alive_help": "Wie lange eine Anzeige (und bei gamescope ihr Spiel) nach dem Trennen bestehen bleibt. 0 = sofort abbauen.",
"display_topology_help": "Was mit den physischen Monitoren des Hosts während des Streamings geschieht.",
"display_conflict": "Wenn ein weiterer Client verbindet",
"display_conflict_help": "Was passiert, wenn ein zweiter Client verbindet, während bereits gestreamt wird, und eine andere Auflösung anfragt.",
"display_conflict_separate": "Eigene Anzeige",
"display_conflict_steal": "Übernehmen",
"display_conflict_join": "Ansicht teilen",
"display_conflict_reject": "Besetzt — ablehnen",
"display_identity": "Client-Identität",
"display_identity_help": "Jedem Client eine stabile Anzeige geben, damit der Desktop seine Monitor-Einstellungen merkt (z. B. Skalierung).",
"display_identity_shared": "Geteilt",
"display_identity_per_client": "Pro Client",
"display_identity_per_client_mode": "Pro Client + Auflösung",
"display_layout_mode": "Multi-Monitor-Anordnung",
"display_layout_help": "Wie mehrere Anzeigen auf dem Desktop angeordnet werden. Manuell nutzt die Anordnungstabelle unten (ab 2 Anzeigen).",
"display_layout_auto_row": "Automatisch (nebeneinander)",
"display_layout_manual": "Manuell",
"clients_title": "Gekoppelte Geräte",
"clients_empty": "Noch keine gekoppelten Geräte.",
"clients_name": "Name",
+20
View File
@@ -80,6 +80,26 @@
"display_arrange": "Arrange displays",
"display_arrange_help": "Set where each streamed display sits on the desktop, in pixels. Saving switches to a manual layout; it applies from the next connect.",
"display_arrange_save": "Save arrangement",
"display_custom_desc": "Set every option yourself.",
"display_preset_current": "Active",
"display_preset_soon": "coming soon",
"display_keep_alive_help": "How long a display (and, on gamescope, its game) survives after the client disconnects. 0 = tear down immediately.",
"display_topology_help": "What happens to the host's physical monitors while streaming.",
"display_conflict": "When another client connects",
"display_conflict_help": "What happens if a second client connects while one is already streaming and asks for a different resolution.",
"display_conflict_separate": "Own display",
"display_conflict_steal": "Take over",
"display_conflict_join": "Share view",
"display_conflict_reject": "Busy — reject",
"display_identity": "Per-client identity",
"display_identity_help": "Give each client a stable display so the desktop remembers its per-monitor settings (e.g. scaling).",
"display_identity_shared": "Shared",
"display_identity_per_client": "Per client",
"display_identity_per_client_mode": "Per client + resolution",
"display_layout_mode": "Multi-monitor layout",
"display_layout_help": "How several displays are arranged on the desktop. Manual uses the arrangement table below (with 2+ displays).",
"display_layout_auto_row": "Auto (side by side)",
"display_layout_manual": "Manual",
"clients_title": "Paired clients",
"clients_empty": "No paired clients yet.",
"clients_name": "Name",