import type { SceneResultsPayload } from "./sceneData"; export const SCENE_CHANNEL = "tjwater:zjb-scene"; export const SCENE_PROTOCOL_VERSION = 2; export type SceneMode = | "network" | "hydraulic" | "map" | "detail" | "pump" | "meters"; export type SceneDisplayMode = "global" | "coordinated"; export type SceneMetricMode = "uniform" | "velocity" | "pressure" | "direction"; export type SceneDirectionMode = "none" | "topology" | "results"; export type SceneLightingPreset = "day" | "studio" | "evening"; export type SceneQuality = "standard" | "high"; export type SceneNetworkStyle = { scale: number; mode: SceneMetricMode; color: string; missingColor: string; lowColor: string; highColor: string; opacity: number; roughness: number; metalness: number; nodes: boolean; direction: SceneDirectionMode; arrowColor: string; autoRange: boolean; min: number; max: number; }; export type SceneAppearance = { preset: SceneLightingPreset; exposure: number; shadows: boolean; effects: boolean; quality: SceneQuality; }; export type SceneCameraView = { id: string; label: string; mode: SceneMode; note?: string; saved: boolean; }; export type SceneCameraState = { active: string | null; note: string; views: SceneCameraView[]; }; export type SceneStyleSummary = { mode?: SceneMetricMode; min?: number; max?: number; dataLinks?: number; totalLinks?: number; arrows?: number; hasResults?: boolean; resultTime?: string | null; scale?: number; }; export type SceneRuntimeState = { mode: SceneMode; status: string; contextVisible: boolean; roofVisible: boolean; displayMode: SceneDisplayMode; style: SceneNetworkStyle; styleSummary: SceneStyleSummary; appearance: SceneAppearance; camera: SceneCameraState; }; export type SceneAssetField = { label: string; value: string; }; export type SceneAssetSection = { title: string; fields: SceneAssetField[]; }; export type SceneAssetNeighbor = { id: string; label: string; }; export type SceneAssetSelection = { assetId: string; elementId: string; kind: string; title: string; sections: SceneAssetSection[]; neighbors: SceneAssetNeighbor[]; }; export type SceneCommand = | { name: "set-mode"; mode: SceneMode } | { name: "visit-camera"; viewId: string } | { name: "save-camera"; label: string } | { name: "remove-camera"; viewId: string } | { name: "set-style"; patch: Partial } | { name: "reset-style" } | { name: "set-display-mode"; mode: SceneDisplayMode } | { name: "set-appearance"; patch: Partial } | { name: "toggle-context" } | { name: "toggle-roof" } | { name: "fit-view" } | { name: "select-asset"; assetId: string } | { name: "locate-selection" } | { name: "clear-selection" }; type HostMessageBase = { channel: typeof SCENE_CHANNEL; version: typeof SCENE_PROTOCOL_VERSION; projectCode: string; modelId: string; }; export type SceneHostMessage = HostMessageBase & ( | { type: "results"; payload: SceneResultsPayload } | { type: "clear-results" } | { type: "command"; command: SceneCommand } ); export type SceneRuntimeMessage = HostMessageBase & ( | { type: "ready"; nodeIds: string[]; linkIds: string[]; state: SceneRuntimeState; } | { type: "scene-state"; state: SceneRuntimeState } | { type: "selection-changed"; selection: SceneAssetSelection | null } | { type: "results-applied"; summary: SceneStyleSummary } | { type: "results-cleared" } | { type: "error"; message: string } ); export const isSceneRuntimeMessage = ( input: unknown, ): input is SceneRuntimeMessage => { if (!input || typeof input !== "object") return false; const message = input as Partial; return ( message.channel === SCENE_CHANNEL && message.version === SCENE_PROTOCOL_VERSION && typeof message.projectCode === "string" && typeof message.modelId === "string" && typeof message.type === "string" ); };