1use std::collections::BTreeMap;
24use std::sync::Arc;
25
26use sim_kernel::{Cx, DefaultFactory, EagerPolicy, Error, Expr, Result, Symbol};
27use sim_lib_view::codec::reduce_for_caps;
28use sim_lib_view::{
29 LensRegistry, SurfaceCaps, UNIVERSAL_EDITOR_ID, UNIVERSAL_VIEW_ID, register_universal_default,
30};
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub enum SurfaceRole {
35 Main,
37 Peer,
39}
40
41#[derive(Clone, Debug)]
46pub struct Broadcast {
47 pub surface: Symbol,
49 pub pane: Symbol,
51 pub scene: Expr,
53 pub diff: Expr,
55}
56
57#[derive(Clone, Debug)]
62pub struct EditRow {
63 pub resource: Symbol,
65 pub operator: Symbol,
67 pub tick: u64,
69 pub operation: Expr,
71}
72
73#[derive(Clone, Debug, PartialEq, Eq)]
75pub struct SurfaceBinding {
76 pub surface: Symbol,
78 pub pane: Symbol,
80 pub resource: Symbol,
82}
83
84struct Binding {
87 surface: Symbol,
88 pane: Symbol,
89 resource: Symbol,
90 last_scene: Expr,
91}
92
93impl Binding {
94 fn snapshot(&self) -> SurfaceBinding {
95 SurfaceBinding {
96 surface: self.surface.clone(),
97 pane: self.pane.clone(),
98 resource: self.resource.clone(),
99 }
100 }
101}
102
103pub struct SurfaceHub {
110 canonical: BTreeMap<Symbol, Expr>,
111 registry: LensRegistry,
112 cx: Cx,
113 surfaces: BTreeMap<Symbol, SurfaceCaps>,
114 roles: BTreeMap<Symbol, SurfaceRole>,
115 bindings: Vec<Binding>,
116 ledger: Vec<EditRow>,
117}
118
119impl Default for SurfaceHub {
120 fn default() -> Self {
121 Self::new()
122 }
123}
124
125impl SurfaceHub {
126 pub fn new() -> Self {
129 let mut registry = LensRegistry::new();
130 register_universal_default(&mut registry, false);
131 Self {
132 canonical: BTreeMap::new(),
133 registry,
134 cx: Cx::new(Arc::new(EagerPolicy), Arc::new(DefaultFactory)),
135 surfaces: BTreeMap::new(),
136 roles: BTreeMap::new(),
137 bindings: Vec::new(),
138 ledger: Vec::new(),
139 }
140 }
141
142 pub fn seed(&mut self, resource: Symbol, value: Expr) {
144 self.canonical.insert(resource, value);
145 }
146
147 pub fn register_surface(&mut self, surface: Symbol, caps: SurfaceCaps) {
150 self.register_surface_with_role(surface, caps, SurfaceRole::Main);
151 }
152
153 pub fn register_surface_with_role(
155 &mut self,
156 surface: Symbol,
157 caps: SurfaceCaps,
158 role: SurfaceRole,
159 ) {
160 self.roles.insert(surface.clone(), role);
161 self.surfaces.insert(surface, caps);
162 }
163
164 pub fn surface_role(&self, surface: &Symbol) -> Option<SurfaceRole> {
166 self.roles.get(surface).copied()
167 }
168
169 pub fn open(&mut self, surface: &Symbol, pane: Symbol, resource: Symbol) -> Result<Expr> {
176 let caps = self.caps_of(surface)?;
177 let value = self.value_of(&resource)?;
178 let scene = render_for_surface(&mut self.cx, &self.registry, &caps, &value)?;
179 self.bindings
180 .retain(|binding| !(binding.surface == *surface && binding.pane == pane));
181 self.bindings.push(Binding {
182 surface: surface.clone(),
183 pane,
184 resource,
185 last_scene: scene.clone(),
186 });
187 Ok(scene)
188 }
189
190 pub fn submit(
204 &mut self,
205 surface: &Symbol,
206 pane: &Symbol,
207 intent: &Expr,
208 ) -> Result<Vec<Broadcast>> {
209 let caps = self.caps_of(surface)?;
210 require_surface_input(&caps, intent)?;
211 let resource = self
212 .bindings
213 .iter()
214 .find(|binding| binding.surface == *surface && binding.pane == *pane)
215 .map(|binding| binding.resource.clone())
216 .ok_or_else(|| Error::HostError(format!("({surface}, {pane}) is not open")))?;
217 let value = self.value_of(&resource)?;
218
219 let editor = Symbol::new(UNIVERSAL_EDITOR_ID);
220 let draft = self
221 .registry
222 .propose(&mut self.cx, &editor, &value, intent)?;
223 let operation = self.registry.commit(&mut self.cx, &editor, &draft)?;
224 let new_value = apply_set_value(&operation.form)?;
225 self.commit_change(surface, pane, intent, new_value, operation.form)
226 }
227
228 pub fn commit_value_from(
236 &mut self,
237 surface: &Symbol,
238 pane: &Symbol,
239 intent: &Expr,
240 new_value: Expr,
241 ) -> Result<Vec<Broadcast>> {
242 sim_lib_intent::validate_intent(intent)
243 .map_err(|error| Error::HostError(format!("invalid intent: {error}")))?;
244 let caps = self.caps_of(surface)?;
245 require_surface_input(&caps, intent)?;
246 let resource = self
247 .bindings
248 .iter()
249 .find(|binding| binding.surface == *surface && binding.pane == *pane)
250 .map(|binding| binding.resource.clone())
251 .ok_or_else(|| Error::HostError(format!("({surface}, {pane}) is not open")))?;
252 self.commit_resource_change(resource, intent, new_value)
253 }
254
255 pub fn detach_surface(&mut self, surface: &Symbol) -> Vec<SurfaceBinding> {
258 self.surfaces.remove(surface);
259 self.roles.remove(surface);
260 let mut removed = Vec::new();
261 self.bindings.retain(|binding| {
262 if binding.surface == *surface {
263 removed.push(binding.snapshot());
264 false
265 } else {
266 true
267 }
268 });
269 removed
270 }
271
272 pub fn bindings_for_resource(&self, resource: &Symbol) -> Vec<SurfaceBinding> {
274 self.bindings
275 .iter()
276 .filter(|binding| binding.resource == *resource)
277 .map(Binding::snapshot)
278 .collect()
279 }
280
281 fn commit_change(
282 &mut self,
283 surface: &Symbol,
284 pane: &Symbol,
285 intent: &Expr,
286 new_value: Expr,
287 operation: Expr,
288 ) -> Result<Vec<Broadcast>> {
289 let resource = self
290 .bindings
291 .iter()
292 .find(|binding| binding.surface == *surface && binding.pane == *pane)
293 .map(|binding| binding.resource.clone())
294 .ok_or_else(|| Error::HostError(format!("({surface}, {pane}) is not open")))?;
295 self.commit_resource_change_with_operation(resource, intent, new_value, operation)
296 }
297
298 fn commit_resource_change(
299 &mut self,
300 resource: Symbol,
301 intent: &Expr,
302 new_value: Expr,
303 ) -> Result<Vec<Broadcast>> {
304 let operation = set_value_operation(new_value.clone());
305 self.commit_resource_change_with_operation(resource, intent, new_value, operation)
306 }
307
308 fn commit_resource_change_with_operation(
309 &mut self,
310 resource: Symbol,
311 intent: &Expr,
312 new_value: Expr,
313 operation: Expr,
314 ) -> Result<Vec<Broadcast>> {
315 let mut staged: Vec<(usize, Broadcast)> = Vec::new();
321 {
322 let Self {
323 registry,
324 cx,
325 surfaces,
326 bindings,
327 ..
328 } = self;
329 for (index, binding) in bindings.iter().enumerate() {
330 if binding.resource != resource {
331 continue;
332 }
333 let caps = surfaces.get(&binding.surface).ok_or_else(|| {
334 Error::HostError(format!(
335 "surface '{}' lost its capabilities",
336 binding.surface
337 ))
338 })?;
339 let scene = render_for_surface(cx, registry, caps, &new_value)?;
340 let diff = sim_lib_scene::diff(&binding.last_scene, &scene);
341 staged.push((
342 index,
343 Broadcast {
344 surface: binding.surface.clone(),
345 pane: binding.pane.clone(),
346 scene,
347 diff,
348 },
349 ));
350 }
351 }
352
353 self.canonical.insert(resource.clone(), new_value);
356 let (operator, tick) = origin_of(intent);
357 self.ledger.push(EditRow {
358 resource,
359 operator,
360 tick,
361 operation,
362 });
363 let mut broadcasts = Vec::with_capacity(staged.len());
364 for (index, broadcast) in staged {
365 self.bindings[index].last_scene = broadcast.scene.clone();
366 broadcasts.push(broadcast);
367 }
368 Ok(broadcasts)
369 }
370
371 pub fn handoff(
377 &mut self,
378 from: &Symbol,
379 to: &Symbol,
380 resource: Symbol,
381 pane: Symbol,
382 ) -> Result<Expr> {
383 let held = self
384 .bindings
385 .iter()
386 .any(|binding| binding.surface == *from && binding.resource == resource);
387 if !held {
388 return Err(Error::HostError(format!(
389 "surface '{from}' does not hold resource '{resource}' to hand off"
390 )));
391 }
392 self.open(to, pane, resource)
393 }
394
395 pub fn ledger(&self) -> &[EditRow] {
397 &self.ledger
398 }
399
400 pub fn canonical(&self, resource: &Symbol) -> Option<&Expr> {
402 self.canonical.get(resource)
403 }
404
405 fn caps_of(&self, surface: &Symbol) -> Result<SurfaceCaps> {
406 self.surfaces
407 .get(surface)
408 .cloned()
409 .ok_or_else(|| Error::HostError(format!("surface '{surface}' is not registered")))
410 }
411
412 fn value_of(&self, resource: &Symbol) -> Result<Expr> {
413 self.canonical.get(resource).cloned().ok_or_else(|| {
414 Error::HostError(format!("resource '{resource}' has no canonical value"))
415 })
416 }
417}
418
419pub fn replay(rows: &[EditRow], seed: BTreeMap<Symbol, Expr>) -> Result<BTreeMap<Symbol, Expr>> {
431 let mut state = seed;
432 for row in rows {
433 let value = apply_set_value(&row.operation)?;
434 state.insert(row.resource.clone(), value);
435 }
436 Ok(state)
437}
438
439fn render_for_surface(
441 cx: &mut Cx,
442 registry: &LensRegistry,
443 caps: &SurfaceCaps,
444 value: &Expr,
445) -> Result<Expr> {
446 let scene = registry.render(cx, &Symbol::new(UNIVERSAL_VIEW_ID), value)?;
447 Ok(reduce_for_caps(&scene, caps))
448}
449
450fn require_surface_input(caps: &SurfaceCaps, intent: &Expr) -> Result<()> {
451 let required = input_capabilities_for_intent(intent)?;
452 if required
453 .iter()
454 .any(|capability| caps.input_flag(capability))
455 {
456 return Ok(());
457 }
458 Err(Error::HostError(format!(
459 "surface '{}' does not accept any required input for this Intent: {}",
460 caps.client_id,
461 required.join(", ")
462 )))
463}
464
465fn input_capabilities_for_intent(intent: &Expr) -> Result<&'static [&'static str]> {
466 let kind = match sim_value::access::field(intent, "kind") {
467 Some(Expr::Symbol(kind)) if kind.namespace.as_deref() == Some("intent") => {
468 kind.name.as_ref()
469 }
470 Some(Expr::Symbol(_)) => {
471 return Err(Error::HostError(
472 "Intent kind must be in the intent namespace".to_owned(),
473 ));
474 }
475 _ => return Err(Error::HostError("submit input is not an Intent".to_owned())),
476 };
477 match kind {
478 "tap" | "dismiss" | "commit" | "cancel" | "approve" | "reject" | "pause-agent"
479 | "rerun-validation" | "replay-cassette" => Ok(&["tap", "pointer", "touch", "keyboard"]),
480 "select" | "move" | "wire" | "unwire" | "create" | "delete" | "scrub"
481 | "piano-roll-edit" | "player-rack-edit" | "arranger-edit" => Ok(&["pointer", "touch"]),
482 "invoke" => Ok(&[
483 "pointer",
484 "touch",
485 "tap",
486 "button",
487 "gaze",
488 "head",
489 "hand",
490 "controller",
491 "voice",
492 ]),
493 "edit" | "edit-field" | "set-param" | "set-lens" | "set-mode" | "open" | "ask"
494 | "split-mission" | "open-source" => Ok(&["keyboard", "touch", "voice"]),
495 "performance-event" => Ok(&["keyboard", "touch", "camera"]),
496 other => Err(Error::HostError(format!(
497 "no surface input capability mapping for intent/{other}"
498 ))),
499 }
500}
501
502fn set_value_operation(value: Expr) -> Expr {
503 Expr::Map(vec![
504 (
505 Expr::Symbol(Symbol::new("op")),
506 Expr::Symbol(Symbol::new("set-value")),
507 ),
508 (Expr::Symbol(Symbol::new("value")), value),
509 ])
510}
511
512fn apply_set_value(operation: &Expr) -> Result<Expr> {
515 let Expr::Map(entries) = operation else {
516 return Err(Error::HostError("operation is not a map".to_owned()));
517 };
518 let is_set_value = matches!(
519 sim_value::access::entry_field(entries, "op"),
520 Some(Expr::Symbol(symbol)) if &*symbol.name == "set-value"
521 );
522 if !is_set_value {
523 return Err(Error::HostError(
524 "operation is not a set-value op".to_owned(),
525 ));
526 }
527 sim_value::access::entry_field(entries, "value")
528 .cloned()
529 .ok_or_else(|| Error::HostError("set-value operation is missing a 'value'".to_owned()))
530}
531
532fn origin_of(intent: &Expr) -> (Symbol, u64) {
535 let origin = sim_value::access::field(intent, "origin");
536 let operator = origin
537 .and_then(|origin| sim_value::access::field_sym(origin, "operator"))
538 .unwrap_or_else(|| Symbol::new("unknown"));
539 let tick = origin
540 .and_then(|origin| sim_value::access::field_any(origin, "at-tick"))
541 .and_then(|tick| match tick {
542 Expr::Number(number) => number.canonical.parse::<u64>().ok(),
543 _ => None,
544 })
545 .unwrap_or(0);
546 (operator, tick)
547}
548
549#[cfg(test)]
550mod tests;