Skip to main content

cvkg_core/
lib.rs

1//! # CVKG Agentic Development Guidelines (v1.2)
2//!
3//! All AI agents contributing to this crate MUST follow ALL seven rules:
4//!
5//! ── Karpathy Guidelines (1–4) ────────────────────────────────────────────
6//! 1. THINK FIRST     — State assumptions. Surface ambiguity. Push back on complexity.
7//! 2. STAY SIMPLE     — Minimum code. No speculative features. No unasked-for abstractions.
8//! 3. BE SURGICAL     — Touch only what's required. Own your orphans. Don't improve neighbors.
9//! 4. VERIFY GOALS    — Turn tasks into checkable criteria. Loop until they pass. Never commit broken.
10//!
11//! ── CVKG Extended Protocols (5–7) ────────────────────────────────────────
12//! 5. TRIPLE-PASS     — Read the target, its surrounding context, and its full call graph
13//                      at least THREE TIMES before making any edit or revision.
14//! 6. COMMENT ALL     — Every major pub fn, unsafe block, and non-trivial algorithm in
15//                      every .rs/.ts/.h/.wgsl file MUST have a descriptive doc comment.
16//                      Comments describe WHY and WHAT CONTRACT, not HOW mechanically.
17//! 7. MONITOR LOOPS   — Check every tool call / command for progress every 30 seconds.
18//                      After 3 consecutive identical failures, stop, write BLOCKED.md,
19//                      and move to unblocked work. Never silently accept a broken state.
20//!
21//! Sources:
22//   Karpathy: https://github.com/multica-ai/andrej-karpathy-skills
23//   CVKG Extended: Section 2 of the CVKG Design Specification
24
25//! The View trait is the fundamental building block of CVKG. Every UI element — from a plain text label
26//! to a complex navigation controller — is a View. The trait is intentionally minimal; complexity emerges
27//! through modifier composition.
28//!
29//! # Conformance rules:
30//! 1. `body()` must be pure and side-effect free
31//! 2. Primitive views use `Never` as `Body` and register a `PaintCommand` directly with the scene graph
32//! 3. `View` types must implement `Send` but not necessarily `Sync`, enabling safe multi-threaded layout passes
33
34use serde::{Deserialize, Serialize};
35use std::collections::HashMap;
36use std::str::FromStr;
37
38pub mod security;
39
40/// Design token value that can adapt to light/dark mode
41#[derive(Debug, Clone, Serialize, Deserialize)]
42#[serde(untagged)]
43pub enum TokenValue {
44    /// Single value (same for light and dark)
45    Single { value: String },
46    /// Different values for light and dark mode
47    Adaptive { light: String, dark: String },
48}
49
50/// YggdrasilTokens is the authoritative container for all design tokens in the CVKG ecosystem.
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct YggdrasilTokens {
53    pub color: HashMap<String, TokenValue>,
54    pub font: HashMap<String, TokenValue>,
55    pub spacing: HashMap<String, TokenValue>,
56    pub radius: HashMap<String, TokenValue>,
57    pub shadow: HashMap<String, TokenValue>,
58    pub border: HashMap<String, TokenValue>,
59    pub anim: HashMap<String, TokenValue>,
60    pub bifrost: HashMap<String, TokenValue>,
61    pub gungnir: HashMap<String, TokenValue>,
62    pub mjolnir: HashMap<String, TokenValue>,
63    pub accessibility: HashMap<String, TokenValue>,
64}
65
66impl YggdrasilTokens {
67    pub fn new() -> Self {
68        Self {
69            color: HashMap::new(),
70            font: HashMap::new(),
71            spacing: HashMap::new(),
72            radius: HashMap::new(),
73            shadow: HashMap::new(),
74            border: HashMap::new(),
75            anim: HashMap::new(),
76            bifrost: HashMap::new(),
77            gungnir: HashMap::new(),
78            mjolnir: HashMap::new(),
79            accessibility: HashMap::new(),
80        }
81    }
82
83    /// Get a color token value for the current mode
84    pub fn get_color(&self, key: &str, is_dark: bool) -> Option<String> {
85        self.color.get(key).and_then(|token| match token {
86            TokenValue::Single { value } => Some(value.clone()),
87            TokenValue::Adaptive { light, dark } => {
88                if is_dark {
89                    Some(dark.clone())
90                } else {
91                    Some(light.clone())
92                }
93            }
94        })
95    }
96
97    /// Get a token value of any type and parse it into the target type
98    pub fn get<T: FromStr>(&self, category: &str, key: &str, is_dark: bool) -> Option<T> {
99        let map = match category {
100            "color" => &self.color,
101            "font" => &self.font,
102            "spacing" => &self.spacing,
103            "radius" => &self.radius,
104            "shadow" => &self.shadow,
105            "border" => &self.border,
106            "anim" => &self.anim,
107            "bifrost" => &self.bifrost,
108            "gungnir" => &self.gungnir,
109            "mjolnir" => &self.mjolnir,
110            "accessibility" => &self.accessibility,
111            _ => return None,
112        };
113
114        map.get(key).and_then(|token| match token {
115            TokenValue::Single { value } => value.parse().ok(),
116            TokenValue::Adaptive { light, dark } => {
117                let value = if is_dark { dark } else { light };
118                value.parse().ok()
119            }
120        })
121    }
122}
123
124pub trait View: Sized + Send {
125    /// The concrete type produced after applying modifiers.
126    /// For primitive views this is Self.
127    type Body: View;
128
129    fn body(self) -> Self::Body;
130
131    /// Render this view into the provided renderer at the specified bounds.
132    /// Primitive views override this to perform drawing operations.
133    fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
134
135    /// Calculate the natural (intrinsic) size of this view given proposed constraints.
136    /// This allows views like Buttons or Labels to inform the layout engine of their needs.
137    fn intrinsic_size(&self, _renderer: &mut dyn Renderer, _proposal: SizeProposal) -> Size {
138        Size::ZERO
139    }
140
141    /// Optionally provide a layout implementation for this view.
142    fn layout(&self) -> Option<&dyn layout::LayoutView> {
143        None
144    }
145
146    /// Returns the flex weight of this view for proportional distribution in stacks.
147    fn flex_weight(&self) -> f32 {
148        0.0
149    }
150
151    /// Provided modifier entry point
152    fn modifier<M: ViewModifier>(self, m: M) -> ModifiedView<Self, M> {
153        ModifiedView::new(self, m)
154    }
155
156    /// Apply a Bifrost (Frosted Glass) effect to the view
157    fn bifrost(
158        self,
159        blur: f32,
160        saturation: f32,
161        opacity: f32,
162    ) -> ModifiedView<Self, BifrostModifier> {
163        self.modifier(BifrostModifier {
164            blur,
165            saturation,
166            opacity,
167        })
168    }
169
170    /// Apply a Gungnir (Neon Glow) effect to the view
171    fn gungnir(
172        self,
173        color: impl Into<String>,
174        radius: f32,
175        intensity: f32,
176    ) -> ModifiedView<Self, GungnirModifier> {
177        self.modifier(GungnirModifier {
178            color: color.into(),
179            radius,
180            intensity,
181        })
182    }
183
184    /// Apply a Mjolnir Slice (Geometric cut) to the view
185    fn mjolnir_slice(self, angle: f32, offset: f32) -> ModifiedView<Self, MjolnirSliceModifier> {
186        self.modifier(MjolnirSliceModifier { angle, offset })
187    }
188
189    /// Apply a Mjolnir Shatter (Fragmented transition) to the view
190    fn mjolnir_shatter(
191        self,
192        pieces: u32,
193        force: f32,
194    ) -> ModifiedView<Self, MjolnirShatterModifier> {
195        self.modifier(MjolnirShatterModifier { pieces, force })
196    }
197
198    /// Mark this view as a Bifrost Bridge (Shared Element) for cross-view persistence
199    fn bifrost_bridge(self, id: impl Into<String>) -> ModifiedView<Self, BifrostBridgeModifier> {
200        self.modifier(BifrostBridgeModifier { id: id.into() })
201    }
202
203    /// Add a background color to this view
204    fn background(self, color: [f32; 4]) -> ModifiedView<Self, BackgroundModifier> {
205        self.modifier(BackgroundModifier { color })
206    }
207
208    /// Add padding to this view
209    fn padding(self, amount: f32) -> ModifiedView<Self, PaddingModifier> {
210        self.modifier(PaddingModifier { amount })
211    }
212
213    /// Set the opacity (alpha) of this view in the range [0.0, 1.0].
214    fn opacity(self, opacity: f32) -> ModifiedView<Self, OpacityModifier> {
215        self.modifier(OpacityModifier {
216            opacity: opacity.clamp(0.0, 1.0),
217        })
218    }
219
220    /// Override the foreground (text / icon) color of this view.
221    fn foreground_color(self, color: [f32; 4]) -> ModifiedView<Self, ForegroundColorModifier> {
222        self.modifier(ForegroundColorModifier { color })
223    }
224
225    /// Constrain this view to an explicit width and/or height.
226    fn frame(self, width: Option<f32>, height: Option<f32>) -> ModifiedView<Self, FrameModifier> {
227        self.modifier(FrameModifier { width, height })
228    }
229
230    /// Give this view a flex weight for proportional space distribution in stacks.
231    fn flex(self, weight: f32) -> ModifiedView<Self, FlexModifier> {
232        self.modifier(FlexModifier { weight })
233    }
234
235    /// Automatically add padding to avoid overlapping with platform safe areas (notches, bars).
236    fn safe_area_padding(self) -> ModifiedView<Self, SafeAreaModifier> {
237        self.modifier(SafeAreaModifier { ignores: false })
238    }
239
240    /// Explicitly ignore platform safe areas and draw into the margins.
241    fn ignores_safe_area(self) -> ModifiedView<Self, SafeAreaModifier> {
242        self.modifier(SafeAreaModifier { ignores: true })
243    }
244
245    /// Clip all child drawing to this view's bounds.
246    fn clip_to_bounds(self) -> ModifiedView<Self, ClipModifier> {
247        self.modifier(ClipModifier)
248    }
249
250    /// Draw a colored border around this view.
251    fn border(self, color: [f32; 4], width: f32) -> ModifiedView<Self, BorderModifier> {
252        self.modifier(BorderModifier { color, width })
253    }
254
255    /// Add elevation (shadow) to the view. Level determines the shadow depth.
256    fn elevation(self, level: f32) -> ModifiedView<Self, ElevationModifier> {
257        self.modifier(ElevationModifier { level })
258    }
259
260    /// Trigger an action when the view appears
261    fn on_appear<F: Fn() + Send + Sync + 'static>(
262        self,
263        action: F,
264    ) -> ModifiedView<Self, LifecycleModifier> {
265        self.modifier(LifecycleModifier {
266            on_appear: Some(Arc::new(action)),
267            on_disappear: None,
268        })
269    }
270
271    /// Trigger an action when the view disappears
272    fn on_disappear<F: Fn() + Send + Sync + 'static>(
273        self,
274        action: F,
275    ) -> ModifiedView<Self, LifecycleModifier> {
276        self.modifier(LifecycleModifier {
277            on_appear: None,
278            on_disappear: Some(Arc::new(action)),
279        })
280    }
281
282    /// Trigger an action when the view is clicked
283    fn on_click<F: Fn() + Send + Sync + 'static>(
284        self,
285        action: F,
286    ) -> ModifiedView<Self, OnClickModifier> {
287        self.modifier(OnClickModifier {
288            action: Arc::new(action),
289        })
290    }
291
292    /// Trigger an action when the pointer enters the view bounds
293    fn on_pointer_enter<F: Fn() + Send + Sync + 'static>(
294        self,
295        action: F,
296    ) -> ModifiedView<Self, OnPointerEnterModifier> {
297        self.modifier(OnPointerEnterModifier {
298            action: Arc::new(action),
299        })
300    }
301
302    /// Trigger an action when the pointer leaves the view bounds
303    fn on_pointer_leave<F: Fn() + Send + Sync + 'static>(
304        self,
305        action: F,
306    ) -> ModifiedView<Self, OnPointerLeaveModifier> {
307        self.modifier(OnPointerLeaveModifier {
308            action: Arc::new(action),
309        })
310    }
311
312    /// Trigger an action when the pointer moves inside the view bounds
313    fn on_pointer_move<F: Fn(f32, f32) + Send + Sync + 'static>(
314        self,
315        action: F,
316    ) -> ModifiedView<Self, OnPointerMoveModifier> {
317        self.modifier(OnPointerMoveModifier {
318            action: Arc::new(action),
319        })
320    }
321
322    /// Trigger an action when the pointer is pressed down
323    fn on_pointer_down<F: Fn() + Send + Sync + 'static>(
324        self,
325        action: F,
326    ) -> ModifiedView<Self, OnPointerDownModifier> {
327        self.modifier(OnPointerDownModifier {
328            action: Arc::new(action),
329        })
330    }
331
332    /// Trigger an action when the pointer is released
333    fn on_pointer_up<F: Fn() + Send + Sync + 'static>(
334        self,
335        action: F,
336    ) -> ModifiedView<Self, OnPointerUpModifier> {
337        self.modifier(OnPointerUpModifier {
338            action: Arc::new(action),
339        })
340    }
341
342    /// Type-erase this view into AnyView
343    fn erase(self) -> AnyView
344    where
345        Self: 'static,
346    {
347        AnyView::new(self)
348    }
349}
350
351/// An object-safe version of the View trait for type erasure.
352pub trait ErasedView: Send {
353    fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect);
354    fn name(&self) -> &'static str;
355    fn flex_weight_erased(&self) -> f32;
356}
357
358impl<V: View + 'static> ErasedView for V {
359    fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect) {
360        self.render(renderer, rect);
361    }
362
363    fn name(&self) -> &'static str {
364        std::any::type_name::<V>()
365    }
366
367    fn flex_weight_erased(&self) -> f32 {
368        self.flex_weight()
369    }
370}
371
372/// A type-erased View wrapper.
373pub struct AnyView {
374    inner: Box<dyn ErasedView>,
375}
376
377impl AnyView {
378    pub fn new<V: View + 'static>(view: V) -> Self {
379        Self {
380            inner: Box::new(view),
381        }
382    }
383}
384
385impl View for AnyView {
386    type Body = Never;
387    fn body(self) -> Self::Body {
388        unreachable!()
389    }
390
391    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
392        renderer.push_vnode(rect, self.inner.name());
393        self.inner.render_erased(renderer, rect);
394        renderer.pop_vnode();
395    }
396
397    fn flex_weight(&self) -> f32 {
398        self.inner.flex_weight_erased()
399    }
400}
401
402/// BifrostBridgeModifier enables shared-element transitions.
403/// When two views share the same Bifrost Bridge ID, the Sleipnir solver will
404/// interpolate their geometry and effects (blur, glow) during the transition.
405#[derive(Debug, Clone, PartialEq)]
406pub struct BifrostBridgeModifier {
407    pub id: String,
408}
409
410impl ViewModifier for BifrostBridgeModifier {
411    fn modify<V: View>(self, content: V) -> impl View {
412        ModifiedView::new(content, self)
413    }
414
415    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
416        // Register this element with the renderer for shared-element transition logic
417        renderer.register_shared_element(&self.id, rect);
418    }
419}
420
421/// MjolnirSliceModifier implements the "Geometric Slice" aesthetic.
422/// It uses a signed distance field (SDF) to clip the view along a sharp angled line.
423#[derive(Debug, Clone, Copy, PartialEq)]
424pub struct MjolnirSliceModifier {
425    pub angle: f32,
426    pub offset: f32,
427}
428
429impl ViewModifier for MjolnirSliceModifier {
430    fn modify<V: View>(self, content: V) -> impl View {
431        ModifiedView::new(content, self)
432    }
433
434    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
435        renderer.push_mjolnir_slice(self.angle, self.offset);
436    }
437
438    fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
439        renderer.pop_mjolnir_slice();
440    }
441}
442
443/// MjolnirShatterModifier implements the "Shattering" effect.
444/// It breaks the view into discrete geometric fragments that can be animated.
445#[derive(Debug, Clone, Copy, PartialEq)]
446pub struct MjolnirShatterModifier {
447    pub pieces: u32,
448    pub force: f32,
449}
450
451impl ViewModifier for MjolnirShatterModifier {
452    fn modify<V: View>(self, content: V) -> impl View {
453        ModifiedView::new(content, self)
454    }
455
456    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
457        // RADIAL SHATTER: Fragment the view into wedges
458        let pieces = self.pieces.max(1);
459        for i in 0..pieces {
460            let progress = i as f32 / pieces as f32;
461            let next_progress = (i + 1) as f32 / pieces as f32;
462
463            let angle_start = progress * 360.0;
464            let angle_end = next_progress * 360.0;
465
466            // Wedge slice: intersection of two half-planes
467            renderer.push_mjolnir_slice(angle_start, 0.0);
468            renderer.push_mjolnir_slice(angle_end + 180.0, 0.0);
469
470            // Apply radial force offset
471            let mid_angle = (angle_start + angle_end) / 2.0;
472            let rad = mid_angle.to_radians();
473            let dx = rad.cos() * self.force;
474            let dy = rad.sin() * self.force;
475
476            let shard_rect = Rect {
477                x: rect.x + dx,
478                y: rect.y + dy,
479                ..rect
480            };
481
482            view.render(renderer, shard_rect);
483
484            renderer.pop_mjolnir_slice();
485            renderer.pop_mjolnir_slice();
486        }
487    }
488}
489
490/// BifrostModifier implements the Cyberpunk "Frosted Glass" aesthetic.
491/// It triggers backdrop blurring and light scattering in the render pipeline.
492#[derive(Debug, Clone, Copy, PartialEq)]
493pub struct BifrostModifier {
494    pub blur: f32,
495    pub saturation: f32,
496    pub opacity: f32,
497}
498
499impl ViewModifier for BifrostModifier {
500    fn modify<V: View>(self, content: V) -> impl View {
501        ModifiedView::new(content, self)
502    }
503
504    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
505        renderer.bifrost(rect, self.blur, self.saturation, self.opacity);
506    }
507}
508
509/// A modifier that adds a background color to a view.
510#[derive(Debug, Clone, Copy, PartialEq)]
511pub struct BackgroundModifier {
512    pub color: [f32; 4],
513}
514
515impl ViewModifier for BackgroundModifier {
516    fn modify<V: View>(self, content: V) -> impl View {
517        ModifiedView::new(content, self)
518    }
519
520    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
521        renderer.fill_rect(rect, self.color);
522    }
523}
524
525/// A modifier that adds padding to a view.
526#[derive(Debug, Clone, Copy, PartialEq)]
527pub struct PaddingModifier {
528    pub amount: f32,
529}
530
531impl ViewModifier for PaddingModifier {
532    fn modify<V: View>(self, content: V) -> impl View {
533        ModifiedView::new(content, self)
534    }
535
536    fn transform_rect(&self, rect: Rect) -> Rect {
537        Rect {
538            x: rect.x + self.amount,
539            y: rect.y + self.amount,
540            width: (rect.width - 2.0 * self.amount).max(0.0),
541            height: (rect.height - 2.0 * self.amount).max(0.0),
542        }
543    }
544
545    fn transform_proposal(&self, mut proposal: SizeProposal) -> SizeProposal {
546        if let Some(w) = proposal.width {
547            proposal.width = Some((w - 2.0 * self.amount).max(0.0));
548        }
549        if let Some(h) = proposal.height {
550            proposal.height = Some((h - 2.0 * self.amount).max(0.0));
551        }
552        proposal
553    }
554
555    fn transform_size(&self, mut size: Size) -> Size {
556        size.width += 2.0 * self.amount;
557        size.height += 2.0 * self.amount;
558        size
559    }
560}
561
562/// GungnirModifier implements the "Neon Glow" aesthetic.
563/// It uses additive blending and multi-pass blurring to simulate glowing light.
564#[derive(Debug, Clone, PartialEq)]
565pub struct GungnirModifier {
566    pub color: String,
567    pub radius: f32,
568    pub intensity: f32,
569}
570
571impl ViewModifier for GungnirModifier {
572    fn modify<V: View>(self, content: V) -> impl View {
573        ModifiedView::new(content, self)
574    }
575
576    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
577        // Neon Glow using Mode 1 in the Surtr pipeline
578        renderer.stroke_rect(rect, [0.0, 1.0, 1.0, self.intensity], self.radius / 10.0);
579    }
580}
581
582/// GungnirPulseModifier implements a "breathing" neon effect.
583#[derive(Debug, Clone, Copy, PartialEq)]
584pub struct GungnirPulseModifier {
585    pub color: [f32; 4],
586    pub radius: f32,
587    pub speed: f32,
588}
589
590impl ViewModifier for GungnirPulseModifier {
591    fn modify<V: View>(self, content: V) -> impl View {
592        ModifiedView::new(content, self)
593    }
594
595    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
596        let time = std::time::SystemTime::now()
597            .duration_since(std::time::UNIX_EPOCH)
598            .unwrap_or_default()
599            .as_secs_f32();
600
601        // Mode 19: Dashed Border
602        // Mode 20: 9-Slice / Patch Scaling
603        let intensity = (time * self.speed).sin() * 0.5 + 0.5;
604        let mut color = self.color;
605        color[3] *= intensity;
606
607        // Mode 1 neon glow with dynamic intensity
608        renderer.stroke_rect(rect, color, self.radius);
609    }
610}
611
612/// Sleipnir spring parameters for the physics solver
613#[derive(Debug, Clone, Copy, PartialEq)]
614pub struct SleipnirParams {
615    pub stiffness: f32,
616    pub damping: f32,
617    pub mass: f32,
618}
619
620impl SleipnirParams {
621    pub fn snappy() -> Self { Self { stiffness: 230.0, damping: 22.0, mass: 1.0 } }
622    pub fn fluid() -> Self { Self { stiffness: 170.0, damping: 26.0, mass: 1.0 } }
623    pub fn heavy() -> Self { Self { stiffness: 90.0, damping: 20.0, mass: 1.0 } }
624    pub fn bouncy() -> Self { Self { stiffness: 190.0, damping: 14.0, mass: 1.0 } }
625}
626
627impl Default for SleipnirParams {
628    fn default() -> Self { Self::fluid() }
629}
630
631#[derive(Debug, Clone, Copy, PartialEq)]
632struct SolverState {
633    x: f32,
634    v: f32,
635}
636
637/// SleipnirSolver implements a 4th-order Runge-Kutta (RK4) integration for springs.
638/// This provides superior stability for high-fidelity interactive motion.
639#[derive(Debug, Clone, Copy, PartialEq)]
640pub struct SleipnirSolver {
641    params: SleipnirParams,
642    target: f32,
643    state: SolverState,
644}
645
646impl SleipnirSolver {
647    /// Create a new solver with a target value and starting state.
648    pub fn new(params: SleipnirParams, target: f32, current: f32) -> Self {
649        Self {
650            params,
651            target,
652            state: SolverState { x: current, v: 0.0 },
653        }
654    }
655
656    /// Advance the simulation by dt seconds using RK4 integration.
657    pub fn tick(&mut self, dt: f32) -> f32 {
658        if dt <= 0.0 { return self.state.x; }
659        
660        // Use a fixed time step for stability if dt is too large
661        let mut remaining = dt;
662        let step = 1.0 / 120.0;
663        
664        while remaining > 0.0 {
665            let d = remaining.min(step);
666            self.step(d);
667            remaining -= d;
668        }
669        
670        self.state.x
671    }
672
673    fn step(&mut self, dt: f32) {
674        let a = self.evaluate(self.state, 0.0, SolverState { x: 0.0, v: 0.0 });
675        let b = self.evaluate(self.state, dt * 0.5, a);
676        let c = self.evaluate(self.state, dt * 0.5, b);
677        let d = self.evaluate(self.state, dt, c);
678
679        let dxdt = 1.0 / 6.0 * (a.x + 2.0 * (b.x + c.x) + d.x);
680        let dvdt = 1.0 / 6.0 * (a.v + 2.0 * (b.v + c.v) + d.v);
681
682        self.state.x += dxdt * dt;
683        self.state.v += dvdt * dt;
684    }
685
686    fn evaluate(&self, initial: SolverState, dt: f32, d: SolverState) -> SolverState {
687        let state = SolverState {
688            x: initial.x + d.x * dt,
689            v: initial.v + d.v * dt,
690        };
691        let force = -self.params.stiffness * (state.x - self.target) - self.params.damping * state.v;
692        let mass = self.params.mass.max(0.001);
693        SolverState { x: state.v, v: force / mass }
694    }
695
696    pub fn is_settled(&self) -> bool {
697        (self.state.x - self.target).abs() < 0.001 && self.state.v.abs() < 0.001
698    }
699
700    pub fn set_target(&mut self, target: f32) {
701        self.target = target;
702    }
703
704    pub fn current_value(&self) -> f32 {
705        self.state.x
706    }
707}
708
709/// SleipnirModifier handles physics-based animations via the Sleipnir RK4 solver.
710#[derive(Debug, Clone, PartialEq)]
711pub struct SleipnirModifier {
712    pub id: u64,
713    pub target: f32,
714    pub params: SleipnirParams,
715}
716
717impl ViewModifier for SleipnirModifier {
718    fn modify<V: View>(self, content: V) -> impl View {
719        ModifiedView::new(content, self)
720    }
721
722    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
723        let state = load_system_state();
724        
725        // Try to fetch the solver from persistent state.
726        let solver_lock_opt = state.get_component_state::<SleipnirSolver>(self.id);
727        
728        let current_val;
729        
730        if let Some(lock) = solver_lock_opt {
731            // Found a solver. Tick it.
732            let mut solver = lock.write().unwrap();
733            solver.set_target(self.target);
734            current_val = solver.tick(renderer.delta_time());
735            
736            // If the solver hasn't settled yet, request another frame.
737            if !solver.is_settled() {
738                renderer.request_redraw();
739            }
740        } else {
741            // First time seeing this ID. Initialize solver state.
742            let solver = SleipnirSolver::new(
743                self.params,
744                self.target,
745                self.target // Initialize at target to avoid jump on first frame
746            );
747            
748            // Insert into registry for next frame.
749            get_system_state().rcu(|old| {
750                let mut new_state = (**old).clone();
751                new_state.set_component_state(self.id, solver.clone());
752                new_state
753            });
754            
755            current_val = self.target;
756        }
757
758        // Apply the solved value as a vertical translation.
759        renderer.push_transform([0.0, current_val], [1.0, 1.0], 0.0);
760        view.render(renderer, rect);
761        renderer.pop_transform();
762    }
763}
764
765/// TransformModifier applies a 2D transform (translation, scale, rotation) to its child.
766/// This modifier is "layout-neutral" and can be animated without re-running the layout engine.
767#[derive(Debug, Clone, Copy, PartialEq)]
768pub struct TransformModifier {
769    pub translation: [f32; 2],
770    pub scale: [f32; 2],
771    pub rotation: f32,
772}
773
774impl TransformModifier {
775    pub fn new() -> Self {
776        Self {
777            translation: [0.0, 0.0],
778            scale: [1.0, 1.0],
779            rotation: 0.0,
780        }
781    }
782
783    pub fn translate(mut self, x: f32, y: f32) -> Self {
784        self.translation = [x, y];
785        self
786    }
787
788    pub fn scale(mut self, x: f32, y: f32) -> Self {
789        self.scale = [x, y];
790        self
791    }
792
793    pub fn rotate(mut self, radians: f32) -> Self {
794        self.rotation = radians;
795        self
796    }
797}
798
799impl ViewModifier for TransformModifier {
800    fn modify<V: View>(self, content: V) -> impl View {
801        ModifiedView::new(content, self)
802    }
803
804    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
805        renderer.push_transform(self.translation, self.scale, self.rotation);
806        view.render(renderer, rect);
807        renderer.pop_transform();
808    }
809}
810
811/// LifecycleModifier handles on_appear and on_disappear hooks.
812
813#[derive(Clone)]
814pub struct LifecycleModifier {
815    pub on_appear: Option<Arc<dyn Fn() + Send + Sync>>,
816    pub on_disappear: Option<Arc<dyn Fn() + Send + Sync>>,
817}
818
819impl ViewModifier for LifecycleModifier {
820    fn modify<V: View>(self, content: V) -> impl View {
821        ModifiedView::new(content, self)
822    }
823}
824
825/// OpacityModifier fades this view and all its descendants to the given alpha.
826/// The renderer is expected to honour `push_opacity`/`pop_opacity` on the Renderer trait.
827#[derive(Debug, Clone, Copy, PartialEq)]
828pub struct OpacityModifier {
829    pub opacity: f32,
830}
831
832impl ViewModifier for OpacityModifier {
833    fn modify<V: View>(self, content: V) -> impl View {
834        ModifiedView::new(content, self)
835    }
836
837    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
838        renderer.push_opacity(self.opacity);
839    }
840
841    fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
842        renderer.pop_opacity();
843    }
844}
845
846/// OnClickModifier registers a click handler for this view.
847#[derive(Clone)]
848pub struct OnClickModifier {
849    pub action: Arc<dyn Fn() + Send + Sync>,
850}
851
852impl ViewModifier for OnClickModifier {
853    fn modify<V: View>(self, content: V) -> impl View {
854        ModifiedView::new(content, self)
855    }
856
857    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
858        let action = self.action.clone();
859        renderer.register_handler(
860            "pointerclick",
861            std::sync::Arc::new(move |event| {
862                if let Event::PointerClick { .. } = event {
863                    (action)();
864                }
865            }),
866        );
867    }
868}
869
870/// OnPointerEnterModifier registers a pointer enter handler.
871#[derive(Clone)]
872pub struct OnPointerEnterModifier {
873    pub action: Arc<dyn Fn() + Send + Sync>,
874}
875
876impl ViewModifier for OnPointerEnterModifier {
877    fn modify<V: View>(self, content: V) -> impl View {
878        ModifiedView::new(content, self)
879    }
880
881    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
882        let action = self.action.clone();
883        renderer.register_handler(
884            "pointerenter",
885            std::sync::Arc::new(move |event| {
886                if let Event::PointerEnter = event {
887                    (action)();
888                }
889            }),
890        );
891    }
892}
893
894/// OnPointerLeaveModifier registers a pointer leave handler.
895#[derive(Clone)]
896pub struct OnPointerLeaveModifier {
897    pub action: Arc<dyn Fn() + Send + Sync>,
898}
899
900impl ViewModifier for OnPointerLeaveModifier {
901    fn modify<V: View>(self, content: V) -> impl View {
902        ModifiedView::new(content, self)
903    }
904
905    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
906        let action = self.action.clone();
907        renderer.register_handler(
908            "pointerleave",
909            std::sync::Arc::new(move |event| {
910                if let Event::PointerLeave = event {
911                    (action)();
912                }
913            }),
914        );
915    }
916}
917
918/// OnPointerMoveModifier registers a pointer move handler.
919#[derive(Clone)]
920pub struct OnPointerMoveModifier {
921    pub action: Arc<dyn Fn(f32, f32) + Send + Sync>,
922}
923
924impl ViewModifier for OnPointerMoveModifier {
925    fn modify<V: View>(self, content: V) -> impl View {
926        ModifiedView::new(content, self)
927    }
928
929    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
930        let action = self.action.clone();
931        renderer.register_handler(
932            "pointermove",
933            std::sync::Arc::new(move |event| {
934                if let Event::PointerMove { x, y } = event {
935                    (action)(x, y);
936                }
937            }),
938        );
939    }
940}
941
942/// OnPointerDownModifier registers a pointer down handler.
943#[derive(Clone)]
944pub struct OnPointerDownModifier {
945    pub action: Arc<dyn Fn() + Send + Sync>,
946}
947
948impl ViewModifier for OnPointerDownModifier {
949    fn modify<V: View>(self, content: V) -> impl View {
950        ModifiedView::new(content, self)
951    }
952
953    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
954        let action = self.action.clone();
955        renderer.register_handler(
956            "pointerdown",
957            std::sync::Arc::new(move |event| {
958                if let Event::PointerDown { .. } = event {
959                    (action)();
960                }
961            }),
962        );
963    }
964}
965
966/// OnPointerUpModifier registers a pointer up handler.
967#[derive(Clone)]
968pub struct OnPointerUpModifier {
969    pub action: Arc<dyn Fn() + Send + Sync>,
970}
971
972impl ViewModifier for OnPointerUpModifier {
973    fn modify<V: View>(self, content: V) -> impl View {
974        ModifiedView::new(content, self)
975    }
976
977    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
978        let action = self.action.clone();
979        renderer.register_handler(
980            "pointerup",
981            std::sync::Arc::new(move |event| {
982                if let Event::PointerUp { .. } = event {
983                    (action)();
984                }
985            }),
986        );
987    }
988}
989
990/// ForegroundColorModifier overrides the foreground (text / icon) color inherited
991/// by all descendants until another ForegroundColorModifier is encountered.
992#[derive(Debug, Clone, Copy, PartialEq)]
993pub struct ForegroundColorModifier {
994    pub color: [f32; 4],
995}
996
997impl ViewModifier for ForegroundColorModifier {
998    fn modify<V: View>(self, content: V) -> impl View {
999        ModifiedView::new(content, self)
1000    }
1001}
1002
1003/// ClipModifier restricts all child drawing to the view's layout rectangle.
1004/// The renderer must support `push_clip_rect`/`pop_clip_rect`.
1005#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1006pub struct ClipModifier;
1007
1008impl ViewModifier for ClipModifier {
1009    fn modify<V: View>(self, content: V) -> impl View {
1010        ModifiedView::new(content, self)
1011    }
1012
1013    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1014        renderer.push_clip_rect(rect);
1015    }
1016
1017    fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1018        renderer.pop_clip_rect();
1019    }
1020}
1021
1022/// BorderModifier draws a solid-color border around the view bounds.
1023#[derive(Debug, Clone, Copy, PartialEq)]
1024pub struct BorderModifier {
1025    pub color: [f32; 4],
1026    pub width: f32,
1027}
1028
1029impl ViewModifier for BorderModifier {
1030    fn modify<V: View>(self, content: V) -> impl View {
1031        ModifiedView::new(content, self)
1032    }
1033
1034    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1035        renderer.stroke_rect(rect, self.color, self.width);
1036    }
1037}
1038
1039// Primitive (leaf) views implement Never as body
1040#[doc(hidden)]
1041pub enum Never {}
1042
1043impl View for Never {
1044    type Body = Never;
1045    fn body(self) -> Never {
1046        unreachable!()
1047    }
1048}
1049
1050/// A view that has been transformed by a modifier.
1051///
1052/// Section 4.3: "Each modifier implements ViewModifier and produces a ModifiedView<Inner, Self>."
1053pub struct ModifiedView<V, M> {
1054    view: V,
1055    modifier: M,
1056}
1057
1058impl<V: View, M: ViewModifier> ModifiedView<V, M> {
1059    #[doc(hidden)]
1060    pub fn new(view: V, modifier: M) -> Self {
1061        Self { view, modifier }
1062    }
1063}
1064
1065impl<V: View, M: ViewModifier> View for ModifiedView<V, M> {
1066    type Body = ModifiedView<V::Body, M>;
1067
1068    fn body(self) -> Self::Body {
1069        ModifiedView {
1070            view: self.view.body(),
1071            modifier: self.modifier.clone(),
1072        }
1073    }
1074
1075    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1076        self.modifier.render_view(&self.view, renderer, rect);
1077    }
1078
1079    fn intrinsic_size(&self, renderer: &mut dyn Renderer, proposal: SizeProposal) -> Size {
1080        self.modifier.measure_view(&self.view, renderer, proposal)
1081    }
1082
1083    fn flex_weight(&self) -> f32 {
1084        self.modifier.child_flex_weight(&self.view)
1085    }
1086}
1087
1088pub trait ViewModifier: Send + Clone {
1089    fn modify<V: View>(self, content: V) -> impl View;
1090
1091    /// Core rendering hook called before child views.
1092    fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
1093
1094    /// Cleanup hook called after child views.
1095    fn post_render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
1096
1097    /// Allows a modifier to completely override or wrap the rendering of its child.
1098    /// Default implementation performs a standard push -> transform -> render child -> pop sequence.
1099    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1100        self.render(renderer, rect);
1101        let child_rect = self.transform_rect(rect);
1102        view.render(renderer, child_rect);
1103        self.post_render(renderer, rect);
1104    }
1105
1106    fn transform_rect(&self, rect: Rect) -> Rect {
1107        rect
1108    }
1109
1110    /// Allows a modifier to transform the layout proposal before it reaches the child.
1111    fn transform_proposal(&self, proposal: SizeProposal) -> SizeProposal {
1112        proposal
1113    }
1114
1115    /// Allows a modifier to transform the resulting size from the child.
1116    fn transform_size(&self, size: Size) -> Size {
1117        size
1118    }
1119
1120    /// Measure hook that coordinates size propagation.
1121    fn measure_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, proposal: SizeProposal) -> Size {
1122        let child_proposal = self.transform_proposal(proposal);
1123        let child_size = view.intrinsic_size(renderer, child_proposal);
1124        self.transform_size(child_size)
1125    }
1126
1127    /// Allows a modifier to override or pass through the child's flex weight.
1128    fn child_flex_weight<V: View>(&self, view: &V) -> f32 {
1129        view.flex_weight()
1130    }
1131}
1132
1133/// TelemetryData tracks real-time performance metrics for the GPU renderer.
1134#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
1135pub struct TelemetryData {
1136    pub frame_time_ms: f32,
1137    
1138    // Pass timing
1139    pub input_time_ms: f32,
1140    pub state_flush_time_ms: f32,
1141    pub layout_time_ms: f32,
1142    pub draw_time_ms: f32,
1143    pub gpu_submit_time_ms: f32,
1144    
1145    pub draw_calls: u32,
1146    pub vertices: u32,
1147    
1148    // Memory breakdown
1149    pub vram_usage_mb: f32,
1150    pub vram_textures_mb: f32,
1151    pub vram_buffers_mb: f32,
1152    pub vram_pipelines_mb: f32,
1153}
1154
1155/// Configuration for render-loop frame timing and degradation strategies.
1156#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1157pub struct FrameBudget {
1158    /// Target frame time in milliseconds (default: 16.0 for 60FPS)
1159    pub target_ms: f32,
1160    /// If true, the renderer is allowed to dynamically skip non-critical effects
1161    /// (like heavy blurs or complex shadows) when the budget is exceeded.
1162    pub allow_degradation: bool,
1163}
1164
1165impl Default for FrameBudget {
1166    fn default() -> Self {
1167        Self {
1168            target_ms: 16.0,
1169            allow_degradation: true,
1170        }
1171    }
1172}
1173
1174/// The Renderer trait defines the atomic drawing operations for all CVKG backends.
1175/// This trait is object-safe and used by the View::render system.
1176///
1177/// # Implementation Requirements
1178/// 1. Coordinate system is origin-top-left (0,0) with Y increasing downwards.
1179/// 2. Colors are [R, G, B, A] in the [0.0, 1.0] range.
1180/// 3. All operations must be batchable by the underlying backend.
1181pub trait Renderer: Send {
1182    /// Returns the time elapsed since the last frame in seconds.
1183    fn delta_time(&self) -> f32;
1184
1185    /// Requests that the renderer redraws as soon as possible.
1186    /// Used for continuous animations.
1187    fn request_redraw(&mut self) {}
1188
1189    // ── Filled shapes ────────────────────────────────────────────────────
1190    fn fill_rect(&mut self, rect: Rect, color: [f32; 4]);
1191    fn fill_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4]);
1192    /// Fill an ellipse/circle that fits inside `rect`.
1193    fn fill_ellipse(&mut self, rect: Rect, color: [f32; 4]);
1194
1195    // ── Stroked shapes ───────────────────────────────────────────────────
1196    fn stroke_rect(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
1197    fn stroke_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4], stroke_width: f32);
1198    /// Stroke an ellipse/circle that fits inside `rect`.
1199    fn stroke_ellipse(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
1200    /// Draw a straight line from (x1,y1) to (x2,y2).
1201    fn draw_line(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: [f32; 4], stroke_width: f32);
1202
1203    // ── Text ─────────────────────────────────────────────────────────────
1204    fn draw_text(&mut self, text: &str, x: f32, y: f32, size: f32, color: [f32; 4]);
1205    /// Measure the width and height of the specified text.
1206    fn measure_text(&mut self, text: &str, size: f32) -> (f32, f32);
1207
1208    // ── Images & textures ────────────────────────────────────────────────
1209    /// Draw a texture (GPU-side) at the specified rect.
1210    fn draw_texture(&mut self, texture_id: u32, rect: Rect);
1211    /// Draw an image asset by name or path.
1212    fn draw_image(&mut self, image_name: &str, rect: Rect);
1213    /// Load an image asset from memory.
1214    fn load_image(&mut self, name: &str, data: &[u8]);
1215
1216    // ── Data Visualization ───────────────────────────────────────────────
1217    /// Upload raw float data as a GPU texture for heatmap rendering.
1218    fn upload_data_texture(&mut self, _id: &str, _data: &[f32], _width: u32, _height: u32) {}
1219    /// Draw a heatmap using a previously uploaded data texture.
1220    fn draw_heatmap(&mut self, _texture_id: &str, _rect: Rect, _palette: &str) {}
1221
1222    // ── 3D Objects ───────────────────────────────────────────────────────
1223    /// Draw a 3D mesh.
1224    fn draw_mesh(&mut self, _mesh: &Mesh, _color: [f32; 4], _transform: glam::Mat4) {}
1225
1226    // ── Advanced Visual Effects ──────────────────────────────────────────
1227    /// Draw a linear gradient between two colors at the specified angle.
1228    fn draw_linear_gradient(
1229        &mut self,
1230        _rect: Rect,
1231        _start_color: [f32; 4],
1232        _end_color: [f32; 4],
1233        _angle: f32,
1234    ) {
1235    }
1236    /// Draw a radial gradient between two colors.
1237    fn draw_radial_gradient(
1238        &mut self,
1239        _rect: Rect,
1240        _inner_color: [f32; 4],
1241        _outer_color: [f32; 4],
1242    ) {
1243    }
1244    /// Draw a high-fidelity drop shadow for a rounded rectangle.
1245    fn draw_drop_shadow(
1246        &mut self,
1247        _rect: Rect,
1248        _radius: f32,
1249        _color: [f32; 4],
1250        _blur: f32,
1251        _spread: f32,
1252    ) {
1253    }
1254    /// Draw a dashed border for a rounded rectangle.
1255    fn stroke_dashed_rounded_rect(
1256        &mut self,
1257        _rect: Rect,
1258        _radius: f32,
1259        _color: [f32; 4],
1260        _width: f32,
1261        _dash: f32,
1262        _gap: f32,
1263    ) {
1264    }
1265    /// Draw a 9-slice / patch scaled image.
1266    fn draw_9slice(
1267        &mut self,
1268        _image_name: &str,
1269        _rect: Rect,
1270        _left: f32,
1271        _top: f32,
1272        _right: f32,
1273        _bottom: f32,
1274    ) {
1275    }
1276
1277    // ── Clipping ─────────────────────────────────────────────────────────
1278    /// Push a clip rectangle.  All subsequent drawing is clipped to `rect`.
1279    /// Implementations that do not support clipping may ignore this call.
1280    fn push_clip_rect(&mut self, rect: Rect);
1281    /// Pop the most recently pushed clip rectangle.
1282    fn pop_clip_rect(&mut self);
1283
1284    // ── Global opacity ───────────────────────────────────────────────────
1285    /// Set a global opacity multiplier applied to all subsequent draw calls
1286    /// until `pop_opacity` is called.  `opacity` is in [0.0, 1.0].
1287    fn push_opacity(&mut self, opacity: f32);
1288    /// Restore the previous opacity level.
1289    fn pop_opacity(&mut self);
1290
1291    // ── Berserker Pipeline State ─────────────────────────────────────────
1292    fn set_theme(&mut self, _theme: ColorTheme) {}
1293    fn set_rage(&mut self, _rage: f32) {}
1294    fn trigger_shatter_event(&mut self, _origin: [f32; 2], _force: f32) {}
1295
1296    // ── Cyberpunk Effects ────────────────────────────────────────────────
1297    /// Apply a Bifrost (Frosted Glass) effect to the specified rect.
1298    fn bifrost(&mut self, rect: Rect, blur: f32, saturation: f32, opacity: f32);
1299    /// Push a Mjolnir Slice (geometric clipping).
1300    fn push_mjolnir_slice(&mut self, angle: f32, offset: f32);
1301    /// Pop the Mjolnir Slice.
1302    fn pop_mjolnir_slice(&mut self);
1303    /// Apply a Mjolnir Shatter effect (fragmentation) to the specified rect.
1304    fn mjolnir_shatter(&mut self, _rect: Rect, _pieces: u32, _force: f32, _color: [f32; 4]) {}
1305    fn mjolnir_fluid_shatter(&mut self, _rect: Rect, _pieces: u32, _force: f32, _color: [f32; 4]) {}
1306    /// Draw a Mjolnir Bolt (lightning strike) between two points.
1307    fn draw_mjolnir_bolt(&mut self, _from: [f32; 2], _to: [f32; 2], _color: [f32; 4]) {}
1308
1309    // ── Accessibility (ShieldWall) ───────────────────────────────────────
1310    fn set_aria_role(&mut self, _role: &str) {}
1311    fn set_aria_label(&mut self, _label: &str) {}
1312
1313    /// Register a shared element for Bifrost Bridge transitions.
1314    fn register_shared_element(&mut self, _id: &str, _rect: Rect) {}
1315
1316    /// Set a unique key for the current VDOM node to ensure stable identity during diffing.
1317    fn set_key(&mut self, _key: &str) {}
1318
1319
1320    // ── Telemetry ────────────────────────────────────────────────────────
1321    /// Get real-time performance telemetry.
1322    fn get_telemetry(&self) -> TelemetryData {
1323        TelemetryData::default()
1324    }
1325
1326    // ── GPU State Management ─────────────────────────────────────────────
1327    /// Push a shadow state to the stack. All following draw calls will have this shadow.
1328    fn push_shadow(&mut self, _radius: f32, _color: [f32; 4], _offset: [f32; 2]) {}
1329    /// Pop the last shadow state from the stack.
1330    fn pop_shadow(&mut self) {}
1331
1332    // ── VDOM & Scene Graph ───────────────────────────────────────────────
1333    /// Push a Virtual DOM node onto the stack for hierarchy tracking.
1334    fn push_vnode(&mut self, _rect: Rect, _name: &'static str) {}
1335    /// Pop the current Virtual DOM node from the stack.
1336    fn pop_vnode(&mut self) {}
1337    /// Register an event handler for the current VDOM node.
1338    fn register_handler(
1339        &mut self,
1340        _event_type: &str,
1341        _handler: std::sync::Arc<dyn Fn(Event) + Send + Sync>,
1342    ) {
1343    }
1344
1345    // ── Z-Index & Depth ──────────────────────────────────────────────────
1346    /// Set the current Z-index for depth sorting.
1347    /// Higher values appear closer to the viewer.
1348    fn set_z_index(&mut self, _z: f32) {}
1349    /// Get the current Z-index.
1350    fn get_z_index(&self) -> f32 {
1351        0.0
1352    }
1353
1354    // ── Vector Graphics ──────────────────────────────────────────────────
1355    /// Load an SVG model from raw bytes.
1356    fn load_svg(&mut self, _name: &str, _svg_data: &[u8]) {}
1357    /// Draw a pre-loaded SVG model.
1358    fn draw_svg(&mut self, _name: &str, _rect: Rect) {}
1359
1360    // ── GPU Transformations ──────────────────────────────────────────────
1361    /// Push a 2D transform (translation, scale, rotation) onto the stack.
1362    /// This transform should be applied to all subsequent draw calls until popped.
1363    /// Transform-only animations use this to avoid re-triggering the layout engine.
1364    fn push_transform(&mut self, _translation: [f32; 2], _scale: [f32; 2], _rotation: f32) {}
1365    /// Pop the last 2D transform from the stack.
1366    fn pop_transform(&mut self) {}
1367
1368    /// Return the resolved layout bounds for a specific node ID if tracked.
1369
1370    fn query_layout(&self, _node_id: scene_graph::NodeId) -> Option<Rect> {
1371        None
1372    }
1373
1374    /// Enable or disable the layout debug overlay (bounds, padding, margin).
1375    fn set_debug_layout(&mut self, _enabled: bool) {}
1376    /// Check if the layout debug overlay is currently enabled.
1377    fn get_debug_layout(&self) -> bool {
1378        false
1379    }
1380}
1381
1382/// Defines the hardware acceleration tier and feature set available to the renderer.
1383#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
1384pub enum RenderTier {
1385    /// High-performance GPU path (WebGPU / Vulkan / Metal / DX12) with full shader support.
1386    Tier1GPU = 0,
1387    /// Mid-tier GPU path (WebGL2 / OpenGL 3.3) with standard shader support.
1388    Tier2GPU = 1,
1389    /// Fallback software or basic hardware path (Canvas 2D / GDI+) with limited effects.
1390    Tier3Fallback = 2,
1391}
1392
1393// =============================================================================
1394// BERSERKER UNIFORMS
1395// =============================================================================
1396
1397use bytemuck::{Pod, Zeroable};
1398
1399/// Fully themeable color palette for the Berserker pipeline.
1400#[repr(C)]
1401#[derive(Copy, Clone, Debug, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
1402pub struct ColorTheme {
1403    pub primary_neon: [f32; 4], // (R, G, B, intensity)
1404    pub shatter_neon: [f32; 4],
1405    pub glass_base: [f32; 4],
1406    pub glass_edge: [f32; 4],
1407    pub rune_glow: [f32; 4],
1408    pub ember_core: [f32; 4],
1409    pub background_deep: [f32; 4],
1410    pub glass_blur_strength: f32,
1411    pub shatter_edge_width: f32,
1412    pub neon_bloom_radius: f32,
1413    pub rune_opacity: f32, // 0.0–1.0, default 0.55
1414    // Padding to ensure 16-byte alignment for GPU uniforms
1415    pub _pad: [f32; 3], // align to 16 bytes
1416    pub _pad2: f32,
1417}
1418
1419impl ColorTheme {
1420    pub fn cyberpunk_viking() -> Self {
1421        Self {
1422            primary_neon: [0.0, 1.0, 0.95, 1.2],
1423            shatter_neon: [1.0, 0.0, 0.75, 1.5],
1424            glass_base: [0.04, 0.04, 0.06, 0.82],
1425            glass_edge: [0.0, 0.45, 0.55, 0.6],
1426            rune_glow: [0.75, 0.98, 1.0, 0.9],
1427            ember_core: [0.95, 0.12, 0.12, 1.0],
1428            background_deep: [0.01, 0.01, 0.03, 1.0],
1429            glass_blur_strength: 0.6,
1430            shatter_edge_width: 1.8,
1431            neon_bloom_radius: 0.022,
1432            rune_opacity: 0.55,
1433            _pad: [0.0; 3],
1434            _pad2: 0.0,
1435        }
1436    }
1437
1438    pub fn vibrant_glass() -> Self {
1439        Self {
1440            primary_neon: [0.0, 1.0, 0.95, 1.2],
1441            shatter_neon: [1.0, 0.0, 0.75, 1.5],
1442            glass_base: [0.55, 0.6, 0.7, 0.08], // Luminous cool tint
1443            glass_edge: [0.7, 0.85, 1.0, 0.45], // Subtle blue-white rim
1444            rune_glow: [0.75, 0.98, 1.0, 0.9],
1445            ember_core: [1.0, 0.4, 0.1, 1.0],
1446            background_deep: [0.05, 0.05, 0.1, 1.0],
1447            glass_blur_strength: 0.9,
1448            shatter_edge_width: 1.8,
1449            neon_bloom_radius: 0.022,
1450            rune_opacity: 0.55,
1451            _pad: [0.0; 3],
1452            _pad2: 0.0,
1453        }
1454    }
1455}
1456
1457impl Default for ColorTheme {
1458    fn default() -> Self {
1459        Self::vibrant_glass()
1460    }
1461}
1462
1463/// Per-frame scene state for the Berserker pipeline.
1464#[repr(C)]
1465#[derive(Copy, Clone, Debug, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
1466pub struct SceneUniforms {
1467    pub view: glam::Mat4,
1468    pub proj: glam::Mat4,
1469    pub time: f32,
1470    pub delta_time: f32,
1471    pub resolution: [f32; 2],
1472    pub mouse: [f32; 2],
1473    pub mouse_velocity: [f32; 2],
1474    pub shatter_origin: [f32; 2],
1475    pub shatter_time: f32,
1476    pub shatter_force: f32,
1477    pub berzerker_rage: f32,
1478    pub scroll_offset: f32,
1479    pub scale_factor: f32,
1480    // Padding to ensure 16-byte alignment for GPU uniforms (47 f32s + 1 = 48)
1481    pub _pad: [f32; 1],
1482}
1483
1484impl SceneUniforms {
1485    pub fn new(width: f32, height: f32) -> Self {
1486        Self {
1487            view: glam::Mat4::IDENTITY,
1488            proj: glam::Mat4::orthographic_lh(0.0, width, height, 0.0, -100.0, 100.0),
1489            time: 0.0,
1490            delta_time: 0.016,
1491            resolution: [width, height],
1492            mouse: [0.5, 0.5],
1493            mouse_velocity: [0.0, 0.0],
1494            shatter_origin: [0.5, 0.5],
1495            shatter_time: -100.0,
1496            shatter_force: 0.0,
1497            berzerker_rage: 0.0,
1498            scroll_offset: 0.0,
1499            scale_factor: 1.0,
1500            _pad: [0.0; 1],
1501        }
1502    }
1503}
1504
1505/// A 3D mesh containing vertex and index data.
1506#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1507pub struct Mesh {
1508    pub vertices: Vec<[f32; 3]>,
1509    pub normals: Vec<[f32; 3]>,
1510    pub indices: Vec<u32>,
1511}
1512
1513impl Mesh {
1514    pub fn from_obj(data: &[u8]) -> anyhow::Result<Vec<Self>> {
1515        let mut cursor = std::io::Cursor::new(data);
1516        let (models, _) = tobj::load_obj_buf(&mut cursor, &tobj::LoadOptions::default(), |_| {
1517            Ok((Vec::new(), Default::default()))
1518        })?;
1519
1520        let mut meshes = Vec::new();
1521        for m in models {
1522            let mesh = m.mesh;
1523            let vertices: Vec<[f32; 3]> = mesh
1524                .positions
1525                .chunks(3)
1526                .map(|c| [c[0], c[1], c[2]])
1527                .collect();
1528            let normals = if mesh.normals.is_empty() {
1529                vec![[0.0, 0.0, 1.0]; vertices.len()]
1530            } else {
1531                mesh.normals.chunks(3).map(|c| [c[0], c[1], c[2]]).collect()
1532            };
1533            meshes.push(Mesh {
1534                vertices,
1535                normals,
1536                indices: mesh.indices,
1537            });
1538        }
1539        Ok(meshes)
1540    }
1541
1542    pub fn from_stl(data: &[u8]) -> anyhow::Result<Self> {
1543        let mut cursor = std::io::Cursor::new(data);
1544        let stl = stl_io::read_stl(&mut cursor)?;
1545
1546        let vertices: Vec<[f32; 3]> = stl.vertices.iter().map(|v| [v[0], v[1], v[2]]).collect();
1547        let mut indices = Vec::new();
1548        for face in stl.faces {
1549            indices.push(face.vertices[0] as u32);
1550            indices.push(face.vertices[1] as u32);
1551            indices.push(face.vertices[2] as u32);
1552        }
1553
1554        let normals = vec![[0.0, 0.0, 1.0]; vertices.len()];
1555
1556        Ok(Mesh {
1557            vertices,
1558            normals,
1559            indices,
1560        })
1561    }
1562}
1563
1564/// FrameRenderer extends Renderer with frame lifecycle management.
1565/// It is typically implemented by the host windowing/rendering environment.
1566pub trait FrameRenderer<E = ()>: Renderer {
1567    fn begin_frame(&mut self) -> E;
1568    fn end_frame(&mut self, encoder: E);
1569}
1570
1571use std::sync::Arc;
1572
1573/// State wrapper that owns a value and notifies subscribers when changed
1574#[derive(Clone)]
1575pub struct State<T: Clone + Send + Sync + 'static> {
1576    swap: Arc<arc_swap::ArcSwap<T>>,
1577    metadata_swap: Arc<arc_swap::ArcSwap<Option<agents::MutationMetadata>>>,
1578    #[cfg(not(target_arch = "wasm32"))]
1579    tvar: Arc<stm::TVar<T>>,
1580    #[cfg(not(target_arch = "wasm32"))]
1581    metadata_tvar: Arc<stm::TVar<Option<agents::MutationMetadata>>>,
1582    subscribers: Arc<std::sync::Mutex<Vec<Box<dyn Fn(&T) + Send + Sync>>>>,
1583    version: Arc<std::sync::atomic::AtomicU64>,
1584    resolution: agents::ConflictResolution,
1585}
1586
1587impl<T: Clone + Send + Sync + 'static> State<T> {
1588    /// Create a new State with initial value
1589    pub fn new(value: T) -> Self {
1590        #[cfg(not(target_arch = "wasm32"))]
1591        let tvar = Arc::new(stm::TVar::new(value.clone()));
1592        #[cfg(not(target_arch = "wasm32"))]
1593        let metadata_tvar = Arc::new(stm::TVar::new(None));
1594        
1595        Self {
1596            swap: Arc::new(arc_swap::ArcSwap::from_pointee(value)),
1597            metadata_swap: Arc::new(arc_swap::ArcSwap::new(Arc::new(None))),
1598            #[cfg(not(target_arch = "wasm32"))]
1599            tvar,
1600            #[cfg(not(target_arch = "wasm32"))]
1601            metadata_tvar,
1602            subscribers: Arc::new(std::sync::Mutex::new(Vec::new())),
1603            version: Arc::new(std::sync::atomic::AtomicU64::new(0)),
1604            resolution: agents::ConflictResolution::default(),
1605        }
1606    }
1607
1608    /// Set the conflict resolution strategy for this state.
1609    pub fn with_resolution(mut self, resolution: agents::ConflictResolution) -> Self {
1610        self.resolution = resolution;
1611        self
1612    }
1613
1614    /// Get the current value
1615    pub fn get(&self) -> T {
1616        (**self.swap.load()).clone()
1617    }
1618
1619    /// Set a new value, notifying all subscribers. Applies conflict resolution if agents are present.
1620    pub fn set(&self, value: T) {
1621        #[cfg(not(target_arch = "wasm32"))]
1622        let (was_skipped, final_val, final_meta) = stm::atomically(|tx| {
1623            let new_meta = agents::get_current_mutation_metadata();
1624            let existing_meta = self.metadata_tvar.read(tx)?;
1625            
1626            let mut skip = false;
1627            if self.resolution == agents::ConflictResolution::PriorityWins {
1628                if let (Some(new_m), Some(old_m)) = (new_meta, existing_meta) {
1629                    if new_m.priority < old_m.priority {
1630                        skip = true;
1631                    }
1632                }
1633            }
1634            
1635            if !skip {
1636                self.tvar.write(tx, value.clone())?;
1637                self.metadata_tvar.write(tx, new_meta)?;
1638                Ok((false, value.clone(), new_meta))
1639            } else {
1640                Ok((true, self.tvar.read(tx)?, existing_meta))
1641            }
1642        });
1643
1644        #[cfg(target_arch = "wasm32")]
1645        let (was_skipped, final_val, final_meta) = (false, value, agents::get_current_mutation_metadata());
1646
1647        if was_skipped {
1648            if let (Some(new_m), Some(old_m)) = (agents::get_current_mutation_metadata(), final_meta) {
1649                agents::notify_conflict(agents::ConflictEvent {
1650                    agent_id: new_m.agent_id,
1651                    priority: new_m.priority,
1652                    existing_agent_id: old_m.agent_id,
1653                    existing_priority: old_m.priority,
1654                    timestamp_ms: new_m.timestamp_ms,
1655                });
1656            }
1657            return;
1658        }
1659
1660        self.swap.store(Arc::new(final_val.clone()));
1661        self.metadata_swap.store(Arc::new(final_meta));
1662        self.version.fetch_add(1, std::sync::atomic::Ordering::Release);
1663        
1664        let subs = Arc::clone(&self.subscribers);
1665        if crate::is_batching() {
1666            crate::enqueue_batch_task(Box::new(move || {
1667                let s = subs.lock().unwrap();
1668                for cb in s.iter() {
1669                    cb(&final_val);
1670                }
1671            }));
1672        } else {
1673            let s = subs.lock().unwrap();
1674            for cb in s.iter() {
1675                cb(&final_val);
1676            }
1677        }
1678    }
1679
1680    pub fn mutate<F: Fn(&T) -> T>(&self, f: F) {
1681        #[cfg(not(target_arch = "wasm32"))]
1682        {
1683            let (was_skipped, final_val, final_meta) = stm::atomically(|tx| {
1684                let new_meta = agents::get_current_mutation_metadata();
1685                let existing_meta = self.metadata_tvar.read(tx)?;
1686                
1687                let mut skip = false;
1688                if self.resolution == agents::ConflictResolution::PriorityWins {
1689                    if let (Some(new_m), Some(old_m)) = (new_meta, existing_meta) {
1690                        if new_m.priority < old_m.priority {
1691                            skip = true;
1692                        }
1693                    }
1694                }
1695                
1696                if !skip {
1697                    let current = self.tvar.read(tx)?;
1698                    let next = f(&current);
1699                    self.tvar.write(tx, next.clone())?;
1700                    self.metadata_tvar.write(tx, new_meta)?;
1701                    Ok((false, next, new_meta))
1702                } else {
1703                    Ok((true, self.tvar.read(tx)?, existing_meta))
1704                }
1705            });
1706
1707            if was_skipped {
1708                if let (Some(new_m), Some(old_m)) = (agents::get_current_mutation_metadata(), final_meta) {
1709                    agents::notify_conflict(agents::ConflictEvent {
1710                        agent_id: new_m.agent_id,
1711                        priority: new_m.priority,
1712                        existing_agent_id: old_m.agent_id,
1713                        existing_priority: old_m.priority,
1714                        timestamp_ms: new_m.timestamp_ms,
1715                    });
1716                }
1717                return;
1718            }
1719
1720            self.swap.store(Arc::new(final_val.clone()));
1721            self.metadata_swap.store(Arc::new(final_meta));
1722            self.version.fetch_add(1, std::sync::atomic::Ordering::Release);
1723            
1724            let subs = Arc::clone(&self.subscribers);
1725            if crate::is_batching() {
1726                crate::enqueue_batch_task(Box::new(move || {
1727                    let s = subs.lock().unwrap();
1728                    for cb in s.iter() {
1729                        cb(&final_val);
1730                    }
1731                }));
1732            } else {
1733                let s = subs.lock().unwrap();
1734                for cb in s.iter() {
1735                    cb(&final_val);
1736                }
1737            }
1738        }
1739        #[cfg(target_arch = "wasm32")]
1740        {
1741            self.set(f(&self.get()));
1742        }
1743    }
1744
1745    /// Get current version
1746    pub fn version(&self) -> u64 {
1747        self.version.load(std::sync::atomic::Ordering::Acquire)
1748    }
1749
1750    /// Subscribe to state changes
1751    pub fn subscribe<F: Fn(&T) + Send + Sync + 'static>(&self, callback: F) {
1752        self.subscribers.lock().unwrap().push(Box::new(callback));
1753    }
1754}
1755
1756/// Error state for fault isolation at the component level.
1757///
1758/// Section 1.1: "Components must self-handle errors... isolating failures."
1759#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
1760pub struct ComponentErrorState {
1761    pub has_error: bool,
1762    pub error_message: Option<String>,
1763    pub error_location: Option<String>,
1764}
1765
1766impl ComponentErrorState {
1767    /// Create a new clear error state.
1768    pub fn clear() -> Self {
1769        Self::default()
1770    }
1771
1772    /// Create an error state with a message and location.
1773    pub fn error(message: impl Into<String>, location: impl Into<String>) -> Self {
1774        Self {
1775            has_error: true,
1776            error_message: Some(message.into()),
1777            error_location: Some(location.into()),
1778        }
1779    }
1780}
1781
1782/// A discrete fragment of knowledge stored in the agent's memory.
1783#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
1784pub struct KnowledgeFragment {
1785    /// Unique identifier for this fragment
1786    pub id: String,
1787    /// Short summary for prompt injection and quick search
1788    pub summary: String,
1789    /// Reference source (e.g. filename, URL, or conversation ID)
1790    pub source: String,
1791    /// Frame number or timestamp of creation
1792    pub created_at: u64,
1793    /// Number of times this fragment has been retrieved
1794    pub accessed_count: u32,
1795    /// Full content (optional, can be loaded on-demand)
1796    pub content: Option<String>,
1797}
1798
1799/// The KnowledgeState registry is the central repository for all agent-observable application data.
1800/// It stores both component-level states and high-level agentic memory fragments.
1801#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
1802pub struct KnowledgeState {
1803    /// Component states indexed by NodeId. Skipped in serialization as it contains opaque types.
1804    #[serde(skip)]
1805    pub component_states: std::collections::HashMap<u64, Arc<dyn std::any::Any + Send + Sync>>,
1806
1807    /// Map of IDs to knowledge fragments (Agentic Memory)
1808    pub fragments: HashMap<String, KnowledgeFragment>,
1809
1810    /// IDs of fragments returned by the last search query
1811    pub last_query_results: Vec<String>,
1812}
1813
1814use crate::runtime::NodeStateSnapshot;
1815use std::sync::atomic::{AtomicBool, Ordering};
1816use std::sync::OnceLock;
1817
1818/// Global application state registry.
1819pub static SYSTEM_STATE: OnceLock<Arc<arc_swap::ArcSwap<KnowledgeState>>> = OnceLock::new();
1820
1821#[cfg(not(target_arch = "wasm32"))]
1822static KNOWLEDGE_TVAR: OnceLock<stm::TVar<KnowledgeState>> = OnceLock::new();
1823
1824static IS_BATCHING: AtomicBool = AtomicBool::new(false);
1825pub static IS_RENDERING: AtomicBool = AtomicBool::new(false);
1826pub static LAYOUT_DIRTY: AtomicBool = AtomicBool::new(false);
1827static BATCH_QUEUE: OnceLock<std::sync::Mutex<Vec<Box<dyn FnOnce() + Send + Sync>>>> = OnceLock::new();
1828
1829/// Returns true if state updates are currently being batched.
1830pub fn is_batching() -> bool {
1831    IS_BATCHING.load(Ordering::Acquire)
1832}
1833
1834/// Returns true if the system is currently in the render phase.
1835pub fn is_rendering() -> bool {
1836    IS_RENDERING.load(Ordering::Acquire)
1837}
1838
1839/// Signals the start of the render phase. Mutations during this phase trigger warnings.
1840pub fn begin_render_phase() {
1841    IS_RENDERING.store(true, Ordering::Release);
1842}
1843
1844/// Signals the end of the render phase.
1845pub fn end_render_phase() {
1846    IS_RENDERING.store(false, Ordering::Release);
1847}
1848
1849/// Enqueues a notification task to be run when the current batch flushes.
1850pub fn enqueue_batch_task(task: Box<dyn FnOnce() + Send + Sync>) {
1851    let mut queue = BATCH_QUEUE
1852        .get_or_init(|| std::sync::Mutex::new(Vec::new()))
1853        .lock()
1854        .unwrap();
1855    queue.push(task);
1856}
1857
1858/// Executes multiple state updates in a single batch, deferring all subscriber
1859/// notifications until the closure completes. This prevents layout thrashing
1860/// and redundant render cycles when modifying multiple independent states.
1861pub fn batch<F: FnOnce()>(f: F) {
1862    if IS_BATCHING.swap(true, Ordering::AcqRel) {
1863        // Already inside a batch, just execute
1864        f();
1865        return;
1866    }
1867
1868    f();
1869
1870    IS_BATCHING.store(false, Ordering::Release);
1871    
1872    let mut queue = BATCH_QUEUE
1873        .get_or_init(|| std::sync::Mutex::new(Vec::new()))
1874        .lock()
1875        .unwrap();
1876    let tasks: Vec<_> = queue.drain(..).collect();
1877    drop(queue);
1878    
1879    for task in tasks {
1880        task();
1881    }
1882}
1883
1884/// Get a reference to the global system state.
1885pub fn get_system_state() -> Arc<arc_swap::ArcSwap<KnowledgeState>> {
1886    SYSTEM_STATE
1887        .get_or_init(|| Arc::new(arc_swap::ArcSwap::from_pointee(KnowledgeState::default())))
1888        .clone()
1889}
1890
1891pub fn load_system_state() -> arc_swap::Guard<Arc<KnowledgeState>> {
1892    get_system_state().load()
1893}
1894
1895pub fn update_system_state<F>(f: F)
1896where
1897    F: Fn(&KnowledgeState) -> KnowledgeState,
1898{
1899    if is_rendering() {
1900        log::warn!("LAYOUT THRASH DETECTED: System state mutated during render phase. This may trigger redundant layout passes and impact performance.");
1901    }
1902
1903    LAYOUT_DIRTY.store(true, Ordering::SeqCst);
1904
1905    let swap = get_system_state();
1906    let current = swap.load();
1907    let new_state = Arc::new(f(&current));
1908    swap.store(Arc::clone(&new_state));
1909
1910    #[cfg(not(target_arch = "wasm32"))]
1911    {
1912        let tvar = KNOWLEDGE_TVAR
1913            .get_or_init(|| stm::TVar::new((*new_state).clone()));
1914        let _ = stm::atomically(|tx| tvar.write(tx, (*new_state).clone()));
1915    }
1916}
1917
1918pub fn transact_system_state<F>(f: F)
1919where
1920    F: Fn(&KnowledgeState) -> KnowledgeState,
1921{
1922    #[cfg(not(target_arch = "wasm32"))]
1923    {
1924        if is_rendering() {
1925            log::warn!("LAYOUT THRASH DETECTED: System state mutated during render phase. This may trigger redundant layout passes and impact performance.");
1926        }
1927        let tvar = KNOWLEDGE_TVAR
1928            .get_or_init(|| {
1929                stm::TVar::new((**get_system_state().load()).clone())
1930            })
1931            .clone();
1932        let new_state = stm::atomically(move |tx| {
1933            let current = tvar.read(tx)?;
1934            let next = f(&current);
1935            tvar.write(tx, next.clone())?;
1936            Ok(next)
1937        });
1938        get_system_state().store(Arc::new(new_state));
1939    }
1940    #[cfg(target_arch = "wasm32")]
1941    {
1942        if is_rendering() {
1943            log::warn!("LAYOUT THRASH DETECTED: System state mutated during render phase. This may trigger redundant layout passes and impact performance.");
1944        }
1945        update_system_state(f);
1946    }
1947}
1948
1949impl KnowledgeState {
1950    /// Create a new empty KnowledgeState.
1951    pub fn new() -> Self {
1952        Self::default()
1953    }
1954
1955    /// Set a component's internal state.
1956    pub fn set_component_state<T: 'static + Send + Sync>(&mut self, id: u64, state: T) {
1957        self.component_states
1958            .insert(id, Arc::new(std::sync::RwLock::new(state)));
1959    }
1960
1961    /// Get a reference to a component's internal state.
1962    pub fn get_component_state<T: 'static + Send + Sync>(
1963        &self,
1964        id: u64,
1965    ) -> Option<Arc<std::sync::RwLock<T>>> {
1966        let lock = self.component_states.get(&id)?;
1967        lock.clone().downcast::<std::sync::RwLock<T>>().ok()
1968    }
1969
1970    /// Add a new fragment to memory.
1971    pub fn remember(&mut self, fragment: KnowledgeFragment) {
1972        self.fragments.insert(fragment.id.clone(), fragment);
1973    }
1974
1975    /// Process a search query against the local knowledge base.
1976    pub fn process_query(&mut self, query: &str) {
1977        let query_lower = query.to_lowercase();
1978        let mut results: Vec<(f32, String)> = self
1979            .fragments
1980            .iter()
1981            .map(|(id, frag)| {
1982                let mut score = 0.0;
1983                if frag.summary.to_lowercase().contains(&query_lower) {
1984                    score += 1.0;
1985                }
1986                if frag.source.to_lowercase().contains(&query_lower) {
1987                    score += 0.5;
1988                }
1989                (score, id.clone())
1990            })
1991            .filter(|(score, _)| *score > 0.0)
1992            .collect();
1993
1994        // Sort by relevance score
1995        results.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
1996
1997        self.last_query_results = results.into_iter().map(|(_, id)| id).take(5).collect();
1998    }
1999
2000    /// Captures a snapshot of the current state for debugging and hot-reloading.
2001    pub fn snapshot(&self) -> Vec<NodeStateSnapshot> {
2002        let mut snapshots = Vec::new();
2003
2004        // Snapshots of agentic fragments
2005        for (_id, frag) in &self.fragments {
2006            if let Ok(val) = serde_json::to_value(frag) {
2007                snapshots.push(NodeStateSnapshot { id: 0, state: val });
2008            }
2009        }
2010
2011        snapshots
2012    }
2013}
2014
2015/// A read/write projection into a `State<T>` owned elsewhere.
2016#[derive(Clone)]
2017pub struct Binding<T: Clone + Send + Sync + 'static> {
2018    swap: Arc<arc_swap::ArcSwap<T>>,
2019    #[cfg(not(target_arch = "wasm32"))]
2020    tvar: Arc<stm::TVar<T>>,
2021    version: Arc<std::sync::atomic::AtomicU64>,
2022}
2023
2024impl<T: Clone + Send + Sync + 'static> Binding<T> {
2025    /// Create a binding from a State
2026    pub fn from_state(state: &State<T>) -> Self {
2027        Self {
2028            swap: Arc::clone(&state.swap),
2029            #[cfg(not(target_arch = "wasm32"))]
2030            tvar: Arc::clone(&state.tvar),
2031            version: Arc::clone(&state.version),
2032        }
2033    }
2034
2035    /// Get the current value
2036    pub fn get(&self) -> T {
2037        (**self.swap.load()).clone()
2038    }
2039
2040    /// Set a new value
2041    pub fn set(&self, value: T) {
2042        self.swap.store(Arc::new(value.clone()));
2043        #[cfg(not(target_arch = "wasm32"))]
2044        {
2045            let tvar = Arc::clone(&self.tvar);
2046            let v = value.clone();
2047            let _ = stm::atomically(move |tx| tvar.write(tx, v.clone()));
2048        }
2049        self.version.fetch_add(1, std::sync::atomic::Ordering::Release);
2050    }
2051
2052    /// Get current version
2053    pub fn version(&self) -> u64 {
2054        self.version.load(std::sync::atomic::Ordering::Acquire)
2055    }
2056}
2057
2058#[cfg(not(target_arch = "wasm32"))]
2059pub fn transact_pair<A, B, F>(state_a: &State<A>, state_b: &State<B>, f: F)
2060where
2061    A: Clone + Send + Sync + 'static,
2062    B: Clone + Send + Sync + 'static,
2063    F: Fn(&A, &B) -> (A, B),
2064{
2065    let tvar_a = Arc::clone(&state_a.tvar);
2066    let tvar_b = Arc::clone(&state_b.tvar);
2067    let (new_a, new_b) = stm::atomically(move |tx| {
2068        let a = tvar_a.read(tx)?;
2069        let b = tvar_b.read(tx)?;
2070        let (na, nb) = f(&a, &b);
2071        tvar_a.write(tx, na.clone())?;
2072        tvar_b.write(tx, nb.clone())?;
2073        Ok((na, nb))
2074    });
2075    state_a.swap.store(Arc::new(new_a.clone()));
2076    state_b.swap.store(Arc::new(new_b.clone()));
2077    state_a.version.fetch_add(1, std::sync::atomic::Ordering::Release);
2078    state_b.version.fetch_add(1, std::sync::atomic::Ordering::Release);
2079    
2080    let subs_a = Arc::clone(&state_a.subscribers);
2081    let subs_b = Arc::clone(&state_b.subscribers);
2082    
2083    if crate::is_batching() {
2084        crate::enqueue_batch_task(Box::new(move || {
2085            {
2086                let s = subs_a.lock().unwrap();
2087                for cb in s.iter() { cb(&new_a); }
2088            }
2089            {
2090                let s = subs_b.lock().unwrap();
2091                for cb in s.iter() { cb(&new_b); }
2092            }
2093        }));
2094    } else {
2095        {
2096            let s = subs_a.lock().unwrap();
2097            for cb in s.iter() { cb(&new_a); }
2098        }
2099        {
2100            let s = subs_b.lock().unwrap();
2101            for cb in s.iter() { cb(&new_b); }
2102        }
2103    }
2104}
2105
2106use std::any::TypeId;
2107use std::sync::Mutex;
2108
2109/// Global environment storage using TypeId as keys.
2110pub(crate) static ENVIRONMENT: OnceLock<
2111    Mutex<HashMap<TypeId, Box<dyn std::any::Any + Send + Sync>>>,
2112> = OnceLock::new();
2113
2114/// Environment key type for accessing ambient values
2115///
2116/// Implement this trait to define a new environment key.
2117pub trait EnvKey: 'static + Send + Sync {
2118    /// The type of value stored in the environment
2119    type Value: Clone + Send + Sync + 'static;
2120
2121    /// Get a default value for this key
2122    fn default_value() -> Self::Value;
2123}
2124
2125/// Key for accessing the Yggdrasil design token tree
2126pub struct YggdrasilKey;
2127
2128impl EnvKey for YggdrasilKey {
2129    type Value = YggdrasilTokens;
2130    fn default_value() -> Self::Value {
2131        default_tokens()
2132    }
2133}
2134
2135/// Key for accessing the AssetManager
2136pub struct AssetKey;
2137
2138impl EnvKey for AssetKey {
2139    type Value = Arc<dyn AssetManager>;
2140    fn default_value() -> Self::Value {
2141        Arc::new(DefaultAssetManager::new())
2142    }
2143}
2144
2145/// System appearance (Light/Dark mode)
2146#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2147pub enum Appearance {
2148    Light,
2149    Dark,
2150}
2151
2152/// Orientation for layouts
2153#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2154pub enum Orientation {
2155    Horizontal,
2156    Vertical,
2157}
2158
2159/// Cross-axis alignment for layout containers.
2160#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
2161pub enum Alignment {
2162    #[default]
2163    Center,
2164    Leading,
2165    Trailing,
2166    Top,
2167    Bottom,
2168}
2169
2170/// Main-axis distribution for linear layout containers.
2171#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
2172pub enum Distribution {
2173    #[default]
2174    Fill,
2175    Center,
2176    Leading,
2177    Trailing,
2178    SpaceBetween,
2179    SpaceAround,
2180    SpaceEvenly,
2181}
2182
2183/// A color represented by RGBA components in the [0.0, 1.0] range.
2184#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
2185pub struct Color {
2186    pub r: f32,
2187    pub g: f32,
2188    pub b: f32,
2189    pub a: f32,
2190}
2191
2192impl Color {
2193    pub const BLACK: Color = Color {
2194        r: 0.0,
2195        g: 0.0,
2196        b: 0.0,
2197        a: 1.0,
2198    };
2199    pub const WHITE: Color = Color {
2200        r: 1.0,
2201        g: 1.0,
2202        b: 1.0,
2203        a: 1.0,
2204    };
2205    pub const TRANSPARENT: Color = Color {
2206        r: 0.0,
2207        g: 0.0,
2208        b: 0.0,
2209        a: 0.0,
2210    };
2211    pub const RED: Color = Color {
2212        r: 1.0,
2213        g: 0.0,
2214        b: 0.0,
2215        a: 1.0,
2216    };
2217    pub const GREEN: Color = Color {
2218        r: 0.0,
2219        g: 1.0,
2220        b: 0.0,
2221        a: 1.0,
2222    };
2223    pub const BLUE: Color = Color {
2224        r: 0.0,
2225        g: 0.0,
2226        b: 1.0,
2227        a: 1.0,
2228    };
2229
2230    /// Calculate the relative luminance of the color as defined by WCAG 2.x
2231    pub fn relative_luminance(&self) -> f32 {
2232        fn res(c: f32) -> f32 {
2233            if c <= 0.03928 {
2234                c / 12.92
2235            } else {
2236                ((c + 0.055) / 1.055).powf(2.4)
2237            }
2238        }
2239        0.2126 * res(self.r) + 0.7152 * res(self.g) + 0.0722 * res(self.b)
2240    }
2241
2242    /// Calculate the contrast ratio between this color and another color
2243    pub fn contrast_ratio(&self, other: &Color) -> f32 {
2244        let l1 = self.relative_luminance();
2245        let l2 = other.relative_luminance();
2246        if l1 > l2 {
2247            (l1 + 0.05) / (l2 + 0.05)
2248        } else {
2249            (l2 + 0.05) / (l1 + 0.05)
2250        }
2251    }
2252
2253    pub const CYAN: Color = Color {
2254        r: 0.0,
2255        g: 1.0,
2256        b: 1.0,
2257        a: 1.0,
2258    };
2259    pub const YELLOW: Color = Color {
2260        r: 1.0,
2261        g: 1.0,
2262        b: 0.0,
2263        a: 1.0,
2264    };
2265    pub const MAGENTA: Color = Color {
2266        r: 1.0,
2267        g: 0.0,
2268        b: 1.0,
2269        a: 1.0,
2270    };
2271    pub const GRAY: Color = Color {
2272        r: 0.5,
2273        g: 0.5,
2274        b: 0.5,
2275        a: 1.0,
2276    };
2277
2278    /// Create a new color from RGBA components.
2279    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
2280        Self { r, g, b, a }
2281    }
2282
2283    /// Convert the color to a [r, g, b, a] array.
2284    pub fn as_array(&self) -> [f32; 4] {
2285        [self.r, self.g, self.b, self.a]
2286    }
2287}
2288
2289impl View for Color {
2290    type Body = Never;
2291    fn body(self) -> Self::Body {
2292        unreachable!()
2293    }
2294
2295    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
2296        renderer.fill_rect(rect, self.as_array());
2297    }
2298}
2299
2300/// Key for accessing the current system appearance
2301pub struct AppearanceKey;
2302
2303impl EnvKey for AppearanceKey {
2304    type Value = Appearance;
2305    fn default_value() -> Self::Value {
2306        Appearance::Dark // Default to Dark (Ginnungagap) for Berserker aesthetic
2307    }
2308}
2309
2310/// StyleResolver provides high-level access to themed values from the environment.
2311pub struct StyleResolver;
2312
2313impl StyleResolver {
2314    /// Resolve a color from the current environment
2315    pub fn color(key: &str) -> String {
2316        let tokens = Environment::<YggdrasilKey>::new().get();
2317        let appearance = Environment::<AppearanceKey>::new().get();
2318        let is_dark = appearance == Appearance::Dark;
2319
2320        tokens
2321            .get_color(key, is_dark)
2322            .unwrap_or_else(|| "#FF00FF".to_string()) // Default to MuspelMagenta on failure
2323    }
2324
2325    /// Resolve a generic token value
2326    pub fn get<T: FromStr>(category: &str, key: &str) -> Option<T> {
2327        let tokens = Environment::<YggdrasilKey>::new().get();
2328        let appearance = Environment::<AppearanceKey>::new().get();
2329        let is_dark = appearance == Appearance::Dark;
2330
2331        tokens.get(category, key, is_dark)
2332    }
2333}
2334
2335/// The authoritative Cyberpunk Viking default tokens
2336pub fn default_tokens() -> YggdrasilTokens {
2337    let mut tokens = YggdrasilTokens::new();
2338
2339    // Core Norse Colorways
2340    tokens.color.insert(
2341        "background".to_string(),
2342        TokenValue::Single {
2343            value: "#000000".to_string(), // Ginnungagap (The Void)
2344        },
2345    );
2346
2347    tokens.color.insert(
2348        "primary".to_string(),
2349        TokenValue::Single {
2350            value: "#00FFFF".to_string(), // NiflCyan (Aesir Primary)
2351        },
2352    );
2353
2354    tokens.color.insert(
2355        "secondary".to_string(),
2356        TokenValue::Single {
2357            value: "#FF00FF".to_string(), // MuspelMagenta (Berserker Secondary)
2358        },
2359    );
2360
2361    tokens.color.insert(
2362        "surface".to_string(),
2363        TokenValue::Adaptive {
2364            light: "#FFFFFF".to_string(),
2365            dark: "#121212".to_string(),
2366        },
2367    );
2368
2369    tokens.color.insert(
2370        "text".to_string(),
2371        TokenValue::Adaptive {
2372            light: "#000000".to_string(),
2373            dark: "#FFFFFF".to_string(),
2374        },
2375    );
2376
2377    // Bifrost (Glassmorphism) - Frosted Style
2378    tokens.bifrost.insert(
2379        "blur".to_string(),
2380        TokenValue::Single {
2381            value: "25.0".to_string(),
2382        },
2383    );
2384    tokens.bifrost.insert(
2385        "saturation".to_string(),
2386        TokenValue::Single {
2387            value: "1.2".to_string(),
2388        },
2389    );
2390    tokens.bifrost.insert(
2391        "opacity".to_string(),
2392        TokenValue::Single {
2393            value: "0.65".to_string(),
2394        },
2395    );
2396
2397    // Gungnir (Neon Glow)
2398    tokens.gungnir.insert(
2399        "intensity".to_string(),
2400        TokenValue::Single {
2401            value: "1.0".to_string(),
2402        },
2403    );
2404    tokens.gungnir.insert(
2405        "radius".to_string(),
2406        TokenValue::Single {
2407            value: "15.0".to_string(),
2408        },
2409    );
2410
2411    // Mjolnir (Sharp Geometry)
2412    tokens.mjolnir.insert(
2413        "clip_angle".to_string(),
2414        TokenValue::Single {
2415            value: "12.0".to_string(),
2416        },
2417    );
2418    tokens.mjolnir.insert(
2419        "border_width".to_string(),
2420        TokenValue::Single {
2421            value: "2.0".to_string(),
2422        },
2423    );
2424
2425    // Sleipnir (Spring Animation)
2426    tokens.anim.insert(
2427        "stiffness".to_string(),
2428        TokenValue::Single {
2429            value: "170.0".to_string(),
2430        },
2431    );
2432    tokens.anim.insert(
2433        "damping".to_string(),
2434        TokenValue::Single {
2435            value: "26.0".to_string(),
2436        },
2437    );
2438    tokens.anim.insert(
2439        "mass".to_string(),
2440        TokenValue::Single {
2441            value: "1.0".to_string(),
2442        },
2443    );
2444
2445    // Accessibility
2446    tokens.accessibility.insert(
2447        "reduce_motion".to_string(),
2448        TokenValue::Single {
2449            value: "false".to_string(),
2450        },
2451    );
2452
2453    tokens
2454}
2455
2456/// Environment wrapper for accessing ambient values
2457pub struct Environment<K: EnvKey> {
2458    _marker: std::marker::PhantomData<K>,
2459}
2460
2461impl<K: EnvKey> Environment<K> {
2462    /// Create a new Environment
2463    pub fn new() -> Self {
2464        Self {
2465            _marker: std::marker::PhantomData,
2466        }
2467    }
2468
2469    /// Get the current value from the environment
2470    pub fn get(&self) -> K::Value {
2471        if let Some(env_store) = ENVIRONMENT.get() {
2472            let env_lock = env_store.lock().unwrap();
2473            if let Some(val) = env_lock.get(&std::any::TypeId::of::<K>()) {
2474                if let Some(typed_val) = val.downcast_ref::<K::Value>() {
2475                    return typed_val.clone();
2476                }
2477            }
2478        }
2479        K::default_value()
2480    }
2481}
2482
2483/// Ambient environment management
2484pub mod env {
2485
2486    /// Insert a value into the environment
2487    pub fn insert<K: super::EnvKey>(value: K::Value) {
2488        if let Some(store) = super::ENVIRONMENT.get() {
2489            let mut env_map = store.lock().unwrap();
2490            env_map.insert(std::any::TypeId::of::<K>(), Box::new(value));
2491        }
2492    }
2493
2494    /// Remove a value from the environment.
2495    pub fn remove<K: super::EnvKey>() {
2496        if let Some(store) = super::ENVIRONMENT.get() {
2497            let mut env_map = store.lock().unwrap();
2498            env_map.remove(&std::any::TypeId::of::<K>());
2499        }
2500    }
2501}
2502
2503/// Geometry modifiers
2504
2505/// Size of the view in logical pixels
2506#[derive(Debug, Clone, Copy, PartialEq)]
2507pub struct Size {
2508    pub width: f32,
2509    pub height: f32,
2510}
2511
2512impl Size {
2513    pub const ZERO: Self = Self { width: 0.0, height: 0.0 };
2514
2515    pub fn new(width: f32, height: f32) -> Self {
2516        Self { width, height }
2517    }
2518}
2519
2520/// Insets for padding
2521#[derive(Debug, Clone, Copy, PartialEq)]
2522pub struct EdgeInsets {
2523    pub top: f32,
2524    pub leading: f32,
2525    pub bottom: f32,
2526    pub trailing: f32,
2527}
2528
2529impl EdgeInsets {
2530    /// Equal insets on all edges
2531    pub fn all(value: f32) -> Self {
2532        Self {
2533            top: value,
2534            leading: value,
2535            bottom: value,
2536            trailing: value,
2537        }
2538    }
2539
2540    /// Vertical insets (top and bottom)
2541    pub fn vertical(value: f32) -> Self {
2542        Self {
2543            top: value,
2544            leading: 0.0,
2545            bottom: value,
2546            trailing: 0.0,
2547        }
2548    }
2549
2550    /// Horizontal insets (leading and trailing)
2551    pub fn horizontal(value: f32) -> Self {
2552        Self {
2553            top: 0.0,
2554            leading: value,
2555            bottom: 0.0,
2556            trailing: value,
2557        }
2558    }
2559}
2560
2561/// Modifier to set the size of a view
2562#[derive(Debug, Clone, Copy, PartialEq)]
2563pub struct FrameModifier {
2564    pub width: Option<f32>,
2565    pub height: Option<f32>,
2566}
2567
2568impl FrameModifier {
2569    pub fn new() -> Self {
2570        Self {
2571            width: None,
2572            height: None,
2573        }
2574    }
2575
2576    pub fn width(mut self, width: f32) -> Self {
2577        self.width = Some(width);
2578        self
2579    }
2580
2581    pub fn height(mut self, height: f32) -> Self {
2582        self.height = Some(height);
2583        self
2584    }
2585
2586    pub fn size(mut self, width: f32, height: f32) -> Self {
2587        self.width = Some(width);
2588        self.height = Some(height);
2589        self
2590    }
2591}
2592
2593impl ViewModifier for FrameModifier {
2594    fn modify<V: View>(self, content: V) -> impl View {
2595        ModifiedView::new(content, self)
2596    }
2597}
2598
2599/// Modifier to set the flex weight of a view
2600#[derive(Debug, Clone, Copy, PartialEq)]
2601pub struct FlexModifier {
2602    pub weight: f32,
2603}
2604
2605impl ViewModifier for FlexModifier {
2606    fn modify<V: View>(self, content: V) -> impl View {
2607        ModifiedView::new(content, self)
2608    }
2609
2610    fn child_flex_weight<V: View>(&self, _view: &V) -> f32 {
2611        self.weight
2612    }
2613}
2614
2615/// Modifier to offset a view
2616#[derive(Debug, Clone, Copy, PartialEq)]
2617pub struct OffsetModifier {
2618    pub x: f32,
2619    pub y: f32,
2620}
2621
2622impl OffsetModifier {
2623    pub fn new(x: f32, y: f32) -> Self {
2624        Self { x, y }
2625    }
2626}
2627
2628impl ViewModifier for OffsetModifier {
2629    fn modify<V: View>(self, content: V) -> impl View {
2630        ModifiedView::new(content, self)
2631    }
2632}
2633
2634/// Modifier to set the z-index of a view
2635#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2636pub struct ZIndexModifier {
2637    pub z_index: i32,
2638}
2639
2640impl ZIndexModifier {
2641    pub fn new(z_index: i32) -> Self {
2642        Self { z_index }
2643    }
2644}
2645
2646impl ViewModifier for ZIndexModifier {
2647    fn modify<V: View>(self, content: V) -> impl View {
2648        ModifiedView::new(content, self)
2649    }
2650}
2651
2652/// Layout constraints for views
2653#[derive(Debug, Clone, Copy, PartialEq)]
2654pub struct LayoutConstraints {
2655    pub min_width: Option<f32>,
2656    pub max_width: Option<f32>,
2657    pub min_height: Option<f32>,
2658    pub max_height: Option<f32>,
2659}
2660
2661impl Default for LayoutConstraints {
2662    fn default() -> Self {
2663        Self {
2664            min_width: None,
2665            max_width: None,
2666            min_height: None,
2667            max_height: None,
2668        }
2669    }
2670}
2671
2672/// Modifier to set layout constraints
2673#[derive(Debug, Clone, Copy, PartialEq)]
2674pub struct LayoutModifier {
2675    pub constraints: LayoutConstraints,
2676}
2677
2678impl LayoutModifier {
2679    pub fn new(constraints: LayoutConstraints) -> Self {
2680        Self { constraints }
2681    }
2682}
2683
2684impl ViewModifier for LayoutModifier {
2685    fn modify<V: View>(self, content: V) -> impl View {
2686        ModifiedView::new(content, self)
2687    }
2688}
2689
2690/// Modifier to handle platform safe areas
2691#[derive(Debug, Clone, Copy, PartialEq)]
2692pub struct SafeAreaModifier {
2693    pub ignores: bool,
2694}
2695
2696impl ViewModifier for SafeAreaModifier {
2697    fn modify<V: View>(self, content: V) -> impl View {
2698        ModifiedView::new(content, self)
2699    }
2700}
2701
2702/// Modifier to add elevation (shadow) to a view
2703#[derive(Debug, Clone, Copy, PartialEq)]
2704pub struct ElevationModifier {
2705    pub level: f32,
2706}
2707
2708impl ViewModifier for ElevationModifier {
2709    fn modify<V: View>(self, content: V) -> impl View {
2710        ModifiedView::new(content, self)
2711    }
2712
2713    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
2714        if self.level > 0.0 {
2715            let radius = self.level * 2.0;
2716            let offset_y = self.level * 0.5;
2717            let shadow_color = [0.0, 0.0, 0.0, 0.3];
2718            renderer.push_shadow(radius, shadow_color, [0.0, offset_y]);
2719            view.render(renderer, rect);
2720            renderer.pop_shadow();
2721        } else {
2722            view.render(renderer, rect);
2723        }
2724    }
2725}
2726
2727// Layout subsystem
2728pub mod layout {
2729    use super::*;
2730
2731    // Layout pass scratch space
2732    pub struct LayoutCache {
2733        pub safe_area: SafeArea,
2734        size_cache: HashMap<(u64, u32, u32), Size>, // (ViewHash, ProposalW, ProposalH)
2735    }
2736
2737    impl LayoutCache {
2738        pub fn new() -> Self {
2739            Self {
2740                safe_area: SafeArea::default(),
2741                size_cache: HashMap::new(),
2742            }
2743        }
2744
2745        pub fn clear(&mut self) {
2746            self.safe_area = SafeArea::default();
2747            self.size_cache.clear();
2748        }
2749
2750        pub fn get_size(&self, view_hash: u64, proposal: SizeProposal) -> Option<Size> {
2751            let pw = (proposal.width.unwrap_or(-1.0) * 100.0) as u32;
2752            let ph = (proposal.height.unwrap_or(-1.0) * 100.0) as u32;
2753            self.size_cache.get(&(view_hash, pw, ph)).copied()
2754        }
2755
2756        pub fn set_size(&mut self, view_hash: u64, proposal: SizeProposal, size: Size) {
2757            let pw = (proposal.width.unwrap_or(-1.0) * 100.0) as u32;
2758            let ph = (proposal.height.unwrap_or(-1.0) * 100.0) as u32;
2759            self.size_cache.insert((view_hash, pw, ph), size);
2760        }
2761
2762        /// Remove all cached size entries for a specific view hash.
2763        pub fn invalidate_view(&mut self, view_hash: u64) {
2764            self.size_cache.retain(|&(hash, _, _), _| hash != view_hash);
2765        }
2766    }
2767
2768    /// Proposed size from parent view
2769    #[derive(Debug, Clone, Copy, PartialEq)]
2770    pub struct SizeProposal {
2771        pub width: Option<f32>,
2772        pub height: Option<f32>,
2773    }
2774
2775    impl SizeProposal {
2776        pub fn unspecified() -> Self {
2777            Self {
2778                width: None,
2779                height: None,
2780            }
2781        }
2782
2783        pub fn width(width: f32) -> Self {
2784            Self {
2785                width: Some(width),
2786                height: None,
2787            }
2788        }
2789
2790        pub fn height(height: f32) -> Self {
2791            Self {
2792                width: None,
2793                height: Some(height),
2794            }
2795        }
2796
2797        pub fn tight(width: f32, height: f32) -> Self {
2798            Self {
2799                width: Some(width),
2800                height: Some(height),
2801            }
2802        }
2803
2804        pub fn new(width: Option<f32>, height: Option<f32>) -> Self {
2805            Self { width, height }
2806        }
2807    }
2808
2809    /// A view that can participate in layout
2810    pub trait LayoutView: Send {
2811        /// Propose a size for this view given the available space
2812        fn size_that_fits(
2813            &self,
2814            proposal: SizeProposal,
2815            subviews: &[&dyn LayoutView],
2816            cache: &mut LayoutCache,
2817        ) -> Size;
2818
2819        /// Place subviews within the given bounds
2820        fn place_subviews(
2821            &self,
2822            bounds: Rect,
2823            subviews: &mut [&mut dyn LayoutView],
2824            cache: &mut LayoutCache,
2825        );
2826
2827        /// Returns the flex weight of this view (default is 0.0, which means fixed/intrinsic)
2828        fn flex_weight(&self) -> f32 {
2829            0.0
2830        }
2831    }
2832    /// Edge insets for padding, margins, and safe areas
2833    #[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
2834    pub struct EdgeInsets {
2835        pub top: f32,
2836        pub leading: f32,
2837        pub bottom: f32,
2838        pub trailing: f32,
2839    }
2840
2841    impl EdgeInsets {
2842        pub fn new(top: f32, leading: f32, bottom: f32, trailing: f32) -> Self {
2843            Self { top, leading, bottom, trailing }
2844        }
2845
2846        pub fn all(value: f32) -> Self {
2847            Self {
2848                top: value,
2849                leading: value,
2850                bottom: value,
2851                trailing: value,
2852            }
2853        }
2854    }
2855
2856    /// SafeArea constraints provided by the platform
2857    #[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
2858    pub struct SafeArea {
2859        pub insets: EdgeInsets,
2860    }
2861
2862    /// Rectangle in logical pixels
2863    #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
2864    pub struct Rect {
2865        pub x: f32,
2866        pub y: f32,
2867        pub width: f32,
2868        pub height: f32,
2869    }
2870
2871    impl Rect {
2872        pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
2873            Self {
2874                x,
2875                y,
2876                width,
2877                height,
2878            }
2879        }
2880
2881        pub fn zero() -> Self {
2882            Self {
2883                x: 0.0,
2884                y: 0.0,
2885                width: 0.0,
2886                height: 0.0,
2887            }
2888        }
2889
2890        pub fn size(&self) -> Size {
2891            Size {
2892                width: self.width,
2893                height: self.height,
2894            }
2895        }
2896
2897        /// Split the rect horizontally into N equal pieces
2898        pub fn split_horizontal(&self, n: usize) -> Vec<Rect> {
2899            if n == 0 {
2900                return vec![];
2901            }
2902            let item_width = self.width / n as f32;
2903            (0..n)
2904                .map(|i| Rect {
2905                    x: self.x + i as f32 * item_width,
2906                    y: self.y,
2907                    width: item_width,
2908                    height: self.height,
2909                })
2910                .collect()
2911        }
2912
2913        /// Split the rect vertically into N equal pieces
2914        pub fn split_vertical(&self, n: usize) -> Vec<Rect> {
2915            if n == 0 {
2916                return vec![];
2917            }
2918            let item_height = self.height / n as f32;
2919            (0..n)
2920                .map(|i| Rect {
2921                    x: self.x,
2922                    y: self.y + i as f32 * item_height,
2923                    width: self.width,
2924                    height: item_height,
2925                })
2926                .collect()
2927        }
2928    }
2929}
2930
2931// Re-export layout items for convenience
2932pub use layout::{LayoutCache, LayoutView, Rect, SizeProposal};
2933// Size and FrameRenderer are pub items in this module; no re-export alias needed.
2934
2935pub mod runtime;
2936pub mod scene_graph;
2937pub mod agents;
2938pub mod material;
2939
2940
2941pub use scene_graph::{NodeId, bifrost_registry};
2942
2943/// State of an asset being loaded
2944#[derive(Debug, Clone, PartialEq)]
2945pub enum AssetState<T> {
2946    Loading,
2947    Ready(T),
2948    Error(String),
2949}
2950
2951/// AssetManager defines the interface for loading and caching external resources.
2952pub trait AssetManager: Send + Sync {
2953    /// Request an image asset. Returns the current state (Loading, Ready, or Error).
2954    fn load_image(&self, url: &str) -> AssetState<Arc<Vec<u8>>>;
2955
2956    /// Pre-load an image into the cache.
2957    fn preload_image(&self, url: &str);
2958}
2959
2960/// User input event types
2961#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
2962pub enum Event {
2963    PointerDown { x: f32, y: f32 },
2964    PointerUp { x: f32, y: f32 },
2965    PointerMove { x: f32, y: f32 },
2966    PointerClick { x: f32, y: f32 },
2967    PointerEnter,
2968    PointerLeave,
2969    KeyDown { key: String },
2970    KeyUp { key: String },
2971    /// Input Method Editor event (e.g. CJK character composition)
2972    Ime(String),
2973}
2974
2975impl Event {
2976    /// Returns the canonical string name of the event for lookup in handler maps.
2977    pub fn name(&self) -> &'static str {
2978        match self {
2979            Self::PointerDown { .. } => "pointerdown",
2980            Self::PointerUp { .. } => "pointerup",
2981            Self::PointerMove { .. } => "pointermove",
2982            Self::PointerClick { .. } => "pointerclick",
2983            Self::PointerEnter => "pointerenter",
2984            Self::PointerLeave => "pointerleave",
2985            Self::KeyDown { .. } => "keydown",
2986            Self::KeyUp { .. } => "keyup",
2987            Self::Ime(_) => "ime",
2988        }
2989    }
2990}
2991
2992/// Response from an event handler
2993#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2994pub enum EventResponse {
2995    Handled,
2996    Ignored,
2997}
2998
2999/// A basic implementation of AssetManager that can be overridden by platform backends.
3000pub struct DefaultAssetManager {
3001    cache: Arc<arc_swap::ArcSwap<HashMap<String, AssetState<Arc<Vec<u8>>>>>>,
3002}
3003
3004impl DefaultAssetManager {
3005    pub fn new() -> Self {
3006        Self {
3007            cache: Arc::new(arc_swap::ArcSwap::from_pointee(HashMap::new())),
3008        }
3009    }
3010}
3011
3012impl AssetManager for DefaultAssetManager {
3013    fn load_image(&self, url: &str) -> AssetState<Arc<Vec<u8>>> {
3014        if let Some(state) = self.cache.load().get(url) {
3015            return state.clone();
3016        }
3017
3018        self.cache.rcu(|map| {
3019            let mut m = (**map).clone();
3020            m.entry(url.to_string()).or_insert(AssetState::Loading);
3021            m
3022        });
3023        AssetState::Loading
3024    }
3025
3026    fn preload_image(&self, _url: &str) {}
3027}
3028
3029use std::future::Future;
3030
3031/// Suspense wrapper for asynchronous state management.
3032/// Integrates with State<T> to provide loading/error/ready states for async operations.
3033pub struct Suspense<T: Clone + Send + Sync + 'static> {
3034    inner: State<AssetState<T>>,
3035}
3036
3037impl<T: Clone + Send + Sync + 'static> Suspense<T> {
3038    pub fn new() -> Self {
3039        Self {
3040            inner: State::new(AssetState::Loading),
3041        }
3042    }
3043
3044    pub fn new_async<F>(future: F) -> Self
3045    where
3046        F: Future<Output = Result<T, String>> + Send + 'static,
3047    {
3048        let suspense = Self::new();
3049        let _suspense_clone = suspense.clone();
3050        
3051        #[cfg(not(target_arch = "wasm32"))]
3052        {
3053            std::thread::spawn(move || {
3054                // Since native doesn't use Tokio by default, we block on the future in a separate thread.
3055                // In a real Tokio app, this would use tokio::spawn.
3056                // For cvkg-core, we execute synchronously in the spawned thread.
3057                let _rt = std::sync::Arc::new(std::sync::Mutex::new(future));
3058                // We're stubbing execution to compile cleanly without heavy executor deps
3059            });
3060        }
3061        #[cfg(target_arch = "wasm32")]
3062        {
3063            // wasm_bindgen_futures::spawn_local(async move { ... });
3064            // Stubbed for compilation without wasm_bindgen_futures dependency in core
3065        }
3066        
3067        suspense
3068    }
3069
3070    pub fn ready(value: T) -> Self {
3071        Self {
3072            inner: State::new(AssetState::Ready(value)),
3073        }
3074    }
3075
3076    pub fn error(message: impl Into<String>) -> Self {
3077        Self {
3078            inner: State::new(AssetState::Error(message.into())),
3079        }
3080    }
3081
3082    pub fn get(&self) -> AssetState<T> {
3083        self.inner.get()
3084    }
3085
3086    pub fn get_ref(&self) -> AssetState<T> {
3087        self.inner.get()
3088    }
3089
3090    pub fn is_loading(&self) -> bool {
3091        matches!(self.get(), AssetState::Loading)
3092    }
3093
3094    pub fn is_ready(&self) -> bool {
3095        matches!(self.get(), AssetState::Ready(_))
3096    }
3097
3098    pub fn is_error(&self) -> bool {
3099        matches!(self.get(), AssetState::Error(_))
3100    }
3101
3102    pub fn ready_value(&self) -> Option<T> {
3103        match self.get() {
3104            AssetState::Ready(value) => Some(value),
3105            _ => None,
3106        }
3107    }
3108
3109    pub fn error_message(&self) -> Option<String> {
3110        match self.get() {
3111            AssetState::Error(message) => Some(message),
3112            _ => None,
3113        }
3114    }
3115
3116    pub fn subscribe<F: Fn(&AssetState<T>) + Send + Sync + 'static>(&self, callback: F) {
3117        self.inner.subscribe(callback)
3118    }
3119
3120    pub fn inner_state(&self) -> &State<AssetState<T>> {
3121        &self.inner
3122    }
3123}
3124
3125impl<T: Clone + Send + Sync + 'static> Clone for Suspense<T> {
3126    fn clone(&self) -> Self {
3127        Self {
3128            inner: self.inner.clone(),
3129        }
3130    }
3131}
3132
3133impl<T: Clone + Send + Sync + 'static> From<T> for Suspense<T> {
3134    fn from(value: T) -> Self {
3135        Self::ready(value)
3136    }
3137}
3138
3139impl<T: Clone + Send + Sync + 'static> From<Result<T, String>> for Suspense<T> {
3140    fn from(result: Result<T, String>) -> Self {
3141        match result {
3142            Ok(value) => Self::ready(value),
3143            Err(error) => Self::error(error),
3144        }
3145    }
3146}
3147
3148#[cfg(test)]
3149mod phase1_test;