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 {
44        value: String,
45    },
46    /// Different values for light and dark mode
47    Adaptive {
48        light: String,
49        dark: String,
50    },
51}
52
53/// YggdrasilTokens is the authoritative container for all design tokens in the CVKG ecosystem.
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct YggdrasilTokens {
56    pub color: HashMap<String, TokenValue>,
57    pub font: HashMap<String, TokenValue>,
58    pub spacing: HashMap<String, TokenValue>,
59    pub radius: HashMap<String, TokenValue>,
60    pub shadow: HashMap<String, TokenValue>,
61    pub border: HashMap<String, TokenValue>,
62    pub anim: HashMap<String, TokenValue>,
63    pub bifrost: HashMap<String, TokenValue>,
64    pub gungnir: HashMap<String, TokenValue>,
65    pub mjolnir: HashMap<String, TokenValue>,
66    pub accessibility: HashMap<String, TokenValue>,
67}
68
69impl YggdrasilTokens {
70    pub fn new() -> Self {
71        Self {
72            color: HashMap::new(),
73            font: HashMap::new(),
74            spacing: HashMap::new(),
75            radius: HashMap::new(),
76            shadow: HashMap::new(),
77            border: HashMap::new(),
78            anim: HashMap::new(),
79            bifrost: HashMap::new(),
80            gungnir: HashMap::new(),
81            mjolnir: HashMap::new(),
82            accessibility: HashMap::new(),
83        }
84    }
85
86    /// Get a color token value for the current mode
87    pub fn get_color(&self, key: &str, is_dark: bool) -> Option<String> {
88        self.color.get(key).and_then(|token| {
89            match token {
90                TokenValue::Single { value } => Some(value.clone()),
91                TokenValue::Adaptive { light, dark } => {
92                    if is_dark { Some(dark.clone()) } else { Some(light.clone()) }
93                }
94            }
95        })
96    }
97
98    /// Get a token value of any type and parse it into the target type
99    pub fn get<T: FromStr>(&self, category: &str, key: &str, is_dark: bool) -> Option<T> {
100        let map = match category {
101            "color" => &self.color,
102            "font" => &self.font,
103            "spacing" => &self.spacing,
104            "radius" => &self.radius,
105            "shadow" => &self.shadow,
106            "border" => &self.border,
107            "anim" => &self.anim,
108            "bifrost" => &self.bifrost,
109            "gungnir" => &self.gungnir,
110            "mjolnir" => &self.mjolnir,
111            "accessibility" => &self.accessibility,
112            _ => return None,
113        };
114
115        map.get(key).and_then(|token| {
116            match token {
117                TokenValue::Single { value } => value.parse().ok(),
118                TokenValue::Adaptive { light, dark } => {
119                    let value = if is_dark { dark } else { light };
120                    value.parse().ok()
121                }
122            }
123        })
124    }
125}
126
127pub trait View: Sized + Send {
128    /// The concrete type produced after applying modifiers.
129    /// For primitive views this is Self.
130    type Body: View;
131
132    fn body(self) -> Self::Body;
133
134    /// Render this view into the provided renderer at the specified bounds.
135    /// Primitive views override this to perform drawing operations.
136    fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
137
138    /// Provided modifier entry point
139    fn modifier<M: ViewModifier>(self, m: M) -> ModifiedView<Self, M> {
140        ModifiedView::new(self, m)
141    }
142
143    /// Apply a Bifrost (Frosted Glass) effect to the view
144    fn bifrost(self, blur: f32, saturation: f32, opacity: f32) -> ModifiedView<Self, BifrostModifier> {
145        self.modifier(BifrostModifier { blur, saturation, opacity })
146    }
147
148    /// Apply a Gungnir (Neon Glow) effect to the view
149    fn gungnir(self, color: impl Into<String>, radius: f32, intensity: f32) -> ModifiedView<Self, GungnirModifier> {
150        self.modifier(GungnirModifier { color: color.into(), radius, intensity })
151    }
152
153    /// Apply a Mjolnir Slice (Geometric cut) to the view
154    fn mjolnir_slice(self, angle: f32, offset: f32) -> ModifiedView<Self, MjolnirSliceModifier> {
155        self.modifier(MjolnirSliceModifier { angle, offset })
156    }
157
158    /// Apply a Mjolnir Shatter (Fragmented transition) to the view
159    fn mjolnir_shatter(self, pieces: u32, force: f32) -> ModifiedView<Self, MjolnirShatterModifier> {
160        self.modifier(MjolnirShatterModifier { pieces, force })
161    }
162
163    /// Mark this view as a Bifrost Bridge (Shared Element) for cross-view persistence
164    fn bifrost_bridge(self, id: impl Into<String>) -> ModifiedView<Self, BifrostBridgeModifier> {
165        self.modifier(BifrostBridgeModifier { id: id.into() })
166    }
167
168    /// Add a background color to this view
169    fn background(self, color: [f32; 4]) -> ModifiedView<Self, BackgroundModifier> {
170        self.modifier(BackgroundModifier { color })
171    }
172
173    /// Add padding to this view
174    fn padding(self, amount: f32) -> ModifiedView<Self, PaddingModifier> {
175        self.modifier(PaddingModifier { amount })
176    }
177
178    /// Trigger an action when the view appears
179    fn on_appear<F: Fn() + Send + Sync + 'static>(self, action: F) -> ModifiedView<Self, LifecycleModifier> {
180        self.modifier(LifecycleModifier {
181            on_appear: Some(Arc::new(action)),
182            on_disappear: None,
183        })
184    }
185
186    /// Trigger an action when the view disappears
187    fn on_disappear<F: Fn() + Send + Sync + 'static>(self, action: F) -> ModifiedView<Self, LifecycleModifier> {
188        self.modifier(LifecycleModifier {
189            on_appear: None,
190            on_disappear: Some(Arc::new(action)),
191        })
192    }
193
194    /// Type-erase this view into AnyView
195    fn erase(self) -> AnyView where Self: 'static {
196        AnyView::new(self)
197    }
198}
199
200/// An object-safe version of the View trait for type erasure.
201pub trait ErasedView: Send {
202    fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect);
203}
204
205impl<V: View + 'static> ErasedView for V {
206    fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect) {
207        self.render(renderer, rect);
208    }
209}
210
211/// A type-erased View wrapper.
212pub struct AnyView {
213    inner: Box<dyn ErasedView>,
214}
215
216impl AnyView {
217    pub fn new<V: View + 'static>(view: V) -> Self {
218        Self { inner: Box::new(view) }
219    }
220}
221
222impl View for AnyView {
223    type Body = Never;
224    fn body(self) -> Self::Body { unreachable!() }
225    
226    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
227        self.inner.render_erased(renderer, rect);
228    }
229}
230
231/// BifrostBridgeModifier enables shared-element transitions.
232/// When two views share the same Bifrost Bridge ID, the Sleipnir solver will
233/// interpolate their geometry and effects (blur, glow) during the transition.
234#[derive(Debug, Clone, PartialEq)]
235pub struct BifrostBridgeModifier {
236    pub id: String,
237}
238
239impl ViewModifier for BifrostBridgeModifier {
240    fn modify<V: View>(self, content: V) -> impl View {
241        ModifiedView::new(content, self)
242    }
243}
244
245/// MjolnirSliceModifier implements the "Geometric Slice" aesthetic.
246/// It uses a signed distance field (SDF) to clip the view along a sharp angled line.
247#[derive(Debug, Clone, Copy, PartialEq)]
248pub struct MjolnirSliceModifier {
249    pub angle: f32,
250    pub offset: f32,
251}
252
253impl ViewModifier for MjolnirSliceModifier {
254    fn modify<V: View>(self, content: V) -> impl View {
255        ModifiedView::new(content, self)
256    }
257}
258
259/// MjolnirShatterModifier implements the "Shattering" effect.
260/// It breaks the view into discrete geometric fragments that can be animated.
261#[derive(Debug, Clone, Copy, PartialEq)]
262pub struct MjolnirShatterModifier {
263    pub pieces: u32,
264    pub force: f32,
265}
266
267impl ViewModifier for MjolnirShatterModifier {
268    fn modify<V: View>(self, content: V) -> impl View {
269        ModifiedView::new(content, self)
270    }
271}
272
273/// BifrostModifier implements the Cyberpunk "Frosted Glass" aesthetic.
274/// It triggers backdrop blurring and light scattering in the render pipeline.
275#[derive(Debug, Clone, Copy, PartialEq)]
276pub struct BifrostModifier {
277    pub blur: f32,
278    pub saturation: f32,
279    pub opacity: f32,
280}
281
282impl ViewModifier for BifrostModifier {
283    fn modify<V: View>(self, content: V) -> impl View {
284        ModifiedView::new(content, self)
285    }
286
287    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
288        // Frosted glass placeholder
289        renderer.fill_rect(rect, [1.0, 1.0, 1.0, 0.1]);
290    }
291}
292
293/// A modifier that adds a background color to a view.
294#[derive(Debug, Clone, Copy, PartialEq)]
295pub struct BackgroundModifier {
296    pub color: [f32; 4],
297}
298
299impl ViewModifier for BackgroundModifier {
300    fn modify<V: View>(self, content: V) -> impl View {
301        ModifiedView::new(content, self)
302    }
303
304    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
305        renderer.fill_rect(rect, self.color);
306    }
307}
308
309/// A modifier that adds padding to a view.
310#[derive(Debug, Clone, Copy, PartialEq)]
311pub struct PaddingModifier {
312    pub amount: f32,
313}
314
315impl ViewModifier for PaddingModifier {
316    fn modify<V: View>(self, content: V) -> impl View {
317        ModifiedView::new(content, self)
318    }
319
320    fn transform_rect(&self, rect: Rect) -> Rect {
321        Rect {
322            x: rect.x + self.amount,
323            y: rect.y + self.amount,
324            width: (rect.width - 2.0 * self.amount).max(0.0),
325            height: (rect.height - 2.0 * self.amount).max(0.0),
326        }
327    }
328}
329
330/// GungnirModifier implements the "Neon Glow" aesthetic.
331/// It uses additive blending and multi-pass blurring to simulate glowing light.
332#[derive(Debug, Clone, PartialEq)]
333pub struct GungnirModifier {
334    pub color: String,
335    pub radius: f32,
336    pub intensity: f32,
337}
338
339impl ViewModifier for GungnirModifier {
340    fn modify<V: View>(self, content: V) -> impl View {
341        ModifiedView::new(content, self)
342    }
343
344    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
345        // Neon Glow placeholder
346        renderer.stroke_rect(rect, [0.0, 1.0, 1.0, self.intensity], self.radius / 10.0);
347    }
348}
349
350/// SleipnirModifier handles physics-based animations via the Sleipnir RK4 solver.
351#[derive(Debug, Clone, PartialEq)]
352pub struct SleipnirModifier<T> {
353    pub target: T,
354    pub stiffness: f32,
355    pub damping: f32,
356}
357
358impl<T: Send + Sync + 'static + Clone> ViewModifier for SleipnirModifier<T> {
359    fn modify<V: View>(self, content: V) -> impl View {
360        ModifiedView::new(content, self)
361    }
362}
363
364/// LifecycleModifier handles on_appear and on_disappear hooks.
365#[derive(Clone)]
366pub struct LifecycleModifier {
367    pub on_appear: Option<Arc<dyn Fn() + Send + Sync>>,
368    pub on_disappear: Option<Arc<dyn Fn() + Send + Sync>>,
369}
370
371impl ViewModifier for LifecycleModifier {
372    fn modify<V: View>(self, content: V) -> impl View {
373        ModifiedView::new(content, self)
374    }
375}
376
377// Primitive (leaf) views implement Never as body
378#[doc(hidden)]
379pub enum Never {}
380
381impl View for Never {
382    type Body = Never;
383    fn body(self) -> Never {
384        unreachable!()
385    }
386}
387
388/// A view that has been transformed by a modifier.
389///
390/// Section 4.3: "Each modifier implements ViewModifier and produces a ModifiedView<Inner, Self>."
391pub struct ModifiedView<V, M> {
392    view: V,
393    modifier: M,
394}
395
396    impl<V: View, M: ViewModifier> ModifiedView<V, M> {
397        #[doc(hidden)]
398        pub fn new(view: V, modifier: M) -> Self {
399            Self { view, modifier }
400        }
401    }
402
403    impl<V: View, M: ViewModifier> View for ModifiedView<V, M> {
404        type Body = ModifiedView<V::Body, M>;
405
406        fn body(self) -> Self::Body {
407            ModifiedView {
408                view: self.view.body(),
409                modifier: self.modifier.clone(),
410            }
411        }
412
413        fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
414            self.modifier.render(renderer, rect);
415            let child_rect = self.modifier.transform_rect(rect);
416            self.view.render(renderer, child_rect);
417        }
418    }
419
420
421    pub trait ViewModifier: Send + Clone {
422        fn modify<V: View>(self, content: V) -> impl View;
423        fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {}
424        fn transform_rect(&self, rect: Rect) -> Rect { rect }
425    }
426
427    /// The Renderer trait defines the atomic drawing operations for all CVKG backends.
428    /// 
429    /// # Implementation Requirements
430    /// 1. Coordinate system is origin-top-left (0,0) with Y increasing downwards.
431    /// 2. Colors are [R, G, B, A] in the [0.0, 1.0] range.
432    /// 3. All operations must be batchable by the underlying backend.
433    pub trait Renderer {
434        fn begin_frame(&mut self);
435        fn end_frame(&mut self);
436
437        fn fill_rect(&mut self, rect: Rect, color: [f32; 4]);
438        fn stroke_rect(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
439        fn fill_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4]);
440        fn draw_text(&mut self, text: &str, x: f32, y: f32, size: f32, color: [f32; 4]);
441        
442        /// Draw a texture at the specified rect
443        fn draw_texture(&mut self, texture_id: u32, rect: Rect);
444        
445        /// Draw an image asset by name or path
446        fn draw_image(&mut self, image_name: &str, rect: Rect);
447    }
448
449
450
451use std::sync::Arc;
452
453/// State wrapper that owns a value and notifies subscribers when changed
454#[derive(Clone)]
455pub struct State<T: Clone + Send + Sync + 'static> {
456    value: Arc<std::sync::RwLock<T>>,
457    subscribers: Arc<std::sync::RwLock<Vec<Box<dyn FnMut(&T) + Send + Sync>>>>,
458}
459
460impl<T: Clone + Send + Sync + 'static> State<T> {
461    /// Create a new State with initial value
462    pub fn new(value: T) -> Self {
463        Self {
464            value: Arc::new(std::sync::RwLock::new(value)),
465            subscribers: Arc::new(std::sync::RwLock::new(Vec::new())),
466        }
467    }
468    
469    /// Get the current value
470    pub fn get(&self) -> T {
471        self.value.read().unwrap().clone()
472    }
473    
474    /// Set a new value, notifying all subscribers
475    pub fn set(&self, value: T) {
476        *self.value.write().unwrap() = value;
477        // Notify subscribers
478        let mut subscribers = self.subscribers.write().unwrap();
479        for subscriber in subscribers.iter_mut() {
480            subscriber(&self.get());
481        }
482    }
483    
484    /// Subscribe to state changes
485    pub fn subscribe<F: FnMut(&T) + Send + Sync + 'static>(&self, callback: F) {
486        self.subscribers.write().unwrap().push(Box::new(callback));
487    }
488}
489
490/// Read/write reference to state owned by another view
491#[derive(Clone)]
492pub struct Binding<T: Clone + Send + Sync + 'static> {
493    state: Arc<std::sync::RwLock<T>>,
494}
495
496impl<T: Clone + Send + Sync + 'static> Binding<T> {
497    /// Create a binding from a State
498    pub fn from_state(state: &State<T>) -> Self {
499        Self {
500            state: state.value.clone(),
501        }
502    }
503    
504    /// Get the current value
505    pub fn get(&self) -> T {
506        self.state.read().unwrap().clone()
507    }
508    
509    /// Set a new value
510    pub fn set(&self, value: T) {
511        *self.state.write().unwrap() = value;
512    }
513}
514
515use std::any::TypeId;
516use std::sync::{Mutex, OnceLock};
517
518/// Global environment storage using TypeId as keys.
519pub(crate) static ENVIRONMENT: OnceLock<Mutex<HashMap<TypeId, Box<dyn std::any::Any + Send + Sync>>>> = OnceLock::new();
520
521/// Environment key type for accessing ambient values
522///
523/// Implement this trait to define a new environment key.
524pub trait EnvKey: 'static + Send + Sync {
525    /// The type of value stored in the environment
526    type Value: Clone + Send + Sync + 'static;
527    
528    /// Get a default value for this key
529    fn default_value() -> Self::Value;
530}
531
532/// Key for accessing the Yggdrasil design token tree
533pub struct YggdrasilKey;
534
535impl EnvKey for YggdrasilKey {
536    type Value = YggdrasilTokens;
537    fn default_value() -> Self::Value {
538        default_tokens()
539    }
540}
541
542/// System appearance (Light/Dark mode)
543#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
544pub enum Appearance {
545    Light,
546    Dark,
547}
548
549/// Orientation for layouts
550#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
551pub enum Orientation {
552    Horizontal,
553    Vertical,
554}
555
556/// A color represented by RGBA components in the [0.0, 1.0] range.
557#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
558pub struct Color {
559    pub r: f32,
560    pub g: f32,
561    pub b: f32,
562    pub a: f32,
563}
564
565impl Color {
566    pub const BLACK: Color = Color { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
567    pub const WHITE: Color = Color { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
568    pub const TRANSPARENT: Color = Color { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
569    pub const RED: Color = Color { r: 1.0, g: 0.0, b: 0.0, a: 1.0 };
570    pub const GREEN: Color = Color { r: 0.0, g: 1.0, b: 0.0, a: 1.0 };
571    pub const BLUE: Color = Color { r: 0.0, g: 0.0, b: 1.0, a: 1.0 };
572
573    /// Create a new color from RGBA components.
574    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
575        Self { r, g, b, a }
576    }
577
578    /// Convert the color to a [r, g, b, a] array.
579    pub fn as_array(&self) -> [f32; 4] {
580        [self.r, self.g, self.b, self.a]
581    }
582}
583
584impl View for Color {
585    type Body = Never;
586    fn body(self) -> Self::Body { unreachable!() }
587    
588    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
589        renderer.fill_rect(rect, self.as_array());
590    }
591}
592
593/// Key for accessing the current system appearance
594pub struct AppearanceKey;
595
596impl EnvKey for AppearanceKey {
597    type Value = Appearance;
598    fn default_value() -> Self::Value {
599        Appearance::Dark // Default to Dark (Ginnungagap) for Berserker aesthetic
600    }
601}
602
603/// StyleResolver provides high-level access to themed values from the environment.
604pub struct StyleResolver;
605
606impl StyleResolver {
607    /// Resolve a color from the current environment
608    pub fn color(key: &str) -> String {
609        let tokens = Environment::<YggdrasilKey>::new().get();
610        let appearance = Environment::<AppearanceKey>::new().get();
611        let is_dark = appearance == Appearance::Dark;
612        
613        tokens.get_color(key, is_dark).unwrap_or_else(|| "#FF00FF".to_string()) // Default to MuspelMagenta on failure
614    }
615
616    /// Resolve a generic token value
617    pub fn get<T: FromStr>(category: &str, key: &str) -> Option<T> {
618        let tokens = Environment::<YggdrasilKey>::new().get();
619        let appearance = Environment::<AppearanceKey>::new().get();
620        let is_dark = appearance == Appearance::Dark;
621        
622        tokens.get(category, key, is_dark)
623    }
624}
625
626/// The authoritative Cyberpunk Viking default tokens
627pub fn default_tokens() -> YggdrasilTokens {
628    let mut tokens = YggdrasilTokens::new();
629    
630    // Core Norse Colorways
631    tokens.color.insert("background".to_string(), TokenValue::Single {
632        value: "#000000".to_string(), // Ginnungagap (The Void)
633    });
634    
635    tokens.color.insert("primary".to_string(), TokenValue::Single {
636        value: "#00FFFF".to_string(), // NiflCyan (Aesir Primary)
637    });
638    
639    tokens.color.insert("secondary".to_string(), TokenValue::Single {
640        value: "#FF00FF".to_string(), // MuspelMagenta (Berserker Secondary)
641    });
642
643    tokens.color.insert("surface".to_string(), TokenValue::Adaptive {
644        light: "#FFFFFF".to_string(),
645        dark: "#121212".to_string(),
646    });
647
648    tokens.color.insert("text".to_string(), TokenValue::Adaptive {
649        light: "#000000".to_string(),
650        dark: "#FFFFFF".to_string(),
651    });
652    
653    // Bifrost (Glassmorphism) - Frosted Style
654    tokens.bifrost.insert("blur".to_string(), TokenValue::Single {
655        value: "25.0".to_string(),
656    });
657    tokens.bifrost.insert("saturation".to_string(), TokenValue::Single {
658        value: "1.2".to_string(),
659    });
660    tokens.bifrost.insert("opacity".to_string(), TokenValue::Single {
661        value: "0.65".to_string(),
662    });
663    
664    // Gungnir (Neon Glow)
665    tokens.gungnir.insert("intensity".to_string(), TokenValue::Single {
666        value: "1.0".to_string(),
667    });
668    tokens.gungnir.insert("radius".to_string(), TokenValue::Single {
669        value: "15.0".to_string(),
670    });
671
672    // Mjolnir (Sharp Geometry)
673    tokens.mjolnir.insert("clip_angle".to_string(), TokenValue::Single {
674        value: "12.0".to_string(),
675    });
676    tokens.mjolnir.insert("border_width".to_string(), TokenValue::Single {
677        value: "2.0".to_string(),
678    });
679    
680    // Sleipnir (Spring Animation)
681    tokens.anim.insert("stiffness".to_string(), TokenValue::Single {
682        value: "170.0".to_string(),
683    });
684    tokens.anim.insert("damping".to_string(), TokenValue::Single {
685        value: "26.0".to_string(),
686    });
687    tokens.anim.insert("mass".to_string(), TokenValue::Single {
688        value: "1.0".to_string(),
689    });
690
691    // Accessibility
692    tokens.accessibility.insert("reduce_motion".to_string(), TokenValue::Single {
693        value: "false".to_string(),
694    });
695    
696    tokens
697}
698
699
700
701
702/// Environment wrapper for accessing ambient values
703pub struct Environment<K: EnvKey> {
704    _marker: std::marker::PhantomData<K>,
705}
706
707impl<K: EnvKey> Environment<K> {
708    /// Create a new Environment
709    pub fn new() -> Self {
710        Self {
711            _marker: std::marker::PhantomData,
712        }
713    }
714    
715    /// Get the current value from the environment
716    pub fn get(&self) -> K::Value {
717        if let Some(env_store) = ENVIRONMENT.get() {
718            let env_lock = env_store.lock().unwrap();
719            if let Some(val) = env_lock.get(&std::any::TypeId::of::<K>()) {
720                if let Some(typed_val) = val.downcast_ref::<K::Value>() {
721                    return typed_val.clone();
722                }
723            }
724        }
725        K::default_value()
726    }
727}
728mod env {
729    
730    
731    
732    
733    /// Insert a value into the environment
734    pub fn insert<K: super::EnvKey>(value: K::Value) {
735        if let Some(store) = super::ENVIRONMENT.get() {
736            let mut env_map = store.lock().unwrap();
737            env_map.insert(std::any::TypeId::of::<K>(), Box::new(value));
738        }
739    }
740    
741    /// Remove a value from the environment.
742    pub fn remove<K: super::EnvKey>() {
743        if let Some(store) = super::ENVIRONMENT.get() {
744            let mut env_map = store.lock().unwrap();
745            env_map.remove(&std::any::TypeId::of::<K>());
746        }
747    }
748}
749
750
751
752/// Geometry modifiers
753
754/// Size of the view in logical pixels
755#[derive(Debug, Clone, Copy, PartialEq)]
756pub struct Size {
757    pub width: f32,
758    pub height: f32,
759}
760
761/// Insets for padding
762#[derive(Debug, Clone, Copy, PartialEq)]
763pub struct EdgeInsets {
764    pub top: f32,
765    pub leading: f32,
766    pub bottom: f32,
767    pub trailing: f32,
768}
769
770impl EdgeInsets {
771    /// Equal insets on all edges
772    pub fn all(value: f32) -> Self {
773        Self {
774            top: value,
775            leading: value,
776            bottom: value,
777            trailing: value,
778        }
779    }
780    
781    /// Vertical insets (top and bottom)
782    pub fn vertical(value: f32) -> Self {
783        Self {
784            top: value,
785            leading: 0.0,
786            bottom: value,
787            trailing: 0.0,
788        }
789    }
790    
791    /// Horizontal insets (leading and trailing)
792    pub fn horizontal(value: f32) -> Self {
793        Self {
794            top: 0.0,
795            leading: value,
796            bottom: 0.0,
797            trailing: value,
798        }
799    }
800}
801
802/// Modifier to set the size of a view
803#[derive(Debug, Clone, Copy, PartialEq)]
804pub struct FrameModifier {
805    pub width: Option<f32>,
806    pub height: Option<f32>,
807}
808
809impl FrameModifier {
810    pub fn new() -> Self {
811        Self {
812            width: None,
813            height: None,
814        }
815    }
816    
817    pub fn width(mut self, width: f32) -> Self {
818        self.width = Some(width);
819        self
820    }
821    
822    pub fn height(mut self, height: f32) -> Self {
823        self.height = Some(height);
824        self
825    }
826    
827    pub fn size(mut self, width: f32, height: f32) -> Self {
828        self.width = Some(width);
829        self.height = Some(height);
830        self
831    }
832}
833
834impl ViewModifier for FrameModifier {
835    fn modify<V: View>(self, content: V) -> impl View {
836        ModifiedView::new(content, self)
837    }
838}
839
840
841/// Modifier to offset a view
842#[derive(Debug, Clone, Copy, PartialEq)]
843pub struct OffsetModifier {
844    pub x: f32,
845    pub y: f32,
846}
847
848impl OffsetModifier {
849    pub fn new(x: f32, y: f32) -> Self {
850        Self { x, y }
851    }
852}
853
854impl ViewModifier for OffsetModifier {
855    fn modify<V: View>(self, content: V) -> impl View {
856        ModifiedView::new(content, self)
857    }
858}
859
860/// Modifier to set the z-index of a view
861#[derive(Debug, Clone, Copy, PartialEq, Eq)]
862pub struct ZIndexModifier {
863    pub z_index: i32,
864}
865
866impl ZIndexModifier {
867    pub fn new(z_index: i32) -> Self {
868        Self { z_index }
869    }
870}
871
872impl ViewModifier for ZIndexModifier {
873    fn modify<V: View>(self, content: V) -> impl View {
874        ModifiedView::new(content, self)
875    }
876}
877
878/// Layout constraints for views
879#[derive(Debug, Clone, Copy, PartialEq)]
880pub struct LayoutConstraints {
881    pub min_width: Option<f32>,
882    pub max_width: Option<f32>,
883    pub min_height: Option<f32>,
884    pub max_height: Option<f32>,
885}
886
887impl Default for LayoutConstraints {
888    fn default() -> Self {
889        Self {
890            min_width: None,
891            max_width: None,
892            min_height: None,
893            max_height: None,
894        }
895    }
896}
897
898/// Modifier to set layout constraints
899#[derive(Debug, Clone, Copy, PartialEq)]
900pub struct LayoutModifier {
901    pub constraints: LayoutConstraints,
902}
903
904impl LayoutModifier {
905    pub fn new(constraints: LayoutConstraints) -> Self {
906        Self { constraints }
907    }
908}
909
910impl ViewModifier for LayoutModifier {
911    fn modify<V: View>(self, content: V) -> impl View {
912        ModifiedView::new(content, self)
913    }
914}
915
916/// Modifier to make a view flexible in layout
917#[derive(Debug, Clone, Copy, PartialEq)]
918pub struct FlexModifier {
919    pub flex: f32,
920}
921
922impl FlexModifier {
923    pub fn new(flex: f32) -> Self {
924        Self { flex }
925    }
926}
927
928impl ViewModifier for FlexModifier {
929    fn modify<V: View>(self, content: V) -> impl View {
930        ModifiedView::new(content, self)
931    }
932}
933
934// Layout subsystem
935pub mod layout {
936    use super::*;
937
938    // Layout pass scratch space
939    pub struct LayoutCache;
940
941    impl LayoutCache {
942        pub fn new() -> Self {
943            Self
944        }
945
946        pub fn clear(&mut self) {
947            // In a real implementation, this would clear cached layout data
948        }
949    }
950    
951    /// Proposed size from parent view
952    #[derive(Debug, Clone, Copy, PartialEq)]
953    pub struct SizeProposal {
954        pub width: Option<f32>,
955        pub height: Option<f32>,
956    }
957    
958    impl SizeProposal {
959        pub fn unspecified() -> Self {
960            Self {
961                width: None,
962                height: None,
963            }
964        }
965        
966        pub fn width(width: f32) -> Self {
967            Self {
968                width: Some(width),
969                height: None,
970            }
971        }
972        
973        pub fn height(height: f32) -> Self {
974            Self {
975                width: None,
976                height: Some(height),
977            }
978        }
979        
980        pub fn tight(width: f32, height: f32) -> Self {
981            Self {
982                width: Some(width),
983                height: Some(height),
984            }
985        }
986    }
987    
988    /// A view that can participate in layout
989    pub trait LayoutView: Send {
990        /// Propose a size for this view given the available space
991        fn size_that_fits(&self, proposal: SizeProposal, subviews: &[&dyn LayoutView], cache: &mut LayoutCache) -> Size;
992        
993        /// Place subviews within the given bounds
994        fn place_subviews(&self, bounds: Rect, subviews: &mut [&mut dyn LayoutView], cache: &mut LayoutCache);
995    }
996    
997    /// Rectangle in logical pixels
998    #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
999    pub struct Rect {
1000        pub x: f32,
1001        pub y: f32,
1002        pub width: f32,
1003        pub height: f32,
1004    }
1005    
1006    impl Rect {
1007        pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
1008            Self { x, y, width, height }
1009        }
1010        
1011        pub fn zero() -> Self {
1012            Self { x: 0.0, y: 0.0, width: 0.0, height: 0.0 }
1013        }
1014        
1015        pub fn size(&self) -> Size {
1016            Size { width: self.width, height: self.height }
1017        }
1018
1019        /// Split the rect horizontally into N equal pieces
1020        pub fn split_horizontal(&self, n: usize) -> Vec<Rect> {
1021            if n == 0 { return vec![]; }
1022            let item_width = self.width / n as f32;
1023            (0..n).map(|i| Rect {
1024                x: self.x + i as f32 * item_width,
1025                y: self.y,
1026                width: item_width,
1027                height: self.height,
1028            }).collect()
1029        }
1030
1031        /// Split the rect vertically into N equal pieces
1032        pub fn split_vertical(&self, n: usize) -> Vec<Rect> {
1033            if n == 0 { return vec![]; }
1034            let item_height = self.height / n as f32;
1035            (0..n).map(|i| Rect {
1036                x: self.x,
1037                y: self.y + i as f32 * item_height,
1038                width: self.width,
1039                height: item_height,
1040            }).collect()
1041        }
1042    }
1043}
1044
1045// Re-export layout items for convenience
1046pub use layout::{LayoutView, SizeProposal, Rect, LayoutCache};
1047
1048pub mod runtime;
1049pub mod scene_graph;
1050
1051pub use scene_graph::{NodeId, bifrost_registry};
1052
1053#[cfg(test)]
1054mod phase1_test;