Files
mommysgoodpuppy 1e964e7871 deno task playtest wrist covers edit mode and the Monitors page
Edit stays off while the windows are hidden and goes off with them. The Monitors
page reassigns the root display's output and back. getWorkspaceLayoutSnapshot lets
scripts read the outputs the wrist lists.

Agent: Claude Code 2.1.296; model claude-opus-5-5; thread 941be8c7-aca1-4664-b284-7cc0eed69e21
2026-10-12 11:57:24 +03:00

95 lines
2.8 KiB
TypeScript

import { useSyncExternalStore } from "react";
import type { LayoutSlotSummary } from "./spatialLayoutPersistence.ts";
import type { SpatialGraph } from "./spatialGraph.ts";
export type WorkspaceLayoutOutput = {
id: string;
name: string;
assignedDisplayId: string | null;
};
export type WorkspaceLayoutDisplay = {
id: string;
ordinal: number;
root: boolean;
outputId: string | null;
outputName: string | null;
};
export type WorkspaceLayoutSnapshot = {
outputs: WorkspaceLayoutOutput[];
displays: WorkspaceLayoutDisplay[];
/** Saved layout slots, `null` when empty; index 0 = slot 1. */
layoutSlots?: (LayoutSlotSummary | null)[];
};
type WorkspaceLayoutActions = {
assignOutput: (displayId: string, outputId: string) => void;
addOutput: (outputId: string) => void;
saveSlot: (slot: number) => void;
loadSlot: (slot: number) => void;
getGraph: () => SpatialGraph;
editGraph: (update: (current: SpatialGraph) => SpatialGraph) => void;
};
let snapshot: WorkspaceLayoutSnapshot = { outputs: [], displays: [] };
let actions: WorkspaceLayoutActions | null = null;
const listeners = new Set<() => void>();
export function publishWorkspaceLayout(next: WorkspaceLayoutSnapshot): void {
if (JSON.stringify(snapshot) === JSON.stringify(next)) return;
snapshot = next;
for (const listener of listeners) listener();
}
export function registerWorkspaceLayoutActions(next: WorkspaceLayoutActions): () => void {
actions = next;
return () => {
if (actions === next) actions = null;
};
}
export function assignWorkspaceLayoutOutput(displayId: string, outputId: string): void {
actions?.assignOutput(displayId, outputId);
}
export function addWorkspaceLayoutOutput(outputId: string): void {
actions?.addOutput(outputId);
}
export function saveLayoutSlot(slot: number): void {
actions?.saveSlot(slot);
}
export function loadLayoutSlot(slot: number): void {
actions?.loadSlot(slot);
}
export function useWorkspaceLayoutSnapshot(): WorkspaceLayoutSnapshot {
return useSyncExternalStore(
(listener) => {
listeners.add(listener);
return () => listeners.delete(listener);
},
() => snapshot,
() => snapshot,
);
}
/** The outputs and displays the wrist menu shows. For scripts and the REPL. */
export function getWorkspaceLayoutSnapshot(): WorkspaceLayoutSnapshot {
return snapshot;
}
/** The live spatial graph, or null before the scene mounts. For scripts and the REPL. */
export function getSpatialGraph(): SpatialGraph | null {
return actions?.getGraph() ?? null;
}
/** Apply `update` to the live graph, as a settings click would (it persists the same way). */
export function editSpatialGraph(update: (current: SpatialGraph) => SpatialGraph): boolean {
if (actions == null) return false;
actions.editGraph(update);
return true;
}