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    /// Optionally provide a layout implementation for this view.
134    fn layout(&self) -> Option<&dyn layout::LayoutView> {
135        None
136    }
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(
145        self,
146        blur: f32,
147        saturation: f32,
148        opacity: f32,
149    ) -> ModifiedView<Self, BifrostModifier> {
150        self.modifier(BifrostModifier {
151            blur,
152            saturation,
153            opacity,
154        })
155    }
156
157    /// Apply a Gungnir (Neon Glow) effect to the view
158    fn gungnir(
159        self,
160        color: impl Into<String>,
161        radius: f32,
162        intensity: f32,
163    ) -> ModifiedView<Self, GungnirModifier> {
164        self.modifier(GungnirModifier {
165            color: color.into(),
166            radius,
167            intensity,
168        })
169    }
170
171    /// Apply a Mjolnir Slice (Geometric cut) to the view
172    fn mjolnir_slice(self, angle: f32, offset: f32) -> ModifiedView<Self, MjolnirSliceModifier> {
173        self.modifier(MjolnirSliceModifier { angle, offset })
174    }
175
176    /// Apply a Mjolnir Shatter (Fragmented transition) to the view
177    fn mjolnir_shatter(
178        self,
179        pieces: u32,
180        force: f32,
181    ) -> ModifiedView<Self, MjolnirShatterModifier> {
182        self.modifier(MjolnirShatterModifier { pieces, force })
183    }
184
185    /// Mark this view as a Bifrost Bridge (Shared Element) for cross-view persistence
186    fn bifrost_bridge(self, id: impl Into<String>) -> ModifiedView<Self, BifrostBridgeModifier> {
187        self.modifier(BifrostBridgeModifier { id: id.into() })
188    }
189
190    /// Add a background color to this view
191    fn background(self, color: [f32; 4]) -> ModifiedView<Self, BackgroundModifier> {
192        self.modifier(BackgroundModifier { color })
193    }
194
195    /// Add padding to this view
196    fn padding(self, amount: f32) -> ModifiedView<Self, PaddingModifier> {
197        self.modifier(PaddingModifier { amount })
198    }
199
200    /// Set the opacity (alpha) of this view in the range [0.0, 1.0].
201    fn opacity(self, opacity: f32) -> ModifiedView<Self, OpacityModifier> {
202        self.modifier(OpacityModifier {
203            opacity: opacity.clamp(0.0, 1.0),
204        })
205    }
206
207    /// Override the foreground (text / icon) color of this view.
208    fn foreground_color(self, color: [f32; 4]) -> ModifiedView<Self, ForegroundColorModifier> {
209        self.modifier(ForegroundColorModifier { color })
210    }
211
212    /// Constrain this view to an explicit width and/or height.
213    fn frame(self, width: Option<f32>, height: Option<f32>) -> ModifiedView<Self, FrameModifier> {
214        self.modifier(FrameModifier { width, height })
215    }
216
217    /// Clip all child drawing to this view's bounds.
218    fn clip_to_bounds(self) -> ModifiedView<Self, ClipModifier> {
219        self.modifier(ClipModifier)
220    }
221
222    /// Draw a colored border around this view.
223    fn border(self, color: [f32; 4], width: f32) -> ModifiedView<Self, BorderModifier> {
224        self.modifier(BorderModifier { color, width })
225    }
226
227    /// Trigger an action when the view appears
228    fn on_appear<F: Fn() + Send + Sync + 'static>(
229        self,
230        action: F,
231    ) -> ModifiedView<Self, LifecycleModifier> {
232        self.modifier(LifecycleModifier {
233            on_appear: Some(Arc::new(action)),
234            on_disappear: None,
235        })
236    }
237
238    /// Trigger an action when the view disappears
239    fn on_disappear<F: Fn() + Send + Sync + 'static>(
240        self,
241        action: F,
242    ) -> ModifiedView<Self, LifecycleModifier> {
243        self.modifier(LifecycleModifier {
244            on_appear: None,
245            on_disappear: Some(Arc::new(action)),
246        })
247    }
248
249    /// Type-erase this view into AnyView
250    fn erase(self) -> AnyView
251    where
252        Self: 'static,
253    {
254        AnyView::new(self)
255    }
256}
257
258/// An object-safe version of the View trait for type erasure.
259pub trait ErasedView: Send {
260    fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect);
261    fn name(&self) -> &'static str;
262}
263
264impl<V: View + 'static> ErasedView for V {
265    fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect) {
266        self.render(renderer, rect);
267    }
268
269    fn name(&self) -> &'static str {
270        std::any::type_name::<V>()
271    }
272}
273
274/// A type-erased View wrapper.
275pub struct AnyView {
276    inner: Box<dyn ErasedView>,
277}
278
279impl AnyView {
280    pub fn new<V: View + 'static>(view: V) -> Self {
281        Self {
282            inner: Box::new(view),
283        }
284    }
285}
286
287impl View for AnyView {
288    type Body = Never;
289    fn body(self) -> Self::Body {
290        unreachable!()
291    }
292
293    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
294        renderer.push_vnode(rect, self.inner.name());
295        self.inner.render_erased(renderer, rect);
296        renderer.pop_vnode();
297    }
298}
299
300/// BifrostBridgeModifier enables shared-element transitions.
301/// When two views share the same Bifrost Bridge ID, the Sleipnir solver will
302/// interpolate their geometry and effects (blur, glow) during the transition.
303#[derive(Debug, Clone, PartialEq)]
304pub struct BifrostBridgeModifier {
305    pub id: String,
306}
307
308impl ViewModifier for BifrostBridgeModifier {
309    fn modify<V: View>(self, content: V) -> impl View {
310        ModifiedView::new(content, self)
311    }
312
313    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
314        // Register this element with the renderer for shared-element transition logic
315        renderer.register_shared_element(&self.id, rect);
316    }
317}
318
319/// MjolnirSliceModifier implements the "Geometric Slice" aesthetic.
320/// It uses a signed distance field (SDF) to clip the view along a sharp angled line.
321#[derive(Debug, Clone, Copy, PartialEq)]
322pub struct MjolnirSliceModifier {
323    pub angle: f32,
324    pub offset: f32,
325}
326
327impl ViewModifier for MjolnirSliceModifier {
328    fn modify<V: View>(self, content: V) -> impl View {
329        ModifiedView::new(content, self)
330    }
331
332    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
333        renderer.push_mjolnir_slice(self.angle, self.offset);
334    }
335
336    fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
337        renderer.pop_mjolnir_slice();
338    }
339}
340
341/// MjolnirShatterModifier implements the "Shattering" effect.
342/// It breaks the view into discrete geometric fragments that can be animated.
343#[derive(Debug, Clone, Copy, PartialEq)]
344pub struct MjolnirShatterModifier {
345    pub pieces: u32,
346    pub force: f32,
347}
348
349impl ViewModifier for MjolnirShatterModifier {
350    fn modify<V: View>(self, content: V) -> impl View {
351        ModifiedView::new(content, self)
352    }
353
354    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
355        // RADIAL SHATTER: Fragment the view into wedges
356        let pieces = self.pieces.max(1);
357        for i in 0..pieces {
358            let progress = i as f32 / pieces as f32;
359            let next_progress = (i + 1) as f32 / pieces as f32;
360
361            let angle_start = progress * 360.0;
362            let angle_end = next_progress * 360.0;
363
364            // Wedge slice: intersection of two half-planes
365            renderer.push_mjolnir_slice(angle_start, 0.0);
366            renderer.push_mjolnir_slice(angle_end + 180.0, 0.0);
367
368            // Apply radial force offset
369            let mid_angle = (angle_start + angle_end) / 2.0;
370            let rad = mid_angle.to_radians();
371            let dx = rad.cos() * self.force;
372            let dy = rad.sin() * self.force;
373
374            let shard_rect = Rect {
375                x: rect.x + dx,
376                y: rect.y + dy,
377                ..rect
378            };
379
380            view.render(renderer, shard_rect);
381
382            renderer.pop_mjolnir_slice();
383            renderer.pop_mjolnir_slice();
384        }
385    }
386}
387
388/// BifrostModifier implements the Cyberpunk "Frosted Glass" aesthetic.
389/// It triggers backdrop blurring and light scattering in the render pipeline.
390#[derive(Debug, Clone, Copy, PartialEq)]
391pub struct BifrostModifier {
392    pub blur: f32,
393    pub saturation: f32,
394    pub opacity: f32,
395}
396
397impl ViewModifier for BifrostModifier {
398    fn modify<V: View>(self, content: V) -> impl View {
399        ModifiedView::new(content, self)
400    }
401
402    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
403        renderer.bifrost(rect, self.blur, self.saturation, self.opacity);
404    }
405}
406
407/// A modifier that adds a background color to a view.
408#[derive(Debug, Clone, Copy, PartialEq)]
409pub struct BackgroundModifier {
410    pub color: [f32; 4],
411}
412
413impl ViewModifier for BackgroundModifier {
414    fn modify<V: View>(self, content: V) -> impl View {
415        ModifiedView::new(content, self)
416    }
417
418    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
419        renderer.fill_rect(rect, self.color);
420    }
421}
422
423/// A modifier that adds padding to a view.
424#[derive(Debug, Clone, Copy, PartialEq)]
425pub struct PaddingModifier {
426    pub amount: f32,
427}
428
429impl ViewModifier for PaddingModifier {
430    fn modify<V: View>(self, content: V) -> impl View {
431        ModifiedView::new(content, self)
432    }
433
434    fn transform_rect(&self, rect: Rect) -> Rect {
435        Rect {
436            x: rect.x + self.amount,
437            y: rect.y + self.amount,
438            width: (rect.width - 2.0 * self.amount).max(0.0),
439            height: (rect.height - 2.0 * self.amount).max(0.0),
440        }
441    }
442}
443
444/// GungnirModifier implements the "Neon Glow" aesthetic.
445/// It uses additive blending and multi-pass blurring to simulate glowing light.
446#[derive(Debug, Clone, PartialEq)]
447pub struct GungnirModifier {
448    pub color: String,
449    pub radius: f32,
450    pub intensity: f32,
451}
452
453impl ViewModifier for GungnirModifier {
454    fn modify<V: View>(self, content: V) -> impl View {
455        ModifiedView::new(content, self)
456    }
457
458    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
459        // Neon Glow using Mode 1 in the Surtr pipeline
460        renderer.stroke_rect(rect, [0.0, 1.0, 1.0, self.intensity], self.radius / 10.0);
461    }
462}
463
464/// GungnirPulseModifier implements a "breathing" neon effect.
465#[derive(Debug, Clone, Copy, PartialEq)]
466pub struct GungnirPulseModifier {
467    pub color: [f32; 4],
468    pub radius: f32,
469    pub speed: f32,
470}
471
472impl ViewModifier for GungnirPulseModifier {
473    fn modify<V: View>(self, content: V) -> impl View {
474        ModifiedView::new(content, self)
475    }
476
477    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
478        let time = std::time::SystemTime::now()
479            .duration_since(std::time::UNIX_EPOCH)
480            .unwrap_or_default()
481            .as_secs_f32();
482
483        // Mode 19: Dashed Border
484        // Mode 20: 9-Slice / Patch Scaling
485        let intensity = (time * self.speed).sin() * 0.5 + 0.5;
486        let mut color = self.color;
487        color[3] *= intensity;
488
489        // Mode 1 neon glow with dynamic intensity
490        renderer.stroke_rect(rect, color, self.radius);
491    }
492}
493
494/// SleipnirModifier handles physics-based animations via the Sleipnir RK4 solver.
495#[derive(Debug, Clone, PartialEq)]
496pub struct SleipnirModifier<T> {
497    pub target: T,
498    pub stiffness: f32,
499    pub damping: f32,
500}
501
502impl<T: Send + Sync + 'static + Clone> ViewModifier for SleipnirModifier<T> {
503    fn modify<V: View>(self, content: V) -> impl View {
504        ModifiedView::new(content, self)
505    }
506}
507
508/// LifecycleModifier handles on_appear and on_disappear hooks.
509#[derive(Clone)]
510pub struct LifecycleModifier {
511    pub on_appear: Option<Arc<dyn Fn() + Send + Sync>>,
512    pub on_disappear: Option<Arc<dyn Fn() + Send + Sync>>,
513}
514
515impl ViewModifier for LifecycleModifier {
516    fn modify<V: View>(self, content: V) -> impl View {
517        ModifiedView::new(content, self)
518    }
519}
520
521/// OpacityModifier fades this view and all its descendants to the given alpha.
522/// The renderer is expected to honour `push_opacity`/`pop_opacity` on the Renderer trait.
523#[derive(Debug, Clone, Copy, PartialEq)]
524pub struct OpacityModifier {
525    pub opacity: f32,
526}
527
528impl ViewModifier for OpacityModifier {
529    fn modify<V: View>(self, content: V) -> impl View {
530        ModifiedView::new(content, self)
531    }
532
533    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
534        renderer.push_opacity(self.opacity);
535    }
536
537    fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
538        renderer.pop_opacity();
539    }
540}
541
542/// ForegroundColorModifier overrides the foreground (text / icon) color inherited
543/// by all descendants until another ForegroundColorModifier is encountered.
544#[derive(Debug, Clone, Copy, PartialEq)]
545pub struct ForegroundColorModifier {
546    pub color: [f32; 4],
547}
548
549impl ViewModifier for ForegroundColorModifier {
550    fn modify<V: View>(self, content: V) -> impl View {
551        ModifiedView::new(content, self)
552    }
553}
554
555/// ClipModifier restricts all child drawing to the view's layout rectangle.
556/// The renderer must support `push_clip_rect`/`pop_clip_rect`.
557#[derive(Debug, Clone, Copy, PartialEq, Eq)]
558pub struct ClipModifier;
559
560impl ViewModifier for ClipModifier {
561    fn modify<V: View>(self, content: V) -> impl View {
562        ModifiedView::new(content, self)
563    }
564
565    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
566        renderer.push_clip_rect(rect);
567    }
568
569    fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
570        renderer.pop_clip_rect();
571    }
572}
573
574/// BorderModifier draws a solid-color border around the view bounds.
575#[derive(Debug, Clone, Copy, PartialEq)]
576pub struct BorderModifier {
577    pub color: [f32; 4],
578    pub width: f32,
579}
580
581impl ViewModifier for BorderModifier {
582    fn modify<V: View>(self, content: V) -> impl View {
583        ModifiedView::new(content, self)
584    }
585
586    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
587        renderer.stroke_rect(rect, self.color, self.width);
588    }
589}
590
591// Primitive (leaf) views implement Never as body
592#[doc(hidden)]
593pub enum Never {}
594
595impl View for Never {
596    type Body = Never;
597    fn body(self) -> Never {
598        unreachable!()
599    }
600}
601
602/// A view that has been transformed by a modifier.
603///
604/// Section 4.3: "Each modifier implements ViewModifier and produces a ModifiedView<Inner, Self>."
605pub struct ModifiedView<V, M> {
606    view: V,
607    modifier: M,
608}
609
610impl<V: View, M: ViewModifier> ModifiedView<V, M> {
611    #[doc(hidden)]
612    pub fn new(view: V, modifier: M) -> Self {
613        Self { view, modifier }
614    }
615}
616
617impl<V: View, M: ViewModifier> View for ModifiedView<V, M> {
618    type Body = ModifiedView<V::Body, M>;
619
620    fn body(self) -> Self::Body {
621        ModifiedView {
622            view: self.view.body(),
623            modifier: self.modifier.clone(),
624        }
625    }
626
627    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
628        self.modifier.render_view(&self.view, renderer, rect);
629    }
630}
631
632pub trait ViewModifier: Send + Clone {
633    fn modify<V: View>(self, content: V) -> impl View;
634
635    /// Core rendering hook called before child views.
636    fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
637
638    /// Cleanup hook called after child views.
639    fn post_render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
640
641    /// Allows a modifier to completely override or wrap the rendering of its child.
642    /// Default implementation performs a standard push -> transform -> render child -> pop sequence.
643    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
644        self.render(renderer, rect);
645        let child_rect = self.transform_rect(rect);
646        view.render(renderer, child_rect);
647        self.post_render(renderer, rect);
648    }
649
650    fn transform_rect(&self, rect: Rect) -> Rect {
651        rect
652    }
653}
654
655/// The Renderer trait defines the atomic drawing operations for all CVKG backends.
656/// This trait is object-safe and used by the View::render system.
657///
658/// # Implementation Requirements
659/// 1. Coordinate system is origin-top-left (0,0) with Y increasing downwards.
660/// 2. Colors are [R, G, B, A] in the [0.0, 1.0] range.
661/// 3. All operations must be batchable by the underlying backend.
662pub trait Renderer: Send {
663    // ── Filled shapes ────────────────────────────────────────────────────
664    fn fill_rect(&mut self, rect: Rect, color: [f32; 4]);
665    fn fill_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4]);
666    /// Fill an ellipse/circle that fits inside `rect`.
667    fn fill_ellipse(&mut self, rect: Rect, color: [f32; 4]);
668
669    // ── Stroked shapes ───────────────────────────────────────────────────
670    fn stroke_rect(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
671    fn stroke_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4], stroke_width: f32);
672    /// Stroke an ellipse/circle that fits inside `rect`.
673    fn stroke_ellipse(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
674    /// Draw a straight line from (x1,y1) to (x2,y2).
675    fn draw_line(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: [f32; 4], stroke_width: f32);
676
677    // ── Text ─────────────────────────────────────────────────────────────
678    fn draw_text(&mut self, text: &str, x: f32, y: f32, size: f32, color: [f32; 4]);
679    /// Measure the width and height of the specified text.
680    fn measure_text(&mut self, text: &str, size: f32) -> (f32, f32);
681
682    // ── Images & textures ────────────────────────────────────────────────
683    /// Draw a texture (GPU-side) at the specified rect.
684    fn draw_texture(&mut self, texture_id: u32, rect: Rect);
685    /// Draw an image asset by name or path.
686    fn draw_image(&mut self, image_name: &str, rect: Rect);
687    /// Load an image asset from memory.
688    fn load_image(&mut self, name: &str, data: &[u8]);
689
690    // ── Data Visualization ───────────────────────────────────────────────
691    /// Upload raw float data as a GPU texture for heatmap rendering.
692    fn upload_data_texture(&mut self, _id: &str, _data: &[f32], _width: u32, _height: u32) {}
693    /// Draw a heatmap using a previously uploaded data texture.
694    fn draw_heatmap(&mut self, _texture_id: &str, _rect: Rect, _palette: &str) {}
695
696    // ── 3D Objects ───────────────────────────────────────────────────────
697    /// Draw a 3D mesh.
698    fn draw_mesh(&mut self, _mesh: &Mesh, _color: [f32; 4], _transform: glam::Mat4) {}
699
700    // ── Advanced Visual Effects ──────────────────────────────────────────
701    /// Draw a linear gradient between two colors at the specified angle.
702    fn draw_linear_gradient(
703        &mut self,
704        _rect: Rect,
705        _start_color: [f32; 4],
706        _end_color: [f32; 4],
707        _angle: f32,
708    ) {
709    }
710    /// Draw a radial gradient between two colors.
711    fn draw_radial_gradient(
712        &mut self,
713        _rect: Rect,
714        _inner_color: [f32; 4],
715        _outer_color: [f32; 4],
716    ) {
717    }
718    /// Draw a high-fidelity drop shadow for a rounded rectangle.
719    fn draw_drop_shadow(
720        &mut self,
721        _rect: Rect,
722        _radius: f32,
723        _color: [f32; 4],
724        _blur: f32,
725        _spread: f32,
726    ) {
727    }
728    /// Draw a dashed border for a rounded rectangle.
729    fn stroke_dashed_rounded_rect(
730        &mut self,
731        _rect: Rect,
732        _radius: f32,
733        _color: [f32; 4],
734        _width: f32,
735        _dash: f32,
736        _gap: f32,
737    ) {
738    }
739    /// Draw a 9-slice / patch scaled image.
740    fn draw_9slice(
741        &mut self,
742        _image_name: &str,
743        _rect: Rect,
744        _left: f32,
745        _top: f32,
746        _right: f32,
747        _bottom: f32,
748    ) {
749    }
750
751    // ── Clipping ─────────────────────────────────────────────────────────
752    /// Push a clip rectangle.  All subsequent drawing is clipped to `rect`.
753    /// Implementations that do not support clipping may ignore this call.
754    fn push_clip_rect(&mut self, rect: Rect);
755    /// Pop the most recently pushed clip rectangle.
756    fn pop_clip_rect(&mut self);
757
758    // ── Global opacity ───────────────────────────────────────────────────
759    /// Set a global opacity multiplier applied to all subsequent draw calls
760    /// until `pop_opacity` is called.  `opacity` is in [0.0, 1.0].
761    fn push_opacity(&mut self, opacity: f32);
762    /// Restore the previous opacity level.
763    fn pop_opacity(&mut self);
764
765    // ── Berserker Pipeline State ─────────────────────────────────────────
766    fn set_theme(&mut self, _theme: ColorTheme) {}
767    fn set_rage(&mut self, _rage: f32) {}
768    fn trigger_shatter_event(&mut self, _origin: [f32; 2], _force: f32) {}
769
770    // ── Cyberpunk Effects ────────────────────────────────────────────────
771    /// Apply a Bifrost (Frosted Glass) effect to the specified rect.
772    fn bifrost(&mut self, rect: Rect, blur: f32, saturation: f32, opacity: f32);
773    /// Push a Mjolnir Slice (geometric clipping).
774    fn push_mjolnir_slice(&mut self, angle: f32, offset: f32);
775    /// Pop the Mjolnir Slice.
776    fn pop_mjolnir_slice(&mut self);
777    /// Apply a Mjolnir Shatter effect (fragmentation) to the specified rect.
778    fn mjolnir_shatter(&mut self, _rect: Rect, _pieces: u32, _force: f32, _color: [f32; 4]) {}
779    fn mjolnir_fluid_shatter(&mut self, _rect: Rect, _pieces: u32, _force: f32, _color: [f32; 4]) {}
780    /// Draw a Mjolnir Bolt (lightning strike) between two points.
781    fn draw_mjolnir_bolt(&mut self, _from: [f32; 2], _to: [f32; 2], _color: [f32; 4]) {}
782
783    // ── Accessibility (ShieldWall) ───────────────────────────────────────
784    fn set_aria_role(&mut self, _role: &str) {}
785    fn set_aria_label(&mut self, _label: &str) {}
786
787    /// Register a shared element for Bifrost Bridge transitions.
788    fn register_shared_element(&mut self, _id: &str, _rect: Rect) {}
789
790    // ── VDOM Hierarchy ───────────────────────────────────────────────────
791    /// Push a Virtual DOM node onto the stack for hierarchy tracking.
792    fn push_vnode(&mut self, _rect: Rect, _name: &'static str) {}
793    /// Pop the current Virtual DOM node from the stack.
794    fn pop_vnode(&mut self) {}
795    /// Register an event handler for the current VDOM node.
796    fn register_handler(
797        &mut self,
798        _event_type: &str,
799        _handler: std::sync::Arc<dyn Fn(Event) + Send + Sync>,
800    ) {
801    }
802}
803
804// =============================================================================
805// BERSERKER UNIFORMS
806// =============================================================================
807
808use bytemuck::{Pod, Zeroable};
809
810/// Fully themeable color palette for the Berserker pipeline.
811#[repr(C)]
812#[derive(Copy, Clone, Debug, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
813pub struct ColorTheme {
814    pub primary_neon: [f32; 4], // (R, G, B, intensity)
815    pub shatter_neon: [f32; 4],
816    pub glass_base: [f32; 4],
817    pub glass_edge: [f32; 4],
818    pub rune_glow: [f32; 4],
819    pub ember_core: [f32; 4],
820    pub background_deep: [f32; 4],
821    pub glass_blur_strength: f32,
822    pub shatter_edge_width: f32,
823    pub neon_bloom_radius: f32,
824    pub rune_opacity: f32, // 0.0–1.0, default 0.55
825    // Padding to ensure 16-byte alignment for GPU uniforms
826    pub _pad: [f32; 3], // align to 16 bytes
827    pub _pad2: f32,
828}
829
830impl ColorTheme {
831    pub fn cyberpunk_viking() -> Self {
832        Self {
833            primary_neon: [0.0, 1.0, 0.95, 1.2],
834            shatter_neon: [1.0, 0.0, 0.75, 1.5],
835            glass_base: [0.04, 0.04, 0.06, 0.82],
836            glass_edge: [0.0, 0.45, 0.55, 0.6],
837            rune_glow: [0.75, 0.98, 1.0, 0.9],
838            ember_core: [0.95, 0.12, 0.12, 1.0],
839            background_deep: [0.01, 0.01, 0.03, 1.0],
840            glass_blur_strength: 0.6,
841            shatter_edge_width: 1.8,
842            neon_bloom_radius: 0.022,
843            rune_opacity: 0.55,
844            _pad: [0.0; 3],
845            _pad2: 0.0,
846        }
847    }
848
849    pub fn vibrant_glass() -> Self {
850        Self {
851            primary_neon: [0.0, 1.0, 0.95, 1.2],
852            shatter_neon: [1.0, 0.0, 0.75, 1.5],
853            glass_base: [0.55, 0.6, 0.7, 0.08], // Luminous cool tint
854            glass_edge: [0.7, 0.85, 1.0, 0.45], // Subtle blue-white rim
855            rune_glow: [0.75, 0.98, 1.0, 0.9],
856            ember_core: [1.0, 0.4, 0.1, 1.0],
857            background_deep: [0.05, 0.05, 0.1, 1.0],
858            glass_blur_strength: 0.9,
859            shatter_edge_width: 1.8,
860            neon_bloom_radius: 0.022,
861            rune_opacity: 0.55,
862            _pad: [0.0; 3],
863            _pad2: 0.0,
864        }
865    }
866}
867
868impl Default for ColorTheme {
869    fn default() -> Self {
870        Self::vibrant_glass()
871    }
872}
873
874/// Per-frame scene state for the Berserker pipeline.
875#[repr(C)]
876#[derive(Copy, Clone, Debug, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
877pub struct SceneUniforms {
878    pub view: glam::Mat4,
879    pub proj: glam::Mat4,
880    pub time: f32,
881    pub delta_time: f32,
882    pub resolution: [f32; 2],
883    pub mouse: [f32; 2],
884    pub mouse_velocity: [f32; 2],
885    pub shatter_origin: [f32; 2],
886    pub shatter_time: f32,
887    pub shatter_force: f32,
888    pub berzerker_rage: f32,
889    pub scroll_offset: f32,
890    // Padding to ensure 16-byte alignment for GPU uniforms
891    pub _pad: [f32; 2],
892}
893
894impl SceneUniforms {
895    pub fn new(width: f32, height: f32) -> Self {
896        Self {
897            view: glam::Mat4::IDENTITY,
898            proj: glam::Mat4::orthographic_lh(0.0, width, height, 0.0, -100.0, 100.0),
899            time: 0.0,
900            delta_time: 0.016,
901            resolution: [width, height],
902            mouse: [0.5, 0.5],
903            mouse_velocity: [0.0, 0.0],
904            shatter_origin: [0.5, 0.5],
905            shatter_time: -100.0,
906            shatter_force: 0.0,
907            berzerker_rage: 0.0,
908            scroll_offset: 0.0,
909            _pad: [0.0; 2],
910        }
911    }
912}
913
914/// A 3D mesh containing vertex and index data.
915#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
916pub struct Mesh {
917    pub vertices: Vec<[f32; 3]>,
918    pub normals: Vec<[f32; 3]>,
919    pub indices: Vec<u32>,
920}
921
922impl Mesh {
923    pub fn from_obj(data: &[u8]) -> anyhow::Result<Vec<Self>> {
924        let mut cursor = std::io::Cursor::new(data);
925        let (models, _) = tobj::load_obj_buf(&mut cursor, &tobj::LoadOptions::default(), |_| {
926            Ok((Vec::new(), Default::default()))
927        })?;
928
929        let mut meshes = Vec::new();
930        for m in models {
931            let mesh = m.mesh;
932            let vertices: Vec<[f32; 3]> = mesh
933                .positions
934                .chunks(3)
935                .map(|c| [c[0], c[1], c[2]])
936                .collect();
937            let normals = if mesh.normals.is_empty() {
938                vec![[0.0, 0.0, 1.0]; vertices.len()]
939            } else {
940                mesh.normals.chunks(3).map(|c| [c[0], c[1], c[2]]).collect()
941            };
942            meshes.push(Mesh {
943                vertices,
944                normals,
945                indices: mesh.indices,
946            });
947        }
948        Ok(meshes)
949    }
950
951    pub fn from_stl(data: &[u8]) -> anyhow::Result<Self> {
952        let mut cursor = std::io::Cursor::new(data);
953        let stl = stl_io::read_stl(&mut cursor)?;
954
955        let vertices: Vec<[f32; 3]> = stl.vertices.iter().map(|v| [v[0], v[1], v[2]]).collect();
956        let mut indices = Vec::new();
957        for face in stl.faces {
958            indices.push(face.vertices[0] as u32);
959            indices.push(face.vertices[1] as u32);
960            indices.push(face.vertices[2] as u32);
961        }
962
963        let normals = vec![[0.0, 0.0, 1.0]; vertices.len()];
964
965        Ok(Mesh {
966            vertices,
967            normals,
968            indices,
969        })
970    }
971}
972
973/// FrameRenderer extends Renderer with frame lifecycle management.
974/// It is typically implemented by the host windowing/rendering environment.
975pub trait FrameRenderer<E = ()>: Renderer {
976    fn begin_frame(&mut self) -> E;
977    fn end_frame(&mut self, encoder: E);
978}
979
980use std::sync::Arc;
981
982/// State wrapper that owns a value and notifies subscribers when changed
983#[derive(Clone)]
984pub struct State<T: Clone + Send + Sync + 'static> {
985    value: Arc<std::sync::RwLock<T>>,
986    subscribers: Arc<std::sync::RwLock<Vec<Box<dyn FnMut(&T) + Send + Sync>>>>,
987}
988
989impl<T: Clone + Send + Sync + 'static> State<T> {
990    /// Create a new State with initial value
991    pub fn new(value: T) -> Self {
992        Self {
993            value: Arc::new(std::sync::RwLock::new(value)),
994            subscribers: Arc::new(std::sync::RwLock::new(Vec::new())),
995        }
996    }
997
998    /// Get the current value
999    pub fn get(&self) -> T {
1000        self.value.read().unwrap().clone()
1001    }
1002
1003    /// Set a new value, notifying all subscribers
1004    pub fn set(&self, value: T) {
1005        *self.value.write().unwrap() = value;
1006        // Notify subscribers
1007        let mut subscribers = self.subscribers.write().unwrap();
1008        for subscriber in subscribers.iter_mut() {
1009            subscriber(&self.get());
1010        }
1011    }
1012
1013    /// Subscribe to state changes
1014    pub fn subscribe<F: FnMut(&T) + Send + Sync + 'static>(&self, callback: F) {
1015        self.subscribers.write().unwrap().push(Box::new(callback));
1016    }
1017}
1018
1019/// Error state for fault isolation at the component level.
1020///
1021/// Section 1.1: "Components must self-handle errors... isolating failures."
1022#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
1023pub struct ComponentErrorState {
1024    pub has_error: bool,
1025    pub error_message: Option<String>,
1026    pub error_location: Option<String>,
1027}
1028
1029impl ComponentErrorState {
1030    /// Create a new clear error state.
1031    pub fn clear() -> Self {
1032        Self::default()
1033    }
1034
1035    /// Create an error state with a message and location.
1036    pub fn error(message: impl Into<String>, location: impl Into<String>) -> Self {
1037        Self {
1038            has_error: true,
1039            error_message: Some(message.into()),
1040            error_location: Some(location.into()),
1041        }
1042    }
1043}
1044
1045/// A discrete fragment of knowledge stored in the agent's memory.
1046#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
1047pub struct KnowledgeFragment {
1048    /// Unique identifier for this fragment
1049    pub id: String,
1050    /// Short summary for prompt injection and quick search
1051    pub summary: String,
1052    /// Reference source (e.g. filename, URL, or conversation ID)
1053    pub source: String,
1054    /// Frame number or timestamp of creation
1055    pub created_at: u64,
1056    /// Number of times this fragment has been retrieved
1057    pub accessed_count: u32,
1058    /// Full content (optional, can be loaded on-demand)
1059    pub content: Option<String>,
1060}
1061
1062/// The KnowledgeState registry is the central repository for all agent-observable application data.
1063/// It stores both component-level states and high-level agentic memory fragments.
1064#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
1065pub struct KnowledgeState {
1066    /// Component states indexed by NodeId. Skipped in serialization as it contains opaque types.
1067    #[serde(skip)]
1068    pub component_states: std::collections::HashMap<u64, Arc<dyn std::any::Any + Send + Sync>>,
1069
1070    /// Map of IDs to knowledge fragments (Agentic Memory)
1071    pub fragments: HashMap<String, KnowledgeFragment>,
1072
1073    /// IDs of fragments returned by the last search query
1074    pub last_query_results: Vec<String>,
1075}
1076
1077use crate::runtime::NodeStateSnapshot;
1078use std::sync::OnceLock;
1079
1080/// Global application state registry.
1081pub static SYSTEM_STATE: OnceLock<Arc<std::sync::RwLock<KnowledgeState>>> = OnceLock::new();
1082
1083/// Get a reference to the global system state.
1084pub fn get_system_state() -> Arc<std::sync::RwLock<KnowledgeState>> {
1085    SYSTEM_STATE
1086        .get_or_init(|| Arc::new(std::sync::RwLock::new(KnowledgeState::default())))
1087        .clone()
1088}
1089
1090impl KnowledgeState {
1091    /// Create a new empty KnowledgeState.
1092    pub fn new() -> Self {
1093        Self::default()
1094    }
1095
1096    /// Set a component's internal state.
1097    pub fn set_component_state<T: 'static + Send + Sync>(&mut self, id: u64, state: T) {
1098        self.component_states
1099            .insert(id, Arc::new(std::sync::RwLock::new(state)));
1100    }
1101
1102    /// Get a reference to a component's internal state.
1103    pub fn get_component_state<T: 'static + Send + Sync>(
1104        &self,
1105        id: u64,
1106    ) -> Option<Arc<std::sync::RwLock<T>>> {
1107        let lock = self.component_states.get(&id)?;
1108        lock.clone().downcast::<std::sync::RwLock<T>>().ok()
1109    }
1110
1111    /// Add a new fragment to memory.
1112    pub fn remember(&mut self, fragment: KnowledgeFragment) {
1113        self.fragments.insert(fragment.id.clone(), fragment);
1114    }
1115
1116    /// Process a search query against the local knowledge base.
1117    pub fn process_query(&mut self, query: &str) {
1118        let query_lower = query.to_lowercase();
1119        let mut results: Vec<(f32, String)> = self
1120            .fragments
1121            .iter()
1122            .map(|(id, frag)| {
1123                let mut score = 0.0;
1124                if frag.summary.to_lowercase().contains(&query_lower) {
1125                    score += 1.0;
1126                }
1127                if frag.source.to_lowercase().contains(&query_lower) {
1128                    score += 0.5;
1129                }
1130                (score, id.clone())
1131            })
1132            .filter(|(score, _)| *score > 0.0)
1133            .collect();
1134
1135        // Sort by relevance score
1136        results.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
1137
1138        self.last_query_results = results.into_iter().map(|(_, id)| id).take(5).collect();
1139    }
1140
1141    /// Captures a snapshot of the current state for debugging and hot-reloading.
1142    pub fn snapshot(&self) -> Vec<NodeStateSnapshot> {
1143        let mut snapshots = Vec::new();
1144
1145        // Snapshots of agentic fragments
1146        for (_id, frag) in &self.fragments {
1147            if let Ok(val) = serde_json::to_value(frag) {
1148                snapshots.push(NodeStateSnapshot { id: 0, state: val });
1149            }
1150        }
1151
1152        snapshots
1153    }
1154}
1155
1156/// Read/write reference to state owned by another view
1157#[derive(Clone)]
1158pub struct Binding<T: Clone + Send + Sync + 'static> {
1159    state: Arc<std::sync::RwLock<T>>,
1160}
1161
1162impl<T: Clone + Send + Sync + 'static> Binding<T> {
1163    /// Create a binding from a State
1164    pub fn from_state(state: &State<T>) -> Self {
1165        Self {
1166            state: state.value.clone(),
1167        }
1168    }
1169
1170    /// Get the current value
1171    pub fn get(&self) -> T {
1172        self.state.read().unwrap().clone()
1173    }
1174
1175    /// Set a new value
1176    pub fn set(&self, value: T) {
1177        *self.state.write().unwrap() = value;
1178    }
1179}
1180
1181use std::any::TypeId;
1182use std::sync::Mutex;
1183
1184/// Global environment storage using TypeId as keys.
1185pub(crate) static ENVIRONMENT: OnceLock<
1186    Mutex<HashMap<TypeId, Box<dyn std::any::Any + Send + Sync>>>,
1187> = OnceLock::new();
1188
1189/// Environment key type for accessing ambient values
1190///
1191/// Implement this trait to define a new environment key.
1192pub trait EnvKey: 'static + Send + Sync {
1193    /// The type of value stored in the environment
1194    type Value: Clone + Send + Sync + 'static;
1195
1196    /// Get a default value for this key
1197    fn default_value() -> Self::Value;
1198}
1199
1200/// Key for accessing the Yggdrasil design token tree
1201pub struct YggdrasilKey;
1202
1203impl EnvKey for YggdrasilKey {
1204    type Value = YggdrasilTokens;
1205    fn default_value() -> Self::Value {
1206        default_tokens()
1207    }
1208}
1209
1210/// Key for accessing the AssetManager
1211pub struct AssetKey;
1212
1213impl EnvKey for AssetKey {
1214    type Value = Arc<dyn AssetManager>;
1215    fn default_value() -> Self::Value {
1216        Arc::new(DefaultAssetManager::new())
1217    }
1218}
1219
1220/// System appearance (Light/Dark mode)
1221#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1222pub enum Appearance {
1223    Light,
1224    Dark,
1225}
1226
1227/// Orientation for layouts
1228#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1229pub enum Orientation {
1230    Horizontal,
1231    Vertical,
1232}
1233
1234/// A color represented by RGBA components in the [0.0, 1.0] range.
1235#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1236pub struct Color {
1237    pub r: f32,
1238    pub g: f32,
1239    pub b: f32,
1240    pub a: f32,
1241}
1242
1243impl Color {
1244    pub const BLACK: Color = Color {
1245        r: 0.0,
1246        g: 0.0,
1247        b: 0.0,
1248        a: 1.0,
1249    };
1250    pub const WHITE: Color = Color {
1251        r: 1.0,
1252        g: 1.0,
1253        b: 1.0,
1254        a: 1.0,
1255    };
1256    pub const TRANSPARENT: Color = Color {
1257        r: 0.0,
1258        g: 0.0,
1259        b: 0.0,
1260        a: 0.0,
1261    };
1262    pub const RED: Color = Color {
1263        r: 1.0,
1264        g: 0.0,
1265        b: 0.0,
1266        a: 1.0,
1267    };
1268    pub const GREEN: Color = Color {
1269        r: 0.0,
1270        g: 1.0,
1271        b: 0.0,
1272        a: 1.0,
1273    };
1274    pub const BLUE: Color = Color {
1275        r: 0.0,
1276        g: 0.0,
1277        b: 1.0,
1278        a: 1.0,
1279    };
1280    pub const CYAN: Color = Color {
1281        r: 0.0,
1282        g: 1.0,
1283        b: 1.0,
1284        a: 1.0,
1285    };
1286    pub const YELLOW: Color = Color {
1287        r: 1.0,
1288        g: 1.0,
1289        b: 0.0,
1290        a: 1.0,
1291    };
1292    pub const MAGENTA: Color = Color {
1293        r: 1.0,
1294        g: 0.0,
1295        b: 1.0,
1296        a: 1.0,
1297    };
1298    pub const GRAY: Color = Color {
1299        r: 0.5,
1300        g: 0.5,
1301        b: 0.5,
1302        a: 1.0,
1303    };
1304
1305    /// Create a new color from RGBA components.
1306    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
1307        Self { r, g, b, a }
1308    }
1309
1310    /// Convert the color to a [r, g, b, a] array.
1311    pub fn as_array(&self) -> [f32; 4] {
1312        [self.r, self.g, self.b, self.a]
1313    }
1314}
1315
1316impl View for Color {
1317    type Body = Never;
1318    fn body(self) -> Self::Body {
1319        unreachable!()
1320    }
1321
1322    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1323        renderer.fill_rect(rect, self.as_array());
1324    }
1325}
1326
1327/// Key for accessing the current system appearance
1328pub struct AppearanceKey;
1329
1330impl EnvKey for AppearanceKey {
1331    type Value = Appearance;
1332    fn default_value() -> Self::Value {
1333        Appearance::Dark // Default to Dark (Ginnungagap) for Berserker aesthetic
1334    }
1335}
1336
1337/// StyleResolver provides high-level access to themed values from the environment.
1338pub struct StyleResolver;
1339
1340impl StyleResolver {
1341    /// Resolve a color from the current environment
1342    pub fn color(key: &str) -> String {
1343        let tokens = Environment::<YggdrasilKey>::new().get();
1344        let appearance = Environment::<AppearanceKey>::new().get();
1345        let is_dark = appearance == Appearance::Dark;
1346
1347        tokens
1348            .get_color(key, is_dark)
1349            .unwrap_or_else(|| "#FF00FF".to_string()) // Default to MuspelMagenta on failure
1350    }
1351
1352    /// Resolve a generic token value
1353    pub fn get<T: FromStr>(category: &str, key: &str) -> Option<T> {
1354        let tokens = Environment::<YggdrasilKey>::new().get();
1355        let appearance = Environment::<AppearanceKey>::new().get();
1356        let is_dark = appearance == Appearance::Dark;
1357
1358        tokens.get(category, key, is_dark)
1359    }
1360}
1361
1362/// The authoritative Cyberpunk Viking default tokens
1363pub fn default_tokens() -> YggdrasilTokens {
1364    let mut tokens = YggdrasilTokens::new();
1365
1366    // Core Norse Colorways
1367    tokens.color.insert(
1368        "background".to_string(),
1369        TokenValue::Single {
1370            value: "#000000".to_string(), // Ginnungagap (The Void)
1371        },
1372    );
1373
1374    tokens.color.insert(
1375        "primary".to_string(),
1376        TokenValue::Single {
1377            value: "#00FFFF".to_string(), // NiflCyan (Aesir Primary)
1378        },
1379    );
1380
1381    tokens.color.insert(
1382        "secondary".to_string(),
1383        TokenValue::Single {
1384            value: "#FF00FF".to_string(), // MuspelMagenta (Berserker Secondary)
1385        },
1386    );
1387
1388    tokens.color.insert(
1389        "surface".to_string(),
1390        TokenValue::Adaptive {
1391            light: "#FFFFFF".to_string(),
1392            dark: "#121212".to_string(),
1393        },
1394    );
1395
1396    tokens.color.insert(
1397        "text".to_string(),
1398        TokenValue::Adaptive {
1399            light: "#000000".to_string(),
1400            dark: "#FFFFFF".to_string(),
1401        },
1402    );
1403
1404    // Bifrost (Glassmorphism) - Frosted Style
1405    tokens.bifrost.insert(
1406        "blur".to_string(),
1407        TokenValue::Single {
1408            value: "25.0".to_string(),
1409        },
1410    );
1411    tokens.bifrost.insert(
1412        "saturation".to_string(),
1413        TokenValue::Single {
1414            value: "1.2".to_string(),
1415        },
1416    );
1417    tokens.bifrost.insert(
1418        "opacity".to_string(),
1419        TokenValue::Single {
1420            value: "0.65".to_string(),
1421        },
1422    );
1423
1424    // Gungnir (Neon Glow)
1425    tokens.gungnir.insert(
1426        "intensity".to_string(),
1427        TokenValue::Single {
1428            value: "1.0".to_string(),
1429        },
1430    );
1431    tokens.gungnir.insert(
1432        "radius".to_string(),
1433        TokenValue::Single {
1434            value: "15.0".to_string(),
1435        },
1436    );
1437
1438    // Mjolnir (Sharp Geometry)
1439    tokens.mjolnir.insert(
1440        "clip_angle".to_string(),
1441        TokenValue::Single {
1442            value: "12.0".to_string(),
1443        },
1444    );
1445    tokens.mjolnir.insert(
1446        "border_width".to_string(),
1447        TokenValue::Single {
1448            value: "2.0".to_string(),
1449        },
1450    );
1451
1452    // Sleipnir (Spring Animation)
1453    tokens.anim.insert(
1454        "stiffness".to_string(),
1455        TokenValue::Single {
1456            value: "170.0".to_string(),
1457        },
1458    );
1459    tokens.anim.insert(
1460        "damping".to_string(),
1461        TokenValue::Single {
1462            value: "26.0".to_string(),
1463        },
1464    );
1465    tokens.anim.insert(
1466        "mass".to_string(),
1467        TokenValue::Single {
1468            value: "1.0".to_string(),
1469        },
1470    );
1471
1472    // Accessibility
1473    tokens.accessibility.insert(
1474        "reduce_motion".to_string(),
1475        TokenValue::Single {
1476            value: "false".to_string(),
1477        },
1478    );
1479
1480    tokens
1481}
1482
1483/// Environment wrapper for accessing ambient values
1484pub struct Environment<K: EnvKey> {
1485    _marker: std::marker::PhantomData<K>,
1486}
1487
1488impl<K: EnvKey> Environment<K> {
1489    /// Create a new Environment
1490    pub fn new() -> Self {
1491        Self {
1492            _marker: std::marker::PhantomData,
1493        }
1494    }
1495
1496    /// Get the current value from the environment
1497    pub fn get(&self) -> K::Value {
1498        if let Some(env_store) = ENVIRONMENT.get() {
1499            let env_lock = env_store.lock().unwrap();
1500            if let Some(val) = env_lock.get(&std::any::TypeId::of::<K>()) {
1501                if let Some(typed_val) = val.downcast_ref::<K::Value>() {
1502                    return typed_val.clone();
1503                }
1504            }
1505        }
1506        K::default_value()
1507    }
1508}
1509
1510/// Ambient environment management
1511pub mod env {
1512
1513    /// Insert a value into the environment
1514    pub fn insert<K: super::EnvKey>(value: K::Value) {
1515        if let Some(store) = super::ENVIRONMENT.get() {
1516            let mut env_map = store.lock().unwrap();
1517            env_map.insert(std::any::TypeId::of::<K>(), Box::new(value));
1518        }
1519    }
1520
1521    /// Remove a value from the environment.
1522    pub fn remove<K: super::EnvKey>() {
1523        if let Some(store) = super::ENVIRONMENT.get() {
1524            let mut env_map = store.lock().unwrap();
1525            env_map.remove(&std::any::TypeId::of::<K>());
1526        }
1527    }
1528}
1529
1530/// Geometry modifiers
1531
1532/// Size of the view in logical pixels
1533#[derive(Debug, Clone, Copy, PartialEq)]
1534pub struct Size {
1535    pub width: f32,
1536    pub height: f32,
1537}
1538
1539/// Insets for padding
1540#[derive(Debug, Clone, Copy, PartialEq)]
1541pub struct EdgeInsets {
1542    pub top: f32,
1543    pub leading: f32,
1544    pub bottom: f32,
1545    pub trailing: f32,
1546}
1547
1548impl EdgeInsets {
1549    /// Equal insets on all edges
1550    pub fn all(value: f32) -> Self {
1551        Self {
1552            top: value,
1553            leading: value,
1554            bottom: value,
1555            trailing: value,
1556        }
1557    }
1558
1559    /// Vertical insets (top and bottom)
1560    pub fn vertical(value: f32) -> Self {
1561        Self {
1562            top: value,
1563            leading: 0.0,
1564            bottom: value,
1565            trailing: 0.0,
1566        }
1567    }
1568
1569    /// Horizontal insets (leading and trailing)
1570    pub fn horizontal(value: f32) -> Self {
1571        Self {
1572            top: 0.0,
1573            leading: value,
1574            bottom: 0.0,
1575            trailing: value,
1576        }
1577    }
1578}
1579
1580/// Modifier to set the size of a view
1581#[derive(Debug, Clone, Copy, PartialEq)]
1582pub struct FrameModifier {
1583    pub width: Option<f32>,
1584    pub height: Option<f32>,
1585}
1586
1587impl FrameModifier {
1588    pub fn new() -> Self {
1589        Self {
1590            width: None,
1591            height: None,
1592        }
1593    }
1594
1595    pub fn width(mut self, width: f32) -> Self {
1596        self.width = Some(width);
1597        self
1598    }
1599
1600    pub fn height(mut self, height: f32) -> Self {
1601        self.height = Some(height);
1602        self
1603    }
1604
1605    pub fn size(mut self, width: f32, height: f32) -> Self {
1606        self.width = Some(width);
1607        self.height = Some(height);
1608        self
1609    }
1610}
1611
1612impl ViewModifier for FrameModifier {
1613    fn modify<V: View>(self, content: V) -> impl View {
1614        ModifiedView::new(content, self)
1615    }
1616}
1617
1618/// Modifier to offset a view
1619#[derive(Debug, Clone, Copy, PartialEq)]
1620pub struct OffsetModifier {
1621    pub x: f32,
1622    pub y: f32,
1623}
1624
1625impl OffsetModifier {
1626    pub fn new(x: f32, y: f32) -> Self {
1627        Self { x, y }
1628    }
1629}
1630
1631impl ViewModifier for OffsetModifier {
1632    fn modify<V: View>(self, content: V) -> impl View {
1633        ModifiedView::new(content, self)
1634    }
1635}
1636
1637/// Modifier to set the z-index of a view
1638#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1639pub struct ZIndexModifier {
1640    pub z_index: i32,
1641}
1642
1643impl ZIndexModifier {
1644    pub fn new(z_index: i32) -> Self {
1645        Self { z_index }
1646    }
1647}
1648
1649impl ViewModifier for ZIndexModifier {
1650    fn modify<V: View>(self, content: V) -> impl View {
1651        ModifiedView::new(content, self)
1652    }
1653}
1654
1655/// Layout constraints for views
1656#[derive(Debug, Clone, Copy, PartialEq)]
1657pub struct LayoutConstraints {
1658    pub min_width: Option<f32>,
1659    pub max_width: Option<f32>,
1660    pub min_height: Option<f32>,
1661    pub max_height: Option<f32>,
1662}
1663
1664impl Default for LayoutConstraints {
1665    fn default() -> Self {
1666        Self {
1667            min_width: None,
1668            max_width: None,
1669            min_height: None,
1670            max_height: None,
1671        }
1672    }
1673}
1674
1675/// Modifier to set layout constraints
1676#[derive(Debug, Clone, Copy, PartialEq)]
1677pub struct LayoutModifier {
1678    pub constraints: LayoutConstraints,
1679}
1680
1681impl LayoutModifier {
1682    pub fn new(constraints: LayoutConstraints) -> Self {
1683        Self { constraints }
1684    }
1685}
1686
1687impl ViewModifier for LayoutModifier {
1688    fn modify<V: View>(self, content: V) -> impl View {
1689        ModifiedView::new(content, self)
1690    }
1691}
1692
1693/// Modifier to make a view flexible in layout
1694#[derive(Debug, Clone, Copy, PartialEq)]
1695pub struct FlexModifier {
1696    pub flex: f32,
1697}
1698
1699impl FlexModifier {
1700    pub fn new(flex: f32) -> Self {
1701        Self { flex }
1702    }
1703}
1704
1705impl ViewModifier for FlexModifier {
1706    fn modify<V: View>(self, content: V) -> impl View {
1707        ModifiedView::new(content, self)
1708    }
1709}
1710
1711// Layout subsystem
1712pub mod layout {
1713    use super::*;
1714
1715    // Layout pass scratch space
1716    pub struct LayoutCache;
1717
1718    impl LayoutCache {
1719        pub fn new() -> Self {
1720            Self
1721        }
1722
1723        pub fn clear(&mut self) {
1724            // In a real implementation, this would clear cached layout data
1725        }
1726    }
1727
1728    /// Proposed size from parent view
1729    #[derive(Debug, Clone, Copy, PartialEq)]
1730    pub struct SizeProposal {
1731        pub width: Option<f32>,
1732        pub height: Option<f32>,
1733    }
1734
1735    impl SizeProposal {
1736        pub fn unspecified() -> Self {
1737            Self {
1738                width: None,
1739                height: None,
1740            }
1741        }
1742
1743        pub fn width(width: f32) -> Self {
1744            Self {
1745                width: Some(width),
1746                height: None,
1747            }
1748        }
1749
1750        pub fn height(height: f32) -> Self {
1751            Self {
1752                width: None,
1753                height: Some(height),
1754            }
1755        }
1756
1757        pub fn tight(width: f32, height: f32) -> Self {
1758            Self {
1759                width: Some(width),
1760                height: Some(height),
1761            }
1762        }
1763    }
1764
1765    /// A view that can participate in layout
1766    pub trait LayoutView: Send {
1767        /// Propose a size for this view given the available space
1768        fn size_that_fits(
1769            &self,
1770            proposal: SizeProposal,
1771            subviews: &[&dyn LayoutView],
1772            cache: &mut LayoutCache,
1773        ) -> Size;
1774
1775        /// Place subviews within the given bounds
1776        fn place_subviews(
1777            &self,
1778            bounds: Rect,
1779            subviews: &mut [&mut dyn LayoutView],
1780            cache: &mut LayoutCache,
1781        );
1782    }
1783
1784    /// Rectangle in logical pixels
1785    #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1786    pub struct Rect {
1787        pub x: f32,
1788        pub y: f32,
1789        pub width: f32,
1790        pub height: f32,
1791    }
1792
1793    impl Rect {
1794        pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
1795            Self {
1796                x,
1797                y,
1798                width,
1799                height,
1800            }
1801        }
1802
1803        pub fn zero() -> Self {
1804            Self {
1805                x: 0.0,
1806                y: 0.0,
1807                width: 0.0,
1808                height: 0.0,
1809            }
1810        }
1811
1812        pub fn size(&self) -> Size {
1813            Size {
1814                width: self.width,
1815                height: self.height,
1816            }
1817        }
1818
1819        /// Split the rect horizontally into N equal pieces
1820        pub fn split_horizontal(&self, n: usize) -> Vec<Rect> {
1821            if n == 0 {
1822                return vec![];
1823            }
1824            let item_width = self.width / n as f32;
1825            (0..n)
1826                .map(|i| Rect {
1827                    x: self.x + i as f32 * item_width,
1828                    y: self.y,
1829                    width: item_width,
1830                    height: self.height,
1831                })
1832                .collect()
1833        }
1834
1835        /// Split the rect vertically into N equal pieces
1836        pub fn split_vertical(&self, n: usize) -> Vec<Rect> {
1837            if n == 0 {
1838                return vec![];
1839            }
1840            let item_height = self.height / n as f32;
1841            (0..n)
1842                .map(|i| Rect {
1843                    x: self.x,
1844                    y: self.y + i as f32 * item_height,
1845                    width: self.width,
1846                    height: item_height,
1847                })
1848                .collect()
1849        }
1850    }
1851}
1852
1853// Re-export layout items for convenience
1854pub use layout::{LayoutCache, LayoutView, Rect, SizeProposal};
1855// Size and FrameRenderer are pub items in this module; no re-export alias needed.
1856
1857pub mod runtime;
1858pub mod scene_graph;
1859
1860pub use scene_graph::{NodeId, bifrost_registry};
1861
1862/// State of an asset being loaded
1863#[derive(Debug, Clone, PartialEq)]
1864pub enum AssetState<T> {
1865    Loading,
1866    Ready(T),
1867    Error(String),
1868}
1869
1870/// AssetManager defines the interface for loading and caching external resources.
1871pub trait AssetManager: Send + Sync {
1872    /// Request an image asset. Returns the current state (Loading, Ready, or Error).
1873    fn load_image(&self, url: &str) -> AssetState<Arc<Vec<u8>>>;
1874
1875    /// Pre-load an image into the cache.
1876    fn preload_image(&self, url: &str);
1877}
1878
1879/// User input event types
1880#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
1881pub enum Event {
1882    PointerDown { x: f32, y: f32 },
1883    PointerUp { x: f32, y: f32 },
1884    PointerMove { x: f32, y: f32 },
1885    KeyDown { key: String },
1886    KeyUp { key: String },
1887}
1888
1889/// Response from an event handler
1890#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1891pub enum EventResponse {
1892    Handled,
1893    Ignored,
1894}
1895
1896/// A basic implementation of AssetManager that can be overridden by platform backends.
1897pub struct DefaultAssetManager {
1898    cache: Arc<std::sync::RwLock<HashMap<String, AssetState<Arc<Vec<u8>>>>>>,
1899}
1900
1901impl DefaultAssetManager {
1902    pub fn new() -> Self {
1903        Self {
1904            cache: Arc::new(std::sync::RwLock::new(HashMap::new())),
1905        }
1906    }
1907}
1908
1909impl AssetManager for DefaultAssetManager {
1910    fn load_image(&self, url: &str) -> AssetState<Arc<Vec<u8>>> {
1911        let mut cache = self.cache.write().unwrap();
1912        if let Some(state) = cache.get(url) {
1913            return state.clone();
1914        }
1915
1916        // In the default manager, we just mark it as Loading and spawn a placeholder
1917        // (Real backends will override this with actual I/O)
1918        cache.insert(url.to_string(), AssetState::Loading);
1919        AssetState::Loading
1920    }
1921
1922    fn preload_image(&self, _url: &str) {
1923        // No-op for default manager
1924    }
1925}
1926
1927#[cfg(test)]
1928mod phase1_test;