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
38/// Design token value that can adapt to light/dark mode
39#[derive(Debug, Clone, Serialize, Deserialize)]
40#[serde(untagged)]
41pub enum TokenValue {
42    /// Single value (same for light and dark)
43    Single { value: String },
44    /// Different values for light and dark mode
45    Adaptive { light: String, dark: String },
46}
47
48/// YggdrasilTokens is the authoritative container for all design tokens in the CVKG ecosystem.
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct YggdrasilTokens {
51    pub color: HashMap<String, TokenValue>,
52    pub font: HashMap<String, TokenValue>,
53    pub spacing: HashMap<String, TokenValue>,
54    pub radius: HashMap<String, TokenValue>,
55    pub shadow: HashMap<String, TokenValue>,
56    pub border: HashMap<String, TokenValue>,
57    pub anim: HashMap<String, TokenValue>,
58    pub bifrost: HashMap<String, TokenValue>,
59    pub gungnir: HashMap<String, TokenValue>,
60    pub mjolnir: HashMap<String, TokenValue>,
61    pub accessibility: HashMap<String, TokenValue>,
62}
63
64impl YggdrasilTokens {
65    pub fn new() -> Self {
66        Self {
67            color: HashMap::new(),
68            font: HashMap::new(),
69            spacing: HashMap::new(),
70            radius: HashMap::new(),
71            shadow: HashMap::new(),
72            border: HashMap::new(),
73            anim: HashMap::new(),
74            bifrost: HashMap::new(),
75            gungnir: HashMap::new(),
76            mjolnir: HashMap::new(),
77            accessibility: HashMap::new(),
78        }
79    }
80
81    /// Get a color token value for the current mode
82    pub fn get_color(&self, key: &str, is_dark: bool) -> Option<String> {
83        self.color.get(key).and_then(|token| match token {
84            TokenValue::Single { value } => Some(value.clone()),
85            TokenValue::Adaptive { light, dark } => {
86                if is_dark {
87                    Some(dark.clone())
88                } else {
89                    Some(light.clone())
90                }
91            }
92        })
93    }
94
95    /// Get a token value of any type and parse it into the target type
96    pub fn get<T: FromStr>(&self, category: &str, key: &str, is_dark: bool) -> Option<T> {
97        let map = match category {
98            "color" => &self.color,
99            "font" => &self.font,
100            "spacing" => &self.spacing,
101            "radius" => &self.radius,
102            "shadow" => &self.shadow,
103            "border" => &self.border,
104            "anim" => &self.anim,
105            "bifrost" => &self.bifrost,
106            "gungnir" => &self.gungnir,
107            "mjolnir" => &self.mjolnir,
108            "accessibility" => &self.accessibility,
109            _ => return None,
110        };
111
112        map.get(key).and_then(|token| match token {
113            TokenValue::Single { value } => value.parse().ok(),
114            TokenValue::Adaptive { light, dark } => {
115                let value = if is_dark { dark } else { light };
116                value.parse().ok()
117            }
118        })
119    }
120}
121
122pub trait View: Sized + Send {
123    /// The concrete type produced after applying modifiers.
124    /// For primitive views this is Self.
125    type Body: View;
126
127    fn body(self) -> Self::Body;
128
129    /// Render this view into the provided renderer at the specified bounds.
130    /// Primitive views override this to perform drawing operations.
131    fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
132
133    /// Calculate the natural (intrinsic) size of this view given proposed constraints.
134    /// This allows views like Buttons or Labels to inform the layout engine of their needs.
135    fn intrinsic_size(&self, _renderer: &mut dyn Renderer, _proposal: SizeProposal) -> Size {
136        Size::ZERO
137    }
138
139    /// Optionally provide a layout implementation for this view.
140    fn layout(&self) -> Option<&dyn layout::LayoutView> {
141        None
142    }
143
144    /// Returns the flex weight of this view for proportional distribution in stacks.
145    fn flex_weight(&self) -> f32 {
146        0.0
147    }
148
149    /// Provided modifier entry point
150    fn modifier<M: ViewModifier>(self, m: M) -> ModifiedView<Self, M> {
151        ModifiedView::new(self, m)
152    }
153
154    /// Apply a Bifrost (Frosted Glass) effect to the view
155    fn bifrost(
156        self,
157        blur: f32,
158        saturation: f32,
159        opacity: f32,
160    ) -> ModifiedView<Self, BifrostModifier> {
161        self.modifier(BifrostModifier {
162            blur,
163            saturation,
164            opacity,
165        })
166    }
167
168    /// Apply a Gungnir (Neon Glow) effect to the view
169    fn gungnir(
170        self,
171        color: impl Into<String>,
172        radius: f32,
173        intensity: f32,
174    ) -> ModifiedView<Self, GungnirModifier> {
175        self.modifier(GungnirModifier {
176            color: color.into(),
177            radius,
178            intensity,
179        })
180    }
181
182    /// Apply a Mjolnir Slice (Geometric cut) to the view
183    fn mjolnir_slice(self, angle: f32, offset: f32) -> ModifiedView<Self, MjolnirSliceModifier> {
184        self.modifier(MjolnirSliceModifier { angle, offset })
185    }
186
187    /// Apply a Mjolnir Shatter (Fragmented transition) to the view
188    fn mjolnir_shatter(
189        self,
190        pieces: u32,
191        force: f32,
192    ) -> ModifiedView<Self, MjolnirShatterModifier> {
193        self.modifier(MjolnirShatterModifier { pieces, force })
194    }
195
196    /// Mark this view as a Bifrost Bridge (Shared Element) for cross-view persistence
197    fn bifrost_bridge(self, id: impl Into<String>) -> ModifiedView<Self, BifrostBridgeModifier> {
198        self.modifier(BifrostBridgeModifier { id: id.into() })
199    }
200
201    /// Add a background color to this view
202    fn background(self, color: [f32; 4]) -> ModifiedView<Self, BackgroundModifier> {
203        self.modifier(BackgroundModifier { color })
204    }
205
206    /// Add padding to this view
207    fn padding(self, amount: f32) -> ModifiedView<Self, PaddingModifier> {
208        self.modifier(PaddingModifier { amount })
209    }
210
211    /// Set the opacity (alpha) of this view in the range [0.0, 1.0].
212    fn opacity(self, opacity: f32) -> ModifiedView<Self, OpacityModifier> {
213        self.modifier(OpacityModifier {
214            opacity: opacity.clamp(0.0, 1.0),
215        })
216    }
217
218    /// Override the foreground (text / icon) color of this view.
219    fn foreground_color(self, color: [f32; 4]) -> ModifiedView<Self, ForegroundColorModifier> {
220        self.modifier(ForegroundColorModifier { color })
221    }
222
223    /// Constrain this view to an explicit width and/or height.
224    fn frame(self, width: Option<f32>, height: Option<f32>) -> ModifiedView<Self, FrameModifier> {
225        self.modifier(FrameModifier { width, height })
226    }
227
228    /// Give this view a flex weight for proportional space distribution in stacks.
229    fn flex(self, weight: f32) -> ModifiedView<Self, FlexModifier> {
230        self.modifier(FlexModifier { weight })
231    }
232
233    /// Automatically add padding to avoid overlapping with platform safe areas (notches, bars).
234    fn safe_area_padding(self) -> ModifiedView<Self, SafeAreaModifier> {
235        self.modifier(SafeAreaModifier { ignores: false })
236    }
237
238    /// Explicitly ignore platform safe areas and draw into the margins.
239    fn ignores_safe_area(self) -> ModifiedView<Self, SafeAreaModifier> {
240        self.modifier(SafeAreaModifier { ignores: true })
241    }
242
243    /// Clip all child drawing to this view's bounds.
244    fn clip_to_bounds(self) -> ModifiedView<Self, ClipModifier> {
245        self.modifier(ClipModifier)
246    }
247
248    /// Draw a colored border around this view.
249    fn border(self, color: [f32; 4], width: f32) -> ModifiedView<Self, BorderModifier> {
250        self.modifier(BorderModifier { color, width })
251    }
252
253    /// Add elevation (shadow) to the view. Level determines the shadow depth.
254    fn elevation(self, level: f32) -> ModifiedView<Self, ElevationModifier> {
255        self.modifier(ElevationModifier { level })
256    }
257
258    /// Trigger an action when the view appears
259    fn on_appear<F: Fn() + Send + Sync + 'static>(
260        self,
261        action: F,
262    ) -> ModifiedView<Self, LifecycleModifier> {
263        self.modifier(LifecycleModifier {
264            on_appear: Some(Arc::new(action)),
265            on_disappear: None,
266        })
267    }
268
269    /// Trigger an action when the view disappears
270    fn on_disappear<F: Fn() + Send + Sync + 'static>(
271        self,
272        action: F,
273    ) -> ModifiedView<Self, LifecycleModifier> {
274        self.modifier(LifecycleModifier {
275            on_appear: None,
276            on_disappear: Some(Arc::new(action)),
277        })
278    }
279
280    /// Trigger an action when the view is clicked
281    fn on_click<F: Fn() + Send + Sync + 'static>(
282        self,
283        action: F,
284    ) -> ModifiedView<Self, OnClickModifier> {
285        self.modifier(OnClickModifier {
286            action: Arc::new(action),
287        })
288    }
289
290    /// Trigger an action when the pointer enters the view bounds
291    fn on_pointer_enter<F: Fn() + Send + Sync + 'static>(
292        self,
293        action: F,
294    ) -> ModifiedView<Self, OnPointerEnterModifier> {
295        self.modifier(OnPointerEnterModifier {
296            action: Arc::new(action),
297        })
298    }
299
300    /// Trigger an action when the pointer leaves the view bounds
301    fn on_pointer_leave<F: Fn() + Send + Sync + 'static>(
302        self,
303        action: F,
304    ) -> ModifiedView<Self, OnPointerLeaveModifier> {
305        self.modifier(OnPointerLeaveModifier {
306            action: Arc::new(action),
307        })
308    }
309
310    /// Trigger an action when the pointer moves inside the view bounds
311    fn on_pointer_move<F: Fn(f32, f32) + Send + Sync + 'static>(
312        self,
313        action: F,
314    ) -> ModifiedView<Self, OnPointerMoveModifier> {
315        self.modifier(OnPointerMoveModifier {
316            action: Arc::new(action),
317        })
318    }
319
320    /// Trigger an action when the pointer is pressed down
321    fn on_pointer_down<F: Fn() + Send + Sync + 'static>(
322        self,
323        action: F,
324    ) -> ModifiedView<Self, OnPointerDownModifier> {
325        self.modifier(OnPointerDownModifier {
326            action: Arc::new(action),
327        })
328    }
329
330    /// Trigger an action when the pointer is released
331    fn on_pointer_up<F: Fn() + Send + Sync + 'static>(
332        self,
333        action: F,
334    ) -> ModifiedView<Self, OnPointerUpModifier> {
335        self.modifier(OnPointerUpModifier {
336            action: Arc::new(action),
337        })
338    }
339
340    /// Type-erase this view into AnyView
341    fn erase(self) -> AnyView
342    where
343        Self: 'static,
344    {
345        AnyView::new(self)
346    }
347}
348
349/// An object-safe version of the View trait for type erasure.
350pub trait ErasedView: Send {
351    fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect);
352    fn name(&self) -> &'static str;
353    fn flex_weight_erased(&self) -> f32;
354}
355
356impl<V: View + 'static> ErasedView for V {
357    fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect) {
358        self.render(renderer, rect);
359    }
360
361    fn name(&self) -> &'static str {
362        std::any::type_name::<V>()
363    }
364
365    fn flex_weight_erased(&self) -> f32 {
366        self.flex_weight()
367    }
368}
369
370/// A type-erased View wrapper.
371pub struct AnyView {
372    inner: Box<dyn ErasedView>,
373}
374
375impl AnyView {
376    pub fn new<V: View + 'static>(view: V) -> Self {
377        Self {
378            inner: Box::new(view),
379        }
380    }
381}
382
383impl View for AnyView {
384    type Body = Never;
385    fn body(self) -> Self::Body {
386        unreachable!()
387    }
388
389    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
390        renderer.push_vnode(rect, self.inner.name());
391        self.inner.render_erased(renderer, rect);
392        renderer.pop_vnode();
393    }
394
395    fn flex_weight(&self) -> f32 {
396        self.inner.flex_weight_erased()
397    }
398}
399
400/// BifrostBridgeModifier enables shared-element transitions.
401/// When two views share the same Bifrost Bridge ID, the Sleipnir solver will
402/// interpolate their geometry and effects (blur, glow) during the transition.
403#[derive(Debug, Clone, PartialEq)]
404pub struct BifrostBridgeModifier {
405    pub id: String,
406}
407
408impl ViewModifier for BifrostBridgeModifier {
409    fn modify<V: View>(self, content: V) -> impl View {
410        ModifiedView::new(content, self)
411    }
412
413    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
414        // Register this element with the renderer for shared-element transition logic
415        renderer.register_shared_element(&self.id, rect);
416    }
417}
418
419/// MjolnirSliceModifier implements the "Geometric Slice" aesthetic.
420/// It uses a signed distance field (SDF) to clip the view along a sharp angled line.
421#[derive(Debug, Clone, Copy, PartialEq)]
422pub struct MjolnirSliceModifier {
423    pub angle: f32,
424    pub offset: f32,
425}
426
427impl ViewModifier for MjolnirSliceModifier {
428    fn modify<V: View>(self, content: V) -> impl View {
429        ModifiedView::new(content, self)
430    }
431
432    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
433        renderer.push_mjolnir_slice(self.angle, self.offset);
434    }
435
436    fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
437        renderer.pop_mjolnir_slice();
438    }
439}
440
441/// MjolnirShatterModifier implements the "Shattering" effect.
442/// It breaks the view into discrete geometric fragments that can be animated.
443#[derive(Debug, Clone, Copy, PartialEq)]
444pub struct MjolnirShatterModifier {
445    pub pieces: u32,
446    pub force: f32,
447}
448
449impl ViewModifier for MjolnirShatterModifier {
450    fn modify<V: View>(self, content: V) -> impl View {
451        ModifiedView::new(content, self)
452    }
453
454    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
455        // RADIAL SHATTER: Fragment the view into wedges
456        let pieces = self.pieces.max(1);
457        for i in 0..pieces {
458            let progress = i as f32 / pieces as f32;
459            let next_progress = (i + 1) as f32 / pieces as f32;
460
461            let angle_start = progress * 360.0;
462            let angle_end = next_progress * 360.0;
463
464            // Wedge slice: intersection of two half-planes
465            renderer.push_mjolnir_slice(angle_start, 0.0);
466            renderer.push_mjolnir_slice(angle_end + 180.0, 0.0);
467
468            // Apply radial force offset
469            let mid_angle = (angle_start + angle_end) / 2.0;
470            let rad = mid_angle.to_radians();
471            let dx = rad.cos() * self.force;
472            let dy = rad.sin() * self.force;
473
474            let shard_rect = Rect {
475                x: rect.x + dx,
476                y: rect.y + dy,
477                ..rect
478            };
479
480            view.render(renderer, shard_rect);
481
482            renderer.pop_mjolnir_slice();
483            renderer.pop_mjolnir_slice();
484        }
485    }
486}
487
488/// BifrostModifier implements the Cyberpunk "Frosted Glass" aesthetic.
489/// It triggers backdrop blurring and light scattering in the render pipeline.
490#[derive(Debug, Clone, Copy, PartialEq)]
491pub struct BifrostModifier {
492    pub blur: f32,
493    pub saturation: f32,
494    pub opacity: f32,
495}
496
497impl ViewModifier for BifrostModifier {
498    fn modify<V: View>(self, content: V) -> impl View {
499        ModifiedView::new(content, self)
500    }
501
502    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
503        renderer.bifrost(rect, self.blur, self.saturation, self.opacity);
504    }
505}
506
507/// A modifier that adds a background color to a view.
508#[derive(Debug, Clone, Copy, PartialEq)]
509pub struct BackgroundModifier {
510    pub color: [f32; 4],
511}
512
513impl ViewModifier for BackgroundModifier {
514    fn modify<V: View>(self, content: V) -> impl View {
515        ModifiedView::new(content, self)
516    }
517
518    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
519        renderer.fill_rect(rect, self.color);
520    }
521}
522
523/// A modifier that adds padding to a view.
524#[derive(Debug, Clone, Copy, PartialEq)]
525pub struct PaddingModifier {
526    pub amount: f32,
527}
528
529impl ViewModifier for PaddingModifier {
530    fn modify<V: View>(self, content: V) -> impl View {
531        ModifiedView::new(content, self)
532    }
533
534    fn transform_rect(&self, rect: Rect) -> Rect {
535        Rect {
536            x: rect.x + self.amount,
537            y: rect.y + self.amount,
538            width: (rect.width - 2.0 * self.amount).max(0.0),
539            height: (rect.height - 2.0 * self.amount).max(0.0),
540        }
541    }
542
543    fn transform_proposal(&self, mut proposal: SizeProposal) -> SizeProposal {
544        if let Some(w) = proposal.width {
545            proposal.width = Some((w - 2.0 * self.amount).max(0.0));
546        }
547        if let Some(h) = proposal.height {
548            proposal.height = Some((h - 2.0 * self.amount).max(0.0));
549        }
550        proposal
551    }
552
553    fn transform_size(&self, mut size: Size) -> Size {
554        size.width += 2.0 * self.amount;
555        size.height += 2.0 * self.amount;
556        size
557    }
558}
559
560/// GungnirModifier implements the "Neon Glow" aesthetic.
561/// It uses additive blending and multi-pass blurring to simulate glowing light.
562#[derive(Debug, Clone, PartialEq)]
563pub struct GungnirModifier {
564    pub color: String,
565    pub radius: f32,
566    pub intensity: f32,
567}
568
569impl ViewModifier for GungnirModifier {
570    fn modify<V: View>(self, content: V) -> impl View {
571        ModifiedView::new(content, self)
572    }
573
574    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
575        // Neon Glow using Mode 1 in the Surtr pipeline
576        renderer.stroke_rect(rect, [0.0, 1.0, 1.0, self.intensity], self.radius / 10.0);
577    }
578}
579
580/// GungnirPulseModifier implements a "breathing" neon effect.
581#[derive(Debug, Clone, Copy, PartialEq)]
582pub struct GungnirPulseModifier {
583    pub color: [f32; 4],
584    pub radius: f32,
585    pub speed: f32,
586}
587
588impl ViewModifier for GungnirPulseModifier {
589    fn modify<V: View>(self, content: V) -> impl View {
590        ModifiedView::new(content, self)
591    }
592
593    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
594        let time = std::time::SystemTime::now()
595            .duration_since(std::time::UNIX_EPOCH)
596            .unwrap_or_default()
597            .as_secs_f32();
598
599        // Mode 19: Dashed Border
600        // Mode 20: 9-Slice / Patch Scaling
601        let intensity = (time * self.speed).sin() * 0.5 + 0.5;
602        let mut color = self.color;
603        color[3] *= intensity;
604
605        // Mode 1 neon glow with dynamic intensity
606        renderer.stroke_rect(rect, color, self.radius);
607    }
608}
609
610/// SleipnirModifier handles physics-based animations via the Sleipnir RK4 solver.
611#[derive(Debug, Clone, PartialEq)]
612pub struct SleipnirModifier<T> {
613    pub target: T,
614    pub stiffness: f32,
615    pub damping: f32,
616}
617
618impl<T: Send + Sync + 'static + Clone> ViewModifier for SleipnirModifier<T> {
619    fn modify<V: View>(self, content: V) -> impl View {
620        ModifiedView::new(content, self)
621    }
622}
623
624/// LifecycleModifier handles on_appear and on_disappear hooks.
625#[derive(Clone)]
626pub struct LifecycleModifier {
627    pub on_appear: Option<Arc<dyn Fn() + Send + Sync>>,
628    pub on_disappear: Option<Arc<dyn Fn() + Send + Sync>>,
629}
630
631impl ViewModifier for LifecycleModifier {
632    fn modify<V: View>(self, content: V) -> impl View {
633        ModifiedView::new(content, self)
634    }
635}
636
637/// OpacityModifier fades this view and all its descendants to the given alpha.
638/// The renderer is expected to honour `push_opacity`/`pop_opacity` on the Renderer trait.
639#[derive(Debug, Clone, Copy, PartialEq)]
640pub struct OpacityModifier {
641    pub opacity: f32,
642}
643
644impl ViewModifier for OpacityModifier {
645    fn modify<V: View>(self, content: V) -> impl View {
646        ModifiedView::new(content, self)
647    }
648
649    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
650        renderer.push_opacity(self.opacity);
651    }
652
653    fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
654        renderer.pop_opacity();
655    }
656}
657
658/// OnClickModifier registers a click handler for this view.
659#[derive(Clone)]
660pub struct OnClickModifier {
661    pub action: Arc<dyn Fn() + Send + Sync>,
662}
663
664impl ViewModifier for OnClickModifier {
665    fn modify<V: View>(self, content: V) -> impl View {
666        ModifiedView::new(content, self)
667    }
668
669    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
670        let action = self.action.clone();
671        renderer.register_handler(
672            "pointerclick",
673            std::sync::Arc::new(move |event| {
674                if let Event::PointerClick { .. } = event {
675                    (action)();
676                }
677            }),
678        );
679    }
680}
681
682/// OnPointerEnterModifier registers a pointer enter handler.
683#[derive(Clone)]
684pub struct OnPointerEnterModifier {
685    pub action: Arc<dyn Fn() + Send + Sync>,
686}
687
688impl ViewModifier for OnPointerEnterModifier {
689    fn modify<V: View>(self, content: V) -> impl View {
690        ModifiedView::new(content, self)
691    }
692
693    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
694        let action = self.action.clone();
695        renderer.register_handler(
696            "pointerenter",
697            std::sync::Arc::new(move |event| {
698                if let Event::PointerEnter = event {
699                    (action)();
700                }
701            }),
702        );
703    }
704}
705
706/// OnPointerLeaveModifier registers a pointer leave handler.
707#[derive(Clone)]
708pub struct OnPointerLeaveModifier {
709    pub action: Arc<dyn Fn() + Send + Sync>,
710}
711
712impl ViewModifier for OnPointerLeaveModifier {
713    fn modify<V: View>(self, content: V) -> impl View {
714        ModifiedView::new(content, self)
715    }
716
717    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
718        let action = self.action.clone();
719        renderer.register_handler(
720            "pointerleave",
721            std::sync::Arc::new(move |event| {
722                if let Event::PointerLeave = event {
723                    (action)();
724                }
725            }),
726        );
727    }
728}
729
730/// OnPointerMoveModifier registers a pointer move handler.
731#[derive(Clone)]
732pub struct OnPointerMoveModifier {
733    pub action: Arc<dyn Fn(f32, f32) + Send + Sync>,
734}
735
736impl ViewModifier for OnPointerMoveModifier {
737    fn modify<V: View>(self, content: V) -> impl View {
738        ModifiedView::new(content, self)
739    }
740
741    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
742        let action = self.action.clone();
743        renderer.register_handler(
744            "pointermove",
745            std::sync::Arc::new(move |event| {
746                if let Event::PointerMove { x, y } = event {
747                    (action)(x, y);
748                }
749            }),
750        );
751    }
752}
753
754/// OnPointerDownModifier registers a pointer down handler.
755#[derive(Clone)]
756pub struct OnPointerDownModifier {
757    pub action: Arc<dyn Fn() + Send + Sync>,
758}
759
760impl ViewModifier for OnPointerDownModifier {
761    fn modify<V: View>(self, content: V) -> impl View {
762        ModifiedView::new(content, self)
763    }
764
765    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
766        let action = self.action.clone();
767        renderer.register_handler(
768            "pointerdown",
769            std::sync::Arc::new(move |event| {
770                if let Event::PointerDown { .. } = event {
771                    (action)();
772                }
773            }),
774        );
775    }
776}
777
778/// OnPointerUpModifier registers a pointer up handler.
779#[derive(Clone)]
780pub struct OnPointerUpModifier {
781    pub action: Arc<dyn Fn() + Send + Sync>,
782}
783
784impl ViewModifier for OnPointerUpModifier {
785    fn modify<V: View>(self, content: V) -> impl View {
786        ModifiedView::new(content, self)
787    }
788
789    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
790        let action = self.action.clone();
791        renderer.register_handler(
792            "pointerup",
793            std::sync::Arc::new(move |event| {
794                if let Event::PointerUp { .. } = event {
795                    (action)();
796                }
797            }),
798        );
799    }
800}
801
802/// ForegroundColorModifier overrides the foreground (text / icon) color inherited
803/// by all descendants until another ForegroundColorModifier is encountered.
804#[derive(Debug, Clone, Copy, PartialEq)]
805pub struct ForegroundColorModifier {
806    pub color: [f32; 4],
807}
808
809impl ViewModifier for ForegroundColorModifier {
810    fn modify<V: View>(self, content: V) -> impl View {
811        ModifiedView::new(content, self)
812    }
813}
814
815/// ClipModifier restricts all child drawing to the view's layout rectangle.
816/// The renderer must support `push_clip_rect`/`pop_clip_rect`.
817#[derive(Debug, Clone, Copy, PartialEq, Eq)]
818pub struct ClipModifier;
819
820impl ViewModifier for ClipModifier {
821    fn modify<V: View>(self, content: V) -> impl View {
822        ModifiedView::new(content, self)
823    }
824
825    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
826        renderer.push_clip_rect(rect);
827    }
828
829    fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
830        renderer.pop_clip_rect();
831    }
832}
833
834/// BorderModifier draws a solid-color border around the view bounds.
835#[derive(Debug, Clone, Copy, PartialEq)]
836pub struct BorderModifier {
837    pub color: [f32; 4],
838    pub width: f32,
839}
840
841impl ViewModifier for BorderModifier {
842    fn modify<V: View>(self, content: V) -> impl View {
843        ModifiedView::new(content, self)
844    }
845
846    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
847        renderer.stroke_rect(rect, self.color, self.width);
848    }
849}
850
851// Primitive (leaf) views implement Never as body
852#[doc(hidden)]
853pub enum Never {}
854
855impl View for Never {
856    type Body = Never;
857    fn body(self) -> Never {
858        unreachable!()
859    }
860}
861
862/// A view that has been transformed by a modifier.
863///
864/// Section 4.3: "Each modifier implements ViewModifier and produces a ModifiedView<Inner, Self>."
865pub struct ModifiedView<V, M> {
866    view: V,
867    modifier: M,
868}
869
870impl<V: View, M: ViewModifier> ModifiedView<V, M> {
871    #[doc(hidden)]
872    pub fn new(view: V, modifier: M) -> Self {
873        Self { view, modifier }
874    }
875}
876
877impl<V: View, M: ViewModifier> View for ModifiedView<V, M> {
878    type Body = ModifiedView<V::Body, M>;
879
880    fn body(self) -> Self::Body {
881        ModifiedView {
882            view: self.view.body(),
883            modifier: self.modifier.clone(),
884        }
885    }
886
887    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
888        self.modifier.render_view(&self.view, renderer, rect);
889    }
890
891    fn intrinsic_size(&self, renderer: &mut dyn Renderer, proposal: SizeProposal) -> Size {
892        self.modifier.measure_view(&self.view, renderer, proposal)
893    }
894
895    fn flex_weight(&self) -> f32 {
896        self.modifier.child_flex_weight(&self.view)
897    }
898}
899
900pub trait ViewModifier: Send + Clone {
901    fn modify<V: View>(self, content: V) -> impl View;
902
903    /// Core rendering hook called before child views.
904    fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
905
906    /// Cleanup hook called after child views.
907    fn post_render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
908
909    /// Allows a modifier to completely override or wrap the rendering of its child.
910    /// Default implementation performs a standard push -> transform -> render child -> pop sequence.
911    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
912        self.render(renderer, rect);
913        let child_rect = self.transform_rect(rect);
914        view.render(renderer, child_rect);
915        self.post_render(renderer, rect);
916    }
917
918    fn transform_rect(&self, rect: Rect) -> Rect {
919        rect
920    }
921
922    /// Allows a modifier to transform the layout proposal before it reaches the child.
923    fn transform_proposal(&self, proposal: SizeProposal) -> SizeProposal {
924        proposal
925    }
926
927    /// Allows a modifier to transform the resulting size from the child.
928    fn transform_size(&self, size: Size) -> Size {
929        size
930    }
931
932    /// Measure hook that coordinates size propagation.
933    fn measure_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, proposal: SizeProposal) -> Size {
934        let child_proposal = self.transform_proposal(proposal);
935        let child_size = view.intrinsic_size(renderer, child_proposal);
936        self.transform_size(child_size)
937    }
938
939    /// Allows a modifier to override or pass through the child's flex weight.
940    fn child_flex_weight<V: View>(&self, view: &V) -> f32 {
941        view.flex_weight()
942    }
943}
944
945/// TelemetryData tracks real-time performance metrics for the GPU renderer.
946#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
947pub struct TelemetryData {
948    pub frame_time_ms: f32,
949    pub draw_calls: u32,
950    pub vertices: u32,
951    pub vram_usage_mb: f32,
952}
953
954/// The Renderer trait defines the atomic drawing operations for all CVKG backends.
955/// This trait is object-safe and used by the View::render system.
956///
957/// # Implementation Requirements
958/// 1. Coordinate system is origin-top-left (0,0) with Y increasing downwards.
959/// 2. Colors are [R, G, B, A] in the [0.0, 1.0] range.
960/// 3. All operations must be batchable by the underlying backend.
961pub trait Renderer: Send {
962    // ── Filled shapes ────────────────────────────────────────────────────
963    fn fill_rect(&mut self, rect: Rect, color: [f32; 4]);
964    fn fill_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4]);
965    /// Fill an ellipse/circle that fits inside `rect`.
966    fn fill_ellipse(&mut self, rect: Rect, color: [f32; 4]);
967
968    // ── Stroked shapes ───────────────────────────────────────────────────
969    fn stroke_rect(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
970    fn stroke_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4], stroke_width: f32);
971    /// Stroke an ellipse/circle that fits inside `rect`.
972    fn stroke_ellipse(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
973    /// Draw a straight line from (x1,y1) to (x2,y2).
974    fn draw_line(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: [f32; 4], stroke_width: f32);
975
976    // ── Text ─────────────────────────────────────────────────────────────
977    fn draw_text(&mut self, text: &str, x: f32, y: f32, size: f32, color: [f32; 4]);
978    /// Measure the width and height of the specified text.
979    fn measure_text(&mut self, text: &str, size: f32) -> (f32, f32);
980
981    // ── Images & textures ────────────────────────────────────────────────
982    /// Draw a texture (GPU-side) at the specified rect.
983    fn draw_texture(&mut self, texture_id: u32, rect: Rect);
984    /// Draw an image asset by name or path.
985    fn draw_image(&mut self, image_name: &str, rect: Rect);
986    /// Load an image asset from memory.
987    fn load_image(&mut self, name: &str, data: &[u8]);
988
989    // ── Data Visualization ───────────────────────────────────────────────
990    /// Upload raw float data as a GPU texture for heatmap rendering.
991    fn upload_data_texture(&mut self, _id: &str, _data: &[f32], _width: u32, _height: u32) {}
992    /// Draw a heatmap using a previously uploaded data texture.
993    fn draw_heatmap(&mut self, _texture_id: &str, _rect: Rect, _palette: &str) {}
994
995    // ── 3D Objects ───────────────────────────────────────────────────────
996    /// Draw a 3D mesh.
997    fn draw_mesh(&mut self, _mesh: &Mesh, _color: [f32; 4], _transform: glam::Mat4) {}
998
999    // ── Advanced Visual Effects ──────────────────────────────────────────
1000    /// Draw a linear gradient between two colors at the specified angle.
1001    fn draw_linear_gradient(
1002        &mut self,
1003        _rect: Rect,
1004        _start_color: [f32; 4],
1005        _end_color: [f32; 4],
1006        _angle: f32,
1007    ) {
1008    }
1009    /// Draw a radial gradient between two colors.
1010    fn draw_radial_gradient(
1011        &mut self,
1012        _rect: Rect,
1013        _inner_color: [f32; 4],
1014        _outer_color: [f32; 4],
1015    ) {
1016    }
1017    /// Draw a high-fidelity drop shadow for a rounded rectangle.
1018    fn draw_drop_shadow(
1019        &mut self,
1020        _rect: Rect,
1021        _radius: f32,
1022        _color: [f32; 4],
1023        _blur: f32,
1024        _spread: f32,
1025    ) {
1026    }
1027    /// Draw a dashed border for a rounded rectangle.
1028    fn stroke_dashed_rounded_rect(
1029        &mut self,
1030        _rect: Rect,
1031        _radius: f32,
1032        _color: [f32; 4],
1033        _width: f32,
1034        _dash: f32,
1035        _gap: f32,
1036    ) {
1037    }
1038    /// Draw a 9-slice / patch scaled image.
1039    fn draw_9slice(
1040        &mut self,
1041        _image_name: &str,
1042        _rect: Rect,
1043        _left: f32,
1044        _top: f32,
1045        _right: f32,
1046        _bottom: f32,
1047    ) {
1048    }
1049
1050    // ── Clipping ─────────────────────────────────────────────────────────
1051    /// Push a clip rectangle.  All subsequent drawing is clipped to `rect`.
1052    /// Implementations that do not support clipping may ignore this call.
1053    fn push_clip_rect(&mut self, rect: Rect);
1054    /// Pop the most recently pushed clip rectangle.
1055    fn pop_clip_rect(&mut self);
1056
1057    // ── Global opacity ───────────────────────────────────────────────────
1058    /// Set a global opacity multiplier applied to all subsequent draw calls
1059    /// until `pop_opacity` is called.  `opacity` is in [0.0, 1.0].
1060    fn push_opacity(&mut self, opacity: f32);
1061    /// Restore the previous opacity level.
1062    fn pop_opacity(&mut self);
1063
1064    // ── Berserker Pipeline State ─────────────────────────────────────────
1065    fn set_theme(&mut self, _theme: ColorTheme) {}
1066    fn set_rage(&mut self, _rage: f32) {}
1067    fn trigger_shatter_event(&mut self, _origin: [f32; 2], _force: f32) {}
1068
1069    // ── Cyberpunk Effects ────────────────────────────────────────────────
1070    /// Apply a Bifrost (Frosted Glass) effect to the specified rect.
1071    fn bifrost(&mut self, rect: Rect, blur: f32, saturation: f32, opacity: f32);
1072    /// Push a Mjolnir Slice (geometric clipping).
1073    fn push_mjolnir_slice(&mut self, angle: f32, offset: f32);
1074    /// Pop the Mjolnir Slice.
1075    fn pop_mjolnir_slice(&mut self);
1076    /// Apply a Mjolnir Shatter effect (fragmentation) to the specified rect.
1077    fn mjolnir_shatter(&mut self, _rect: Rect, _pieces: u32, _force: f32, _color: [f32; 4]) {}
1078    fn mjolnir_fluid_shatter(&mut self, _rect: Rect, _pieces: u32, _force: f32, _color: [f32; 4]) {}
1079    /// Draw a Mjolnir Bolt (lightning strike) between two points.
1080    fn draw_mjolnir_bolt(&mut self, _from: [f32; 2], _to: [f32; 2], _color: [f32; 4]) {}
1081
1082    // ── Accessibility (ShieldWall) ───────────────────────────────────────
1083    fn set_aria_role(&mut self, _role: &str) {}
1084    fn set_aria_label(&mut self, _label: &str) {}
1085
1086    /// Register a shared element for Bifrost Bridge transitions.
1087    fn register_shared_element(&mut self, _id: &str, _rect: Rect) {}
1088
1089    /// Set a unique key for the current VDOM node to ensure stable identity during diffing.
1090    fn set_key(&mut self, _key: &str) {}
1091
1092    // ── Telemetry ────────────────────────────────────────────────────────
1093    /// Get real-time performance telemetry.
1094    fn get_telemetry(&self) -> TelemetryData {
1095        TelemetryData::default()
1096    }
1097
1098    // ── GPU State Management ─────────────────────────────────────────────
1099    /// Push a shadow state to the stack. All following draw calls will have this shadow.
1100    fn push_shadow(&mut self, _radius: f32, _color: [f32; 4], _offset: [f32; 2]) {}
1101    /// Pop the last shadow state from the stack.
1102    fn pop_shadow(&mut self) {}
1103
1104    // ── VDOM & Scene Graph ───────────────────────────────────────────────
1105    /// Push a Virtual DOM node onto the stack for hierarchy tracking.
1106    fn push_vnode(&mut self, _rect: Rect, _name: &'static str) {}
1107    /// Pop the current Virtual DOM node from the stack.
1108    fn pop_vnode(&mut self) {}
1109    /// Register an event handler for the current VDOM node.
1110    fn register_handler(
1111        &mut self,
1112        _event_type: &str,
1113        _handler: std::sync::Arc<dyn Fn(Event) + Send + Sync>,
1114    ) {
1115    }
1116
1117    // ── Z-Index & Depth ──────────────────────────────────────────────────
1118    /// Set the current Z-index for depth sorting.
1119    /// Higher values appear closer to the viewer.
1120    fn set_z_index(&mut self, _z: f32) {}
1121    /// Get the current Z-index.
1122    fn get_z_index(&self) -> f32 {
1123        0.0
1124    }
1125
1126    // ── Vector Graphics ──────────────────────────────────────────────────
1127    /// Load an SVG model from raw bytes.
1128    fn load_svg(&mut self, _name: &str, _svg_data: &[u8]) {}
1129    /// Draw a pre-loaded SVG model.
1130    fn draw_svg(&mut self, _name: &str, _rect: Rect) {}
1131}
1132
1133// =============================================================================
1134// BERSERKER UNIFORMS
1135// =============================================================================
1136
1137use bytemuck::{Pod, Zeroable};
1138
1139/// Fully themeable color palette for the Berserker pipeline.
1140#[repr(C)]
1141#[derive(Copy, Clone, Debug, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
1142pub struct ColorTheme {
1143    pub primary_neon: [f32; 4], // (R, G, B, intensity)
1144    pub shatter_neon: [f32; 4],
1145    pub glass_base: [f32; 4],
1146    pub glass_edge: [f32; 4],
1147    pub rune_glow: [f32; 4],
1148    pub ember_core: [f32; 4],
1149    pub background_deep: [f32; 4],
1150    pub glass_blur_strength: f32,
1151    pub shatter_edge_width: f32,
1152    pub neon_bloom_radius: f32,
1153    pub rune_opacity: f32, // 0.0–1.0, default 0.55
1154    // Padding to ensure 16-byte alignment for GPU uniforms
1155    pub _pad: [f32; 3], // align to 16 bytes
1156    pub _pad2: f32,
1157}
1158
1159impl ColorTheme {
1160    pub fn cyberpunk_viking() -> Self {
1161        Self {
1162            primary_neon: [0.0, 1.0, 0.95, 1.2],
1163            shatter_neon: [1.0, 0.0, 0.75, 1.5],
1164            glass_base: [0.04, 0.04, 0.06, 0.82],
1165            glass_edge: [0.0, 0.45, 0.55, 0.6],
1166            rune_glow: [0.75, 0.98, 1.0, 0.9],
1167            ember_core: [0.95, 0.12, 0.12, 1.0],
1168            background_deep: [0.01, 0.01, 0.03, 1.0],
1169            glass_blur_strength: 0.6,
1170            shatter_edge_width: 1.8,
1171            neon_bloom_radius: 0.022,
1172            rune_opacity: 0.55,
1173            _pad: [0.0; 3],
1174            _pad2: 0.0,
1175        }
1176    }
1177
1178    pub fn vibrant_glass() -> Self {
1179        Self {
1180            primary_neon: [0.0, 1.0, 0.95, 1.2],
1181            shatter_neon: [1.0, 0.0, 0.75, 1.5],
1182            glass_base: [0.55, 0.6, 0.7, 0.08], // Luminous cool tint
1183            glass_edge: [0.7, 0.85, 1.0, 0.45], // Subtle blue-white rim
1184            rune_glow: [0.75, 0.98, 1.0, 0.9],
1185            ember_core: [1.0, 0.4, 0.1, 1.0],
1186            background_deep: [0.05, 0.05, 0.1, 1.0],
1187            glass_blur_strength: 0.9,
1188            shatter_edge_width: 1.8,
1189            neon_bloom_radius: 0.022,
1190            rune_opacity: 0.55,
1191            _pad: [0.0; 3],
1192            _pad2: 0.0,
1193        }
1194    }
1195}
1196
1197impl Default for ColorTheme {
1198    fn default() -> Self {
1199        Self::vibrant_glass()
1200    }
1201}
1202
1203/// Per-frame scene state for the Berserker pipeline.
1204#[repr(C)]
1205#[derive(Copy, Clone, Debug, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
1206pub struct SceneUniforms {
1207    pub view: glam::Mat4,
1208    pub proj: glam::Mat4,
1209    pub time: f32,
1210    pub delta_time: f32,
1211    pub resolution: [f32; 2],
1212    pub mouse: [f32; 2],
1213    pub mouse_velocity: [f32; 2],
1214    pub shatter_origin: [f32; 2],
1215    pub shatter_time: f32,
1216    pub shatter_force: f32,
1217    pub berzerker_rage: f32,
1218    pub scroll_offset: f32,
1219    pub scale_factor: f32,
1220    // Padding to ensure 16-byte alignment for GPU uniforms (47 f32s + 1 = 48)
1221    pub _pad: [f32; 1],
1222}
1223
1224impl SceneUniforms {
1225    pub fn new(width: f32, height: f32) -> Self {
1226        Self {
1227            view: glam::Mat4::IDENTITY,
1228            proj: glam::Mat4::orthographic_lh(0.0, width, height, 0.0, -100.0, 100.0),
1229            time: 0.0,
1230            delta_time: 0.016,
1231            resolution: [width, height],
1232            mouse: [0.5, 0.5],
1233            mouse_velocity: [0.0, 0.0],
1234            shatter_origin: [0.5, 0.5],
1235            shatter_time: -100.0,
1236            shatter_force: 0.0,
1237            berzerker_rage: 0.0,
1238            scroll_offset: 0.0,
1239            scale_factor: 1.0,
1240            _pad: [0.0; 1],
1241        }
1242    }
1243}
1244
1245/// A 3D mesh containing vertex and index data.
1246#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1247pub struct Mesh {
1248    pub vertices: Vec<[f32; 3]>,
1249    pub normals: Vec<[f32; 3]>,
1250    pub indices: Vec<u32>,
1251}
1252
1253impl Mesh {
1254    pub fn from_obj(data: &[u8]) -> anyhow::Result<Vec<Self>> {
1255        let mut cursor = std::io::Cursor::new(data);
1256        let (models, _) = tobj::load_obj_buf(&mut cursor, &tobj::LoadOptions::default(), |_| {
1257            Ok((Vec::new(), Default::default()))
1258        })?;
1259
1260        let mut meshes = Vec::new();
1261        for m in models {
1262            let mesh = m.mesh;
1263            let vertices: Vec<[f32; 3]> = mesh
1264                .positions
1265                .chunks(3)
1266                .map(|c| [c[0], c[1], c[2]])
1267                .collect();
1268            let normals = if mesh.normals.is_empty() {
1269                vec![[0.0, 0.0, 1.0]; vertices.len()]
1270            } else {
1271                mesh.normals.chunks(3).map(|c| [c[0], c[1], c[2]]).collect()
1272            };
1273            meshes.push(Mesh {
1274                vertices,
1275                normals,
1276                indices: mesh.indices,
1277            });
1278        }
1279        Ok(meshes)
1280    }
1281
1282    pub fn from_stl(data: &[u8]) -> anyhow::Result<Self> {
1283        let mut cursor = std::io::Cursor::new(data);
1284        let stl = stl_io::read_stl(&mut cursor)?;
1285
1286        let vertices: Vec<[f32; 3]> = stl.vertices.iter().map(|v| [v[0], v[1], v[2]]).collect();
1287        let mut indices = Vec::new();
1288        for face in stl.faces {
1289            indices.push(face.vertices[0] as u32);
1290            indices.push(face.vertices[1] as u32);
1291            indices.push(face.vertices[2] as u32);
1292        }
1293
1294        let normals = vec![[0.0, 0.0, 1.0]; vertices.len()];
1295
1296        Ok(Mesh {
1297            vertices,
1298            normals,
1299            indices,
1300        })
1301    }
1302}
1303
1304/// FrameRenderer extends Renderer with frame lifecycle management.
1305/// It is typically implemented by the host windowing/rendering environment.
1306pub trait FrameRenderer<E = ()>: Renderer {
1307    fn begin_frame(&mut self) -> E;
1308    fn end_frame(&mut self, encoder: E);
1309}
1310
1311use std::sync::Arc;
1312
1313/// State wrapper that owns a value and notifies subscribers when changed
1314#[derive(Clone)]
1315pub struct State<T: Clone + Send + Sync + 'static> {
1316    value: Arc<std::sync::RwLock<T>>,
1317    subscribers: Arc<std::sync::RwLock<Vec<Box<dyn Fn(&T) + Send + Sync>>>>,
1318    version: Arc<std::sync::atomic::AtomicU64>,
1319}
1320
1321impl<T: Clone + Send + Sync + 'static> State<T> {
1322    /// Create a new State with initial value
1323    pub fn new(value: T) -> Self {
1324        Self {
1325            value: Arc::new(std::sync::RwLock::new(value)),
1326            subscribers: Arc::new(std::sync::RwLock::new(Vec::new())),
1327            version: Arc::new(std::sync::atomic::AtomicU64::new(0)),
1328        }
1329    }
1330
1331    /// Get the current value
1332    pub fn get(&self) -> T {
1333        self.value.read().unwrap().clone()
1334    }
1335
1336    /// Set a new value, notifying all subscribers
1337    pub fn set(&self, value: T) {
1338        *self.value.write().unwrap() = value;
1339        self.version.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
1340        // Notify subscribers
1341        let subscribers = self.subscribers.read().unwrap();
1342        for subscriber in subscribers.iter() {
1343            subscriber(&self.get());
1344        }
1345    }
1346
1347    /// Get current version
1348    pub fn version(&self) -> u64 {
1349        self.version.load(std::sync::atomic::Ordering::SeqCst)
1350    }
1351
1352    /// Subscribe to state changes
1353    pub fn subscribe<F: Fn(&T) + Send + Sync + 'static>(&self, callback: F) {
1354        self.subscribers.write().unwrap().push(Box::new(callback));
1355    }
1356}
1357
1358/// Error state for fault isolation at the component level.
1359///
1360/// Section 1.1: "Components must self-handle errors... isolating failures."
1361#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
1362pub struct ComponentErrorState {
1363    pub has_error: bool,
1364    pub error_message: Option<String>,
1365    pub error_location: Option<String>,
1366}
1367
1368impl ComponentErrorState {
1369    /// Create a new clear error state.
1370    pub fn clear() -> Self {
1371        Self::default()
1372    }
1373
1374    /// Create an error state with a message and location.
1375    pub fn error(message: impl Into<String>, location: impl Into<String>) -> Self {
1376        Self {
1377            has_error: true,
1378            error_message: Some(message.into()),
1379            error_location: Some(location.into()),
1380        }
1381    }
1382}
1383
1384/// A discrete fragment of knowledge stored in the agent's memory.
1385#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
1386pub struct KnowledgeFragment {
1387    /// Unique identifier for this fragment
1388    pub id: String,
1389    /// Short summary for prompt injection and quick search
1390    pub summary: String,
1391    /// Reference source (e.g. filename, URL, or conversation ID)
1392    pub source: String,
1393    /// Frame number or timestamp of creation
1394    pub created_at: u64,
1395    /// Number of times this fragment has been retrieved
1396    pub accessed_count: u32,
1397    /// Full content (optional, can be loaded on-demand)
1398    pub content: Option<String>,
1399}
1400
1401/// The KnowledgeState registry is the central repository for all agent-observable application data.
1402/// It stores both component-level states and high-level agentic memory fragments.
1403#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
1404pub struct KnowledgeState {
1405    /// Component states indexed by NodeId. Skipped in serialization as it contains opaque types.
1406    #[serde(skip)]
1407    pub component_states: std::collections::HashMap<u64, Arc<dyn std::any::Any + Send + Sync>>,
1408
1409    /// Map of IDs to knowledge fragments (Agentic Memory)
1410    pub fragments: HashMap<String, KnowledgeFragment>,
1411
1412    /// IDs of fragments returned by the last search query
1413    pub last_query_results: Vec<String>,
1414}
1415
1416use crate::runtime::NodeStateSnapshot;
1417use std::sync::OnceLock;
1418
1419/// Global application state registry.
1420pub static SYSTEM_STATE: OnceLock<Arc<std::sync::RwLock<KnowledgeState>>> = OnceLock::new();
1421
1422/// Get a reference to the global system state.
1423pub fn get_system_state() -> Arc<std::sync::RwLock<KnowledgeState>> {
1424    SYSTEM_STATE
1425        .get_or_init(|| Arc::new(std::sync::RwLock::new(KnowledgeState::default())))
1426        .clone()
1427}
1428
1429impl KnowledgeState {
1430    /// Create a new empty KnowledgeState.
1431    pub fn new() -> Self {
1432        Self::default()
1433    }
1434
1435    /// Set a component's internal state.
1436    pub fn set_component_state<T: 'static + Send + Sync>(&mut self, id: u64, state: T) {
1437        self.component_states
1438            .insert(id, Arc::new(std::sync::RwLock::new(state)));
1439    }
1440
1441    /// Get a reference to a component's internal state.
1442    pub fn get_component_state<T: 'static + Send + Sync>(
1443        &self,
1444        id: u64,
1445    ) -> Option<Arc<std::sync::RwLock<T>>> {
1446        let lock = self.component_states.get(&id)?;
1447        lock.clone().downcast::<std::sync::RwLock<T>>().ok()
1448    }
1449
1450    /// Add a new fragment to memory.
1451    pub fn remember(&mut self, fragment: KnowledgeFragment) {
1452        self.fragments.insert(fragment.id.clone(), fragment);
1453    }
1454
1455    /// Process a search query against the local knowledge base.
1456    pub fn process_query(&mut self, query: &str) {
1457        let query_lower = query.to_lowercase();
1458        let mut results: Vec<(f32, String)> = self
1459            .fragments
1460            .iter()
1461            .map(|(id, frag)| {
1462                let mut score = 0.0;
1463                if frag.summary.to_lowercase().contains(&query_lower) {
1464                    score += 1.0;
1465                }
1466                if frag.source.to_lowercase().contains(&query_lower) {
1467                    score += 0.5;
1468                }
1469                (score, id.clone())
1470            })
1471            .filter(|(score, _)| *score > 0.0)
1472            .collect();
1473
1474        // Sort by relevance score
1475        results.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
1476
1477        self.last_query_results = results.into_iter().map(|(_, id)| id).take(5).collect();
1478    }
1479
1480    /// Captures a snapshot of the current state for debugging and hot-reloading.
1481    pub fn snapshot(&self) -> Vec<NodeStateSnapshot> {
1482        let mut snapshots = Vec::new();
1483
1484        // Snapshots of agentic fragments
1485        for (_id, frag) in &self.fragments {
1486            if let Ok(val) = serde_json::to_value(frag) {
1487                snapshots.push(NodeStateSnapshot { id: 0, state: val });
1488            }
1489        }
1490
1491        snapshots
1492    }
1493}
1494
1495/// Read/write reference to state owned by another view
1496#[derive(Clone)]
1497pub struct Binding<T: Clone + Send + Sync + 'static> {
1498    state: Arc<std::sync::RwLock<T>>,
1499    version: Arc<std::sync::atomic::AtomicU64>,
1500}
1501
1502impl<T: Clone + Send + Sync + 'static> Binding<T> {
1503    /// Create a binding from a State
1504    pub fn from_state(state: &State<T>) -> Self {
1505        Self {
1506            state: state.value.clone(),
1507            version: state.version.clone(),
1508        }
1509    }
1510
1511    /// Get the current value
1512    pub fn get(&self) -> T {
1513        self.state.read().unwrap().clone()
1514    }
1515
1516    /// Set a new value
1517    pub fn set(&self, value: T) {
1518        *self.state.write().unwrap() = value;
1519        self.version.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
1520    }
1521
1522    /// Get current version
1523    pub fn version(&self) -> u64 {
1524        self.version.load(std::sync::atomic::Ordering::SeqCst)
1525    }
1526}
1527
1528use std::any::TypeId;
1529use std::sync::Mutex;
1530
1531/// Global environment storage using TypeId as keys.
1532pub(crate) static ENVIRONMENT: OnceLock<
1533    Mutex<HashMap<TypeId, Box<dyn std::any::Any + Send + Sync>>>,
1534> = OnceLock::new();
1535
1536/// Environment key type for accessing ambient values
1537///
1538/// Implement this trait to define a new environment key.
1539pub trait EnvKey: 'static + Send + Sync {
1540    /// The type of value stored in the environment
1541    type Value: Clone + Send + Sync + 'static;
1542
1543    /// Get a default value for this key
1544    fn default_value() -> Self::Value;
1545}
1546
1547/// Key for accessing the Yggdrasil design token tree
1548pub struct YggdrasilKey;
1549
1550impl EnvKey for YggdrasilKey {
1551    type Value = YggdrasilTokens;
1552    fn default_value() -> Self::Value {
1553        default_tokens()
1554    }
1555}
1556
1557/// Key for accessing the AssetManager
1558pub struct AssetKey;
1559
1560impl EnvKey for AssetKey {
1561    type Value = Arc<dyn AssetManager>;
1562    fn default_value() -> Self::Value {
1563        Arc::new(DefaultAssetManager::new())
1564    }
1565}
1566
1567/// System appearance (Light/Dark mode)
1568#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1569pub enum Appearance {
1570    Light,
1571    Dark,
1572}
1573
1574/// Orientation for layouts
1575#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1576pub enum Orientation {
1577    Horizontal,
1578    Vertical,
1579}
1580
1581/// Cross-axis alignment for layout containers.
1582#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
1583pub enum Alignment {
1584    #[default]
1585    Center,
1586    Leading,
1587    Trailing,
1588    Top,
1589    Bottom,
1590}
1591
1592/// Main-axis distribution for linear layout containers.
1593#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
1594pub enum Distribution {
1595    #[default]
1596    Fill,
1597    Center,
1598    Leading,
1599    Trailing,
1600    SpaceBetween,
1601    SpaceAround,
1602    SpaceEvenly,
1603}
1604
1605/// A color represented by RGBA components in the [0.0, 1.0] range.
1606#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1607pub struct Color {
1608    pub r: f32,
1609    pub g: f32,
1610    pub b: f32,
1611    pub a: f32,
1612}
1613
1614impl Color {
1615    pub const BLACK: Color = Color {
1616        r: 0.0,
1617        g: 0.0,
1618        b: 0.0,
1619        a: 1.0,
1620    };
1621    pub const WHITE: Color = Color {
1622        r: 1.0,
1623        g: 1.0,
1624        b: 1.0,
1625        a: 1.0,
1626    };
1627    pub const TRANSPARENT: Color = Color {
1628        r: 0.0,
1629        g: 0.0,
1630        b: 0.0,
1631        a: 0.0,
1632    };
1633    pub const RED: Color = Color {
1634        r: 1.0,
1635        g: 0.0,
1636        b: 0.0,
1637        a: 1.0,
1638    };
1639    pub const GREEN: Color = Color {
1640        r: 0.0,
1641        g: 1.0,
1642        b: 0.0,
1643        a: 1.0,
1644    };
1645    pub const BLUE: Color = Color {
1646        r: 0.0,
1647        g: 0.0,
1648        b: 1.0,
1649        a: 1.0,
1650    };
1651
1652    /// Calculate the relative luminance of the color as defined by WCAG 2.x
1653    pub fn relative_luminance(&self) -> f32 {
1654        fn res(c: f32) -> f32 {
1655            if c <= 0.03928 {
1656                c / 12.92
1657            } else {
1658                ((c + 0.055) / 1.055).powf(2.4)
1659            }
1660        }
1661        0.2126 * res(self.r) + 0.7152 * res(self.g) + 0.0722 * res(self.b)
1662    }
1663
1664    /// Calculate the contrast ratio between this color and another color
1665    pub fn contrast_ratio(&self, other: &Color) -> f32 {
1666        let l1 = self.relative_luminance();
1667        let l2 = other.relative_luminance();
1668        if l1 > l2 {
1669            (l1 + 0.05) / (l2 + 0.05)
1670        } else {
1671            (l2 + 0.05) / (l1 + 0.05)
1672        }
1673    }
1674
1675    pub const CYAN: Color = Color {
1676        r: 0.0,
1677        g: 1.0,
1678        b: 1.0,
1679        a: 1.0,
1680    };
1681    pub const YELLOW: Color = Color {
1682        r: 1.0,
1683        g: 1.0,
1684        b: 0.0,
1685        a: 1.0,
1686    };
1687    pub const MAGENTA: Color = Color {
1688        r: 1.0,
1689        g: 0.0,
1690        b: 1.0,
1691        a: 1.0,
1692    };
1693    pub const GRAY: Color = Color {
1694        r: 0.5,
1695        g: 0.5,
1696        b: 0.5,
1697        a: 1.0,
1698    };
1699
1700    /// Create a new color from RGBA components.
1701    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
1702        Self { r, g, b, a }
1703    }
1704
1705    /// Convert the color to a [r, g, b, a] array.
1706    pub fn as_array(&self) -> [f32; 4] {
1707        [self.r, self.g, self.b, self.a]
1708    }
1709}
1710
1711impl View for Color {
1712    type Body = Never;
1713    fn body(self) -> Self::Body {
1714        unreachable!()
1715    }
1716
1717    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1718        renderer.fill_rect(rect, self.as_array());
1719    }
1720}
1721
1722/// Key for accessing the current system appearance
1723pub struct AppearanceKey;
1724
1725impl EnvKey for AppearanceKey {
1726    type Value = Appearance;
1727    fn default_value() -> Self::Value {
1728        Appearance::Dark // Default to Dark (Ginnungagap) for Berserker aesthetic
1729    }
1730}
1731
1732/// StyleResolver provides high-level access to themed values from the environment.
1733pub struct StyleResolver;
1734
1735impl StyleResolver {
1736    /// Resolve a color from the current environment
1737    pub fn color(key: &str) -> String {
1738        let tokens = Environment::<YggdrasilKey>::new().get();
1739        let appearance = Environment::<AppearanceKey>::new().get();
1740        let is_dark = appearance == Appearance::Dark;
1741
1742        tokens
1743            .get_color(key, is_dark)
1744            .unwrap_or_else(|| "#FF00FF".to_string()) // Default to MuspelMagenta on failure
1745    }
1746
1747    /// Resolve a generic token value
1748    pub fn get<T: FromStr>(category: &str, key: &str) -> Option<T> {
1749        let tokens = Environment::<YggdrasilKey>::new().get();
1750        let appearance = Environment::<AppearanceKey>::new().get();
1751        let is_dark = appearance == Appearance::Dark;
1752
1753        tokens.get(category, key, is_dark)
1754    }
1755}
1756
1757/// The authoritative Cyberpunk Viking default tokens
1758pub fn default_tokens() -> YggdrasilTokens {
1759    let mut tokens = YggdrasilTokens::new();
1760
1761    // Core Norse Colorways
1762    tokens.color.insert(
1763        "background".to_string(),
1764        TokenValue::Single {
1765            value: "#000000".to_string(), // Ginnungagap (The Void)
1766        },
1767    );
1768
1769    tokens.color.insert(
1770        "primary".to_string(),
1771        TokenValue::Single {
1772            value: "#00FFFF".to_string(), // NiflCyan (Aesir Primary)
1773        },
1774    );
1775
1776    tokens.color.insert(
1777        "secondary".to_string(),
1778        TokenValue::Single {
1779            value: "#FF00FF".to_string(), // MuspelMagenta (Berserker Secondary)
1780        },
1781    );
1782
1783    tokens.color.insert(
1784        "surface".to_string(),
1785        TokenValue::Adaptive {
1786            light: "#FFFFFF".to_string(),
1787            dark: "#121212".to_string(),
1788        },
1789    );
1790
1791    tokens.color.insert(
1792        "text".to_string(),
1793        TokenValue::Adaptive {
1794            light: "#000000".to_string(),
1795            dark: "#FFFFFF".to_string(),
1796        },
1797    );
1798
1799    // Bifrost (Glassmorphism) - Frosted Style
1800    tokens.bifrost.insert(
1801        "blur".to_string(),
1802        TokenValue::Single {
1803            value: "25.0".to_string(),
1804        },
1805    );
1806    tokens.bifrost.insert(
1807        "saturation".to_string(),
1808        TokenValue::Single {
1809            value: "1.2".to_string(),
1810        },
1811    );
1812    tokens.bifrost.insert(
1813        "opacity".to_string(),
1814        TokenValue::Single {
1815            value: "0.65".to_string(),
1816        },
1817    );
1818
1819    // Gungnir (Neon Glow)
1820    tokens.gungnir.insert(
1821        "intensity".to_string(),
1822        TokenValue::Single {
1823            value: "1.0".to_string(),
1824        },
1825    );
1826    tokens.gungnir.insert(
1827        "radius".to_string(),
1828        TokenValue::Single {
1829            value: "15.0".to_string(),
1830        },
1831    );
1832
1833    // Mjolnir (Sharp Geometry)
1834    tokens.mjolnir.insert(
1835        "clip_angle".to_string(),
1836        TokenValue::Single {
1837            value: "12.0".to_string(),
1838        },
1839    );
1840    tokens.mjolnir.insert(
1841        "border_width".to_string(),
1842        TokenValue::Single {
1843            value: "2.0".to_string(),
1844        },
1845    );
1846
1847    // Sleipnir (Spring Animation)
1848    tokens.anim.insert(
1849        "stiffness".to_string(),
1850        TokenValue::Single {
1851            value: "170.0".to_string(),
1852        },
1853    );
1854    tokens.anim.insert(
1855        "damping".to_string(),
1856        TokenValue::Single {
1857            value: "26.0".to_string(),
1858        },
1859    );
1860    tokens.anim.insert(
1861        "mass".to_string(),
1862        TokenValue::Single {
1863            value: "1.0".to_string(),
1864        },
1865    );
1866
1867    // Accessibility
1868    tokens.accessibility.insert(
1869        "reduce_motion".to_string(),
1870        TokenValue::Single {
1871            value: "false".to_string(),
1872        },
1873    );
1874
1875    tokens
1876}
1877
1878/// Environment wrapper for accessing ambient values
1879pub struct Environment<K: EnvKey> {
1880    _marker: std::marker::PhantomData<K>,
1881}
1882
1883impl<K: EnvKey> Environment<K> {
1884    /// Create a new Environment
1885    pub fn new() -> Self {
1886        Self {
1887            _marker: std::marker::PhantomData,
1888        }
1889    }
1890
1891    /// Get the current value from the environment
1892    pub fn get(&self) -> K::Value {
1893        if let Some(env_store) = ENVIRONMENT.get() {
1894            let env_lock = env_store.lock().unwrap();
1895            if let Some(val) = env_lock.get(&std::any::TypeId::of::<K>()) {
1896                if let Some(typed_val) = val.downcast_ref::<K::Value>() {
1897                    return typed_val.clone();
1898                }
1899            }
1900        }
1901        K::default_value()
1902    }
1903}
1904
1905/// Ambient environment management
1906pub mod env {
1907
1908    /// Insert a value into the environment
1909    pub fn insert<K: super::EnvKey>(value: K::Value) {
1910        if let Some(store) = super::ENVIRONMENT.get() {
1911            let mut env_map = store.lock().unwrap();
1912            env_map.insert(std::any::TypeId::of::<K>(), Box::new(value));
1913        }
1914    }
1915
1916    /// Remove a value from the environment.
1917    pub fn remove<K: super::EnvKey>() {
1918        if let Some(store) = super::ENVIRONMENT.get() {
1919            let mut env_map = store.lock().unwrap();
1920            env_map.remove(&std::any::TypeId::of::<K>());
1921        }
1922    }
1923}
1924
1925/// Geometry modifiers
1926
1927/// Size of the view in logical pixels
1928#[derive(Debug, Clone, Copy, PartialEq)]
1929pub struct Size {
1930    pub width: f32,
1931    pub height: f32,
1932}
1933
1934impl Size {
1935    pub const ZERO: Self = Self { width: 0.0, height: 0.0 };
1936
1937    pub fn new(width: f32, height: f32) -> Self {
1938        Self { width, height }
1939    }
1940}
1941
1942/// Insets for padding
1943#[derive(Debug, Clone, Copy, PartialEq)]
1944pub struct EdgeInsets {
1945    pub top: f32,
1946    pub leading: f32,
1947    pub bottom: f32,
1948    pub trailing: f32,
1949}
1950
1951impl EdgeInsets {
1952    /// Equal insets on all edges
1953    pub fn all(value: f32) -> Self {
1954        Self {
1955            top: value,
1956            leading: value,
1957            bottom: value,
1958            trailing: value,
1959        }
1960    }
1961
1962    /// Vertical insets (top and bottom)
1963    pub fn vertical(value: f32) -> Self {
1964        Self {
1965            top: value,
1966            leading: 0.0,
1967            bottom: value,
1968            trailing: 0.0,
1969        }
1970    }
1971
1972    /// Horizontal insets (leading and trailing)
1973    pub fn horizontal(value: f32) -> Self {
1974        Self {
1975            top: 0.0,
1976            leading: value,
1977            bottom: 0.0,
1978            trailing: value,
1979        }
1980    }
1981}
1982
1983/// Modifier to set the size of a view
1984#[derive(Debug, Clone, Copy, PartialEq)]
1985pub struct FrameModifier {
1986    pub width: Option<f32>,
1987    pub height: Option<f32>,
1988}
1989
1990impl FrameModifier {
1991    pub fn new() -> Self {
1992        Self {
1993            width: None,
1994            height: None,
1995        }
1996    }
1997
1998    pub fn width(mut self, width: f32) -> Self {
1999        self.width = Some(width);
2000        self
2001    }
2002
2003    pub fn height(mut self, height: f32) -> Self {
2004        self.height = Some(height);
2005        self
2006    }
2007
2008    pub fn size(mut self, width: f32, height: f32) -> Self {
2009        self.width = Some(width);
2010        self.height = Some(height);
2011        self
2012    }
2013}
2014
2015impl ViewModifier for FrameModifier {
2016    fn modify<V: View>(self, content: V) -> impl View {
2017        ModifiedView::new(content, self)
2018    }
2019}
2020
2021/// Modifier to set the flex weight of a view
2022#[derive(Debug, Clone, Copy, PartialEq)]
2023pub struct FlexModifier {
2024    pub weight: f32,
2025}
2026
2027impl ViewModifier for FlexModifier {
2028    fn modify<V: View>(self, content: V) -> impl View {
2029        ModifiedView::new(content, self)
2030    }
2031
2032    fn child_flex_weight<V: View>(&self, _view: &V) -> f32 {
2033        self.weight
2034    }
2035}
2036
2037/// Modifier to offset a view
2038#[derive(Debug, Clone, Copy, PartialEq)]
2039pub struct OffsetModifier {
2040    pub x: f32,
2041    pub y: f32,
2042}
2043
2044impl OffsetModifier {
2045    pub fn new(x: f32, y: f32) -> Self {
2046        Self { x, y }
2047    }
2048}
2049
2050impl ViewModifier for OffsetModifier {
2051    fn modify<V: View>(self, content: V) -> impl View {
2052        ModifiedView::new(content, self)
2053    }
2054}
2055
2056/// Modifier to set the z-index of a view
2057#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2058pub struct ZIndexModifier {
2059    pub z_index: i32,
2060}
2061
2062impl ZIndexModifier {
2063    pub fn new(z_index: i32) -> Self {
2064        Self { z_index }
2065    }
2066}
2067
2068impl ViewModifier for ZIndexModifier {
2069    fn modify<V: View>(self, content: V) -> impl View {
2070        ModifiedView::new(content, self)
2071    }
2072}
2073
2074/// Layout constraints for views
2075#[derive(Debug, Clone, Copy, PartialEq)]
2076pub struct LayoutConstraints {
2077    pub min_width: Option<f32>,
2078    pub max_width: Option<f32>,
2079    pub min_height: Option<f32>,
2080    pub max_height: Option<f32>,
2081}
2082
2083impl Default for LayoutConstraints {
2084    fn default() -> Self {
2085        Self {
2086            min_width: None,
2087            max_width: None,
2088            min_height: None,
2089            max_height: None,
2090        }
2091    }
2092}
2093
2094/// Modifier to set layout constraints
2095#[derive(Debug, Clone, Copy, PartialEq)]
2096pub struct LayoutModifier {
2097    pub constraints: LayoutConstraints,
2098}
2099
2100impl LayoutModifier {
2101    pub fn new(constraints: LayoutConstraints) -> Self {
2102        Self { constraints }
2103    }
2104}
2105
2106impl ViewModifier for LayoutModifier {
2107    fn modify<V: View>(self, content: V) -> impl View {
2108        ModifiedView::new(content, self)
2109    }
2110}
2111
2112/// Modifier to handle platform safe areas
2113#[derive(Debug, Clone, Copy, PartialEq)]
2114pub struct SafeAreaModifier {
2115    pub ignores: bool,
2116}
2117
2118impl ViewModifier for SafeAreaModifier {
2119    fn modify<V: View>(self, content: V) -> impl View {
2120        ModifiedView::new(content, self)
2121    }
2122}
2123
2124/// Modifier to add elevation (shadow) to a view
2125#[derive(Debug, Clone, Copy, PartialEq)]
2126pub struct ElevationModifier {
2127    pub level: f32,
2128}
2129
2130impl ViewModifier for ElevationModifier {
2131    fn modify<V: View>(self, content: V) -> impl View {
2132        ModifiedView::new(content, self)
2133    }
2134
2135    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
2136        if self.level > 0.0 {
2137            let radius = self.level * 2.0;
2138            let offset_y = self.level * 0.5;
2139            let shadow_color = [0.0, 0.0, 0.0, 0.3];
2140            renderer.push_shadow(radius, shadow_color, [0.0, offset_y]);
2141            view.render(renderer, rect);
2142            renderer.pop_shadow();
2143        } else {
2144            view.render(renderer, rect);
2145        }
2146    }
2147}
2148
2149// Layout subsystem
2150pub mod layout {
2151    use super::*;
2152
2153    // Layout pass scratch space
2154    pub struct LayoutCache {
2155        pub safe_area: SafeArea,
2156        size_cache: HashMap<(u64, u32, u32), Size>, // (ViewHash, ProposalW, ProposalH)
2157    }
2158
2159    impl LayoutCache {
2160        pub fn new() -> Self {
2161            Self {
2162                safe_area: SafeArea::default(),
2163                size_cache: HashMap::new(),
2164            }
2165        }
2166
2167        pub fn clear(&mut self) {
2168            self.safe_area = SafeArea::default();
2169            self.size_cache.clear();
2170        }
2171
2172        pub fn get_size(&self, view_hash: u64, proposal: SizeProposal) -> Option<Size> {
2173            let pw = (proposal.width.unwrap_or(-1.0) * 100.0) as u32;
2174            let ph = (proposal.height.unwrap_or(-1.0) * 100.0) as u32;
2175            self.size_cache.get(&(view_hash, pw, ph)).copied()
2176        }
2177
2178        pub fn set_size(&mut self, view_hash: u64, proposal: SizeProposal, size: Size) {
2179            let pw = (proposal.width.unwrap_or(-1.0) * 100.0) as u32;
2180            let ph = (proposal.height.unwrap_or(-1.0) * 100.0) as u32;
2181            self.size_cache.insert((view_hash, pw, ph), size);
2182        }
2183    }
2184
2185    /// Proposed size from parent view
2186    #[derive(Debug, Clone, Copy, PartialEq)]
2187    pub struct SizeProposal {
2188        pub width: Option<f32>,
2189        pub height: Option<f32>,
2190    }
2191
2192    impl SizeProposal {
2193        pub fn unspecified() -> Self {
2194            Self {
2195                width: None,
2196                height: None,
2197            }
2198        }
2199
2200        pub fn width(width: f32) -> Self {
2201            Self {
2202                width: Some(width),
2203                height: None,
2204            }
2205        }
2206
2207        pub fn height(height: f32) -> Self {
2208            Self {
2209                width: None,
2210                height: Some(height),
2211            }
2212        }
2213
2214        pub fn tight(width: f32, height: f32) -> Self {
2215            Self {
2216                width: Some(width),
2217                height: Some(height),
2218            }
2219        }
2220
2221        pub fn new(width: Option<f32>, height: Option<f32>) -> Self {
2222            Self { width, height }
2223        }
2224    }
2225
2226    /// A view that can participate in layout
2227    pub trait LayoutView: Send {
2228        /// Propose a size for this view given the available space
2229        fn size_that_fits(
2230            &self,
2231            proposal: SizeProposal,
2232            subviews: &[&dyn LayoutView],
2233            cache: &mut LayoutCache,
2234        ) -> Size;
2235
2236        /// Place subviews within the given bounds
2237        fn place_subviews(
2238            &self,
2239            bounds: Rect,
2240            subviews: &mut [&mut dyn LayoutView],
2241            cache: &mut LayoutCache,
2242        );
2243
2244        /// Returns the flex weight of this view (default is 0.0, which means fixed/intrinsic)
2245        fn flex_weight(&self) -> f32 {
2246            0.0
2247        }
2248    }
2249    /// Edge insets for padding, margins, and safe areas
2250    #[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
2251    pub struct EdgeInsets {
2252        pub top: f32,
2253        pub leading: f32,
2254        pub bottom: f32,
2255        pub trailing: f32,
2256    }
2257
2258    impl EdgeInsets {
2259        pub fn new(top: f32, leading: f32, bottom: f32, trailing: f32) -> Self {
2260            Self { top, leading, bottom, trailing }
2261        }
2262
2263        pub fn all(value: f32) -> Self {
2264            Self {
2265                top: value,
2266                leading: value,
2267                bottom: value,
2268                trailing: value,
2269            }
2270        }
2271    }
2272
2273    /// SafeArea constraints provided by the platform
2274    #[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
2275    pub struct SafeArea {
2276        pub insets: EdgeInsets,
2277    }
2278
2279    /// Rectangle in logical pixels
2280    #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
2281    pub struct Rect {
2282        pub x: f32,
2283        pub y: f32,
2284        pub width: f32,
2285        pub height: f32,
2286    }
2287
2288    impl Rect {
2289        pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
2290            Self {
2291                x,
2292                y,
2293                width,
2294                height,
2295            }
2296        }
2297
2298        pub fn zero() -> Self {
2299            Self {
2300                x: 0.0,
2301                y: 0.0,
2302                width: 0.0,
2303                height: 0.0,
2304            }
2305        }
2306
2307        pub fn size(&self) -> Size {
2308            Size {
2309                width: self.width,
2310                height: self.height,
2311            }
2312        }
2313
2314        /// Split the rect horizontally into N equal pieces
2315        pub fn split_horizontal(&self, n: usize) -> Vec<Rect> {
2316            if n == 0 {
2317                return vec![];
2318            }
2319            let item_width = self.width / n as f32;
2320            (0..n)
2321                .map(|i| Rect {
2322                    x: self.x + i as f32 * item_width,
2323                    y: self.y,
2324                    width: item_width,
2325                    height: self.height,
2326                })
2327                .collect()
2328        }
2329
2330        /// Split the rect vertically into N equal pieces
2331        pub fn split_vertical(&self, n: usize) -> Vec<Rect> {
2332            if n == 0 {
2333                return vec![];
2334            }
2335            let item_height = self.height / n as f32;
2336            (0..n)
2337                .map(|i| Rect {
2338                    x: self.x,
2339                    y: self.y + i as f32 * item_height,
2340                    width: self.width,
2341                    height: item_height,
2342                })
2343                .collect()
2344        }
2345    }
2346}
2347
2348// Re-export layout items for convenience
2349pub use layout::{LayoutCache, LayoutView, Rect, SizeProposal};
2350// Size and FrameRenderer are pub items in this module; no re-export alias needed.
2351
2352pub mod runtime;
2353pub mod scene_graph;
2354
2355pub use scene_graph::{NodeId, bifrost_registry};
2356
2357/// State of an asset being loaded
2358#[derive(Debug, Clone, PartialEq)]
2359pub enum AssetState<T> {
2360    Loading,
2361    Ready(T),
2362    Error(String),
2363}
2364
2365/// AssetManager defines the interface for loading and caching external resources.
2366pub trait AssetManager: Send + Sync {
2367    /// Request an image asset. Returns the current state (Loading, Ready, or Error).
2368    fn load_image(&self, url: &str) -> AssetState<Arc<Vec<u8>>>;
2369
2370    /// Pre-load an image into the cache.
2371    fn preload_image(&self, url: &str);
2372}
2373
2374/// User input event types
2375#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
2376pub enum Event {
2377    PointerDown { x: f32, y: f32 },
2378    PointerUp { x: f32, y: f32 },
2379    PointerMove { x: f32, y: f32 },
2380    PointerClick { x: f32, y: f32 },
2381    PointerEnter,
2382    PointerLeave,
2383    KeyDown { key: String },
2384    KeyUp { key: String },
2385    /// Input Method Editor event (e.g. CJK character composition)
2386    Ime(String),
2387}
2388
2389impl Event {
2390    /// Returns the canonical string name of the event for lookup in handler maps.
2391    pub fn name(&self) -> &'static str {
2392        match self {
2393            Self::PointerDown { .. } => "pointerdown",
2394            Self::PointerUp { .. } => "pointerup",
2395            Self::PointerMove { .. } => "pointermove",
2396            Self::PointerClick { .. } => "pointerclick",
2397            Self::PointerEnter => "pointerenter",
2398            Self::PointerLeave => "pointerleave",
2399            Self::KeyDown { .. } => "keydown",
2400            Self::KeyUp { .. } => "keyup",
2401            Self::Ime(_) => "ime",
2402        }
2403    }
2404}
2405
2406/// Response from an event handler
2407#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2408pub enum EventResponse {
2409    Handled,
2410    Ignored,
2411}
2412
2413/// A basic implementation of AssetManager that can be overridden by platform backends.
2414pub struct DefaultAssetManager {
2415    cache: Arc<std::sync::RwLock<HashMap<String, AssetState<Arc<Vec<u8>>>>>>,
2416}
2417
2418impl DefaultAssetManager {
2419    pub fn new() -> Self {
2420        Self {
2421            cache: Arc::new(std::sync::RwLock::new(HashMap::new())),
2422        }
2423    }
2424}
2425
2426impl AssetManager for DefaultAssetManager {
2427    fn load_image(&self, url: &str) -> AssetState<Arc<Vec<u8>>> {
2428        let mut cache = self.cache.write().unwrap();
2429        if let Some(state) = cache.get(url) {
2430            return state.clone();
2431        }
2432
2433        // In the default manager, we just mark it as Loading and spawn a placeholder
2434        // (Real backends will override this with actual I/O)
2435        cache.insert(url.to_string(), AssetState::Loading);
2436        AssetState::Loading
2437    }
2438
2439    fn preload_image(&self, _url: &str) {
2440        // No-op for default manager
2441    }
2442}
2443
2444#[cfg(test)]
2445mod phase1_test;