code wiki / (root) / nx_video_ui_core.nx

nx_video_ui_core.nx source

↩ module page · 77 lines · 4465 B

1// nx_video_ui_core.nx -- SOVEREIGN call-UI state machine. NO browser, NO WebSocket, NO getUserMedia, NO 2// WebAssembly-host, NO navigator, NO addEventListener anywhere. Owns the call's interaction LOGIC as pure 3// INTENTS over a flat state region (operator 2026-07-04: "we need this to work on NishiOS + the Nishi 4// browser without third parties -- build with that in mind"). The browser is ONE throwaway adapter (click 5// -> intent, WebSocket frame -> transport call, camera frame -> media call); NishiOS is ANOTHER adapter 6// over the SAME core. State is a FLAT i64[6] region (not a struct) so it compiles to WASM (browser last 7// mile) AND native (NishiOS) from this one source -- the WAT backend is flat memory + i64. The browser 8// passes a linear-memory offset; the native gate passes an mmap'd buffer. Because the logic is 9// intents-over-state (no host API), it is verified SOVEREIGNLY -- drive intents + assert state/layout 10// (nx_video_ui_core_gate), not by simulating DOM events. That survives the NishiOS transition. 11// license_tier: ORIGINAL 12 13// state slots (i64 each) -- the flat UiState region 14const UST_PEERS: i64 = 0 // remote peer count (self implicit) 15const UST_MIC: i64 = 1 16const UST_CAM: i64 = 2 17const UST_SWAP: i64 = 3 // DUO: is the self tile the full-screen one (tap-to-swap) 18const UST_SHARE: i64 = 4 // screen-share active 19const UST_CHAT: i64 = 5 // chat panel open 20const UST_LEN: i64 = 6 21 22// INTENTS -- abstract user actions. The ADAPTER maps its native input (browser click / NishiOS touch / 23// hardware button) to one of these. The core never knows which adapter fired it. 24const UI_TOGGLE_MIC: i64 = 1 25const UI_TOGGLE_CAM: i64 = 2 26const UI_PEER_JOIN: i64 = 3 27const UI_PEER_LEAVE: i64 = 4 28const UI_SWAP_VIEW: i64 = 5 29const UI_TOGGLE_SHARE: i64 = 6 30const UI_TOGGLE_CHAT: i64 = 7 31 32// LAYOUT modes -- the DERIVED presentation the adapter must render. The "2-person = remote FULL + self in 33// the corner" law the operator asked for lives HERE (sovereign SSOT), not in browser JS. 34const LAYOUT_SOLO: i64 = 1 // just me 35const LAYOUT_DUO: i64 = 2 // exactly 2 -> remote full-screen + self PiP corner (+ tap-to-swap) 36const LAYOUT_GRID: i64 = 3 // 3+ -> equal tiles 37 38func uic_init(s: *i64) -> i64 { 39 s[UST_PEERS]=0; s[UST_MIC]=1; s[UST_CAM]=1; s[UST_SWAP]=0; s[UST_SHARE]=0; s[UST_CHAT]=0 40 return 0 41} 42// the adapter reports the current remote peer count (it owns the roster/transport); the core DERIVES the 43// layout. Clears swap when we drop out of DUO so the UI can't wedge. Returns the derived layout. 44func uic_set_peers(s: *i64, n: i64) -> i64 { 45 var m: i64 = n; if m < 0 { m = 0 } 46 s[UST_PEERS] = m 47 if s[UST_PEERS] != 1 { s[UST_SWAP] = 0 } 48 return uic_layout(s) 49} 50 51// DERIVED layout from peer count -- the sovereign rule any adapter renders. 52func uic_layout(s: *i64) -> i64 { 53 let total: i64 = s[UST_PEERS] + 1 54 if total <= 1 { return LAYOUT_SOLO } 55 if total == 2 { return LAYOUT_DUO } 56 return LAYOUT_GRID 57} 58 59// apply ONE intent. returns 0 ok, 1 = unknown intent (HONEST error, never a silent no-op). 60func uic_intent(s: *i64, intent: i64) -> i64 { 61 if intent == UI_TOGGLE_MIC { if s[UST_MIC]==1 { s[UST_MIC]=0 } else { s[UST_MIC]=1 } return 0 } 62 if intent == UI_TOGGLE_CAM { if s[UST_CAM]==1 { s[UST_CAM]=0 } else { s[UST_CAM]=1 } return 0 } 63 if intent == UI_PEER_JOIN { s[UST_PEERS]=s[UST_PEERS]+1; return 0 } 64 if intent == UI_PEER_LEAVE { if s[UST_PEERS]>0 { s[UST_PEERS]=s[UST_PEERS]-1 } if s[UST_PEERS]!=1 { s[UST_SWAP]=0 } return 0 } 65 if intent == UI_SWAP_VIEW { if uic_layout(s)==LAYOUT_DUO { if s[UST_SWAP]==1 { s[UST_SWAP]=0 } else { s[UST_SWAP]=1 } } return 0 } // swap ONLY applies in DUO -- the core enforces it so no adapter can wedge the UI 66 if intent == UI_TOGGLE_SHARE { if s[UST_SHARE]==1 { s[UST_SHARE]=0 } else { s[UST_SHARE]=1 } return 0 } 67 if intent == UI_TOGGLE_CHAT { if s[UST_CHAT]==1 { s[UST_CHAT]=0 } else { s[UST_CHAT]=1 } return 0 } 68 return 1 69} 70 71// state getters (the adapter reads these to render -- never touches the slots directly) 72func uic_mic(s: *i64) -> i64 { return s[UST_MIC] } 73func uic_cam(s: *i64) -> i64 { return s[UST_CAM] } 74func uic_peers(s: *i64) -> i64 { return s[UST_PEERS] } 75func uic_swapped(s: *i64) -> i64 { if uic_layout(s) == LAYOUT_DUO { return s[UST_SWAP] } return 0 } 76func uic_sharing(s: *i64) -> i64 { return s[UST_SHARE] } 77func uic_chat_open(s: *i64) -> i64 { return s[UST_CHAT] }