P4 of design/per-monitor-portal-capture.md. The pin was an env var read once at startup, which a console picker can never write — so it becomes a field of the display policy (orthogonal to presets, like game_session), and `vdisplay::capture_monitor()` resolves env-over-policy: an appliance that pinned in its unit's host.env stays pinned there, and a console click cannot re-aim a machine whose operator already declared the answer. Read per `open` rather than cached, so a picker change takes effect on the next session instead of the next host restart. The host re-resolves the input anchor on every policy write — including clearing it when the pin is cleared, or a later virtual-display session inherits an anchor aimed at a monitor it is not showing. `with_manual_layout` carries the pin through like the other orthogonal axes: without that, saving a display ARRANGEMENT would silently swap the streamed screen back to virtual. Console: a "Streamed screen" card listing the host's real monitors beside "Virtual screen (default)", saving on selection. A disabled head is listed but not selectable (so "why isn't it here?" has an answer), and an env-pinned host renders read-only with the reason rather than offering controls that silently lose to the environment. Verified on GNOME/Mutter: pinning via the policy file alone (no env) routes sessions to the mirror and anchors input; setting the env to a different connector overrides it, exactly as documented. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
31 lines
1.1 KiB
TypeScript
31 lines
1.1 KiB
TypeScript
import Section from "@unom/ui/section";
|
|
import type { FC } from "react";
|
|
import { useLocale } from "@/lib/i18n";
|
|
import { m } from "@/paraglide/messages";
|
|
import { DisplaySection } from "./DisplayCard";
|
|
import { MonitorCard } from "./MonitorCard";
|
|
import { SessionGameCard } from "./SessionGameCard";
|
|
|
|
/**
|
|
* The **Virtual displays** page (design/display-management.md): the host's virtual-display policy
|
|
* (presets + every axis) plus the live-display list + multi-monitor arrangement. Its own nav
|
|
* section — the config surface is large enough to warrant the room, and it kept the Host page busy.
|
|
*
|
|
* The session⇄game lifetime card sits here rather than on its own page because it is the same
|
|
* question one step further out: keep-alive decides how long a *display* outlives a disconnect, and
|
|
* this decides whether the *game* does.
|
|
*/
|
|
export const SectionDisplays: FC = () => {
|
|
useLocale();
|
|
return (
|
|
<Section maxWidth={false}>
|
|
<div className="flex flex-col gap-card">
|
|
<h1 className="text-2xl font-semibold">{m.nav_displays()}</h1>
|
|
<DisplaySection />
|
|
<MonitorCard />
|
|
<SessionGameCard />
|
|
</div>
|
|
</Section>
|
|
);
|
|
};
|