Skip to main content

cvkg_core/
lib.rs

1//! # CVKG Agentic Development Guidelines (v1.2)
2//!
3//! All AI agents contributing to this crate MUST follow ALL seven rules:
4//!
5//! ── Karpathy Guidelines (1–4) ────────────────────────────────────────────
6//! 1. THINK FIRST     — State assumptions. Surface ambiguity. Push back on complexity.
7//! 2. STAY SIMPLE     — Minimum code. No speculative features. No unasked-for abstractions.
8//! 3. BE SURGICAL     — Touch only what's required. Own your orphans. Don't improve neighbors.
9//! 4. VERIFY GOALS    — Turn tasks into checkable criteria. Loop until they pass. Never commit broken.
10//!
11//! ── CVKG Extended Protocols (5–7) ────────────────────────────────────────
12//! 5. TRIPLE-PASS     — Read the target, its surrounding context, and its full call graph
13//                      at least THREE TIMES before making any edit or revision.
14//! 6. COMMENT ALL     — Every major pub fn, unsafe block, and non-trivial algorithm in
15//                      every .rs/.ts/.h/.wgsl file MUST have a descriptive doc comment.
16//                      Comments describe WHY and WHAT CONTRACT, not HOW mechanically.
17//! 7. MONITOR LOOPS   — Check every tool call / command for progress every 30 seconds.
18//                      After 3 consecutive identical failures, stop, write BLOCKED.md,
19//                      and move to unblocked work. Never silently accept a broken state.
20//!
21//! Sources:
22//   Karpathy: https://github.com/multica-ai/andrej-karpathy-skills
23//   CVKG Extended: Section 2 of the CVKG Design Specification
24
25//! The View trait is the fundamental building block of CVKG. Every UI element — from a plain text label
26//! to a complex navigation controller — is a View. The trait is intentionally minimal; complexity emerges
27//! through modifier composition.
28//!
29//! # Conformance rules:
30//! 1. `body()` must be pure and side-effect free
31//! 2. Primitive views use `Never` as `Body` and register a `PaintCommand` directly with the scene graph
32//! 3. `View` types must implement `Send` but not necessarily `Sync`, enabling safe multi-threaded layout passes
33
34use serde::{Deserialize, Serialize};
35use std::collections::HashMap;
36use std::str::FromStr;
37
38pub mod error_types;
39
40pub mod security;
41
42/// Error state for fault isolation at the component level.
43#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
44pub struct ComponentErrorState {
45    pub has_error: bool,
46    pub error_message: Option<String>,
47    pub error_location: Option<String>,
48}
49impl ComponentErrorState {
50    pub fn clear() -> Self {
51        Self::default()
52    }
53
54    pub fn error(message: impl Into<String>, location: impl Into<String>) -> Self {
55        Self {
56            has_error: true,
57            error_message: Some(message.into()),
58            error_location: Some(location.into()),
59        }
60    }
61}
62
63/// Knowledge state for the agentic memory system.
64#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
65pub struct KnowledgeState {
66    pub thoughts: Vec<String>,
67    pub actions: Vec<String>,
68    pub context: HashMap<String, String>,
69    pub last_query_results: Vec<KnowledgeId>,
70    #[serde(alias = "items")]
71    pub fragments: std::collections::HashMap<KnowledgeId, KnowledgeFragment>,
72    /// The Temporal Graph nodes
73    pub nodes: Vec<TemporalNode>,
74    /// The Temporal Graph edges
75    pub edges: Vec<TemporalEdge>,
76    /// The current operational Realm (Midgard/Asgard)
77    pub realm: Realm,
78    /// Last known pointer position (X, Y)
79    pub last_pointer_pos: [f32; 2],
80    /// Resolved pointer velocity (pixels per frame)
81    pub pointer_velocity: [f32; 2],
82    /// The current 'Focus' node ID (Odin's Eye focus)
83    pub odin_focus: Option<String>,
84    /// Agent attention heatmap (node_id -> intensity)
85    pub agent_attention: HashMap<String, f32>,
86    // Component state storage for dynamic state
87    #[serde(skip)]
88    pub component_states: HashMap<u64, Arc<std::sync::RwLock<dyn std::any::Any + Send + Sync>>>,
89    /// Global undo/redo manager tracking document and input states.
90    #[serde(skip)]
91    pub undo_manager: UndoManager,
92    /// Active notification list.
93    #[serde(default)]
94    pub notifications: Vec<Notification>,
95    /// Flag indicating whether the notification center panel is visible.
96    #[serde(default)]
97    pub notification_center_visible: bool,
98    /// Modifier key state: shift key pressed.
99    #[serde(default)]
100    pub modifiers_shift: bool,
101    /// Modifier key state: control key pressed.
102    #[serde(default)]
103    pub modifiers_ctrl: bool,
104    /// Modifier key state: alt/option key pressed.
105    #[serde(default)]
106    pub modifiers_alt: bool,
107    /// Modifier key state: logo/command/windows key pressed.
108    #[serde(default)]
109    pub modifiers_logo: bool,
110    /// Whether the performance profiling overlay (Cmd+Shift+P) is currently visible.
111    #[serde(default)]
112    pub performance_overlay_visible: bool,
113}
114
115impl KnowledgeState {
116    /// Apply activation decay to all temporal nodes and evolving components.
117    /// Nodes with weight below a threshold drift out of the primary context.
118    /// Components lose vitality (Fafnir's Decay) if not actively 'fed'.
119    pub fn apply_decay(&mut self, decay_factor: f32) {
120        for node in &mut self.nodes {
121            node.weight *= decay_factor;
122        }
123
124        // Fafnir's Decay: Components naturally revert to base state over time
125        for state in self.component_states.values() {
126            if let Ok(mut lock) = state.write()
127                && let Some(v) = lock.downcast_mut::<f32>()
128            {
129                *v = (*v * decay_factor).max(1.0);
130            }
131        }
132    }
133
134    /// Increase the importance weight of nodes associated with a successful task.
135    pub fn reinforce(&mut self, node_ids: &[String], boost: f32) {
136        for node in &mut self.nodes {
137            if node_ids.contains(&node.id) {
138                node.weight += boost;
139            }
140        }
141    }
142
143    /// Update pointer kinematics based on a new position.
144    pub fn update_pointer(&mut self, new_pos: [f32; 2]) {
145        self.pointer_velocity = [
146            new_pos[0] - self.last_pointer_pos[0],
147            new_pos[1] - self.last_pointer_pos[1],
148        ];
149        self.last_pointer_pos = new_pos;
150    }
151}
152// Knowledge System Types
153/// Unique identifier for knowledge fragments
154pub type KnowledgeId = String;
155
156/// A knowledge fragment stored in the memory system
157#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct KnowledgeFragment {
159    /// Unique identifier for this fragment
160    pub id: String,
161    /// Short summary for prompt injection and quick search
162    pub summary: String,
163    /// Reference source (e.g. filename, URL, or conversation ID)
164    pub source: String,
165    /// Frame number or timestamp of creation
166    pub created_at: u64,
167    /// Number of times this fragment has been retrieved
168    pub accessed_count: u32,
169    /// Full content (optional, can be loaded on-demand)
170    pub content: Option<String>,
171}
172
173impl KnowledgeFragment {
174    pub fn new(id: String, summary: String, source: String) -> Self {
175        Self {
176            id,
177            summary,
178            source,
179            created_at: 0,
180            accessed_count: 0,
181            content: None,
182        }
183    }
184}
185
186/// Memory layers for the layered cognitive engine
187#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
188pub enum MemoryLayer {
189    /// Raw mission events (short-term)
190    Episodic,
191    /// Extracted facts and tactical intelligence (long-term)
192    Semantic,
193    /// Successful command sequences and tool chains
194    Procedural,
195}
196
197/// The operational Realm of the UI.
198/// Midgard: Classic, functional, 2D tactical UI for mortals.
199/// Asgard: High-fidelity, cognitive, shader-heavy UI for the Singularity.
200#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
201pub enum Realm {
202    Midgard,
203    #[default]
204    Asgard,
205}
206
207/// A node in the Temporal Graph representing a cognitive anchor
208#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct TemporalNode {
210    /// Unique identifier for this node
211    pub id: String,
212    /// ID of the underlying knowledge fragment
213    pub fragment_id: KnowledgeId,
214    /// Timestamp of the event
215    pub timestamp: u64,
216    /// The memory layer this node belongs to
217    pub layer: MemoryLayer,
218    /// Importance weight for activation decay and retrieval
219    pub weight: f32,
220}
221
222/// An edge in the Temporal Graph representing a relationship between nodes
223#[derive(Debug, Clone, Serialize, Deserialize)]
224pub struct TemporalEdge {
225    /// Source node ID
226    pub source: String,
227    /// Target node ID
228    pub target: String,
229    /// Type of relationship (e.g. "causal", "semantic", "temporal")
230    pub relation: String,
231    /// Weight/strength of the connection
232    pub weight: f32,
233}
234
235/// A single action group representing an undo/redo step.
236pub struct UndoGroup {
237    /// Descriptive label of the action (e.g. "Type", "Delete").
238    pub label: String,
239    /// Time when the action was recorded, in seconds.
240    pub timestamp: f32,
241    /// Closure to revert the action.
242    pub undo: Arc<dyn Fn() + Send + Sync>,
243    /// Closure to re-apply the action.
244    pub redo: Arc<dyn Fn() + Send + Sync>,
245}
246
247impl Clone for UndoGroup {
248    /// Clone the undo/redo group. The closures are shared via Arc.
249    fn clone(&self) -> Self {
250        Self {
251            label: self.label.clone(),
252            timestamp: self.timestamp,
253            undo: Arc::clone(&self.undo),
254            redo: Arc::clone(&self.redo),
255        }
256    }
257}
258
259impl std::fmt::Debug for UndoGroup {
260    /// Debug format helper to avoid printing closures.
261    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262        f.debug_struct("UndoGroup")
263            .field("label", &self.label)
264            .field("timestamp", &self.timestamp)
265            .finish()
266    }
267}
268
269/// Unified manager for undo and redo stacks.
270/// Supports grouping of actions, max undo depth clamping, and coalescing.
271pub struct UndoManager {
272    /// History stack of undo/redo groups.
273    stack: Vec<UndoGroup>,
274    /// Current position/index in the stack.
275    position: usize,
276    /// Maximum allowed undo steps before discarding oldest.
277    max_depth: usize,
278    /// Time window in seconds to coalesce consecutive actions of the same type.
279    coalesce_window: f32,
280}
281
282impl Default for UndoManager {
283    /// Create a default UndoManager with a depth of 100 and a 0.5s coalesce window.
284    fn default() -> Self {
285        Self {
286            stack: Vec::new(),
287            position: 0,
288            max_depth: 100,
289            coalesce_window: 0.5,
290        }
291    }
292}
293
294impl Clone for UndoManager {
295    /// Clone the undo manager, preserving stacks and position.
296    fn clone(&self) -> Self {
297        Self {
298            stack: self.stack.clone(),
299            position: self.position,
300            max_depth: self.max_depth,
301            coalesce_window: self.coalesce_window,
302        }
303    }
304}
305
306impl std::fmt::Debug for UndoManager {
307    /// Debug format helper for UndoManager.
308    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
309        f.debug_struct("UndoManager")
310            .field("stack_len", &self.stack.len())
311            .field("position", &self.position)
312            .field("max_depth", &self.max_depth)
313            .field("coalesce_window", &self.coalesce_window)
314            .finish()
315    }
316}
317
318impl UndoManager {
319    /// Create a new UndoManager with custom settings.
320    pub fn new(max_depth: usize, coalesce_window: f32) -> Self {
321        Self {
322            stack: Vec::new(),
323            position: 0,
324            max_depth,
325            coalesce_window,
326        }
327    }
328
329    /// Push a new undo/redo group to the stack, clearing any forward redo history.
330    pub fn push(
331        &mut self,
332        label: &str,
333        undo: impl Fn() + Send + Sync + 'static,
334        redo: impl Fn() + Send + Sync + 'static,
335    ) {
336        if self.position < self.stack.len() {
337            self.stack.truncate(self.position);
338        }
339
340        let timestamp = std::time::SystemTime::now()
341            .duration_since(std::time::UNIX_EPOCH)
342            .unwrap_or_default()
343            .as_secs_f32();
344
345        self.stack.push(UndoGroup {
346            label: label.to_string(),
347            timestamp,
348            undo: Arc::new(undo),
349            redo: Arc::new(redo),
350        });
351
352        if self.stack.len() > self.max_depth {
353            self.stack.remove(0);
354        }
355        self.position = self.stack.len();
356    }
357
358    /// Perform the undo action if possible, moving the position back.
359    /// Returns the undo closure to be executed outside of any state lock.
360    pub fn undo(&mut self) -> Option<Arc<dyn Fn() + Send + Sync>> {
361        if self.can_undo() {
362            self.position -= 1;
363            Some(Arc::clone(&self.stack[self.position].undo))
364        } else {
365            None
366        }
367    }
368
369    /// Perform the redo action if possible, moving the position forward.
370    /// Returns the redo closure to be executed outside of any state lock.
371    pub fn redo(&mut self) -> Option<Arc<dyn Fn() + Send + Sync>> {
372        if self.can_redo() {
373            let group = &self.stack[self.position];
374            self.position += 1;
375            Some(Arc::clone(&group.redo))
376        } else {
377            None
378        }
379    }
380
381    /// Returns true if there is an action that can be undone.
382    pub fn can_undo(&self) -> bool {
383        self.position > 0
384    }
385
386    /// Returns true if there is an action that can be redone.
387    pub fn can_redo(&self) -> bool {
388        self.position < self.stack.len()
389    }
390
391    /// Clear all undo/redo history.
392    pub fn clear(&mut self) {
393        self.stack.clear();
394        self.position = 0;
395    }
396
397    /// Push a new coalesceable action. If the last action in the stack matches the label,
398    /// is within the coalesce window, and the position is at the end of the stack, their undo/redo
399    /// functions will be combined instead of creating a new group.
400    pub fn push_coalesceable(
401        &mut self,
402        label: &str,
403        undo: impl Fn() + Send + Sync + 'static,
404        redo: impl Fn() + Send + Sync + 'static,
405    ) {
406        let now = std::time::SystemTime::now()
407            .duration_since(std::time::UNIX_EPOCH)
408            .unwrap_or_default()
409            .as_secs_f32();
410
411        if self.position == self.stack.len() && !self.stack.is_empty() {
412            let last_idx = self.stack.len() - 1;
413            let last = &self.stack[last_idx];
414            if last.label == label && (now - last.timestamp).abs() <= self.coalesce_window {
415                let old_undo = Arc::clone(&last.undo);
416                let old_redo = Arc::clone(&last.redo);
417                let new_undo = Arc::new(undo);
418                let new_redo = Arc::new(redo);
419
420                self.stack[last_idx].undo = Arc::new(move || {
421                    new_undo();
422                    old_undo();
423                });
424                self.stack[last_idx].redo = Arc::new(move || {
425                    old_redo();
426                    new_redo();
427                });
428                self.stack[last_idx].timestamp = now;
429                return;
430            }
431        }
432
433        self.push(label, undo, redo);
434    }
435}
436
437/// Unique identifier for a window instance.
438#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
439pub struct WindowId(pub u64);
440
441/// Specifies the layering behavior of the window relative to other windows.
442#[derive(
443    Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Default,
444)]
445pub enum WindowLevel {
446    /// Standard window.
447    #[default]
448    Normal,
449    /// Window stays above all standard windows.
450    AlwaysOnTop,
451    /// Menu or pop-up level window.
452    PopUpMenu,
453}
454
455/// Configuration settings for creating a new window.
456#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
457pub struct WindowConfig {
458    /// The window title bar text.
459    pub title: String,
460    /// Default width and height of the window.
461    pub size: (f32, f32),
462    /// Minimum allowed dimensions.
463    pub min_size: Option<(f32, f32)>,
464    /// Maximum allowed dimensions.
465    pub max_size: Option<(f32, f32)>,
466    /// Whether the window can be resized by the user.
467    pub resizable: bool,
468    /// Whether the window background is transparent.
469    pub transparent: bool,
470    /// Whether the window title bar and border decorations are drawn.
471    pub decorations: bool,
472    /// The window level layer.
473    pub level: WindowLevel,
474}
475
476impl Default for WindowConfig {
477    /// Create a standard default window configuration.
478    fn default() -> Self {
479        Self {
480            title: "CVKG Window".to_string(),
481            size: (800.0, 600.0),
482            min_size: None,
483            max_size: None,
484            resizable: true,
485            transparent: false,
486            decorations: true,
487            level: WindowLevel::Normal,
488        }
489    }
490}
491
492/// Abstract trait representing a platform-native window.
493/// Implementations delegate calls back to the platform renderers and events.
494pub trait Window: Send + Sync {
495    /// Request closing of the window.
496    fn close(&self);
497    /// Change the title bar text of the window.
498    fn set_title(&self, title: &str);
499    /// Update the window's physical dimensions.
500    fn set_size(&self, width: f32, height: f32);
501    /// Check if the window currently has keyboard focus.
502    fn is_key(&self) -> bool;
503    /// Check if this is the primary main application window.
504    fn is_main(&self) -> bool;
505    /// Check if the window is currently visible/mapped.
506    fn is_visible(&self) -> bool;
507    /// Hide or show the window.
508    fn set_visible(&self, visible: bool);
509    /// Bring the window to the front and focus it.
510    fn bring_to_front(&self);
511}
512
513/// A handle to a native window that can be used by application code.
514#[derive(Clone)]
515pub struct WindowHandle {
516    /// The unique identifier of this window.
517    pub id: WindowId,
518    /// Reference to the underlying platform window.
519    pub inner: Arc<dyn Window>,
520}
521
522impl std::fmt::Debug for WindowHandle {
523    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
524        f.debug_struct("WindowHandle")
525            .field("id", &self.id)
526            .finish()
527    }
528}
529
530impl WindowHandle {
531    /// Create a new WindowHandle.
532    pub fn new(id: WindowId, inner: Arc<dyn Window>) -> Self {
533        Self { id, inner }
534    }
535    /// Request the window to close.
536    pub fn close(self) {
537        self.inner.close();
538    }
539    /// Set the title text of the window.
540    pub fn set_title(&self, title: &str) {
541        self.inner.set_title(title);
542    }
543    /// Resize the window.
544    pub fn set_size(&self, width: f32, height: f32) {
545        self.inner.set_size(width, height);
546    }
547    /// Returns true if this window has key focus.
548    pub fn is_key(&self) -> bool {
549        self.inner.is_key()
550    }
551    /// Returns true if this is the main application window.
552    pub fn is_main(&self) -> bool {
553        self.inner.is_main()
554    }
555    /// Returns true if the window is visible.
556    pub fn is_visible(&self) -> bool {
557        self.inner.is_visible()
558    }
559    /// Set visibility of the window.
560    pub fn set_visible(&self, visible: bool) {
561        self.inner.set_visible(visible);
562    }
563    /// Bring this window to the foreground.
564    pub fn bring_to_front(&self) {
565        self.inner.bring_to_front();
566    }
567}
568
569/// Action to take when a window close request event is received.
570#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
571pub enum WindowCloseAction {
572    /// Close the window immediately.
573    Allow,
574    /// Request confirmation from the user (e.g. show dialog).
575    Confirm,
576    /// Ignore the close request.
577    Deny,
578}
579
580#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
581pub struct AssetKey(pub String);
582
583impl EnvKey for AssetKey {
584    type Value = Arc<dyn AssetManager>;
585    fn default_value() -> Self::Value {
586        Arc::new(DefaultAssetManager::new())
587    }
588}
589
590/// Asset state for async resource loading.
591#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
592pub enum AssetState<T> {
593    Loading,
594    Ready(T),
595    Error(String),
596}
597
598/// Design token value that can adapt to light/dark mode
599#[derive(Debug, Clone, Serialize, Deserialize)]
600#[serde(untagged)]
601pub enum TokenValue {
602    /// Single value (same for light and dark)
603    Single { value: String },
604    /// Different values for light and dark mode
605    Adaptive { light: String, dark: String },
606}
607
608/// YggdrasilTokens is the authoritative container for all design tokens in the CVKG ecosystem.
609#[derive(Debug, Clone, Serialize, Deserialize)]
610pub struct YggdrasilTokens {
611    pub color: HashMap<String, TokenValue>,
612    pub font: HashMap<String, TokenValue>,
613    pub spacing: HashMap<String, TokenValue>,
614    pub radius: HashMap<String, TokenValue>,
615    pub shadow: HashMap<String, TokenValue>,
616    pub border: HashMap<String, TokenValue>,
617    pub anim: HashMap<String, TokenValue>,
618    pub bifrost: HashMap<String, TokenValue>,
619    pub gungnir: HashMap<String, TokenValue>,
620    pub mjolnir: HashMap<String, TokenValue>,
621    pub accessibility: HashMap<String, TokenValue>,
622}
623
624impl Default for YggdrasilTokens {
625    fn default() -> Self {
626        Self::new()
627    }
628}
629
630impl YggdrasilTokens {
631    pub fn new() -> Self {
632        Self {
633            color: HashMap::new(),
634            font: HashMap::new(),
635            spacing: HashMap::new(),
636            radius: HashMap::new(),
637            shadow: HashMap::new(),
638            border: HashMap::new(),
639            anim: HashMap::new(),
640            bifrost: HashMap::new(),
641            gungnir: HashMap::new(),
642            mjolnir: HashMap::new(),
643            accessibility: HashMap::new(),
644        }
645    }
646
647    /// Get a color token value for the current mode
648    pub fn get_color(&self, key: &str, is_dark: bool) -> Option<String> {
649        self.color.get(key).map(|token| match token {
650            TokenValue::Single { value } => value.clone(),
651            TokenValue::Adaptive { light, dark } => {
652                if is_dark {
653                    dark.clone()
654                } else {
655                    light.clone()
656                }
657            }
658        })
659    }
660
661    /// Get a token value of any type and parse it into the target type
662    pub fn get<T: FromStr>(&self, category: &str, key: &str, is_dark: bool) -> Option<T> {
663        let map = match category {
664            "color" => &self.color,
665            "font" => &self.font,
666            "spacing" => &self.spacing,
667            "radius" => &self.radius,
668            "shadow" => &self.shadow,
669            "border" => &self.border,
670            "anim" => &self.anim,
671            "bifrost" => &self.bifrost,
672            "gungnir" => &self.gungnir,
673            "mjolnir" => &self.mjolnir,
674            "accessibility" => &self.accessibility,
675            _ => return None,
676        };
677
678        map.get(key).and_then(|token| match token {
679            TokenValue::Single { value } => value.parse().ok(),
680            TokenValue::Adaptive { light, dark } => {
681                let value = if is_dark { dark } else { light };
682                value.parse().ok()
683            }
684        })
685    }
686}
687
688pub trait View: Sized + Send {
689    /// The concrete type produced after applying modifiers.
690    /// For primitive views this is Self.
691    type Body: View;
692
693    fn body(self) -> Self::Body;
694
695    /// Render this view into the provided renderer at the specified bounds.
696    /// Primitive views override this to perform drawing operations.
697    fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
698
699    /// Calculate the natural (intrinsic) size of this view given proposed constraints.
700    /// This allows views like Buttons or Labels to inform the layout engine of their needs.
701    fn intrinsic_size(&self, _renderer: &mut dyn Renderer, _proposal: SizeProposal) -> Size {
702        Size::ZERO
703    }
704
705    /// Optionally provide a layout implementation for this view.
706    fn layout(&self) -> Option<&dyn layout::LayoutView> {
707        None
708    }
709
710    /// Returns the flex weight of this view for proportional distribution in stacks.
711    fn flex_weight(&self) -> f32 {
712        0.0
713    }
714
715    /// Returns the grid placement configuration for this view if it is laid out in a Grid.
716    fn get_grid_placement(&self) -> Option<GridPlacement> {
717        None
718    }
719
720    /// Provided modifier entry point
721    fn modifier<M: ViewModifier>(self, m: M) -> ModifiedView<Self, M> {
722        ModifiedView::new(self, m)
723    }
724
725    /// Apply a Bifrost (Frosted Glass) effect to the view
726    fn bifrost(
727        self,
728        blur: f32,
729        saturation: f32,
730        opacity: f32,
731    ) -> ModifiedView<Self, BifrostModifier> {
732        self.modifier(BifrostModifier {
733            blur,
734            saturation,
735            opacity,
736        })
737    }
738
739    /// Apply a Gungnir (Neon Glow) effect to the view
740    fn gungnir(
741        self,
742        color: impl Into<String>,
743        radius: f32,
744        intensity: f32,
745    ) -> ModifiedView<Self, GungnirModifier> {
746        self.modifier(GungnirModifier {
747            color: color.into(),
748            radius,
749            intensity,
750        })
751    }
752
753    /// Apply a Mjolnir Slice (Geometric cut) to the view
754    fn mjolnir_slice(self, angle: f32, offset: f32) -> ModifiedView<Self, MjolnirSliceModifier> {
755        self.modifier(MjolnirSliceModifier { angle, offset })
756    }
757
758    /// Apply a Mjolnir Shatter (Fragmented transition) to the view
759    fn mjolnir_shatter(
760        self,
761        pieces: u32,
762        force: f32,
763    ) -> ModifiedView<Self, MjolnirShatterModifier> {
764        self.modifier(MjolnirShatterModifier { pieces, force })
765    }
766
767    /// Mark this view as a Bifrost Bridge (Shared Element) for cross-view persistence
768    fn bifrost_bridge(self, id: impl Into<String>) -> ModifiedView<Self, BifrostBridgeModifier> {
769        self.modifier(BifrostBridgeModifier { id: id.into() })
770    }
771
772    /// Add a background color to this view
773    fn background(self, color: [f32; 4]) -> ModifiedView<Self, BackgroundModifier> {
774        self.modifier(BackgroundModifier { color })
775    }
776
777    /// Add padding to this view
778    fn padding(self, amount: f32) -> ModifiedView<Self, PaddingModifier> {
779        self.modifier(PaddingModifier { amount })
780    }
781
782    /// Set the opacity (alpha) of this view in the range [0.0, 1.0].
783    fn opacity(self, opacity: f32) -> ModifiedView<Self, OpacityModifier> {
784        self.modifier(OpacityModifier {
785            opacity: opacity.clamp(0.0, 1.0),
786        })
787    }
788
789    /// Override the foreground (text / icon) color of this view.
790    fn foreground_color(self, color: [f32; 4]) -> ModifiedView<Self, ForegroundColorModifier> {
791        self.modifier(ForegroundColorModifier { color })
792    }
793
794    /// Constrain this view to an explicit width and/or height.
795    /// Constrains the size of this view using fixed width/height values.
796    fn frame(self, width: Option<f32>, height: Option<f32>) -> ModifiedView<Self, FrameModifier> {
797        self.modifier(FrameModifier {
798            width,
799            height,
800            min_width: None,
801            max_width: None,
802            min_height: None,
803            max_height: None,
804            alignment: Alignment::Center,
805        })
806    }
807
808    /// Give this view a flex weight for proportional space distribution in stacks.
809    fn flex(self, weight: f32) -> ModifiedView<Self, FlexModifier> {
810        self.modifier(FlexModifier { weight })
811    }
812
813    /// Specify the grid placement configuration (column, row, column_span, row_span) for this view.
814    fn grid_placement(self, placement: GridPlacement) -> ModifiedView<Self, GridPlacementModifier> {
815        self.modifier(GridPlacementModifier { placement })
816    }
817
818    /// Overlay a view on top of this view, aligned and offset relative to it.
819    fn overlay<O: View + Clone + 'static>(
820        self,
821        overlay: O,
822        alignment: Alignment,
823        offset: [f32; 2],
824        on_dismiss: Option<Arc<dyn Fn() + Send + Sync>>,
825    ) -> ModifiedView<Self, OverlayModifier> {
826        self.modifier(OverlayModifier {
827            overlay: overlay.erase(),
828            alignment,
829            offset,
830            on_dismiss,
831        })
832    }
833
834    /// Automatically add padding to avoid overlapping with platform safe areas (notches, bars).
835    fn safe_area_padding(self) -> ModifiedView<Self, SafeAreaModifier> {
836        self.modifier(SafeAreaModifier { ignores: false })
837    }
838
839    /// Explicitly ignore platform safe areas and draw into the margins.
840    fn ignores_safe_area(self) -> ModifiedView<Self, SafeAreaModifier> {
841        self.modifier(SafeAreaModifier { ignores: true })
842    }
843
844    /// Clip all child drawing to this view's bounds.
845    fn clip_to_bounds(self) -> ModifiedView<Self, ClipModifier> {
846        self.modifier(ClipModifier)
847    }
848
849    /// Draw a colored border around this view.
850    fn border(self, color: [f32; 4], width: f32) -> ModifiedView<Self, BorderModifier> {
851        self.modifier(BorderModifier { color, width })
852    }
853
854    /// Add elevation (shadow) to the view. Level determines the shadow depth.
855    fn elevation(self, level: f32) -> ModifiedView<Self, ElevationModifier> {
856        self.modifier(ElevationModifier { level })
857    }
858
859    /// Add a magnetic effect that pulls the view towards the cursor.
860    fn magnetic(self, radius: f32, intensity: f32) -> ModifiedView<Self, MagneticModifier> {
861        self.modifier(MagneticModifier { radius, intensity })
862    }
863
864    /// Add a ManiGlow (Lunar Illuminator) effect that glows near the cursor.
865    fn mani_glow(self, color: [f32; 4], radius: f32) -> ModifiedView<Self, ManiGlowModifier> {
866        self.modifier(ManiGlowModifier { color, radius })
867    }
868
869    /// Theme this view based on a specific memory layer.
870    fn memory_layer(self, layer: MemoryLayer) -> ModifiedView<Self, BifrostLayerModifier> {
871        self.modifier(BifrostLayerModifier { layer })
872    }
873
874    /// Enable Fafnir's Evolution: The component grows and glows as it is used.
875    fn fafnir_evolve(self, id: u64) -> ModifiedView<Self, FafnirModifier> {
876        self.modifier(FafnirModifier { id })
877    }
878
879    /// Enable Mimir's Intent: The component anticipates user interaction via pointer kinematics.
880    fn mimir_intent(self) -> ModifiedView<Self, MimirIntentModifier> {
881        self.modifier(MimirIntentModifier)
882    }
883
884    /// Enable Kvasir's Vibes: Subconscious telemetry representing cognitive complexity.
885    fn kvasir_vibes(self, complexity: f32) -> ModifiedView<Self, KvasirVibeModifier> {
886        self.modifier(KvasirVibeModifier { complexity })
887    }
888
889    /// Bestow Odin's Eye: Global omniscient observability layer.
890    fn odins_eye(self) -> ModifiedView<Self, OdinsEyeModifier> {
891        self.modifier(OdinsEyeModifier)
892    }
893
894    /// Trigger an action when the view appears
895    fn on_appear<F: Fn() + Send + Sync + 'static>(
896        self,
897        action: F,
898    ) -> ModifiedView<Self, LifecycleModifier> {
899        self.modifier(LifecycleModifier {
900            on_appear: Some(Arc::new(action)),
901            on_disappear: None,
902        })
903    }
904
905    /// Trigger an action when the view disappears
906    fn on_disappear<F: Fn() + Send + Sync + 'static>(
907        self,
908        action: F,
909    ) -> ModifiedView<Self, LifecycleModifier> {
910        self.modifier(LifecycleModifier {
911            on_appear: None,
912            on_disappear: Some(Arc::new(action)),
913        })
914    }
915
916    /// Trigger an action when the view is clicked
917    fn on_click<F: Fn() + Send + Sync + 'static>(
918        self,
919        action: F,
920    ) -> ModifiedView<Self, OnClickModifier> {
921        self.modifier(OnClickModifier {
922            action: Arc::new(action),
923        })
924    }
925
926    /// Trigger an action when the pointer enters the view bounds
927    fn on_pointer_enter<F: Fn() + Send + Sync + 'static>(
928        self,
929        action: F,
930    ) -> ModifiedView<Self, OnPointerEnterModifier> {
931        self.modifier(OnPointerEnterModifier {
932            action: Arc::new(action),
933        })
934    }
935
936    /// Trigger an action when the pointer leaves the view bounds
937    fn on_pointer_leave<F: Fn() + Send + Sync + 'static>(
938        self,
939        action: F,
940    ) -> ModifiedView<Self, OnPointerLeaveModifier> {
941        self.modifier(OnPointerLeaveModifier {
942            action: Arc::new(action),
943        })
944    }
945
946    /// Trigger an action when the pointer moves inside the view bounds
947    fn on_pointer_move<F: Fn(f32, f32) + Send + Sync + 'static>(
948        self,
949        action: F,
950    ) -> ModifiedView<Self, OnPointerMoveModifier> {
951        self.modifier(OnPointerMoveModifier {
952            action: Arc::new(action),
953        })
954    }
955
956    /// Trigger an action when the pointer is pressed down
957    fn on_pointer_down<F: Fn() + Send + Sync + 'static>(
958        self,
959        action: F,
960    ) -> ModifiedView<Self, OnPointerDownModifier> {
961        self.modifier(OnPointerDownModifier {
962            action: Arc::new(action),
963        })
964    }
965
966    /// Trigger an action when the pointer is released
967    fn on_pointer_up<F: Fn() + Send + Sync + 'static>(
968        self,
969        action: F,
970    ) -> ModifiedView<Self, OnPointerUpModifier> {
971        self.modifier(OnPointerUpModifier {
972            action: Arc::new(action),
973        })
974    }
975
976    /// Type-erase this view into AnyView
977    fn erase(self) -> AnyView
978    where
979        Self: Clone + 'static,
980    {
981        AnyView::new(self)
982    }
983}
984
985/// An object-safe version of the View trait for type erasure.
986pub trait ErasedView: Send {
987    fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect);
988    fn name(&self) -> &'static str;
989    fn flex_weight_erased(&self) -> f32;
990    fn layout_erased(&self) -> Option<&dyn layout::LayoutView>;
991    fn grid_placement_erased(&self) -> Option<GridPlacement>;
992    fn clone_box(&self) -> Box<dyn ErasedView>;
993}
994
995impl<V: View + Clone + 'static> ErasedView for V {
996    fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect) {
997        self.render(renderer, rect);
998    }
999
1000    fn name(&self) -> &'static str {
1001        std::any::type_name::<V>()
1002    }
1003
1004    fn flex_weight_erased(&self) -> f32 {
1005        self.flex_weight()
1006    }
1007
1008    fn layout_erased(&self) -> Option<&dyn layout::LayoutView> {
1009        self.layout()
1010    }
1011
1012    fn grid_placement_erased(&self) -> Option<GridPlacement> {
1013        self.get_grid_placement()
1014    }
1015
1016    fn clone_box(&self) -> Box<dyn ErasedView> {
1017        Box::new(self.clone())
1018    }
1019}
1020
1021/// A view that memoizes its rendering based on a stable ID and data hash.
1022/// The renderer can use this to skip re-rendering the sub-tree if the data hasn't changed.
1023pub struct MemoView<V, F> {
1024    id: u64,
1025    data_hash: u64,
1026    builder: F,
1027    _v: std::marker::PhantomData<V>,
1028}
1029
1030impl<V: View, F: Fn() -> V + Send + Sync> MemoView<V, F> {
1031    /// Create a new MemoView with a stable ID and a data hash.
1032    pub fn new(id: u64, data_hash: u64, builder: F) -> Self {
1033        Self {
1034            id,
1035            data_hash,
1036            builder,
1037            _v: std::marker::PhantomData,
1038        }
1039    }
1040}
1041
1042impl<V: View + 'static, F: Fn() -> V + Send + Sync + 'static> View for MemoView<V, F> {
1043    type Body = Never;
1044    fn body(self) -> Self::Body {
1045        unreachable!("MemoView does not have a body")
1046    }
1047
1048    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1049        renderer.memoize(self.id, self.data_hash, &|r| {
1050            let view = (self.builder)();
1051            view.render(r, rect);
1052        });
1053    }
1054}
1055
1056/// A type-erased View wrapper.
1057pub struct AnyView {
1058    inner: Box<dyn ErasedView>,
1059}
1060
1061impl Clone for AnyView {
1062    fn clone(&self) -> Self {
1063        Self {
1064            inner: self.inner.clone_box(),
1065        }
1066    }
1067}
1068
1069impl AnyView {
1070    pub fn new<V: View + Clone + 'static>(view: V) -> Self {
1071        Self {
1072            inner: Box::new(view),
1073        }
1074    }
1075}
1076
1077impl View for AnyView {
1078    type Body = Never;
1079    fn body(self) -> Self::Body {
1080        unreachable!()
1081    }
1082
1083    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1084        renderer.push_vnode(rect, self.inner.name());
1085        self.inner.render_erased(renderer, rect);
1086        renderer.pop_vnode();
1087    }
1088
1089    fn flex_weight(&self) -> f32 {
1090        self.inner.flex_weight_erased()
1091    }
1092
1093    fn layout(&self) -> Option<&dyn layout::LayoutView> {
1094        self.inner.layout_erased()
1095    }
1096
1097    fn get_grid_placement(&self) -> Option<GridPlacement> {
1098        self.inner.grid_placement_erased()
1099    }
1100}
1101
1102/// BifrostBridgeModifier enables shared-element transitions.
1103/// When two views share the same Bifrost Bridge ID, the Sleipnir solver will
1104/// interpolate their geometry and effects (blur, glow) during the transition.
1105#[derive(Debug, Clone, PartialEq)]
1106pub struct BifrostBridgeModifier {
1107    pub id: String,
1108}
1109
1110impl ViewModifier for BifrostBridgeModifier {
1111    fn modify<V: View>(self, content: V) -> impl View {
1112        ModifiedView::new(content, self)
1113    }
1114
1115    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1116        // Register this element with the renderer for shared-element transition logic
1117        renderer.register_shared_element(&self.id, rect);
1118    }
1119}
1120
1121/// MjolnirSliceModifier implements the "Geometric Slice" aesthetic.
1122/// It uses a signed distance field (SDF) to clip the view along a sharp angled line.
1123#[derive(Debug, Clone, Copy, PartialEq)]
1124pub struct MjolnirSliceModifier {
1125    pub angle: f32,
1126    pub offset: f32,
1127}
1128
1129impl ViewModifier for MjolnirSliceModifier {
1130    fn modify<V: View>(self, content: V) -> impl View {
1131        ModifiedView::new(content, self)
1132    }
1133
1134    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1135        renderer.push_mjolnir_slice(self.angle, self.offset);
1136    }
1137
1138    fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1139        renderer.pop_mjolnir_slice();
1140    }
1141}
1142
1143/// MjolnirShatterModifier implements the "Shattering" effect.
1144/// It breaks the view into discrete geometric fragments that can be animated.
1145#[derive(Debug, Clone, Copy, PartialEq)]
1146pub struct MjolnirShatterModifier {
1147    pub pieces: u32,
1148    pub force: f32,
1149}
1150
1151impl ViewModifier for MjolnirShatterModifier {
1152    fn modify<V: View>(self, content: V) -> impl View {
1153        ModifiedView::new(content, self)
1154    }
1155
1156    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1157        // RADIAL SHATTER: Fragment the view into wedges
1158        let pieces = self.pieces.max(1);
1159        for i in 0..pieces {
1160            let progress = i as f32 / pieces as f32;
1161            let next_progress = (i + 1) as f32 / pieces as f32;
1162
1163            let angle_start = progress * 360.0;
1164            let angle_end = next_progress * 360.0;
1165
1166            // Wedge slice: intersection of two half-planes
1167            renderer.push_mjolnir_slice(angle_start, 0.0);
1168            renderer.push_mjolnir_slice(angle_end + 180.0, 0.0);
1169
1170            // Apply radial force offset
1171            let mid_angle = (angle_start + angle_end) / 2.0;
1172            let rad = mid_angle.to_radians();
1173            let dx = rad.cos() * self.force;
1174            let dy = rad.sin() * self.force;
1175
1176            let shard_rect = Rect {
1177                x: rect.x + dx,
1178                y: rect.y + dy,
1179                ..rect
1180            };
1181
1182            view.render(renderer, shard_rect);
1183
1184            renderer.pop_mjolnir_slice();
1185            renderer.pop_mjolnir_slice();
1186        }
1187    }
1188}
1189
1190/// BifrostModifier implements the Cyberpunk "Frosted Glass" aesthetic.
1191/// It triggers backdrop blurring and light scattering in the render pipeline.
1192#[derive(Debug, Clone, Copy, PartialEq)]
1193pub struct BifrostModifier {
1194    pub blur: f32,
1195    pub saturation: f32,
1196    pub opacity: f32,
1197}
1198
1199impl ViewModifier for BifrostModifier {
1200    fn modify<V: View>(self, content: V) -> impl View {
1201        ModifiedView::new(content, self)
1202    }
1203
1204    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1205        if renderer.is_over_budget() {
1206            // Degrade: Use lower quality (half blur) if over budget
1207            renderer.bifrost(rect, self.blur * 0.5, self.saturation, self.opacity);
1208        } else {
1209            renderer.bifrost(rect, self.blur, self.saturation, self.opacity);
1210        }
1211    }
1212}
1213
1214/// A modifier that adds a background color to a view.
1215#[derive(Debug, Clone, Copy, PartialEq)]
1216pub struct BackgroundModifier {
1217    pub color: [f32; 4],
1218}
1219
1220impl ViewModifier for BackgroundModifier {
1221    fn modify<V: View>(self, content: V) -> impl View {
1222        ModifiedView::new(content, self)
1223    }
1224
1225    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1226        renderer.fill_rect(rect, self.color);
1227    }
1228}
1229
1230/// A modifier that adds padding to a view.
1231#[derive(Debug, Clone, Copy, PartialEq)]
1232pub struct PaddingModifier {
1233    pub amount: f32,
1234}
1235
1236impl ViewModifier for PaddingModifier {
1237    fn modify<V: View>(self, content: V) -> impl View {
1238        ModifiedView::new(content, self)
1239    }
1240
1241    fn transform_rect(&self, rect: Rect) -> Rect {
1242        Rect {
1243            x: rect.x + self.amount,
1244            y: rect.y + self.amount,
1245            width: (rect.width - 2.0 * self.amount).max(0.0),
1246            height: (rect.height - 2.0 * self.amount).max(0.0),
1247        }
1248    }
1249
1250    fn transform_proposal(&self, mut proposal: SizeProposal) -> SizeProposal {
1251        if let Some(w) = proposal.width {
1252            proposal.width = Some((w - 2.0 * self.amount).max(0.0));
1253        }
1254        if let Some(h) = proposal.height {
1255            proposal.height = Some((h - 2.0 * self.amount).max(0.0));
1256        }
1257        proposal
1258    }
1259
1260    fn transform_size(&self, mut size: Size) -> Size {
1261        size.width += 2.0 * self.amount;
1262        size.height += 2.0 * self.amount;
1263        size
1264    }
1265}
1266
1267/// GungnirModifier implements the "Neon Glow" aesthetic.
1268/// It uses additive blending and multi-pass blurring to simulate glowing light.
1269#[derive(Debug, Clone, PartialEq)]
1270pub struct GungnirModifier {
1271    pub color: String,
1272    pub radius: f32,
1273    pub intensity: f32,
1274}
1275
1276impl ViewModifier for GungnirModifier {
1277    fn modify<V: View>(self, content: V) -> impl View {
1278        ModifiedView::new(content, self)
1279    }
1280
1281    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1282        // Neon Glow using Mode 1 in the Surtr pipeline
1283        renderer.stroke_rect(rect, [0.0, 1.0, 1.0, self.intensity], self.radius / 10.0);
1284    }
1285}
1286
1287/// GungnirPulseModifier implements a "breathing" neon effect.
1288#[derive(Debug, Clone, Copy, PartialEq)]
1289pub struct GungnirPulseModifier {
1290    pub color: [f32; 4],
1291    pub radius: f32,
1292    pub speed: f32,
1293}
1294
1295impl ViewModifier for GungnirPulseModifier {
1296    fn modify<V: View>(self, content: V) -> impl View {
1297        ModifiedView::new(content, self)
1298    }
1299
1300    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1301        let time = std::time::SystemTime::now()
1302            .duration_since(std::time::UNIX_EPOCH)
1303            .unwrap_or_default()
1304            .as_secs_f32();
1305
1306        // Mode 19: Dashed Border
1307        // Mode 20: 9-Slice / Patch Scaling
1308        let intensity = (time * self.speed).sin() * 0.5 + 0.5;
1309        let mut color = self.color;
1310        color[3] *= intensity;
1311
1312        // Mode 1 neon glow with dynamic intensity
1313        renderer.stroke_rect(rect, color, self.radius);
1314    }
1315}
1316
1317/// MagneticModifier makes a view "magnetic", subtly leaning towards or pulling the cursor.
1318#[derive(Debug, Clone, Copy, PartialEq)]
1319pub struct MagneticModifier {
1320    pub radius: f32,
1321    pub intensity: f32,
1322}
1323
1324impl ViewModifier for MagneticModifier {
1325    fn modify<V: View>(self, content: V) -> impl View {
1326        ModifiedView::new(content, self)
1327    }
1328
1329    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1330        let [px, py] = renderer.get_pointer_position();
1331        let center_x = rect.x + rect.width / 2.0;
1332        let center_y = rect.y + rect.height / 2.0;
1333
1334        let dx = px - center_x;
1335        let dy = py - center_y;
1336        let dist = (dx * dx + dy * dy).sqrt();
1337
1338        let mut offset_x = 0.0;
1339        let mut offset_y = 0.0;
1340
1341        if dist < self.radius && dist > 0.0 {
1342            let force = (1.0 - dist / self.radius) * self.intensity;
1343            offset_x = dx * force;
1344            offset_y = dy * force;
1345        }
1346
1347        let magnetic_rect = Rect {
1348            x: rect.x + offset_x,
1349            y: rect.y + offset_y,
1350            ..rect
1351        };
1352
1353        view.render(renderer, magnetic_rect);
1354    }
1355}
1356
1357/// ManiGlowModifier adds a soft, lunar-like cursor glow to a view.
1358/// Named after Máni, the personification of the Moon.
1359#[derive(Debug, Clone, Copy, PartialEq)]
1360pub struct ManiGlowModifier {
1361    pub color: [f32; 4],
1362    pub radius: f32,
1363}
1364
1365impl ViewModifier for ManiGlowModifier {
1366    fn modify<V: View>(self, content: V) -> impl View {
1367        ModifiedView::new(content, self)
1368    }
1369
1370    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1371        if crate::load_system_state().realm == Realm::Asgard {
1372            renderer.mani_glow(rect, self.color, self.radius);
1373        }
1374        view.render(renderer, rect);
1375    }
1376}
1377
1378/// BifrostLayerModifier themes a view based on its cognitive memory layer.
1379/// Episodic: Shifting aurora clouds.
1380/// Semantic: Crystalline gold.
1381/// Procedural: Heavy obsidian stone.
1382#[derive(Debug, Clone, Copy, PartialEq)]
1383pub struct BifrostLayerModifier {
1384    pub layer: MemoryLayer,
1385}
1386
1387impl ViewModifier for BifrostLayerModifier {
1388    fn modify<V: View>(self, content: V) -> impl View {
1389        ModifiedView::new(content, self)
1390    }
1391
1392    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1393        let realm = crate::load_system_state().realm;
1394        match self.layer {
1395            MemoryLayer::Episodic => {
1396                if realm == Realm::Asgard {
1397                    renderer.bifrost(rect, 40.0, 1.2, 0.7);
1398                } else {
1399                    renderer.fill_rect(rect, [0.1, 0.12, 0.15, 0.8]);
1400                }
1401            }
1402            MemoryLayer::Semantic => {
1403                if realm == Realm::Asgard {
1404                    renderer.gungnir(rect, [1.0, 0.84, 0.0, 1.0], 15.0, 0.6);
1405                } else {
1406                    renderer.stroke_rect(rect, [0.4, 0.4, 0.4, 1.0], 1.5);
1407                }
1408            }
1409            MemoryLayer::Procedural => {
1410                renderer.fill_rect(rect, [0.05, 0.05, 0.07, 0.95]);
1411                let stroke_color = if realm == Realm::Asgard {
1412                    [0.3, 0.3, 0.3, 1.0]
1413                } else {
1414                    [0.2, 0.2, 0.2, 1.0]
1415                };
1416                renderer.stroke_rect(rect, stroke_color, 2.0);
1417            }
1418        }
1419        view.render(renderer, rect);
1420    }
1421}
1422
1423/// FafnirModifier enables self-evolving UI capabilities.
1424/// Named after Fafnir, the dragon who grows in power based on the gold he hoards.
1425/// In CVKG, 'Gold' is user attention/interaction.
1426#[derive(Debug, Clone, Copy, PartialEq)]
1427pub struct FafnirModifier {
1428    /// Unique ID for tracking this component's vitality across frames.
1429    pub id: u64,
1430}
1431
1432impl ViewModifier for FafnirModifier {
1433    fn modify<V: View>(self, content: V) -> impl View {
1434        ModifiedView::new(content, self)
1435    }
1436
1437    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1438        let state = crate::load_system_state();
1439        let vitality = state
1440            .get_component_state::<f32>(self.id)
1441            .map(|v| *v.read().unwrap())
1442            .unwrap_or(1.0);
1443
1444        // Calculate evolutionary growth factors
1445        // Max growth at vitality 5.0 (50% scale increase, strong glow)
1446        let growth = (vitality - 1.0).clamp(0.0, 4.0);
1447        let scale = 1.0 + growth * 0.12;
1448        let glow_intensity = growth * 0.25;
1449
1450        // Feed Fafnir: Register interaction to boost vitality
1451        let id = self.id;
1452        renderer.register_handler(
1453            "pointermove",
1454            std::sync::Arc::new(move |_| {
1455                crate::update_system_state(|s| {
1456                    let mut s = s.clone();
1457                    let v = s
1458                        .get_component_state::<f32>(id)
1459                        .map(|v| *v.read().unwrap())
1460                        .unwrap_or(1.0);
1461                    s.set_component_state(id, (v + 0.05).min(5.0)); // Cap at 5.0
1462                    s
1463                });
1464            }),
1465        );
1466
1467        if scale > 1.01 {
1468            renderer.push_transform([0.0, 0.0], [scale, scale], 0.0);
1469        }
1470
1471        if glow_intensity > 0.1 && state.realm == Realm::Asgard {
1472            renderer.gungnir(rect, [1.0, 0.84, 0.0, 1.0], 15.0 * vitality, glow_intensity);
1473        }
1474
1475        view.render(renderer, rect);
1476
1477        if scale > 1.01 {
1478            renderer.pop_transform();
1479        }
1480    }
1481}
1482
1483/// MimirIntentModifier anticipates user movement and manifests holographic ghosts.
1484#[derive(Debug, Clone, Copy, PartialEq)]
1485pub struct MimirIntentModifier;
1486
1487impl ViewModifier for MimirIntentModifier {
1488    fn modify<V: View>(self, content: V) -> impl View {
1489        ModifiedView::new(content, self)
1490    }
1491
1492    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1493        let state = crate::load_system_state();
1494        let pos = state.last_pointer_pos;
1495        let vel = state.pointer_velocity;
1496
1497        // Calculate if the cursor is moving towards this rect
1498        let center = [rect.x + rect.width / 2.0, rect.y + rect.height / 2.0];
1499        let dx = center[0] - pos[0];
1500        let dy = center[1] - pos[1];
1501
1502        // Dot product of velocity and direction to center
1503        let dot = vel[0] * dx + vel[1] * dy;
1504        let speed_sq = vel[0] * vel[0] + vel[1] * vel[1];
1505        let dist_sq = dx * dx + dy * dy;
1506
1507        if dot > 0.0 && dist_sq < 250.0 * 250.0 && speed_sq > 0.5 && state.realm == Realm::Asgard {
1508            // Intent detected: render a subtle "ghost" reveal
1509            let intent_strength = (dot / (speed_sq.sqrt() * dist_sq.sqrt())).clamp(0.0, 1.0);
1510            renderer.stroke_rect(rect, [0.0, 0.9, 1.0, 0.3 * intent_strength], 1.5);
1511        }
1512
1513        view.render(renderer, rect);
1514    }
1515}
1516
1517/// KvasirVibeModifier renders a cognitive telemetry cloud representing agent complexity.
1518#[derive(Debug, Clone, Copy, PartialEq)]
1519pub struct KvasirVibeModifier {
1520    pub complexity: f32,
1521}
1522
1523impl ViewModifier for KvasirVibeModifier {
1524    fn modify<V: View>(self, content: V) -> impl View {
1525        ModifiedView::new(content, self)
1526    }
1527
1528    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1529        if crate::load_system_state().realm == Realm::Asgard {
1530            let t = renderer.elapsed_time();
1531            let c = self.complexity.clamp(0.0, 1.0);
1532
1533            // 1. Core Cognitive Cloud (Bifrost)
1534            // Turbulence increases with complexity
1535            let blur = 20.0 + c * 40.0;
1536            let turbulence_x = (t * (1.0 + c * 2.0)).sin() * 8.0 * c;
1537            let turbulence_y = (t * (0.8 + c * 1.5)).cos() * 5.0 * c;
1538            renderer.bifrost(
1539                rect.offset(turbulence_x, turbulence_y),
1540                blur,
1541                0.8 + c * 0.4,
1542                0.25,
1543            );
1544
1545            // 2. Synaptic Discharge (Gungnir pulses)
1546            if c > 0.2 {
1547                let pulse = (t * (3.0 + c * 5.0)).sin().abs() * c;
1548                let color = [0.0, 0.9, 1.0, 0.4 * pulse]; // Cyan synaptic pulse
1549                renderer.gungnir(rect, color, 12.0 + c * 24.0, 0.6 * pulse);
1550            }
1551
1552            // 3. Unstable Resonance (Magenta/Red shift for high complexity)
1553            if c > 0.7 {
1554                let instability = (t * 15.0).cos().abs() * (c - 0.7) * 3.3;
1555                let warning_color = [1.0, 0.0, 0.4, 0.12 * instability];
1556                renderer.fill_rect(rect, warning_color);
1557                renderer.stroke_rect(rect, [1.0, 0.0, 0.2, 0.45 * instability], 1.8);
1558            }
1559        }
1560        view.render(renderer, rect);
1561    }
1562}
1563
1564/// OdinsEyeModifier bestows omniscient observability over the entire scene graph.
1565#[derive(Debug, Clone, Copy, PartialEq)]
1566pub struct OdinsEyeModifier;
1567
1568impl ViewModifier for OdinsEyeModifier {
1569    fn modify<V: View>(self, content: V) -> impl View {
1570        ModifiedView::new(content, self)
1571    }
1572
1573    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1574        let state = crate::load_system_state();
1575        let t = renderer.elapsed_time();
1576
1577        // 1. Render Background content
1578        view.render(renderer, rect);
1579
1580        if state.realm == Realm::Asgard {
1581            // 2. Bestow Odin's Eye (Atmospheric Overlay)
1582            // Soft, large circular pulse representing the 'Eye'
1583            let eye_pulse = (t * 0.5).sin().abs() * 0.05;
1584            renderer.draw_radial_gradient(
1585                rect,
1586                [0.0, 0.6, 0.8, 0.08 + eye_pulse], // Inner Cyan
1587                [0.0, 0.0, 0.0, 0.0],              // Outer Black
1588            );
1589
1590            // 3. Hugin (Thought) Telemetry - Left Side
1591            let hugin_rect = Rect {
1592                x: rect.x + 20.0,
1593                y: rect.y + 40.0,
1594                width: 200.0,
1595                height: rect.height - 80.0,
1596            };
1597            renderer.draw_text(
1598                "HUGIN: THOUGHT",
1599                hugin_rect.x,
1600                hugin_rect.y,
1601                10.0,
1602                [0.0, 1.0, 1.0, 0.6],
1603            );
1604            for (i, thought) in state.thoughts.iter().rev().take(10).enumerate() {
1605                renderer.draw_text(
1606                    thought,
1607                    hugin_rect.x,
1608                    hugin_rect.y + 20.0 + i as f32 * 14.0,
1609                    9.0,
1610                    [1.0, 1.0, 1.0, 0.4],
1611                );
1612            }
1613
1614            // 4. Munin (Memory) Telemetry - Right Side
1615            let munin_rect = Rect {
1616                x: rect.x + rect.width - 220.0,
1617                y: rect.y + 40.0,
1618                width: 200.0,
1619                height: rect.height - 80.0,
1620            };
1621            renderer.draw_text(
1622                "MUNIN: MEMORY",
1623                munin_rect.x,
1624                munin_rect.y,
1625                10.0,
1626                [1.0, 0.84, 0.0, 0.6],
1627            );
1628            for (i, node) in state.nodes.iter().take(10).enumerate() {
1629                let opacity = (node.weight.min(1.0)) * 0.5;
1630                renderer.draw_text(
1631                    &node.id,
1632                    munin_rect.x,
1633                    munin_rect.y + 20.0 + i as f32 * 14.0,
1634                    9.0,
1635                    [1.0, 1.0, 1.0, opacity],
1636                );
1637            }
1638
1639            // 5. Omniscient Focus Beams (Gungnir Beams)
1640            if let Some(focus_id) = &state.odin_focus {
1641                // Visualize causal links to the focus node
1642                renderer.draw_text(
1643                    &format!("EYE FOCUS: {}", focus_id),
1644                    rect.x + rect.width / 2.0 - 50.0,
1645                    rect.y + 20.0,
1646                    12.0,
1647                    [0.0, 1.0, 1.0, 0.8],
1648                );
1649
1650                // In a real implementation, we would find the rect of the focus_id component.
1651                // For the 'Eye', we manifest a central beam of wisdom.
1652                renderer.gungnir(
1653                    Rect {
1654                        x: rect.x + rect.width / 2.0 - 1.0,
1655                        y: rect.y,
1656                        width: 2.0,
1657                        height: rect.height,
1658                    },
1659                    [0.0, 1.0, 1.0, 1.0],
1660                    20.0,
1661                    0.4,
1662                );
1663            }
1664        }
1665    }
1666}
1667
1668/// Sleipnir spring parameters for the physics solver
1669#[derive(Debug, Clone, Copy, PartialEq)]
1670pub struct SleipnirParams {
1671    pub stiffness: f32,
1672    pub damping: f32,
1673    pub mass: f32,
1674}
1675
1676impl SleipnirParams {
1677    pub fn snappy() -> Self {
1678        Self {
1679            stiffness: 230.0,
1680            damping: 22.0,
1681            mass: 1.0,
1682        }
1683    }
1684    pub fn fluid() -> Self {
1685        Self {
1686            stiffness: 170.0,
1687            damping: 26.0,
1688            mass: 1.0,
1689        }
1690    }
1691    pub fn heavy() -> Self {
1692        Self {
1693            stiffness: 90.0,
1694            damping: 20.0,
1695            mass: 1.0,
1696        }
1697    }
1698    pub fn bouncy() -> Self {
1699        Self {
1700            stiffness: 190.0,
1701            damping: 14.0,
1702            mass: 1.0,
1703        }
1704    }
1705}
1706
1707impl Default for SleipnirParams {
1708    fn default() -> Self {
1709        Self::fluid()
1710    }
1711}
1712
1713#[derive(Debug, Clone, Copy, PartialEq)]
1714struct SolverState {
1715    x: f32,
1716    v: f32,
1717}
1718
1719/// SleipnirSolver implements a 4th-order Runge-Kutta (RK4) integration for springs.
1720/// This provides superior stability for high-fidelity interactive motion.
1721#[derive(Debug, Clone, Copy, PartialEq)]
1722pub struct SleipnirSolver {
1723    params: SleipnirParams,
1724    target: f32,
1725    state: SolverState,
1726}
1727
1728impl SleipnirSolver {
1729    /// Create a new solver with a target value and starting state.
1730    pub fn new(params: SleipnirParams, target: f32, current: f32) -> Self {
1731        Self {
1732            params,
1733            target,
1734            state: SolverState { x: current, v: 0.0 },
1735        }
1736    }
1737
1738    /// Advance the simulation by dt seconds using RK4 integration.
1739    pub fn tick(&mut self, dt: f32) -> f32 {
1740        if dt <= 0.0 {
1741            return self.state.x;
1742        }
1743
1744        // Use a fixed time step for stability if dt is too large
1745        let mut remaining = dt;
1746        let step = 1.0 / 120.0;
1747
1748        while remaining > 0.0 {
1749            let d = remaining.min(step);
1750            self.step(d);
1751            remaining -= d;
1752        }
1753
1754        self.state.x
1755    }
1756
1757    fn step(&mut self, dt: f32) {
1758        let a = self.evaluate(self.state, 0.0, SolverState { x: 0.0, v: 0.0 });
1759        let b = self.evaluate(self.state, dt * 0.5, a);
1760        let c = self.evaluate(self.state, dt * 0.5, b);
1761        let d = self.evaluate(self.state, dt, c);
1762
1763        let dxdt = 1.0 / 6.0 * (a.x + 2.0 * (b.x + c.x) + d.x);
1764        let dvdt = 1.0 / 6.0 * (a.v + 2.0 * (b.v + c.v) + d.v);
1765
1766        self.state.x += dxdt * dt;
1767        self.state.v += dvdt * dt;
1768    }
1769
1770    fn evaluate(&self, initial: SolverState, dt: f32, d: SolverState) -> SolverState {
1771        let state = SolverState {
1772            x: initial.x + d.x * dt,
1773            v: initial.v + d.v * dt,
1774        };
1775        let force =
1776            -self.params.stiffness * (state.x - self.target) - self.params.damping * state.v;
1777        let mass = self.params.mass.max(0.001);
1778        SolverState {
1779            x: state.v,
1780            v: force / mass,
1781        }
1782    }
1783
1784    pub fn is_settled(&self) -> bool {
1785        (self.state.x - self.target).abs() < 0.001 && self.state.v.abs() < 0.001
1786    }
1787
1788    pub fn set_target(&mut self, target: f32) {
1789        self.target = target;
1790    }
1791
1792    pub fn current_value(&self) -> f32 {
1793        self.state.x
1794    }
1795}
1796
1797/// SleipnirModifier handles physics-based animations via the Sleipnir RK4 solver.
1798#[derive(Debug, Clone, PartialEq)]
1799pub struct SleipnirModifier {
1800    pub id: u64,
1801    pub target: f32,
1802    pub params: SleipnirParams,
1803}
1804
1805impl ViewModifier for SleipnirModifier {
1806    fn modify<V: View>(self, content: V) -> impl View {
1807        ModifiedView::new(content, self)
1808    }
1809
1810    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1811        let state = load_system_state();
1812
1813        // Try to fetch the solver from persistent state.
1814        let solver_lock_opt = state.get_component_state::<SleipnirSolver>(self.id);
1815
1816        let current_val;
1817
1818        if let Some(lock) = solver_lock_opt {
1819            // Found a solver. Tick it.
1820            let mut solver = lock.write().unwrap();
1821            solver.set_target(self.target);
1822            current_val = solver.tick(renderer.delta_time());
1823
1824            // If the solver hasn't settled yet, request another frame.
1825            if !solver.is_settled() {
1826                renderer.request_redraw();
1827            }
1828        } else {
1829            // First time seeing this ID. Initialize solver state.
1830            let solver = SleipnirSolver::new(
1831                self.params,
1832                self.target,
1833                self.target, // Initialize at target to avoid jump on first frame
1834            );
1835
1836            // Insert into registry for next frame.
1837            get_system_state().rcu(|old| {
1838                let mut new_state = (**old).clone();
1839                new_state.set_component_state(self.id, solver);
1840                new_state
1841            });
1842
1843            current_val = self.target;
1844        }
1845
1846        // Apply the solved value as a vertical translation.
1847        renderer.push_transform([0.0, current_val], [1.0, 1.0], 0.0);
1848        view.render(renderer, rect);
1849        renderer.pop_transform();
1850    }
1851}
1852
1853/// TransformModifier applies a 2D transform (translation, scale, rotation) to its child.
1854/// This modifier is "layout-neutral" and can be animated without re-running the layout engine.
1855#[derive(Debug, Clone, Copy, PartialEq)]
1856pub struct TransformModifier {
1857    pub translation: [f32; 2],
1858    pub scale: [f32; 2],
1859    pub rotation: f32,
1860}
1861
1862impl Default for TransformModifier {
1863    fn default() -> Self {
1864        Self::new()
1865    }
1866}
1867
1868impl TransformModifier {
1869    pub fn new() -> Self {
1870        Self {
1871            translation: [0.0, 0.0],
1872            scale: [1.0, 1.0],
1873            rotation: 0.0,
1874        }
1875    }
1876
1877    pub fn translate(mut self, x: f32, y: f32) -> Self {
1878        self.translation = [x, y];
1879        self
1880    }
1881
1882    pub fn scale(mut self, x: f32, y: f32) -> Self {
1883        self.scale = [x, y];
1884        self
1885    }
1886
1887    pub fn rotate(mut self, radians: f32) -> Self {
1888        self.rotation = radians;
1889        self
1890    }
1891}
1892
1893impl ViewModifier for TransformModifier {
1894    fn modify<V: View>(self, content: V) -> impl View {
1895        ModifiedView::new(content, self)
1896    }
1897
1898    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1899        renderer.push_transform(self.translation, self.scale, self.rotation);
1900        view.render(renderer, rect);
1901        renderer.pop_transform();
1902    }
1903}
1904
1905/// LifecycleModifier handles on_appear and on_disappear hooks.
1906
1907#[derive(Clone)]
1908pub struct LifecycleModifier {
1909    pub on_appear: Option<Arc<dyn Fn() + Send + Sync>>,
1910    pub on_disappear: Option<Arc<dyn Fn() + Send + Sync>>,
1911}
1912
1913impl ViewModifier for LifecycleModifier {
1914    fn modify<V: View>(self, content: V) -> impl View {
1915        ModifiedView::new(content, self)
1916    }
1917}
1918
1919/// OpacityModifier fades this view and all its descendants to the given alpha.
1920/// The renderer is expected to honour `push_opacity`/`pop_opacity` on the Renderer trait.
1921#[derive(Debug, Clone, Copy, PartialEq)]
1922pub struct OpacityModifier {
1923    pub opacity: f32,
1924}
1925
1926impl ViewModifier for OpacityModifier {
1927    fn modify<V: View>(self, content: V) -> impl View {
1928        ModifiedView::new(content, self)
1929    }
1930
1931    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1932        renderer.push_opacity(self.opacity);
1933    }
1934
1935    fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1936        renderer.pop_opacity();
1937    }
1938}
1939
1940/// OnClickModifier registers a click handler for this view.
1941#[derive(Clone)]
1942pub struct OnClickModifier {
1943    pub action: Arc<dyn Fn() + Send + Sync>,
1944}
1945
1946impl ViewModifier for OnClickModifier {
1947    fn modify<V: View>(self, content: V) -> impl View {
1948        ModifiedView::new(content, self)
1949    }
1950
1951    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1952        let action = self.action.clone();
1953        renderer.register_handler(
1954            "pointerclick",
1955            std::sync::Arc::new(move |event| {
1956                if let Event::PointerClick { .. } = event {
1957                    (action)();
1958                }
1959            }),
1960        );
1961    }
1962}
1963
1964/// OnPointerEnterModifier registers a pointer enter handler.
1965#[derive(Clone)]
1966pub struct OnPointerEnterModifier {
1967    pub action: Arc<dyn Fn() + Send + Sync>,
1968}
1969
1970impl ViewModifier for OnPointerEnterModifier {
1971    fn modify<V: View>(self, content: V) -> impl View {
1972        ModifiedView::new(content, self)
1973    }
1974
1975    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1976        let action = self.action.clone();
1977        renderer.register_handler(
1978            "pointerenter",
1979            std::sync::Arc::new(move |event| {
1980                if let Event::PointerEnter = event {
1981                    (action)();
1982                }
1983            }),
1984        );
1985    }
1986}
1987
1988/// OnPointerLeaveModifier registers a pointer leave handler.
1989#[derive(Clone)]
1990pub struct OnPointerLeaveModifier {
1991    pub action: Arc<dyn Fn() + Send + Sync>,
1992}
1993
1994impl ViewModifier for OnPointerLeaveModifier {
1995    fn modify<V: View>(self, content: V) -> impl View {
1996        ModifiedView::new(content, self)
1997    }
1998
1999    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
2000        let action = self.action.clone();
2001        renderer.register_handler(
2002            "pointerleave",
2003            std::sync::Arc::new(move |event| {
2004                if let Event::PointerLeave = event {
2005                    (action)();
2006                }
2007            }),
2008        );
2009    }
2010}
2011
2012/// OnPointerMoveModifier registers a pointer move handler.
2013#[derive(Clone)]
2014pub struct OnPointerMoveModifier {
2015    pub action: Arc<dyn Fn(f32, f32) + Send + Sync>,
2016}
2017
2018impl ViewModifier for OnPointerMoveModifier {
2019    fn modify<V: View>(self, content: V) -> impl View {
2020        ModifiedView::new(content, self)
2021    }
2022
2023    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
2024        let action = self.action.clone();
2025        renderer.register_handler(
2026            "pointermove",
2027            std::sync::Arc::new(move |event| {
2028                if let Event::PointerMove { x, y, .. } = event {
2029                    (action)(x, y);
2030                }
2031            }),
2032        );
2033    }
2034}
2035
2036/// OnPointerDownModifier registers a pointer down handler.
2037#[derive(Clone)]
2038pub struct OnPointerDownModifier {
2039    pub action: Arc<dyn Fn() + Send + Sync>,
2040}
2041
2042impl ViewModifier for OnPointerDownModifier {
2043    fn modify<V: View>(self, content: V) -> impl View {
2044        ModifiedView::new(content, self)
2045    }
2046
2047    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
2048        let action = self.action.clone();
2049        renderer.register_handler(
2050            "pointerdown",
2051            std::sync::Arc::new(move |event| {
2052                if let Event::PointerDown { .. } = event {
2053                    (action)();
2054                }
2055            }),
2056        );
2057    }
2058}
2059
2060/// OnPointerUpModifier registers a pointer up handler.
2061#[derive(Clone)]
2062pub struct OnPointerUpModifier {
2063    pub action: Arc<dyn Fn() + Send + Sync>,
2064}
2065
2066impl ViewModifier for OnPointerUpModifier {
2067    fn modify<V: View>(self, content: V) -> impl View {
2068        ModifiedView::new(content, self)
2069    }
2070
2071    fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
2072        let action = self.action.clone();
2073        renderer.register_handler(
2074            "pointerup",
2075            std::sync::Arc::new(move |event| {
2076                if let Event::PointerUp { .. } = event {
2077                    (action)();
2078                }
2079            }),
2080        );
2081    }
2082}
2083
2084/// ForegroundColorModifier overrides the foreground (text / icon) color inherited
2085/// by all descendants until another ForegroundColorModifier is encountered.
2086#[derive(Debug, Clone, Copy, PartialEq)]
2087pub struct ForegroundColorModifier {
2088    pub color: [f32; 4],
2089}
2090
2091impl ViewModifier for ForegroundColorModifier {
2092    fn modify<V: View>(self, content: V) -> impl View {
2093        ModifiedView::new(content, self)
2094    }
2095}
2096
2097/// ClipModifier restricts all child drawing to the view's layout rectangle.
2098/// The renderer must support `push_clip_rect`/`pop_clip_rect`.
2099#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2100pub struct ClipModifier;
2101
2102impl ViewModifier for ClipModifier {
2103    fn modify<V: View>(self, content: V) -> impl View {
2104        ModifiedView::new(content, self)
2105    }
2106
2107    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
2108        renderer.push_clip_rect(rect);
2109    }
2110
2111    fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
2112        renderer.pop_clip_rect();
2113    }
2114}
2115
2116/// BorderModifier draws a solid-color border around the view bounds.
2117#[derive(Debug, Clone, Copy, PartialEq)]
2118pub struct BorderModifier {
2119    pub color: [f32; 4],
2120    pub width: f32,
2121}
2122
2123impl ViewModifier for BorderModifier {
2124    fn modify<V: View>(self, content: V) -> impl View {
2125        ModifiedView::new(content, self)
2126    }
2127
2128    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
2129        renderer.stroke_rect(rect, self.color, self.width);
2130    }
2131}
2132
2133// Primitive (leaf) views implement Never as body
2134#[doc(hidden)]
2135pub enum Never {}
2136
2137impl View for Never {
2138    type Body = Never;
2139    fn body(self) -> Never {
2140        unreachable!()
2141    }
2142}
2143
2144/// EmptyView - A view that renders nothing and takes up no space.
2145#[derive(Debug, Clone, Copy, Default)]
2146pub struct EmptyView;
2147
2148impl View for EmptyView {
2149    type Body = Never;
2150    fn body(self) -> Self::Body {
2151        unreachable!()
2152    }
2153    fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
2154    fn intrinsic_size(&self, _renderer: &mut dyn Renderer, _proposal: SizeProposal) -> Size {
2155        Size {
2156            width: 0.0,
2157            height: 0.0,
2158        }
2159    }
2160}
2161
2162/// A view that has been transformed by a modifier.
2163/// Section 4.3: "Each modifier implements ViewModifier and produces a ModifiedView<Inner, Self>."
2164#[derive(Clone)]
2165pub struct ModifiedView<V, M> {
2166    view: V,
2167    modifier: M,
2168}
2169
2170impl<V: View, M: ViewModifier> ModifiedView<V, M> {
2171    #[doc(hidden)]
2172    pub fn new(view: V, modifier: M) -> Self {
2173        Self { view, modifier }
2174    }
2175}
2176
2177impl<V: View, M: ViewModifier> View for ModifiedView<V, M> {
2178    type Body = ModifiedView<V::Body, M>;
2179
2180    fn body(self) -> Self::Body {
2181        ModifiedView {
2182            view: self.view.body(),
2183            modifier: self.modifier.clone(),
2184        }
2185    }
2186
2187    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
2188        self.modifier.render_view(&self.view, renderer, rect);
2189    }
2190
2191    fn intrinsic_size(&self, renderer: &mut dyn Renderer, proposal: SizeProposal) -> Size {
2192        self.modifier.measure_view(&self.view, renderer, proposal)
2193    }
2194
2195    fn flex_weight(&self) -> f32 {
2196        self.modifier.child_flex_weight(&self.view)
2197    }
2198
2199    fn layout(&self) -> Option<&dyn layout::LayoutView> {
2200        self.modifier.layout().or_else(|| self.view.layout())
2201    }
2202
2203    fn get_grid_placement(&self) -> Option<GridPlacement> {
2204        self.modifier
2205            .get_grid_placement()
2206            .or_else(|| self.view.get_grid_placement())
2207    }
2208}
2209
2210pub trait ViewModifier: Send + Clone {
2211    fn modify<V: View>(self, content: V) -> impl View;
2212
2213    /// Returns the grid placement configuration if this modifier defines one.
2214    fn get_grid_placement(&self) -> Option<GridPlacement> {
2215        None
2216    }
2217
2218    /// Core rendering hook called before child views.
2219    fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
2220
2221    /// Cleanup hook called after child views.
2222    fn post_render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
2223
2224    /// Allows a modifier to completely override or wrap the rendering of its child.
2225    /// Default implementation performs a standard push -> transform -> render child -> pop sequence.
2226    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
2227        self.render(renderer, rect);
2228        let child_rect = self.transform_rect(rect);
2229        view.render(renderer, child_rect);
2230        self.post_render(renderer, rect);
2231    }
2232
2233    fn transform_rect(&self, rect: Rect) -> Rect {
2234        rect
2235    }
2236
2237    /// Allows a modifier to transform the layout proposal before it reaches the child.
2238    fn transform_proposal(&self, proposal: SizeProposal) -> SizeProposal {
2239        proposal
2240    }
2241
2242    /// Allows a modifier to transform the resulting size from the child.
2243    fn transform_size(&self, size: Size) -> Size {
2244        size
2245    }
2246
2247    /// Measure hook that coordinates size propagation.
2248    fn measure_view<V: View>(
2249        &self,
2250        view: &V,
2251        renderer: &mut dyn Renderer,
2252        proposal: SizeProposal,
2253    ) -> Size {
2254        let child_proposal = self.transform_proposal(proposal);
2255        let child_size = view.intrinsic_size(renderer, child_proposal);
2256        self.transform_size(child_size)
2257    }
2258
2259    /// Allows a modifier to override or pass through the child's flex weight.
2260    fn child_flex_weight<V: View>(&self, view: &V) -> f32 {
2261        view.flex_weight()
2262    }
2263
2264    fn layout(&self) -> Option<&dyn layout::LayoutView> {
2265        None
2266    }
2267}
2268
2269/// TelemetryData tracks real-time performance metrics for the GPU renderer.
2270#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
2271pub struct TelemetryData {
2272    pub frame_time_ms: f32,
2273    /// 99th percentile frame time over the last window, used to detect tail latency.
2274    pub p99_frame_time_ms: f32,
2275    /// Statistical jitter (variance in frame timing).
2276    pub frame_jitter_ms: f32,
2277    /// Indicates if a hardware stall (DRAM refresh, thermal spike) was detected.
2278    pub hardware_stall_detected: bool,
2279
2280    // Pass timing
2281    pub input_time_ms: f32,
2282    pub state_flush_time_ms: f32,
2283    pub layout_time_ms: f32,
2284    pub draw_time_ms: f32,
2285    pub gpu_submit_time_ms: f32,
2286
2287    pub draw_calls: u32,
2288    pub vertices: u32,
2289
2290    /// Global Berserker Pipeline Intensity (0.0 - 1.0+)
2291    pub berserker_rage: f32,
2292
2293    // Memory breakdown
2294    pub vram_usage_mb: f32,
2295    pub vram_textures_mb: f32,
2296    pub vram_buffers_mb: f32,
2297    pub vram_pipelines_mb: f32,
2298    /// Indicates if the Mega-Atlas or VRAM pools are at capacity.
2299    pub vram_exhausted: bool,
2300}
2301
2302/// Configuration for render-loop frame timing and degradation strategies.
2303#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
2304pub struct FrameBudget {
2305    /// Target frame time in milliseconds (default: 16.0 for 60FPS)
2306    pub target_ms: f32,
2307    /// If true, the renderer is allowed to dynamically skip non-critical effects
2308    /// (like heavy blurs or complex shadows) when the budget is exceeded.
2309    pub allow_degradation: bool,
2310}
2311
2312impl Default for FrameBudget {
2313    fn default() -> Self {
2314        Self {
2315            target_ms: 16.0,
2316            allow_degradation: true,
2317        }
2318    }
2319}
2320
2321/// The Renderer trait defines the atomic drawing operations for all CVKG backends.
2322/// This trait is object-safe and used by the View::render system.
2323/// # Implementation Requirements
2324/// 1. Coordinate system is origin-top-left (0,0) with Y increasing downwards.
2325/// 2. Colors are [R, G, B, A] in the [0.0, 1.0] range.
2326/// 3. All operations must be batchable by the underlying backend.
2327///    Trait providing timing information for the render loop.
2328pub trait ElapsedTime {
2329    /// Returns the cumulative time since the renderer started in seconds.
2330    fn elapsed_time(&self) -> f32;
2331
2332    /// Returns the time elapsed since the last frame in seconds.
2333    fn delta_time(&self) -> f32;
2334}
2335
2336/// The Renderer trait defines the atomic drawing operations for all CVKG backends.
2337/// This trait is object-safe and used by the View::render system.
2338/// # Implementation Requirements
2339/// 1. Coordinate system is origin-top-left (0,0) with Y increasing downwards.
2340/// 2. Colors are [R, G, B, A] in the [0.0, 1.0] range.
2341/// 3. All operations must be batchable by the underlying backend.
2342pub trait Renderer: ElapsedTime + Send {
2343    /// Requests that the renderer redraws as soon as possible.
2344    /// Used for continuous animations.
2345    fn request_redraw(&mut self) {}
2346
2347    /// Returns true if the current frame is over the time budget.
2348    /// This can be used to skip expensive visual effects.
2349    fn is_over_budget(&self) -> bool {
2350        false
2351    }
2352
2353    // ── Filled shapes ────────────────────────────────────────────────────
2354    fn fill_rect(&mut self, rect: Rect, color: [f32; 4]);
2355    fn fill_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4]);
2356    /// Fill an ellipse/circle that fits inside `rect`.
2357    fn fill_ellipse(&mut self, rect: Rect, color: [f32; 4]);
2358
2359    /// Draw a high-fidelity 3D cube inside the given rectangle using specialized shader logic.
2360    /// `rotation` is [pitch, yaw, roll] in radians.
2361    fn draw_3d_cube(&mut self, _rect: Rect, _color: [f32; 4], _rotation: [f32; 3]) {}
2362
2363    // ── Stroked shapes ───────────────────────────────────────────────────
2364    fn stroke_rect(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
2365    fn stroke_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4], stroke_width: f32);
2366    /// Stroke an ellipse/circle that fits inside `rect`.
2367    fn stroke_ellipse(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
2368    /// Draw a straight line from (x1,y1) to (x2,y2).
2369    fn draw_line(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: [f32; 4], stroke_width: f32);
2370    /// Fill a polygon defined by a set of vertices.
2371    fn fill_polygon(&mut self, _vertices: &[[f32; 2]], _color: [f32; 4]) {}
2372    /// Stroke a polygon defined by a set of vertices.
2373    fn stroke_polygon(&mut self, _vertices: &[[f32; 2]], _color: [f32; 4], _stroke_width: f32) {}
2374
2375    // ── Text ─────────────────────────────────────────────────────────────
2376    fn draw_text(&mut self, text: &str, x: f32, y: f32, size: f32, color: [f32; 4]);
2377    /// Measure the width and height of the specified text.
2378    fn measure_text(&mut self, text: &str, size: f32) -> (f32, f32);
2379
2380    fn shape_rich_text(
2381        &mut self,
2382        _spans: &[cvkg_runic_text::TextSpan],
2383        _max_width: Option<f32>,
2384        _align: cvkg_runic_text::TextAlign,
2385        _overflow: cvkg_runic_text::TextOverflow,
2386    ) -> Option<cvkg_runic_text::ShapedText> {
2387        None
2388    }
2389
2390    fn draw_shaped_text(&mut self, _text: &cvkg_runic_text::ShapedText, _x: f32, _y: f32) {}
2391
2392    // ── Images & textures ────────────────────────────────────────────────
2393    /// Draw a texture (GPU-side) at the specified rect.
2394    fn draw_texture(&mut self, _texture_id: u32, _rect: Rect) {}
2395    /// Draw an image asset by name or path.
2396    fn draw_image(&mut self, _image_name: &str, _rect: Rect) {}
2397    /// Load an image asset from memory.
2398    fn load_image(&mut self, _name: &str, _data: &[u8]) {}
2399    /// Pre-warm the renderer with assets. Implementations can use this
2400    /// to populate texture atlases or warm up shader caches.
2401    fn prewarm_vram(&mut self, _assets: Vec<(String, Vec<u8>)>) {}
2402
2403    /// Get the current pointer (mouse/touch) position.
2404    fn get_pointer_position(&self) -> [f32; 2] {
2405        [0.0, 0.0]
2406    }
2407
2408    // ── Data Visualization ───────────────────────────────────────────────
2409    /// Upload raw float data as a GPU texture for heatmap rendering.
2410    fn upload_data_texture(&mut self, _id: &str, _data: &[f32], _width: u32, _height: u32) {}
2411    /// Draw a heatmap using a previously uploaded data texture.
2412    fn draw_heatmap(&mut self, _texture_id: &str, _rect: Rect, _palette: &str) {}
2413
2414    // ── 3D Objects ───────────────────────────────────────────────────────
2415    /// Draw a 3D mesh.
2416    fn draw_mesh(&mut self, _mesh: &Mesh, _color: [f32; 4], _transform: glam::Mat4) {}
2417
2418    // ── Advanced Visual Effects ──────────────────────────────────────────
2419    /// Draw a linear gradient between two colors at the specified angle.
2420    fn draw_linear_gradient(
2421        &mut self,
2422        _rect: Rect,
2423        _start_color: [f32; 4],
2424        _end_color: [f32; 4],
2425        _angle: f32,
2426    ) {
2427    }
2428    /// Draw a radial gradient between two colors.
2429    fn draw_radial_gradient(
2430        &mut self,
2431        _rect: Rect,
2432        _inner_color: [f32; 4],
2433        _outer_color: [f32; 4],
2434    ) {
2435    }
2436    /// Draw a high-fidelity drop shadow for a rounded rectangle.
2437    fn draw_drop_shadow(
2438        &mut self,
2439        _rect: Rect,
2440        _radius: f32,
2441        _color: [f32; 4],
2442        _blur: f32,
2443        _spread: f32,
2444    ) {
2445    }
2446    /// Draw a dashed border for a rounded rectangle.
2447    fn stroke_dashed_rounded_rect(
2448        &mut self,
2449        _rect: Rect,
2450        _radius: f32,
2451        _color: [f32; 4],
2452        _width: f32,
2453        _dash: f32,
2454        _gap: f32,
2455    ) {
2456    }
2457    /// Draw a 9-slice / patch scaled image.
2458    fn draw_9slice(
2459        &mut self,
2460        _image_name: &str,
2461        _rect: Rect,
2462        _left: f32,
2463        _top: f32,
2464        _right: f32,
2465        _bottom: f32,
2466    ) {
2467    }
2468
2469    // ── Clipping ─────────────────────────────────────────────────────────
2470    /// Push a clip rectangle.  All subsequent drawing is clipped to `rect`.
2471    /// Implementations that do not support clipping may ignore this call.
2472    fn push_clip_rect(&mut self, _rect: Rect) {}
2473    /// Pop the most recently pushed clip rectangle.
2474    fn pop_clip_rect(&mut self) {}
2475    /// Get the current clip rectangle in screen coordinates.
2476    /// Returns a rect covering the entire screen if no clip is active.
2477    fn current_clip_rect(&self) -> Rect {
2478        Rect::new(-10000.0, -10000.0, 20000.0, 20000.0)
2479    }
2480
2481    // ── Global opacity ───────────────────────────────────────────────────
2482    /// Set a global opacity multiplier applied to all subsequent draw calls
2483    /// until `pop_opacity` is called.  `opacity` is in [0.0, 1.0].
2484    fn push_opacity(&mut self, _opacity: f32) {}
2485    /// Restore the previous opacity level.
2486    fn pop_opacity(&mut self) {}
2487
2488    // ── Berserker Pipeline State ─────────────────────────────────────────
2489    fn set_theme(&mut self, _theme: ColorTheme) {}
2490    fn set_rage(&mut self, _rage: f32) {}
2491    fn set_berserker_mode(&mut self, _state: BerserkerMode) {}
2492    fn trigger_shatter_event(&mut self, _origin: [f32; 2], _force: f32) {}
2493    /// Set the desktop scene preset (Aurora, Void, Nebula, Glitch, Yggdrasil).
2494    fn set_scene(&mut self, _scene: &str) {}
2495
2496    // ── Export & Print ───────────────────────────────────────────────────
2497    /// Capture the current frame as a PNG byte buffer.
2498    fn capture_png(&mut self) -> Vec<u8> {
2499        Vec::new()
2500    }
2501    /// Trigger a native print dialog or spooling operation.
2502    fn print(&mut self) {}
2503
2504    fn set_scene_preset(&mut self, _preset: u32) {}
2505
2506    // ── Cyberpunk Effects ────────────────────────────────────────────────
2507    /// Apply a Bifrost (Frosted Glass) effect to the specified rect.
2508    fn bifrost(&mut self, _rect: Rect, _blur: f32, _saturation: f32, _opacity: f32) {}
2509    /// Apply a Gungnir (Neon Glow) effect to the specified rect.
2510    fn gungnir(&mut self, _rect: Rect, _color: [f32; 4], _radius: f32, _intensity: f32) {}
2511    /// Apply a ManiGlow (Lunar Illuminator) effect.
2512    fn mani_glow(&mut self, _rect: Rect, _color: [f32; 4], _radius: f32) {}
2513    /// Push a Mjolnir Slice (geometric clipping).
2514    fn push_mjolnir_slice(&mut self, _angle: f32, _offset: f32) {}
2515    fn pop_mjolnir_slice(&mut self) {}
2516    /// Execute a render function with memoization.
2517    /// If the renderer supports caching and the `id` + `data_hash` match a previous run,
2518    /// it may replay cached commands instead of executing the function.
2519    fn memoize(&mut self, id: u64, data_hash: u64, render_fn: &dyn Fn(&mut dyn Renderer));
2520    /// Apply a Mjolnir Shatter effect (fragmentation) to the specified rect.
2521    fn mjolnir_shatter(&mut self, _rect: Rect, _pieces: u32, _force: f32, _color: [f32; 4]) {}
2522    fn mjolnir_fluid_shatter(&mut self, _rect: Rect, _pieces: u32, _force: f32, _color: [f32; 4]) {}
2523    /// Draw a Mjolnir Bolt (lightning strike) between two points.
2524    fn draw_mjolnir_bolt(&mut self, _from: [f32; 2], _to: [f32; 2], _color: [f32; 4]) {}
2525
2526    // ── Accessibility (ShieldWall) ───────────────────────────────────────
2527    fn set_aria_role(&mut self, _role: &str) {}
2528    fn set_aria_label(&mut self, _label: &str) {}
2529
2530    /// Register a shared element for Bifrost Bridge transitions.
2531    fn register_shared_element(&mut self, _id: &str, _rect: Rect) {}
2532
2533    /// Set a unique key for the current VDOM node to ensure stable identity during diffing.
2534    fn set_key(&mut self, _key: &str) {}
2535
2536    // ── Telemetry ────────────────────────────────────────────────────────
2537    /// Get real-time performance telemetry.
2538    fn get_telemetry(&self) -> TelemetryData {
2539        TelemetryData::default()
2540    }
2541
2542    // ── GPU State Management ─────────────────────────────────────────────
2543    /// Push a shadow state to the stack. All following draw calls will have this shadow.
2544    fn push_shadow(&mut self, _radius: f32, _color: [f32; 4], _offset: [f32; 2]) {}
2545    /// Pop the last shadow state from the stack.
2546    fn pop_shadow(&mut self) {}
2547
2548    // ── VDOM & Scene Graph ───────────────────────────────────────────────
2549    /// Push a Virtual DOM node onto the stack for hierarchy tracking.
2550    fn push_vnode(&mut self, _rect: Rect, _name: &'static str) {}
2551    /// Pop the current Virtual DOM node from the stack.
2552    fn pop_vnode(&mut self) {}
2553    /// Register an event handler for the current VDOM node.
2554    fn register_handler(
2555        &mut self,
2556        _event_type: &str,
2557        _handler: std::sync::Arc<dyn Fn(Event) + Send + Sync>,
2558    ) {
2559    }
2560
2561    // ── Z-Index & Depth ──────────────────────────────────────────────────
2562    /// Set the current Z-index for depth sorting.
2563    /// Higher values appear closer to the viewer.
2564    fn set_z_index(&mut self, _z: f32) {}
2565    /// Get the current Z-index.
2566    fn get_z_index(&self) -> f32 {
2567        0.0
2568    }
2569
2570    // ── Vector Graphics ──────────────────────────────────────────────────
2571    /// Load an SVG model from raw bytes.
2572    fn load_svg(&mut self, _name: &str, _svg_data: &[u8]) {}
2573    /// Draw a pre-loaded SVG model.
2574    fn draw_svg(&mut self, _name: &str, _rect: Rect) {}
2575    /// Serialize a pre-loaded SVG model back to SVG XML markup.
2576    /// Returns the serialized SVG string, or an error if the model is not loaded
2577    /// or serialization is not supported by this renderer.
2578    fn serialize_svg(&mut self, _name: &str) -> Result<String, String> {
2579        Err("SVG serialization not supported by this renderer".into())
2580    }
2581    /// Apply an SVG filter to a pre-loaded SVG model by filter element ID.
2582    /// The filter is evaluated and the result composited back into the SVG.
2583    /// Returns the filtered SVG as XML, or an error if not supported.
2584    fn apply_svg_filter(
2585        &mut self,
2586        _name: &str,
2587        _filter_id: &str,
2588        _region: Rect,
2589    ) -> Result<String, String> {
2590        Err("SVG filter not supported by this renderer".into())
2591    }
2592
2593    // ── GPU Transformations ──────────────────────────────────────────────
2594    /// Push a 2D transform (translation, scale, rotation) onto the stack.
2595    /// This transform should be applied to all subsequent draw calls until popped.
2596    /// Transform-only animations use this to avoid re-triggering the layout engine.
2597    fn push_transform(&mut self, _translation: [f32; 2], _scale: [f32; 2], _rotation: f32) {}
2598    /// Push a raw 2D affine transform matrix [a, b, c, d, e, f] corresponding to
2599    /// [m11, m12, m21, m22, tx, ty].
2600    fn push_affine(&mut self, _transform: [f32; 6]) {}
2601    /// Pop the last 2D transform from the stack.
2602    fn pop_transform(&mut self) {}
2603    /// Return the resolved layout bounds for a specific node ID if it exists.
2604    fn query_layout(&self, _node_id: scene_graph::NodeId) -> Option<Rect> {
2605        None
2606    }
2607    /// Enable or disable the layout debug overlay (bounds, padding, margin).
2608    fn set_debug_layout(&mut self, _enabled: bool) {}
2609    /// Check if the layout debug overlay is currently enabled.
2610    fn get_debug_layout(&self) -> bool {
2611        false
2612    }
2613
2614    // ── Material Routing ─────────────────────────────────────────────────
2615    /// Set the active material for subsequent draw calls.
2616    /// Controls which pass a draw call is routed to in the multi-pass pipeline.
2617    fn set_material(&mut self, _material: crate::material::DrawMaterial) {}
2618    /// Return the currently active material (defaults to Opaque).
2619    fn current_material(&self) -> crate::material::DrawMaterial {
2620        crate::material::DrawMaterial::Opaque
2621    }
2622
2623    // ── Vili Interaction Paradigm ──────────────────────────────────────────
2624    /// Compute the user's velocity/intent vector.
2625    fn mimir_intent(&self) -> [f32; 2] {
2626        [0.0, 0.0]
2627    }
2628    /// Calculate magnetic coordinate warp towards an anchor.
2629    fn magnetic_warp(&self, pointer: [f32; 2], anchor_rect: Rect, strength: f32) -> [f32; 2] {
2630        if strength <= 0.0 {
2631            return pointer;
2632        }
2633        let cx = anchor_rect.x + anchor_rect.width / 2.0;
2634        let cy = anchor_rect.y + anchor_rect.height / 2.0;
2635        let dx = pointer[0] - cx;
2636        let dy = pointer[1] - cy;
2637        let dist = (dx * dx + dy * dy).sqrt();
2638        let radius = 120.0;
2639        if dist < radius && dist > 0.0 {
2640            let force = (1.0 - dist / radius) * strength;
2641            [pointer[0] - dx * force, pointer[1] - dy * force]
2642        } else {
2643            pointer
2644        }
2645    }
2646    /// Calculate kinematic glow intensity based on proximity.
2647    fn mani_glow_intensity(&self, pointer: [f32; 2], bounds: Rect, radius: f32) -> f32 {
2648        let cx = bounds.x + bounds.width / 2.0;
2649        let cy = bounds.y + bounds.height / 2.0;
2650        let dist = ((pointer[0] - cx).powi(2) + (pointer[1] - cy).powi(2)).sqrt();
2651        if dist < radius {
2652            (1.0 - dist / radius).clamp(0.0, 1.0)
2653        } else {
2654            0.0
2655        }
2656    }
2657    /// Calculate dynamic element attention (scaling/morphing) statelessly per frame.
2658    fn fafnir_evolve(&self, pointer: [f32; 2], bounds: Rect, max_scale: f32) -> f32 {
2659        let prox = self.mani_glow_intensity(pointer, bounds, 120.0);
2660        1.0 + (max_scale - 1.0) * prox
2661    }
2662    /// Sets the precise Vili SDF Shape boundary for hit-testing.
2663    fn set_sdf_shape(&mut self, _shape: crate::layout::SdfShape) {}
2664}
2665
2666/// Utility for accessibility compliance (WCAG 2.1).
2667pub mod accessibility {
2668    /// Calculate the relative luminance of an sRGB color.
2669    pub fn relative_luminance(color: [f32; 4]) -> f32 {
2670        let f = |c: f32| {
2671            if c <= 0.03928 {
2672                c / 12.92
2673            } else {
2674                ((c + 0.055) / 1.055).powf(2.4)
2675            }
2676        };
2677        0.2126 * f(color[0]) + 0.7152 * f(color[1]) + 0.0722 * f(color[2])
2678    }
2679
2680    /// Calculate the contrast ratio between two colors.
2681    pub fn contrast_ratio(c1: [f32; 4], c2: [f32; 4]) -> f32 {
2682        let l1 = relative_luminance(c1);
2683        let l2 = relative_luminance(c2);
2684        let (light, dark) = if l1 > l2 { (l1, l2) } else { (l2, l1) };
2685        (light + 0.05) / (dark + 0.05)
2686    }
2687}
2688/// Defines the hardware acceleration tier and feature set available to the renderer.
2689#[derive(
2690    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
2691)]
2692pub enum RenderTier {
2693    /// High-performance GPU path (WebGPU / Vulkan / Metal / DX12) with full shader support.
2694    Tier1GPU = 0,
2695    /// Mid-tier GPU path (WebGL2 / OpenGL 3.3) with standard shader support.
2696    Tier2GPU = 1,
2697    /// Fallback software or basic hardware path (Canvas 2D / GDI+) with limited effects.
2698    Tier3Fallback = 2,
2699}
2700// =============================================================================
2701// BERSERKER UNIFORMS
2702// =============================================================================
2703use bytemuck::{Pod, Zeroable};
2704/// Fully themeable color palette for the Berserker pipeline.
2705#[repr(C)]
2706#[derive(Copy, Clone, Debug, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
2707pub struct ColorTheme {
2708    pub primary_neon: [f32; 4], // (R, G, B, intensity)
2709    pub shatter_neon: [f32; 4],
2710    pub glass_base: [f32; 4],
2711    pub glass_edge: [f32; 4],
2712    pub rune_glow: [f32; 4],
2713    pub ember_core: [f32; 4],
2714    pub background_deep: [f32; 4],
2715    pub mani_glow: [f32; 4], // (R, G, B, radius)
2716    pub glass_blur_strength: f32,
2717    pub shatter_edge_width: f32,
2718    pub neon_bloom_radius: f32,
2719    pub rune_opacity: f32,
2720}
2721impl ColorTheme {
2722    /// Asgard Mode: The high-fidelity "Cyberpunk Viking" aesthetic.
2723    pub fn asgard() -> Self {
2724        Self {
2725            primary_neon: [0.0, 1.0, 0.95, 1.2],
2726            shatter_neon: [1.0, 0.0, 0.75, 1.5],
2727            glass_base: [0.04, 0.04, 0.06, 0.82],
2728            glass_edge: [0.0, 0.45, 0.55, 0.6],
2729            rune_glow: [0.75, 0.98, 1.0, 0.9],
2730            ember_core: [0.95, 0.12, 0.12, 1.0],
2731            background_deep: [0.01, 0.01, 0.03, 1.0],
2732            mani_glow: [0.7, 0.9, 1.0, 0.05],
2733            glass_blur_strength: 0.6,
2734            shatter_edge_width: 1.8,
2735            neon_bloom_radius: 0.022,
2736            rune_opacity: 0.55,
2737        }
2738    }
2739
2740    /// Midgard Mode: A clean, functional tactical HUD for standard operations.
2741    pub fn midgard() -> Self {
2742        Self {
2743            primary_neon: [0.2, 0.4, 0.6, 1.0], // Muted blue
2744            shatter_neon: [0.5, 0.5, 0.5, 1.0], // Neutral gray
2745            glass_base: [0.1, 0.12, 0.15, 1.0], // Solid slate
2746            glass_edge: [0.3, 0.35, 0.4, 1.0],  // Subtle border
2747            rune_glow: [0.8, 0.8, 0.8, 0.0],    // Runes disabled
2748            ember_core: [0.5, 0.5, 0.5, 1.0],
2749            background_deep: [0.05, 0.05, 0.07, 1.0],
2750            mani_glow: [0.0, 0.0, 0.0, 0.0], // No cursor glow
2751            glass_blur_strength: 0.0,        // No blur
2752            shatter_edge_width: 1.0,
2753            neon_bloom_radius: 0.0,
2754            rune_opacity: 0.0,
2755        }
2756    }
2757
2758    pub fn cyberpunk_viking() -> Self {
2759        Self::asgard()
2760    }
2761    pub fn vibrant_glass() -> Self {
2762        Self {
2763            primary_neon: [0.0, 1.0, 0.95, 1.2],
2764            shatter_neon: [1.0, 0.0, 0.75, 1.5],
2765            glass_base: [0.55, 0.6, 0.7, 0.08], // Luminous cool tint
2766            glass_edge: [0.7, 0.85, 1.0, 0.45], // Subtle blue-white rim
2767            rune_glow: [0.75, 0.98, 1.0, 0.9],
2768            ember_core: [1.0, 0.4, 0.1, 1.0],
2769            background_deep: [0.05, 0.05, 0.1, 1.0],
2770            mani_glow: [0.7, 0.9, 1.0, 0.05],
2771            glass_blur_strength: 0.9,
2772            shatter_edge_width: 1.8,
2773            neon_bloom_radius: 0.022,
2774            rune_opacity: 0.55,
2775        }
2776    }
2777}
2778impl Default for ColorTheme {
2779    fn default() -> Self {
2780        Self::vibrant_glass()
2781    }
2782}
2783/// Per-frame scene state for the Berserker pipeline.
2784#[repr(C)]
2785#[derive(Copy, Clone, Debug, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
2786pub struct SceneUniforms {
2787    pub view: glam::Mat4,
2788    pub proj: glam::Mat4,
2789    pub time: f32,
2790    pub delta_time: f32,
2791    pub resolution: [f32; 2],
2792    pub mouse: [f32; 2],
2793    pub mouse_velocity: [f32; 2],
2794    pub shatter_origin: [f32; 2],
2795    pub shatter_time: f32,
2796    pub shatter_force: f32,
2797    pub berzerker_rage: f32,
2798    pub berzerker_mode: u32,
2799    pub scroll_offset: f32,
2800    pub scale_factor: f32,
2801    pub scene_type: u32,
2802    pub _pad: [f32; 3], // Align to 16 bytes if needed, but current struct is 4x16 + 4x16 + 4x16 + ...
2803}
2804
2805pub const SCENE_AURORA: u32 = 0;
2806pub const SCENE_VOID: u32 = 1;
2807pub const SCENE_NEBULA: u32 = 2;
2808pub const SCENE_GLITCH: u32 = 3;
2809pub const SCENE_YGGDRASIL: u32 = 4;
2810
2811impl SceneUniforms {
2812    pub fn new(width: f32, height: f32) -> Self {
2813        Self {
2814            view: glam::Mat4::IDENTITY,
2815            proj: glam::Mat4::orthographic_lh(0.0, width, height, 0.0, -100.0, 100.0),
2816            time: 0.0,
2817            delta_time: 0.016,
2818            resolution: [width, height],
2819            mouse: [0.5, 0.5],
2820            mouse_velocity: [0.0, 0.0],
2821            shatter_origin: [0.5, 0.5],
2822            shatter_time: -100.0,
2823            shatter_force: 0.0,
2824            berzerker_rage: 0.0,
2825            berzerker_mode: 0,
2826            scroll_offset: 0.0,
2827            scale_factor: 1.0,
2828            scene_type: SCENE_AURORA,
2829            _pad: [0.0; 3],
2830        }
2831    }
2832}
2833/// A 3D mesh containing vertex and index data.
2834#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
2835pub struct Mesh {
2836    pub vertices: Vec<[f32; 3]>,
2837    pub normals: Vec<[f32; 3]>,
2838    pub indices: Vec<u32>,
2839}
2840impl Mesh {
2841    pub fn from_obj(data: &[u8]) -> anyhow::Result<Vec<Self>> {
2842        let mut cursor = std::io::Cursor::new(data);
2843        let (models, _) = tobj::load_obj_buf(&mut cursor, &tobj::LoadOptions::default(), |_| {
2844            Ok((Vec::new(), Default::default()))
2845        })?;
2846        let mut meshes = Vec::new();
2847        for m in models {
2848            let mesh = m.mesh;
2849            let vertices: Vec<[f32; 3]> = mesh
2850                .positions
2851                .chunks(3)
2852                .map(|c| [c[0], c[1], c[2]])
2853                .collect();
2854            let normals = if mesh.normals.is_empty() {
2855                vec![[0.0, 0.0, 1.0]; vertices.len()]
2856            } else {
2857                mesh.normals.chunks(3).map(|c| [c[0], c[1], c[2]]).collect()
2858            };
2859            meshes.push(Mesh {
2860                vertices,
2861                normals,
2862                indices: mesh.indices,
2863            });
2864        }
2865        Ok(meshes)
2866    }
2867    pub fn from_stl(data: &[u8]) -> anyhow::Result<Self> {
2868        let mut cursor = std::io::Cursor::new(data);
2869        let stl = stl_io::read_stl(&mut cursor)?;
2870        let vertices: Vec<[f32; 3]> = stl.vertices.iter().map(|v| [v[0], v[1], v[2]]).collect();
2871        let mut indices = Vec::new();
2872        for face in stl.faces {
2873            indices.push(face.vertices[0] as u32);
2874            indices.push(face.vertices[1] as u32);
2875            indices.push(face.vertices[2] as u32);
2876        }
2877        let normals = vec![[0.0, 0.0, 1.0]; vertices.len()];
2878        Ok(Mesh {
2879            vertices,
2880            normals,
2881            indices,
2882        })
2883    }
2884}
2885/// FrameRenderer extends Renderer with frame lifecycle management.
2886/// It is typically implemented by the host windowing/rendering environment.
2887pub trait FrameRenderer<E = ()>: Renderer {
2888    fn begin_frame(&mut self) -> E;
2889    fn render_frame(&mut self) {
2890        // Default implementation does nothing - override for custom frame rendering
2891    }
2892    fn end_frame(&mut self, encoder: E);
2893}
2894use std::sync::Arc;
2895type SubscriberList<T> = Arc<std::sync::Mutex<Vec<Box<dyn Fn(&T) + Send + Sync>>>>;
2896/// State wrapper that owns a value and notifies subscribers when changed
2897#[derive(Clone)]
2898pub struct State<T: Clone + Send + Sync + 'static> {
2899    swap: Arc<arc_swap::ArcSwap<T>>,
2900    metadata_swap: Arc<arc_swap::ArcSwap<Option<agents::MutationMetadata>>>,
2901    #[cfg(not(target_arch = "wasm32"))]
2902    tvar: Arc<stm::TVar<T>>,
2903    #[cfg(not(target_arch = "wasm32"))]
2904    metadata_tvar: Arc<stm::TVar<Option<agents::MutationMetadata>>>,
2905    subscribers: SubscriberList<T>,
2906    version: Arc<std::sync::atomic::AtomicU64>,
2907    resolution: agents::ConflictResolution,
2908}
2909impl<T: Clone + Send + Sync + 'static> State<T> {
2910    /// Create a new State with initial value
2911    pub fn new(value: T) -> Self {
2912        #[cfg(not(target_arch = "wasm32"))]
2913        let tvar = Arc::new(stm::TVar::new(value.clone()));
2914        #[cfg(not(target_arch = "wasm32"))]
2915        let metadata_tvar = Arc::new(stm::TVar::new(None));
2916        Self {
2917            swap: Arc::new(arc_swap::ArcSwap::from_pointee(value)),
2918            metadata_swap: Arc::new(arc_swap::ArcSwap::new(Arc::new(None))),
2919            #[cfg(not(target_arch = "wasm32"))]
2920            tvar,
2921            #[cfg(not(target_arch = "wasm32"))]
2922            metadata_tvar,
2923            subscribers: Arc::new(std::sync::Mutex::new(Vec::new())),
2924            version: Arc::new(std::sync::atomic::AtomicU64::new(0)),
2925            resolution: agents::ConflictResolution::default(),
2926        }
2927    }
2928    /// Set the conflict resolution strategy for this state.
2929    pub fn with_resolution(mut self, resolution: agents::ConflictResolution) -> Self {
2930        self.resolution = resolution;
2931        self
2932    }
2933    /// Get the current value
2934    pub fn get(&self) -> T {
2935        (**self.swap.load()).clone()
2936    }
2937    /// Set a new value, notifying all subscribers. Applies conflict resolution if agents are present.
2938    pub fn set(&self, value: T) {
2939        #[cfg(not(target_arch = "wasm32"))]
2940        let (was_skipped, final_val, final_meta) = stm::atomically(|tx| {
2941            let new_meta = agents::get_current_mutation_metadata();
2942            let existing_meta = self.metadata_tvar.read(tx)?;
2943            let mut skip = false;
2944            if self.resolution == agents::ConflictResolution::PriorityWins
2945                && let (Some(new_m), Some(old_m)) = (new_meta, existing_meta)
2946                && new_m.priority < old_m.priority
2947            {
2948                skip = true;
2949            }
2950            if !skip {
2951                self.tvar.write(tx, value.clone())?;
2952                self.metadata_tvar.write(tx, new_meta)?;
2953                Ok((false, value.clone(), new_meta))
2954            } else {
2955                Ok((true, self.tvar.read(tx)?, existing_meta))
2956            }
2957        });
2958        #[cfg(target_arch = "wasm32")]
2959        let (was_skipped, final_val, final_meta) =
2960            (false, value, agents::get_current_mutation_metadata());
2961        if was_skipped {
2962            if let (Some(new_m), Some(old_m)) =
2963                (agents::get_current_mutation_metadata(), final_meta)
2964            {
2965                agents::notify_conflict(agents::ConflictEvent {
2966                    agent_id: new_m.agent_id,
2967                    priority: new_m.priority,
2968                    existing_agent_id: old_m.agent_id,
2969                    existing_priority: old_m.priority,
2970                    timestamp_ms: new_m.timestamp_ms,
2971                });
2972            }
2973            return;
2974        }
2975        self.swap.store(Arc::new(final_val.clone()));
2976        self.metadata_swap.store(Arc::new(final_meta));
2977        self.version
2978            .fetch_add(1, std::sync::atomic::Ordering::Release);
2979        let subs = Arc::clone(&self.subscribers);
2980        if crate::is_batching() {
2981            crate::enqueue_batch_task(Box::new(move || {
2982                let s = subs.lock().unwrap();
2983                for cb in s.iter() {
2984                    cb(&final_val);
2985                }
2986            }));
2987        } else {
2988            let s = subs.lock().unwrap();
2989            for cb in s.iter() {
2990                cb(&final_val);
2991            }
2992        }
2993    }
2994    pub fn mutate<F: Fn(&T) -> T>(&self, f: F) {
2995        #[cfg(not(target_arch = "wasm32"))]
2996        {
2997            let (was_skipped, final_val, final_meta) = stm::atomically(|tx| {
2998                let new_meta = agents::get_current_mutation_metadata();
2999                let existing_meta = self.metadata_tvar.read(tx)?;
3000                let mut skip = false;
3001                if self.resolution == agents::ConflictResolution::PriorityWins
3002                    && let (Some(new_m), Some(old_m)) = (new_meta, existing_meta)
3003                    && new_m.priority < old_m.priority
3004                {
3005                    skip = true;
3006                }
3007                if !skip {
3008                    let current = self.tvar.read(tx)?;
3009                    let next = f(&current);
3010                    self.tvar.write(tx, next.clone())?;
3011                    self.metadata_tvar.write(tx, new_meta)?;
3012                    Ok((false, next, new_meta))
3013                } else {
3014                    Ok((true, self.tvar.read(tx)?, existing_meta))
3015                }
3016            });
3017            if was_skipped {
3018                if let (Some(new_m), Some(old_m)) =
3019                    (agents::get_current_mutation_metadata(), final_meta)
3020                {
3021                    agents::notify_conflict(agents::ConflictEvent {
3022                        agent_id: new_m.agent_id,
3023                        priority: new_m.priority,
3024                        existing_agent_id: old_m.agent_id,
3025                        existing_priority: old_m.priority,
3026                        timestamp_ms: new_m.timestamp_ms,
3027                    });
3028                }
3029                return;
3030            }
3031            self.swap.store(Arc::new(final_val.clone()));
3032            self.metadata_swap.store(Arc::new(final_meta));
3033            self.version
3034                .fetch_add(1, std::sync::atomic::Ordering::Release);
3035            let subs = Arc::clone(&self.subscribers);
3036            if crate::is_batching() {
3037                crate::enqueue_batch_task(Box::new(move || {
3038                    let s = subs.lock().unwrap();
3039                    for cb in s.iter() {
3040                        cb(&final_val);
3041                    }
3042                }));
3043            } else {
3044                let s = subs.lock().unwrap();
3045                for cb in s.iter() {
3046                    cb(&final_val);
3047                }
3048            }
3049        }
3050        #[cfg(target_arch = "wasm32")]
3051        {
3052            self.set(f(&self.get()));
3053        }
3054    }
3055    /// Get current version
3056    pub fn version(&self) -> u64 {
3057        self.version.load(std::sync::atomic::Ordering::Acquire)
3058    }
3059    /// Subscribe to state changes
3060    pub fn subscribe<F: Fn(&T) + Send + Sync + 'static>(&self, callback: F) {
3061        self.subscribers.lock().unwrap().push(Box::new(callback));
3062    }
3063}
3064use crate::runtime::NodeStateSnapshot;
3065use std::sync::OnceLock;
3066use std::sync::atomic::{AtomicBool, Ordering};
3067/// Global application state registry.
3068pub static SYSTEM_STATE: OnceLock<Arc<arc_swap::ArcSwap<KnowledgeState>>> = OnceLock::new();
3069#[cfg(not(target_arch = "wasm32"))]
3070static KNOWLEDGE_TVAR: OnceLock<stm::TVar<KnowledgeState>> = OnceLock::new();
3071static IS_BATCHING: AtomicBool = AtomicBool::new(false);
3072pub static IS_RENDERING: AtomicBool = AtomicBool::new(false);
3073pub static LAYOUT_DIRTY: AtomicBool = AtomicBool::new(false);
3074type BatchQueue = OnceLock<std::sync::Mutex<Vec<Box<dyn FnOnce() + Send + Sync>>>>;
3075static BATCH_QUEUE: BatchQueue = OnceLock::new();
3076/// Global write lock to serialize updates to SYSTEM_STATE and KNOWLEDGE_TVAR,
3077/// preventing parallel race conditions between STM transactions and the lock-free reader state.
3078static STATE_WRITE_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
3079/// Returns true if state updates are currently being batched.
3080pub fn is_batching() -> bool {
3081    IS_BATCHING.load(Ordering::Acquire)
3082}
3083/// Returns true if the system is currently in the render phase.
3084pub fn is_rendering() -> bool {
3085    IS_RENDERING.load(Ordering::Acquire)
3086}
3087/// Signals the start of the render phase. Mutations during this phase trigger warnings.
3088pub fn begin_render_phase() {
3089    IS_RENDERING.store(true, Ordering::Release);
3090}
3091/// Signals the end of the render phase.
3092pub fn end_render_phase() {
3093    IS_RENDERING.store(false, Ordering::Release);
3094}
3095/// Enqueues a notification task to be run when the current batch flushes.
3096pub fn enqueue_batch_task(task: Box<dyn FnOnce() + Send + Sync>) {
3097    let mut queue = BATCH_QUEUE
3098        .get_or_init(|| std::sync::Mutex::new(Vec::new()))
3099        .lock()
3100        .unwrap();
3101    queue.push(task);
3102}
3103/// Executes multiple state updates in a single batch, deferring all subscriber
3104/// notifications until the closure completes. This prevents layout thrashing
3105/// and redundant render cycles when modifying multiple independent states.
3106pub fn batch<F: FnOnce()>(f: F) {
3107    if IS_BATCHING.swap(true, Ordering::AcqRel) {
3108        // Already inside a batch, just execute
3109        f();
3110        return;
3111    }
3112    f();
3113    IS_BATCHING.store(false, Ordering::Release);
3114    let mut queue = BATCH_QUEUE
3115        .get_or_init(|| std::sync::Mutex::new(Vec::new()))
3116        .lock()
3117        .unwrap();
3118    let tasks: Vec<_> = queue.drain(..).collect();
3119    drop(queue);
3120    for task in tasks {
3121        task();
3122    }
3123}
3124/// Get a reference to the global system state.
3125pub fn get_system_state() -> Arc<arc_swap::ArcSwap<KnowledgeState>> {
3126    SYSTEM_STATE
3127        .get_or_init(|| Arc::new(arc_swap::ArcSwap::from_pointee(KnowledgeState::default())))
3128        .clone()
3129}
3130pub fn load_system_state() -> arc_swap::Guard<Arc<KnowledgeState>> {
3131    get_system_state().load()
3132}
3133pub fn update_system_state<F>(f: F)
3134where
3135    F: FnOnce(&KnowledgeState) -> KnowledgeState,
3136{
3137    let _lock = STATE_WRITE_MUTEX.lock().unwrap();
3138    if is_rendering() {
3139        log::warn!(
3140            "LAYOUT THRASH DETECTED: System state mutated during render phase. This may trigger redundant layout passes and impact performance."
3141        );
3142    }
3143    LAYOUT_DIRTY.store(true, Ordering::SeqCst);
3144    let swap = get_system_state();
3145    let current = swap.load();
3146    let new_state = Arc::new(f(&current));
3147    swap.store(Arc::clone(&new_state));
3148    #[cfg(not(target_arch = "wasm32"))]
3149    {
3150        let tvar = KNOWLEDGE_TVAR.get_or_init(|| stm::TVar::new((*new_state).clone()));
3151        stm::atomically(|tx| tvar.write(tx, (*new_state).clone()));
3152    }
3153}
3154pub fn transact_system_state<F>(f: F)
3155where
3156    F: Fn(&KnowledgeState) -> KnowledgeState,
3157{
3158    let _lock = STATE_WRITE_MUTEX.lock().unwrap();
3159    #[cfg(not(target_arch = "wasm32"))]
3160    {
3161        if is_rendering() {
3162            log::warn!(
3163                "LAYOUT THRASH DETECTED: System state mutated during render phase. This may trigger redundant layout passes and impact performance."
3164            );
3165        }
3166        let tvar = KNOWLEDGE_TVAR
3167            .get_or_init(|| stm::TVar::new((**get_system_state().load()).clone()))
3168            .clone();
3169        let new_state = stm::atomically(move |tx| {
3170            let current = tvar.read(tx)?;
3171            let next = f(&current);
3172            tvar.write(tx, next.clone())?;
3173            Ok(next)
3174        });
3175        get_system_state().store(Arc::new(new_state));
3176    }
3177    #[cfg(target_arch = "wasm32")]
3178    {
3179        if is_rendering() {
3180            log::warn!(
3181                "LAYOUT THRASH DETECTED: System state mutated during render phase. This may trigger redundant layout passes and impact performance."
3182            );
3183        }
3184        update_system_state(f);
3185    }
3186}
3187impl KnowledgeState {
3188    /// Create a new empty KnowledgeState.
3189    pub fn new() -> Self {
3190        Self::default()
3191    }
3192    /// Set a component's internal state.
3193    pub fn set_component_state<T: 'static + Send + Sync>(&mut self, id: u64, state: T) {
3194        self.component_states
3195            .insert(id, Arc::new(std::sync::RwLock::new(state)));
3196    }
3197    /// Get a reference to a component's internal state.
3198    pub fn get_component_state<T: 'static + Send + Sync>(
3199        &self,
3200        id: u64,
3201    ) -> Option<Arc<std::sync::RwLock<T>>> {
3202        let lock = self.component_states.get(&id)?;
3203        // Attempt to clone the Arc and downcast the inner RwLock<dyn Any> to RwLock<T>
3204        // We use a two-step approach: check if the inner type matches via Any, then transmute the Arc
3205        // SAFETY: We verify the type via Any::is::<T> before transmuting
3206        let any_ref = lock.read().ok()?;
3207        if any_ref.is::<T>() {
3208            // Type matches — safe to transmute the Arc
3209            drop(any_ref);
3210            let cloned: Arc<std::sync::RwLock<dyn std::any::Any + Send + Sync>> = Arc::clone(lock);
3211            // Transmute Arc<RwLock<dyn Any>> to Arc<RwLock<T>>
3212            // This is safe because we just verified the inner type is T
3213            Some(unsafe {
3214                let raw = Arc::into_raw(cloned);
3215                Arc::from_raw(raw as *const std::sync::RwLock<T>)
3216            })
3217        } else {
3218            None
3219        }
3220    }
3221    /// Add a new fragment to memory.
3222    pub fn remember(&mut self, fragment: KnowledgeFragment) {
3223        self.fragments.insert(fragment.id.clone(), fragment);
3224    }
3225    /// Process a search query against the local knowledge base.
3226    pub fn process_query(&mut self, query: &str) {
3227        let query_lower = query.to_lowercase();
3228        let mut results: Vec<(f32, String)> = self
3229            .fragments
3230            .iter()
3231            .map(|(id, frag)| {
3232                let mut score = 0.0;
3233                if frag.summary.to_lowercase().contains(&query_lower) {
3234                    score += 1.0;
3235                }
3236                if frag.source.to_lowercase().contains(&query_lower) {
3237                    score += 0.5;
3238                }
3239                (score, id.clone())
3240            })
3241            .filter(|(score, _)| *score > 0.0)
3242            .collect();
3243        // Sort by relevance score
3244        results.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
3245        self.last_query_results = results.into_iter().map(|(_, id)| id).take(5).collect();
3246    }
3247    /// Captures a snapshot of the current state for debugging and hot-reloading.
3248    pub fn snapshot(&self) -> Vec<NodeStateSnapshot> {
3249        let mut snapshots = Vec::new();
3250        // Snapshots of agentic fragments
3251        for frag in self.fragments.values() {
3252            if let Ok(val) = serde_json::to_value(frag) {
3253                snapshots.push(NodeStateSnapshot { id: 0, state: val });
3254            }
3255        }
3256        snapshots
3257    }
3258}
3259/// A read/write projection into a `State<T>` owned elsewhere.
3260#[derive(Clone)]
3261pub struct Binding<T: Clone + Send + Sync + 'static> {
3262    swap: Arc<arc_swap::ArcSwap<T>>,
3263    #[cfg(not(target_arch = "wasm32"))]
3264    tvar: Arc<stm::TVar<T>>,
3265    version: Arc<std::sync::atomic::AtomicU64>,
3266}
3267impl<T: Clone + Send + Sync + 'static> Binding<T> {
3268    /// Create a binding from a State
3269    pub fn from_state(state: &State<T>) -> Self {
3270        Self {
3271            swap: Arc::clone(&state.swap),
3272            #[cfg(not(target_arch = "wasm32"))]
3273            tvar: Arc::clone(&state.tvar),
3274            version: Arc::clone(&state.version),
3275        }
3276    }
3277    /// Get the current value
3278    pub fn get(&self) -> T {
3279        (**self.swap.load()).clone()
3280    }
3281    /// Set a new value
3282    pub fn set(&self, value: T) {
3283        self.swap.store(Arc::new(value.clone()));
3284        #[cfg(not(target_arch = "wasm32"))]
3285        {
3286            let tvar = Arc::clone(&self.tvar);
3287            let v = value.clone();
3288            stm::atomically(move |tx| tvar.write(tx, v.clone()));
3289        }
3290        self.version
3291            .fetch_add(1, std::sync::atomic::Ordering::Release);
3292    }
3293    /// Get current version
3294    pub fn version(&self) -> u64 {
3295        self.version.load(std::sync::atomic::Ordering::Acquire)
3296    }
3297}
3298#[cfg(not(target_arch = "wasm32"))]
3299pub fn transact_pair<A, B, F>(state_a: &State<A>, state_b: &State<B>, f: F)
3300where
3301    A: Clone + Send + Sync + 'static,
3302    B: Clone + Send + Sync + 'static,
3303    F: Fn(&A, &B) -> (A, B),
3304{
3305    let tvar_a = Arc::clone(&state_a.tvar);
3306    let tvar_b = Arc::clone(&state_b.tvar);
3307    let (new_a, new_b) = stm::atomically(move |tx| {
3308        let a = tvar_a.read(tx)?;
3309        let b = tvar_b.read(tx)?;
3310        let (na, nb) = f(&a, &b);
3311        tvar_a.write(tx, na.clone())?;
3312        tvar_b.write(tx, nb.clone())?;
3313        Ok((na, nb))
3314    });
3315    state_a.swap.store(Arc::new(new_a.clone()));
3316    state_b.swap.store(Arc::new(new_b.clone()));
3317    state_a
3318        .version
3319        .fetch_add(1, std::sync::atomic::Ordering::Release);
3320    state_b
3321        .version
3322        .fetch_add(1, std::sync::atomic::Ordering::Release);
3323    let subs_a = Arc::clone(&state_a.subscribers);
3324    let subs_b = Arc::clone(&state_b.subscribers);
3325    if crate::is_batching() {
3326        crate::enqueue_batch_task(Box::new(move || {
3327            {
3328                let s = subs_a.lock().unwrap();
3329                for cb in s.iter() {
3330                    cb(&new_a);
3331                }
3332            }
3333            {
3334                let s = subs_b.lock().unwrap();
3335                for cb in s.iter() {
3336                    cb(&new_b);
3337                }
3338            }
3339        }));
3340    } else {
3341        {
3342            let s = subs_a.lock().unwrap();
3343            for cb in s.iter() {
3344                cb(&new_a);
3345            }
3346        }
3347        {
3348            let s = subs_b.lock().unwrap();
3349            for cb in s.iter() {
3350                cb(&new_b);
3351            }
3352        }
3353    }
3354}
3355use std::any::TypeId;
3356use std::sync::Mutex;
3357/// Global environment storage using TypeId as keys.
3358pub(crate) static ENVIRONMENT: OnceLock<
3359    Mutex<HashMap<TypeId, Box<dyn std::any::Any + Send + Sync>>>,
3360> = OnceLock::new();
3361/// Environment key type for accessing ambient values
3362/// Implement this trait to define a new environment key.
3363pub trait EnvKey: 'static + Send + Sync {
3364    /// The type of value stored in the environment
3365    type Value: Clone + Send + Sync + 'static;
3366    /// Get a default value for this key
3367    fn default_value() -> Self::Value;
3368}
3369/// Key for accessing the Yggdrasil design token tree
3370pub struct YggdrasilKey;
3371impl EnvKey for YggdrasilKey {
3372    type Value = YggdrasilTokens;
3373    fn default_value() -> Self::Value {
3374        default_tokens()
3375    }
3376}
3377// Duplicate AssetKey removed - original definition at line 63
3378/// System appearance (Light/Dark mode)
3379#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
3380pub enum Appearance {
3381    Light,
3382    Dark,
3383}
3384/// Orientation for layouts
3385#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
3386pub enum Orientation {
3387    Horizontal,
3388    Vertical,
3389}
3390/// Placement configuration for placing a view within a Grid layout.
3391#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
3392pub struct GridPlacement {
3393    /// 0-based column index. Negative values count from the end of columns.
3394    pub column: i32,
3395    /// Number of columns the view spans (default is 1).
3396    pub column_span: u32,
3397    /// 0-based row index. Negative values count from the end of rows.
3398    pub row: i32,
3399    /// Number of rows the view spans (default is 1).
3400    pub row_span: u32,
3401}
3402/// Cross-axis alignment for layout containers.
3403#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
3404pub enum Alignment {
3405    #[default]
3406    Center,
3407    Leading,
3408    Trailing,
3409    Top,
3410    Bottom,
3411}
3412/// Main-axis distribution for linear layout containers.
3413#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
3414pub enum Distribution {
3415    #[default]
3416    Fill,
3417    Center,
3418    Leading,
3419    Trailing,
3420    SpaceBetween,
3421    SpaceAround,
3422    SpaceEvenly,
3423}
3424/// A color represented by RGBA components in the [0.0, 1.0] range.
3425#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
3426pub struct Color {
3427    pub r: f32,
3428    pub g: f32,
3429    pub b: f32,
3430    pub a: f32,
3431}
3432impl Color {
3433    pub const BLACK: Color = Color {
3434        r: 0.0,
3435        g: 0.0,
3436        b: 0.0,
3437        a: 1.0,
3438    };
3439    pub const WHITE: Color = Color {
3440        r: 1.0,
3441        g: 1.0,
3442        b: 1.0,
3443        a: 1.0,
3444    };
3445    pub const TRANSPARENT: Color = Color {
3446        r: 0.0,
3447        g: 0.0,
3448        b: 0.0,
3449        a: 0.0,
3450    };
3451    pub const RED: Color = Color {
3452        r: 1.0,
3453        g: 0.0,
3454        b: 0.0,
3455        a: 1.0,
3456    };
3457    pub const GREEN: Color = Color {
3458        r: 0.0,
3459        g: 1.0,
3460        b: 0.0,
3461        a: 1.0,
3462    };
3463    pub const BLUE: Color = Color {
3464        r: 0.0,
3465        g: 0.0,
3466        b: 1.0,
3467        a: 1.0,
3468    };
3469    pub const VIKING_GOLD: Color = Color {
3470        r: 1.0,
3471        g: 0.84,
3472        b: 0.0,
3473        a: 1.0,
3474    };
3475    pub const MAGENTA_LIQUID: Color = Color {
3476        r: 1.0,
3477        g: 0.0,
3478        b: 1.0,
3479        a: 1.0,
3480    };
3481    pub const TACTICAL_OBSIDIAN: Color = Color {
3482        r: 0.05,
3483        g: 0.05,
3484        b: 0.07,
3485        a: 1.0,
3486    };
3487    /// Calculate the relative luminance of the color as defined by WCAG 2.x
3488    pub fn relative_luminance(&self) -> f32 {
3489        fn res(c: f32) -> f32 {
3490            if c <= 0.03928 {
3491                c / 12.92
3492            } else {
3493                ((c + 0.055) / 1.055).powf(2.4)
3494            }
3495        }
3496        0.2126 * res(self.r) + 0.7152 * res(self.g) + 0.0722 * res(self.b)
3497    }
3498    /// Calculate the contrast ratio between this color and another color
3499    pub fn contrast_ratio(&self, other: &Color) -> f32 {
3500        let l1 = self.relative_luminance();
3501        let l2 = other.relative_luminance();
3502        if l1 > l2 {
3503            (l1 + 0.05) / (l2 + 0.05)
3504        } else {
3505            (l2 + 0.05) / (l1 + 0.05)
3506        }
3507    }
3508    pub const CYAN: Color = Color {
3509        r: 0.0,
3510        g: 1.0,
3511        b: 1.0,
3512        a: 1.0,
3513    };
3514    pub const YELLOW: Color = Color {
3515        r: 1.0,
3516        g: 1.0,
3517        b: 0.0,
3518        a: 1.0,
3519    };
3520    pub const MAGENTA: Color = Color {
3521        r: 1.0,
3522        g: 0.0,
3523        b: 1.0,
3524        a: 1.0,
3525    };
3526    pub const GRAY: Color = Color {
3527        r: 0.5,
3528        g: 0.5,
3529        b: 0.5,
3530        a: 1.0,
3531    };
3532    /// Create a new color from RGBA components.
3533    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
3534        Self { r, g, b, a }
3535    }
3536    /// Convert the color to a [r, g, b, a] array.
3537    pub fn as_array(&self) -> [f32; 4] {
3538        [self.r, self.g, self.b, self.a]
3539    }
3540
3541    /// Return a new color with lightness increased by `amount`.
3542    ///
3543    /// Adds `amount` to each RGB channel and clamps to [0.0, 1.0].
3544    /// This is a simple sRGB lightness adjustment, not perceptually uniform.
3545    /// For perceptually uniform adjustments, use OKLCH via cvkg-themes.
3546    pub fn lighten(&self, amount: f32) -> Self {
3547        Self {
3548            r: (self.r + amount).clamp(0.0, 1.0),
3549            g: (self.g + amount).clamp(0.0, 1.0),
3550            b: (self.b + amount).clamp(0.0, 1.0),
3551            a: self.a,
3552        }
3553    }
3554
3555    /// Return a new color with lightness decreased by `amount`.
3556    pub fn darken(&self, amount: f32) -> Self {
3557        Self {
3558            r: (self.r - amount).clamp(0.0, 1.0),
3559            g: (self.g - amount).clamp(0.0, 1.0),
3560            b: (self.b - amount).clamp(0.0, 1.0),
3561            a: self.a,
3562        }
3563    }
3564}
3565impl View for Color {
3566    type Body = Never;
3567    fn body(self) -> Self::Body {
3568        unreachable!()
3569    }
3570    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
3571        renderer.fill_rect(rect, self.as_array());
3572    }
3573}
3574/// Key for accessing the current system appearance
3575pub struct AppearanceKey;
3576impl EnvKey for AppearanceKey {
3577    type Value = Appearance;
3578    fn default_value() -> Self::Value {
3579        Appearance::Dark // Default to Dark (Ginnungagap) for Berserker aesthetic
3580    }
3581}
3582/// StyleResolver provides high-level access to themed values from the environment.
3583pub struct StyleResolver;
3584impl StyleResolver {
3585    /// Resolve a color from the current environment
3586    pub fn color(key: &str) -> String {
3587        let tokens = Environment::<YggdrasilKey>::new().get();
3588        let appearance = Environment::<AppearanceKey>::new().get();
3589        let is_dark = appearance == Appearance::Dark;
3590        tokens
3591            .get_color(key, is_dark)
3592            .unwrap_or_else(|| "#FF00FF".to_string()) // Default to MuspelMagenta on failure
3593    }
3594    /// Resolve a generic token value
3595    pub fn get<T: FromStr>(category: &str, key: &str) -> Option<T> {
3596        let tokens = Environment::<YggdrasilKey>::new().get();
3597        let appearance = Environment::<AppearanceKey>::new().get();
3598        let is_dark = appearance == Appearance::Dark;
3599        tokens.get(category, key, is_dark)
3600    }
3601    /// Resolve a color from the current environment as a [f32; 4] RGBA array.
3602    /// Returns the color value for the current appearance (light/dark).
3603    /// Falls back to magenta (#FF00FF) if the key is not found.
3604    pub fn color_array(key: &str) -> [f32; 4] {
3605        let hex = Self::color(key);
3606        parse_hex_color(&hex)
3607    }
3608}
3609
3610/// Parse a hex color string (#RRGGBB or #RRGGBBAA) into [f32; 4] RGBA.
3611fn parse_hex_color(hex: &str) -> [f32; 4] {
3612    let hex = hex.trim_start_matches('#');
3613    if hex.len() >= 6 {
3614        let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(255) as f32 / 255.0;
3615        let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(0) as f32 / 255.0;
3616        let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(255) as f32 / 255.0;
3617        let a = if hex.len() >= 8 {
3618            u8::from_str_radix(&hex[6..8], 16).unwrap_or(255) as f32 / 255.0
3619        } else {
3620            1.0
3621        };
3622        [r, g, b, a]
3623    } else {
3624        [1.0, 0.0, 1.0, 1.0] // Magenta fallback
3625    }
3626}
3627
3628/// The authoritative Cyberpunk Viking default tokens
3629pub fn default_tokens() -> YggdrasilTokens {
3630    let mut tokens = YggdrasilTokens::new();
3631    // Core Norse Colorways
3632    tokens.color.insert(
3633        "background".to_string(),
3634        TokenValue::Single {
3635            value: "#000000".to_string(), // Ginnungagap (The Void)
3636        },
3637    );
3638    tokens.color.insert(
3639        "primary".to_string(),
3640        TokenValue::Single {
3641            value: "#00FFFF".to_string(), // NiflCyan (Aesir Primary)
3642        },
3643    );
3644    tokens.color.insert(
3645        "secondary".to_string(),
3646        TokenValue::Single {
3647            value: "#FF00FF".to_string(), // MuspelMagenta (Berserker Secondary)
3648        },
3649    );
3650    tokens.color.insert(
3651        "surface".to_string(),
3652        TokenValue::Adaptive {
3653            light: "#FFFFFF".to_string(),
3654            dark: "#121212".to_string(),
3655        },
3656    );
3657    tokens.color.insert(
3658        "text".to_string(),
3659        TokenValue::Adaptive {
3660            light: "#000000".to_string(),
3661            dark: "#FFFFFF".to_string(),
3662        },
3663    );
3664    // Semantic component tokens
3665    tokens.color.insert(
3666        "surface_elevated".to_string(),
3667        TokenValue::Adaptive {
3668            light: "#FFFFFF".to_string(),
3669            dark: "#1A1A24".to_string(),
3670        },
3671    );
3672    tokens.color.insert(
3673        "surface_overlay".to_string(),
3674        TokenValue::Adaptive {
3675            light: "#FFFFFF".to_string(),
3676            dark: "#1E1E2E".to_string(),
3677        },
3678    );
3679    tokens.color.insert(
3680        "border".to_string(),
3681        TokenValue::Adaptive {
3682            light: "#D0D0D8".to_string(),
3683            dark: "#2A2A3A".to_string(),
3684        },
3685    );
3686    tokens.color.insert(
3687        "border_strong".to_string(),
3688        TokenValue::Adaptive {
3689            light: "#A0A0B0".to_string(),
3690            dark: "#3A3A50".to_string(),
3691        },
3692    );
3693    tokens.color.insert(
3694        "text_muted".to_string(),
3695        TokenValue::Adaptive {
3696            light: "#606070".to_string(),
3697            dark: "#8080A0".to_string(),
3698        },
3699    );
3700    tokens.color.insert(
3701        "text_dim".to_string(),
3702        TokenValue::Adaptive {
3703            light: "#9090A0".to_string(),
3704            dark: "#505070".to_string(),
3705        },
3706    );
3707    tokens.color.insert(
3708        "accent".to_string(),
3709        TokenValue::Single {
3710            value: "#00FFFF".to_string(), // NiflCyan
3711        },
3712    );
3713    tokens.color.insert(
3714        "accent_hover".to_string(),
3715        TokenValue::Single {
3716            value: "#33FFFF".to_string(),
3717        },
3718    );
3719    tokens.color.insert(
3720        "success".to_string(),
3721        TokenValue::Single {
3722            value: "#00E676".to_string(),
3723        },
3724    );
3725    tokens.color.insert(
3726        "warning".to_string(),
3727        TokenValue::Single {
3728            value: "#FFB300".to_string(),
3729        },
3730    );
3731    tokens.color.insert(
3732        "error".to_string(),
3733        TokenValue::Single {
3734            value: "#FF5252".to_string(),
3735        },
3736    );
3737    tokens.color.insert(
3738        "info".to_string(),
3739        TokenValue::Single {
3740            value: "#448AFF".to_string(),
3741        },
3742    );
3743    tokens.color.insert(
3744        "hover".to_string(),
3745        TokenValue::Adaptive {
3746            light: "#F0F0F5".to_string(),
3747            dark: "#252535".to_string(),
3748        },
3749    );
3750    tokens.color.insert(
3751        "active".to_string(),
3752        TokenValue::Adaptive {
3753            light: "#E0E0EB".to_string(),
3754            dark: "#303045".to_string(),
3755        },
3756    );
3757    tokens.color.insert(
3758        "disabled".to_string(),
3759        TokenValue::Adaptive {
3760            light: "#E8E8F0".to_string(),
3761            dark: "#1A1A28".to_string(),
3762        },
3763    );
3764    tokens.color.insert(
3765        "disabled_text".to_string(),
3766        TokenValue::Adaptive {
3767            light: "#B0B0C0".to_string(),
3768            dark: "#404060".to_string(),
3769        },
3770    );
3771    tokens.color.insert(
3772        "focus_ring".to_string(),
3773        TokenValue::Single {
3774            value: "#00FFFF".to_string(),
3775        },
3776    );
3777    tokens.color.insert(
3778        "shadow".to_string(),
3779        TokenValue::Adaptive {
3780            light: "#00000020".to_string(),
3781            dark: "#00000060".to_string(),
3782        },
3783    );
3784    tokens.color.insert(
3785        "code_bg".to_string(),
3786        TokenValue::Adaptive {
3787            light: "#F5F5FA".to_string(),
3788            dark: "#0D0D18".to_string(),
3789        },
3790    );
3791    // Bifrost (Glassmorphism) - Frosted Style
3792    tokens.bifrost.insert(
3793        "blur".to_string(),
3794        TokenValue::Single {
3795            value: "25.0".to_string(),
3796        },
3797    );
3798    tokens.bifrost.insert(
3799        "saturation".to_string(),
3800        TokenValue::Single {
3801            value: "1.2".to_string(),
3802        },
3803    );
3804    tokens.bifrost.insert(
3805        "opacity".to_string(),
3806        TokenValue::Single {
3807            value: "0.65".to_string(),
3808        },
3809    );
3810    // Gungnir (Neon Glow)
3811    tokens.gungnir.insert(
3812        "intensity".to_string(),
3813        TokenValue::Single {
3814            value: "1.0".to_string(),
3815        },
3816    );
3817    tokens.gungnir.insert(
3818        "radius".to_string(),
3819        TokenValue::Single {
3820            value: "15.0".to_string(),
3821        },
3822    );
3823    // Mjolnir (Sharp Geometry)
3824    tokens.mjolnir.insert(
3825        "clip_angle".to_string(),
3826        TokenValue::Single {
3827            value: "12.0".to_string(),
3828        },
3829    );
3830    tokens.mjolnir.insert(
3831        "border_width".to_string(),
3832        TokenValue::Single {
3833            value: "2.0".to_string(),
3834        },
3835    );
3836    // Sleipnir (Spring Animation)
3837    tokens.anim.insert(
3838        "stiffness".to_string(),
3839        TokenValue::Single {
3840            value: "170.0".to_string(),
3841        },
3842    );
3843    tokens.anim.insert(
3844        "damping".to_string(),
3845        TokenValue::Single {
3846            value: "26.0".to_string(),
3847        },
3848    );
3849    tokens.anim.insert(
3850        "mass".to_string(),
3851        TokenValue::Single {
3852            value: "1.0".to_string(),
3853        },
3854    );
3855    // Accessibility
3856    tokens.accessibility.insert(
3857        "reduce_motion".to_string(),
3858        TokenValue::Single {
3859            value: "false".to_string(),
3860        },
3861    );
3862    tokens
3863}
3864/// Environment wrapper for accessing ambient values
3865pub struct Environment<K: EnvKey> {
3866    _marker: std::marker::PhantomData<K>,
3867}
3868impl<K: EnvKey> Default for Environment<K> {
3869    fn default() -> Self {
3870        Self::new()
3871    }
3872}
3873impl<K: EnvKey> Environment<K> {
3874    /// Create a new Environment
3875    pub fn new() -> Self {
3876        Self {
3877            _marker: std::marker::PhantomData,
3878        }
3879    }
3880    /// Get the current value from the environment
3881    pub fn get(&self) -> K::Value {
3882        if let Some(env_store) = ENVIRONMENT.get() {
3883            let env_lock = env_store.lock().unwrap();
3884            if let Some(val) = env_lock.get(&std::any::TypeId::of::<K>()) {
3885                if let Some(typed_val) = val.downcast_ref::<K::Value>() {
3886                    return typed_val.clone();
3887                } else {
3888                    log::warn!(
3889                        "Environment: Downcast failed for key type {:?}",
3890                        std::any::type_name::<K>()
3891                    );
3892                }
3893            } else {
3894                log::debug!(
3895                    "Environment: Key not found: {:?}. Returning default.",
3896                    std::any::type_name::<K>()
3897                );
3898            }
3899        } else {
3900            log::debug!(
3901                "Environment: Store not initialized. Key: {:?}. Returning default.",
3902                std::any::type_name::<K>()
3903            );
3904        }
3905        K::default_value()
3906    }
3907}
3908/// Ambient environment management
3909pub mod env {
3910    /// Insert a value into the environment
3911    pub fn insert<K: super::EnvKey>(value: K::Value) {
3912        let store = super::ENVIRONMENT
3913            .get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()));
3914        let mut env_map = store.lock().unwrap();
3915        env_map.insert(std::any::TypeId::of::<K>(), Box::new(value));
3916    }
3917    /// Remove a value from the environment.
3918    pub fn remove<K: super::EnvKey>() {
3919        if let Some(store) = super::ENVIRONMENT.get() {
3920            let mut env_map = store.lock().unwrap();
3921            env_map.remove(&std::any::TypeId::of::<K>());
3922        }
3923    }
3924}
3925/// Geometry modifiers
3926/// Size of the view in logical pixels
3927#[derive(Debug, Clone, Copy, PartialEq)]
3928pub struct Size {
3929    pub width: f32,
3930    pub height: f32,
3931}
3932
3933impl Size {
3934    pub const ZERO: Self = Self {
3935        width: 0.0,
3936        height: 0.0,
3937    };
3938
3939    pub fn new(width: f32, height: f32) -> Self {
3940        Self { width, height }
3941    }
3942}
3943
3944/// Insets for padding
3945#[derive(Debug, Clone, Copy, PartialEq)]
3946pub struct EdgeInsets {
3947    pub top: f32,
3948    pub leading: f32,
3949    pub bottom: f32,
3950    pub trailing: f32,
3951}
3952
3953impl EdgeInsets {
3954    /// Equal insets on all edges
3955    pub fn all(value: f32) -> Self {
3956        Self {
3957            top: value,
3958            leading: value,
3959            bottom: value,
3960            trailing: value,
3961        }
3962    }
3963
3964    /// Vertical insets (top and bottom)
3965    pub fn vertical(value: f32) -> Self {
3966        Self {
3967            top: value,
3968            leading: 0.0,
3969            bottom: value,
3970            trailing: 0.0,
3971        }
3972    }
3973
3974    /// Horizontal insets (leading and trailing)
3975    pub fn horizontal(value: f32) -> Self {
3976        Self {
3977            top: 0.0,
3978            leading: value,
3979            bottom: 0.0,
3980            trailing: value,
3981        }
3982    }
3983}
3984
3985/// Modifier to set the size and alignment constraints of a view.
3986/// This determines the proposal size passed to the child and how the child is aligned
3987/// within the layout rect allocated to the frame.
3988#[derive(Debug, Clone, Copy, PartialEq)]
3989pub struct FrameModifier {
3990    /// Exact width to assign to the child view.
3991    pub width: Option<f32>,
3992    /// Exact height to assign to the child view.
3993    pub height: Option<f32>,
3994    /// Minimum width constraint for the view.
3995    pub min_width: Option<f32>,
3996    /// Maximum width constraint for the view.
3997    pub max_width: Option<f32>,
3998    /// Minimum height constraint for the view.
3999    pub min_height: Option<f32>,
4000    /// Maximum height constraint for the view.
4001    pub max_height: Option<f32>,
4002    /// The alignment strategy for positioning the child view within the frame.
4003    pub alignment: Alignment,
4004}
4005
4006impl Default for FrameModifier {
4007    /// Returns the default frame configuration which has no constraints and center alignment.
4008    fn default() -> Self {
4009        Self::new()
4010    }
4011}
4012
4013impl FrameModifier {
4014    /// Creates a new FrameModifier with all dimensions unspecified and center alignment.
4015    pub fn new() -> Self {
4016        Self {
4017            width: None,
4018            height: None,
4019            min_width: None,
4020            max_width: None,
4021            min_height: None,
4022            max_height: None,
4023            alignment: Alignment::Center,
4024        }
4025    }
4026
4027    /// Sets the fixed width of the frame.
4028    pub fn width(mut self, width: f32) -> Self {
4029        self.width = Some(width);
4030        self
4031    }
4032
4033    /// Sets the fixed height of the frame.
4034    pub fn height(mut self, height: f32) -> Self {
4035        self.height = Some(height);
4036        self
4037    }
4038
4039    /// Sets both the fixed width and height of the frame.
4040    pub fn size(mut self, width: f32, height: f32) -> Self {
4041        self.width = Some(width);
4042        self.height = Some(height);
4043        self
4044    }
4045
4046    /// Sets the minimum width constraint.
4047    pub fn min_width(mut self, min_width: f32) -> Self {
4048        self.min_width = Some(min_width);
4049        self
4050    }
4051
4052    /// Sets the maximum width constraint.
4053    pub fn max_width(mut self, max_width: f32) -> Self {
4054        self.max_width = Some(max_width);
4055        self
4056    }
4057
4058    /// Sets the minimum height constraint.
4059    pub fn min_height(mut self, min_height: f32) -> Self {
4060        self.min_height = Some(min_height);
4061        self
4062    }
4063
4064    /// Sets the maximum height constraint.
4065    pub fn max_height(mut self, max_height: f32) -> Self {
4066        self.max_height = Some(max_height);
4067        self
4068    }
4069
4070    /// Sets the alignment strategy for the child within the frame's layout bounds.
4071    pub fn alignment(mut self, alignment: Alignment) -> Self {
4072        self.alignment = alignment;
4073        self
4074    }
4075}
4076
4077impl ViewModifier for FrameModifier {
4078    /// Wraps the child view in a ModifiedView using this frame modifier.
4079    fn modify<V: View>(self, content: V) -> impl View {
4080        ModifiedView::new(content, self)
4081    }
4082
4083    /// Transforms the layout size proposal offered to the child to comply with frame constraints.
4084    fn transform_proposal(&self, proposal: SizeProposal) -> SizeProposal {
4085        let w = if let Some(width) = self.width {
4086            Some(width)
4087        } else {
4088            proposal.width.map(|pw| {
4089                pw.clamp(
4090                    self.min_width.unwrap_or(0.0),
4091                    self.max_width.unwrap_or(f32::INFINITY),
4092                )
4093            })
4094        };
4095        let h = if let Some(height) = self.height {
4096            Some(height)
4097        } else {
4098            proposal.height.map(|ph| {
4099                ph.clamp(
4100                    self.min_height.unwrap_or(0.0),
4101                    self.max_height.unwrap_or(f32::INFINITY),
4102                )
4103            })
4104        };
4105        SizeProposal {
4106            width: w,
4107            height: h,
4108        }
4109    }
4110
4111    /// Constraints and transforms the child's resulting size to fit the frame's bounds.
4112    fn transform_size(&self, child_size: Size) -> Size {
4113        let w = if let Some(width) = self.width {
4114            width
4115        } else {
4116            child_size.width.clamp(
4117                self.min_width.unwrap_or(0.0),
4118                self.max_width.unwrap_or(f32::INFINITY),
4119            )
4120        };
4121        let h = if let Some(height) = self.height {
4122            height
4123        } else {
4124            child_size.height.clamp(
4125                self.min_height.unwrap_or(0.0),
4126                self.max_height.unwrap_or(f32::INFINITY),
4127            )
4128        };
4129        Size {
4130            width: w,
4131            height: h,
4132        }
4133    }
4134
4135    /// Renders the frame's child view aligned within the layout rect.
4136    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
4137        self.render(renderer, rect);
4138        let child_proposal =
4139            self.transform_proposal(SizeProposal::new(Some(rect.width), Some(rect.height)));
4140        let child_size = view.intrinsic_size(renderer, child_proposal);
4141
4142        let mut child_x = rect.x;
4143        let mut child_y = rect.y;
4144
4145        match self.alignment {
4146            Alignment::Leading => {
4147                child_y = rect.y + (rect.height - child_size.height) / 2.0;
4148            }
4149            Alignment::Trailing => {
4150                child_x = rect.x + rect.width - child_size.width;
4151                child_y = rect.y + (rect.height - child_size.height) / 2.0;
4152            }
4153            Alignment::Top => {
4154                child_x = rect.x + (rect.width - child_size.width) / 2.0;
4155            }
4156            Alignment::Bottom => {
4157                child_x = rect.x + (rect.width - child_size.width) / 2.0;
4158                child_y = rect.y + rect.height - child_size.height;
4159            }
4160            Alignment::Center => {
4161                child_x = rect.x + (rect.width - child_size.width) / 2.0;
4162                child_y = rect.y + (rect.height - child_size.height) / 2.0;
4163            }
4164        }
4165
4166        let child_rect = Rect {
4167            x: child_x,
4168            y: child_y,
4169            width: child_size.width,
4170            height: child_size.height,
4171        };
4172
4173        view.render(renderer, child_rect);
4174        self.post_render(renderer, rect);
4175    }
4176}
4177
4178/// Modifier to set the flex weight of a view
4179#[derive(Debug, Clone, Copy, PartialEq)]
4180pub struct FlexModifier {
4181    pub weight: f32,
4182}
4183
4184impl ViewModifier for FlexModifier {
4185    fn modify<V: View>(self, content: V) -> impl View {
4186        ModifiedView::new(content, self)
4187    }
4188
4189    fn child_flex_weight<V: View>(&self, _view: &V) -> f32 {
4190        self.weight
4191    }
4192}
4193
4194/// Modifier that specifies the column and row placement of a view inside a Grid layout.
4195#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4196pub struct GridPlacementModifier {
4197    /// The grid placement settings containing column/row indexes and spans.
4198    pub placement: GridPlacement,
4199}
4200
4201impl ViewModifier for GridPlacementModifier {
4202    /// Wraps the child view in a ModifiedView using this modifier.
4203    fn modify<V: View>(self, content: V) -> impl View {
4204        ModifiedView::new(content, self)
4205    }
4206
4207    /// Exposes the grid placement metadata to parent layout engines.
4208    fn get_grid_placement(&self) -> Option<GridPlacement> {
4209        Some(self.placement)
4210    }
4211}
4212
4213/// Modifier to render a popover, tooltip, or menu view overlaying an anchored view.
4214/// It supports alignment positioning and outside-click dismissal.
4215#[derive(Clone)]
4216pub struct OverlayModifier {
4217    /// The overlay content view.
4218    pub overlay: AnyView,
4219    /// Where the overlay is aligned relative to the anchored view.
4220    pub alignment: Alignment,
4221    /// Additional offset in logical pixels.
4222    pub offset: [f32; 2],
4223    /// Optional dismissal callback triggered by click-outside events.
4224    pub on_dismiss: Option<Arc<dyn Fn() + Send + Sync>>,
4225}
4226
4227impl ViewModifier for OverlayModifier {
4228    /// Wraps the child view in a ModifiedView using this overlay modifier.
4229    fn modify<V: View>(self, content: V) -> impl View {
4230        ModifiedView::new(content, self)
4231    }
4232
4233    /// Renders the overlay content positioned above the child view.
4234    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
4235        // 1. Render primary anchored view
4236        view.render(renderer, rect);
4237
4238        // 2. Measure overlay content
4239        let overlay_size = self
4240            .overlay
4241            .intrinsic_size(renderer, SizeProposal::unspecified());
4242
4243        // 3. Align overlay rect relative to anchored rect
4244        let mut overlay_x;
4245        let mut overlay_y;
4246
4247        match self.alignment {
4248            Alignment::Leading => {
4249                overlay_x = rect.x - overlay_size.width;
4250                overlay_y = rect.y + (rect.height - overlay_size.height) / 2.0;
4251            }
4252            Alignment::Trailing => {
4253                overlay_x = rect.x + rect.width;
4254                overlay_y = rect.y + (rect.height - overlay_size.height) / 2.0;
4255            }
4256            Alignment::Top => {
4257                overlay_x = rect.x + (rect.width - overlay_size.width) / 2.0;
4258                overlay_y = rect.y - overlay_size.height;
4259            }
4260            Alignment::Bottom => {
4261                overlay_x = rect.x + (rect.width - overlay_size.width) / 2.0;
4262                overlay_y = rect.y + rect.height;
4263            }
4264            Alignment::Center => {
4265                overlay_x = rect.x + (rect.width - overlay_size.width) / 2.0;
4266                overlay_y = rect.y + (rect.height - overlay_size.height) / 2.0;
4267            }
4268        }
4269
4270        overlay_x += self.offset[0];
4271        overlay_y += self.offset[1];
4272
4273        let overlay_rect = Rect {
4274            x: overlay_x,
4275            y: overlay_y,
4276            width: overlay_size.width,
4277            height: overlay_size.height,
4278        };
4279
4280        // 4. Handle click-outside dismissal
4281        if let Some(on_dismiss) = &self.on_dismiss {
4282            let dismiss = on_dismiss.clone();
4283            renderer.register_handler(
4284                "pointerdown",
4285                Arc::new(move |event| {
4286                    if let Event::PointerDown { x, y, .. } = event {
4287                        let click_inside = x >= overlay_rect.x
4288                            && x <= overlay_rect.x + overlay_rect.width
4289                            && y >= overlay_rect.y
4290                            && y <= overlay_rect.y + overlay_rect.height;
4291                        if !click_inside {
4292                            dismiss();
4293                        }
4294                    }
4295                }),
4296            );
4297        }
4298
4299        // 5. Render overlay view
4300        self.overlay.render(renderer, overlay_rect);
4301    }
4302}
4303
4304/// Modifier to offset a view
4305#[derive(Debug, Clone, Copy, PartialEq)]
4306pub struct OffsetModifier {
4307    pub x: f32,
4308    pub y: f32,
4309}
4310
4311impl OffsetModifier {
4312    pub fn new(x: f32, y: f32) -> Self {
4313        Self { x, y }
4314    }
4315}
4316
4317impl ViewModifier for OffsetModifier {
4318    fn modify<V: View>(self, content: V) -> impl View {
4319        ModifiedView::new(content, self)
4320    }
4321}
4322
4323/// Modifier to set the z-index of a view
4324#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4325pub struct ZIndexModifier {
4326    pub z_index: i32,
4327}
4328
4329impl ZIndexModifier {
4330    pub fn new(z_index: i32) -> Self {
4331        Self { z_index }
4332    }
4333}
4334
4335impl ViewModifier for ZIndexModifier {
4336    fn modify<V: View>(self, content: V) -> impl View {
4337        ModifiedView::new(content, self)
4338    }
4339}
4340
4341/// Layout constraints for views
4342#[derive(Debug, Clone, Copy, PartialEq, Default)]
4343pub struct LayoutConstraints {
4344    pub min_width: Option<f32>,
4345    pub max_width: Option<f32>,
4346    pub min_height: Option<f32>,
4347    pub max_height: Option<f32>,
4348}
4349
4350/// Modifier to set layout constraints
4351#[derive(Debug, Clone, Copy, PartialEq)]
4352pub struct LayoutModifier {
4353    pub constraints: LayoutConstraints,
4354}
4355
4356impl LayoutModifier {
4357    pub fn new(constraints: LayoutConstraints) -> Self {
4358        Self { constraints }
4359    }
4360}
4361
4362impl ViewModifier for LayoutModifier {
4363    fn modify<V: View>(self, content: V) -> impl View {
4364        ModifiedView::new(content, self)
4365    }
4366}
4367
4368/// Modifier to handle platform safe areas
4369#[derive(Debug, Clone, Copy, PartialEq)]
4370pub struct SafeAreaModifier {
4371    pub ignores: bool,
4372}
4373
4374impl ViewModifier for SafeAreaModifier {
4375    fn modify<V: View>(self, content: V) -> impl View {
4376        ModifiedView::new(content, self)
4377    }
4378}
4379
4380/// Modifier to add elevation (shadow) to a view
4381#[derive(Debug, Clone, Copy, PartialEq)]
4382pub struct ElevationModifier {
4383    pub level: f32,
4384}
4385
4386impl ViewModifier for ElevationModifier {
4387    fn modify<V: View>(self, content: V) -> impl View {
4388        ModifiedView::new(content, self)
4389    }
4390
4391    fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
4392        if self.level > 0.0 {
4393            let radius = self.level * 2.0;
4394            let offset_y = self.level * 0.5;
4395            let shadow_color = [0.0, 0.0, 0.0, 0.3];
4396            renderer.push_shadow(radius, shadow_color, [0.0, offset_y]);
4397            view.render(renderer, rect);
4398            renderer.pop_shadow();
4399        } else {
4400            view.render(renderer, rect);
4401        }
4402    }
4403}
4404
4405// Layout subsystem
4406pub mod layout {
4407    use super::*;
4408
4409    /// Key used to identify a cached layout entry.
4410    /// Combines a view hash with a generation counter for cache invalidation.
4411    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4412    pub struct LayoutKey {
4413        pub view_hash: u64,
4414        pub generation: u64,
4415    }
4416
4417    // Layout pass scratch space
4418    pub struct LayoutCache {
4419        pub safe_area: SafeArea,
4420        size_cache: HashMap<(u64, u32, u32), Size>, // (ViewHash, ProposalW, ProposalH)
4421        /// Monotonically increasing generation counter for cache invalidation.
4422        /// When a view tree changes, bumping the generation causes stale entries
4423        /// to be treated as invalid without eagerly clearing the entire cache.
4424        generation: u64,
4425    }
4426
4427    impl Default for LayoutCache {
4428        fn default() -> Self {
4429            Self::new()
4430        }
4431    }
4432
4433    impl LayoutCache {
4434        pub fn new() -> Self {
4435            Self {
4436                safe_area: SafeArea::default(),
4437                size_cache: HashMap::new(),
4438                generation: 0,
4439            }
4440        }
4441
4442        /// Returns the current generation counter.
4443        pub fn generation(&self) -> u64 {
4444            self.generation
4445        }
4446
4447        /// Bump the generation counter, logically invalidating all cached entries
4448        /// without eagerly clearing them. Subsequent lookups with the old generation
4449        /// will miss until re-populated.
4450        pub fn invalidate(&mut self) {
4451            self.generation = self.generation.wrapping_add(1);
4452        }
4453
4454        /// Check whether a cached entry for the given key is still valid
4455        /// against the current generation.
4456        pub fn is_valid(&self, key: LayoutKey, current_gen: u64) -> bool {
4457            key.generation == current_gen && key.generation == self.generation
4458        }
4459
4460        pub fn clear(&mut self) {
4461            self.safe_area = SafeArea::default();
4462            self.size_cache.clear();
4463        }
4464
4465        pub fn get_size(&self, view_hash: u64, proposal: SizeProposal) -> Option<Size> {
4466            let pw = (proposal.width.unwrap_or(-1.0) * 100.0) as u32;
4467            let ph = (proposal.height.unwrap_or(-1.0) * 100.0) as u32;
4468            self.size_cache.get(&(view_hash, pw, ph)).copied()
4469        }
4470
4471        pub fn set_size(&mut self, view_hash: u64, proposal: SizeProposal, size: Size) {
4472            let pw = (proposal.width.unwrap_or(-1.0) * 100.0) as u32;
4473            let ph = (proposal.height.unwrap_or(-1.0) * 100.0) as u32;
4474            self.size_cache.insert((view_hash, pw, ph), size);
4475        }
4476
4477        /// Remove all cached size entries for a specific view hash.
4478        pub fn invalidate_view(&mut self, view_hash: u64) {
4479            self.size_cache.retain(|&(hash, _, _), _| hash != view_hash);
4480        }
4481    }
4482
4483    /// Proposed size from parent view
4484    #[derive(Debug, Clone, Copy, PartialEq)]
4485    pub struct SizeProposal {
4486        pub width: Option<f32>,
4487        pub height: Option<f32>,
4488    }
4489
4490    impl SizeProposal {
4491        pub fn unspecified() -> Self {
4492            Self {
4493                width: None,
4494                height: None,
4495            }
4496        }
4497
4498        pub fn width(width: f32) -> Self {
4499            Self {
4500                width: Some(width),
4501                height: None,
4502            }
4503        }
4504
4505        pub fn height(height: f32) -> Self {
4506            Self {
4507                width: None,
4508                height: Some(height),
4509            }
4510        }
4511
4512        pub fn tight(width: f32, height: f32) -> Self {
4513            Self {
4514                width: Some(width),
4515                height: Some(height),
4516            }
4517        }
4518
4519        pub fn new(width: Option<f32>, height: Option<f32>) -> Self {
4520            Self { width, height }
4521        }
4522    }
4523
4524    /// A view that can participate in layout
4525    pub trait LayoutView: Send {
4526        /// Propose a size for this view given the available space
4527        fn size_that_fits(
4528            &self,
4529            proposal: SizeProposal,
4530            subviews: &[&dyn LayoutView],
4531            cache: &mut LayoutCache,
4532        ) -> Size;
4533
4534        /// Place subviews within the given bounds
4535        fn place_subviews(
4536            &self,
4537            bounds: Rect,
4538            subviews: &mut [&mut dyn LayoutView],
4539            cache: &mut LayoutCache,
4540        );
4541
4542        /// Returns the flex weight of this view (default is 0.0, which means fixed/intrinsic)
4543        fn flex_weight(&self) -> f32 {
4544            0.0
4545        }
4546
4547        /// Return a debug representation of this layout subtree.
4548        /// The `indent` parameter controls the indentation level for nested display.
4549        fn debug_layout(&self, indent: usize) -> String {
4550            let prefix = " ".repeat(indent);
4551            format!("{}LayoutView", prefix)
4552        }
4553    }
4554    /// Edge insets for padding, margins, and safe areas
4555    #[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
4556    pub struct EdgeInsets {
4557        pub top: f32,
4558        pub leading: f32,
4559        pub bottom: f32,
4560        pub trailing: f32,
4561    }
4562
4563    impl EdgeInsets {
4564        pub fn new(top: f32, leading: f32, bottom: f32, trailing: f32) -> Self {
4565            Self {
4566                top,
4567                leading,
4568                bottom,
4569                trailing,
4570            }
4571        }
4572
4573        pub fn all(value: f32) -> Self {
4574            Self {
4575                top: value,
4576                leading: value,
4577                bottom: value,
4578                trailing: value,
4579            }
4580        }
4581    }
4582
4583    /// SafeArea constraints provided by the platform
4584    #[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
4585    pub struct SafeArea {
4586        pub insets: EdgeInsets,
4587    }
4588
4589    /// SDF Shape definitions for Vili Interaction Paradigm hit-testing.
4590    #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
4591    pub enum SdfShape {
4592        Rect(Rect),
4593        RoundedRect { rect: Rect, radius: f32 },
4594        Circle { center: [f32; 2], radius: f32 },
4595    }
4596
4597    /// Rectangle in logical pixels
4598    #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
4599    pub struct Rect {
4600        pub x: f32,
4601        pub y: f32,
4602        pub width: f32,
4603        pub height: f32,
4604    }
4605
4606    impl Rect {
4607        pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
4608            Self {
4609                x,
4610                y,
4611                width,
4612                height,
4613            }
4614        }
4615
4616        pub fn inset(&self, amount: f32) -> Self {
4617            Self {
4618                x: self.x + amount,
4619                y: self.y + amount,
4620                width: (self.width - amount * 2.0).max(0.0),
4621                height: (self.height - amount * 2.0).max(0.0),
4622            }
4623        }
4624
4625        pub fn offset(&self, dx: f32, dy: f32) -> Self {
4626            Self {
4627                x: self.x + dx,
4628                y: self.y + dy,
4629                ..*self
4630            }
4631        }
4632
4633        pub fn zero() -> Self {
4634            Self {
4635                x: 0.0,
4636                y: 0.0,
4637                width: 0.0,
4638                height: 0.0,
4639            }
4640        }
4641
4642        pub fn contains(&self, x: f32, y: f32) -> bool {
4643            x >= self.x && x <= self.x + self.width && y >= self.y && y <= self.y + self.height
4644        }
4645
4646        pub fn size(&self) -> Size {
4647            Size {
4648                width: self.width,
4649                height: self.height,
4650            }
4651        }
4652
4653        /// Split the rect horizontally into N equal pieces
4654        pub fn split_horizontal(&self, n: usize) -> Vec<Rect> {
4655            if n == 0 {
4656                return vec![];
4657            }
4658            let item_width = self.width / n as f32;
4659            (0..n)
4660                .map(|i| Rect {
4661                    x: self.x + i as f32 * item_width,
4662                    y: self.y,
4663                    width: item_width,
4664                    height: self.height,
4665                })
4666                .collect()
4667        }
4668
4669        /// Split the rect vertically into N equal pieces
4670        pub fn split_vertical(&self, n: usize) -> Vec<Rect> {
4671            if n == 0 {
4672                return vec![];
4673            }
4674            let item_height = self.height / n as f32;
4675            (0..n)
4676                .map(|i| Rect {
4677                    x: self.x,
4678                    y: self.y + i as f32 * item_height,
4679                    width: self.width,
4680                    height: item_height,
4681                })
4682                .collect()
4683        }
4684    }
4685}
4686
4687// Re-export layout items for convenience
4688pub use layout::{LayoutCache, LayoutKey, LayoutView, Rect, SizeProposal};
4689// Size and FrameRenderer are pub items in this module; no re-export alias needed.
4690
4691pub mod agents;
4692pub mod animation;
4693pub mod gpu;
4694pub mod material;
4695pub mod runtime;
4696pub mod scene_graph;
4697pub mod sdf_shadow;
4698
4699pub use material::DrawMaterial;
4700pub use scene_graph::{NodeId, bifrost_registry};
4701
4702// Duplicate AssetState removed - original definition at line 67
4703
4704/// AssetManager defines the interface for loading and caching external resources.
4705pub trait AssetManager: Send + Sync {
4706    /// Request an image asset. Returns the current state (Loading, Ready, or Error).
4707    fn load_image(&self, url: &str) -> AssetState<Arc<Vec<u8>>>;
4708
4709    /// Pre-load an image into the cache.
4710    fn preload_image(&self, url: &str);
4711}
4712
4713/// The phase of a touch or gesture event in its lifecycle.
4714#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
4715pub enum TouchPhase {
4716    /// The touch/gesture has just begun.
4717    Began,
4718    /// The touch/gesture is moving.
4719    Moved,
4720    /// The touch/gesture has ended normally.
4721    Ended,
4722    /// The touch/gesture was cancelled (e.g., by the system).
4723    Cancelled,
4724}
4725
4726/// User input event types
4727#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
4728pub enum Event {
4729    PointerDown {
4730        x: f32,
4731        y: f32,
4732        button: u32,
4733        proximity_field: f32,
4734    },
4735    PointerUp {
4736        x: f32,
4737        y: f32,
4738        button: u32,
4739    },
4740    PointerMove {
4741        x: f32,
4742        y: f32,
4743        proximity_field: f32,
4744    },
4745    PointerClick {
4746        x: f32,
4747        y: f32,
4748        button: u32,
4749    },
4750    PointerEnter,
4751    PointerLeave,
4752    /// Mouse wheel / trackpad scroll event.
4753    /// `delta_x` is the horizontal scroll amount, `delta_y` is the vertical scroll amount (positive = scroll down).
4754    PointerWheel {
4755        x: f32,
4756        y: f32,
4757        delta_x: f32,
4758        delta_y: f32,
4759    },
4760    /// Double-click event (rapid successive clicks).
4761    PointerDoubleClick {
4762        x: f32,
4763        y: f32,
4764        button: u32,
4765    },
4766    /// Drag-and-drop: drag started (pointer moved while button held past threshold).
4767    DragStart {
4768        x: f32,
4769        y: f32,
4770        button: u32,
4771    },
4772    /// Drag-and-drop: drag in progress.
4773    DragMove {
4774        x: f32,
4775        y: f32,
4776    },
4777    /// Drag-and-drop: drag ended (pointer released).
4778    DragEnd {
4779        x: f32,
4780        y: f32,
4781    },
4782    KeyDown {
4783        key: String,
4784    },
4785    KeyUp {
4786        key: String,
4787    },
4788    /// Focus gained by a node.
4789    FocusIn,
4790    /// Focus lost by a node.
4791    FocusOut,
4792    /// Clipboard copy event.
4793    Copy,
4794    /// Clipboard cut event.
4795    Cut,
4796    /// Clipboard paste event with the pasted text content.
4797    Paste(String),
4798    /// Input Method Editor event (e.g. CJK character composition)
4799    Ime(String),
4800    /// Touch began at the given position.
4801    TouchStart {
4802        x: f32,
4803        y: f32,
4804        touch_id: u64,
4805    },
4806    /// Touch moved to a new position.
4807    TouchMove {
4808        x: f32,
4809        y: f32,
4810        touch_id: u64,
4811    },
4812    /// Touch ended at the given position.
4813    TouchEnd {
4814        x: f32,
4815        y: f32,
4816        touch_id: u64,
4817    },
4818    /// Touch cancelled.
4819    TouchCancel {
4820        touch_id: u64,
4821    },
4822    /// Multi-touch pinch gesture.
4823    /// `center` is the gesture anchor point in device-independent pixels.
4824    /// `scale` is the relative pinch scale (>1 = expand, <1 = contract).
4825    /// `velocity` is the instantaneous velocity of the pinch.
4826    /// `phase` indicates the current phase of the gesture lifecycle.
4827    GesturePinch {
4828        center: [f32; 2],
4829        scale: f32,
4830        velocity: f32,
4831        phase: TouchPhase,
4832    },
4833    /// Multi-touch swipe/pan gesture.
4834    /// `direction` is the normalized direction vector [dx, dy].
4835    /// `velocity` is the instantaneous velocity of the swipe.
4836    /// `phase` indicates the current phase of the gesture lifecycle.
4837    GestureSwipe {
4838        direction: [f32; 2],
4839        velocity: f32,
4840        phase: TouchPhase,
4841    },
4842    /// Drag-and-drop: external file dropped onto window.
4843    FileDrop {
4844        path: String,
4845    },
4846}
4847
4848impl Event {
4849    /// Returns the canonical string name of the event for lookup in handler maps.
4850    pub fn name(&self) -> &'static str {
4851        match self {
4852            Self::PointerDown { .. } => "pointerdown",
4853            Self::PointerUp { .. } => "pointerup",
4854            Self::PointerMove { .. } => "pointermove",
4855            Self::PointerClick { .. } => "pointerclick",
4856            Self::PointerEnter => "pointerenter",
4857            Self::PointerLeave => "pointerleave",
4858            Self::PointerWheel { .. } => "pointerwheel",
4859            Self::PointerDoubleClick { .. } => "pointerdoubleclick",
4860            Self::DragStart { .. } => "dragstart",
4861            Self::DragMove { .. } => "dragmove",
4862            Self::DragEnd { .. } => "dragend",
4863            Self::KeyDown { .. } => "keydown",
4864            Self::KeyUp { .. } => "keyup",
4865            Self::FocusIn => "focusin",
4866            Self::FocusOut => "focusout",
4867            Self::Copy => "copy",
4868            Self::Cut => "cut",
4869            Self::Paste(_) => "paste",
4870            Self::Ime(_) => "ime",
4871            Self::TouchStart { .. } => "touchstart",
4872            Self::TouchMove { .. } => "touchmove",
4873            Self::TouchEnd { .. } => "touchend",
4874            Self::TouchCancel { .. } => "touchcancel",
4875            Self::GesturePinch { .. } => "gesturepinch",
4876            Self::GestureSwipe { .. } => "gestureswipe",
4877            Self::FileDrop { .. } => "filedrop",
4878        }
4879    }
4880}
4881
4882/// Response from an event handler
4883#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4884pub enum EventResponse {
4885    Handled,
4886    Ignored,
4887}
4888
4889/// A basic implementation of AssetManager that can be overridden by platform backends.
4890pub struct DefaultAssetManager {
4891    cache: AssetCache,
4892}
4893type AssetCache = Arc<arc_swap::ArcSwap<HashMap<String, AssetState<Arc<Vec<u8>>>>>>;
4894
4895impl Default for DefaultAssetManager {
4896    fn default() -> Self {
4897        Self::new()
4898    }
4899}
4900
4901impl DefaultAssetManager {
4902    pub fn new() -> Self {
4903        Self {
4904            cache: Arc::new(arc_swap::ArcSwap::from_pointee(HashMap::new())),
4905        }
4906    }
4907}
4908
4909impl AssetManager for DefaultAssetManager {
4910    fn load_image(&self, url: &str) -> AssetState<Arc<Vec<u8>>> {
4911        if let Some(state) = self.cache.load().get(url) {
4912            return state.clone();
4913        }
4914
4915        self.cache.rcu(|map| {
4916            let mut m = (**map).clone();
4917            m.entry(url.to_string()).or_insert(AssetState::Loading);
4918            m
4919        });
4920        AssetState::Loading
4921    }
4922
4923    fn preload_image(&self, _url: &str) {}
4924}
4925
4926use std::future::Future;
4927
4928/// Suspense wrapper for asynchronous state management.
4929/// Integrates with State<T> to provide loading/error/ready states for async operations.
4930pub struct Suspense<T: Clone + Send + Sync + 'static> {
4931    inner: State<AssetState<T>>,
4932}
4933
4934impl<T: Clone + Send + Sync + 'static> Default for Suspense<T> {
4935    fn default() -> Self {
4936        Self::new()
4937    }
4938}
4939
4940impl<T: Clone + Send + Sync + 'static> Suspense<T> {
4941    pub fn new() -> Self {
4942        Self {
4943            inner: State::new(AssetState::Loading),
4944        }
4945    }
4946
4947    pub fn new_async<F>(future: F) -> Self
4948    where
4949        F: Future<Output = Result<T, String>> + Send + 'static,
4950    {
4951        let suspense = Self::new();
4952        let suspense_clone = suspense.clone();
4953
4954        #[cfg(not(target_arch = "wasm32"))]
4955        {
4956            // Try to use an existing tokio runtime, or fallback to a dedicated thread
4957            if let Ok(handle) = tokio::runtime::Handle::try_current() {
4958                handle.spawn(async move {
4959                    let result = future.await;
4960                    match result {
4961                        Ok(val) => suspense_clone.inner.set(AssetState::Ready(val)),
4962                        Err(err) => suspense_clone.inner.set(AssetState::Error(err)),
4963                    }
4964                });
4965            } else {
4966                std::thread::spawn(move || {
4967                    let rt = tokio::runtime::Builder::new_current_thread()
4968                        .enable_all()
4969                        .build()
4970                        .unwrap();
4971                    rt.block_on(async {
4972                        let result = future.await;
4973                        match result {
4974                            Ok(val) => suspense_clone.inner.set(AssetState::Ready(val)),
4975                            Err(err) => suspense_clone.inner.set(AssetState::Error(err)),
4976                        }
4977                    });
4978                });
4979            }
4980        }
4981        #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
4982        {
4983            wasm_bindgen_futures::spawn_local(async move {
4984                let result = future.await;
4985                match result {
4986                    Ok(val) => suspense_clone.inner.set(AssetState::Ready(val)),
4987                    Err(err) => suspense_clone.inner.set(AssetState::Error(err)),
4988                }
4989            });
4990        }
4991
4992        suspense
4993    }
4994
4995    pub fn ready(value: T) -> Self {
4996        Self {
4997            inner: State::new(AssetState::Ready(value)),
4998        }
4999    }
5000
5001    pub fn error(message: impl Into<String>) -> Self {
5002        Self {
5003            inner: State::new(AssetState::Error(message.into())),
5004        }
5005    }
5006
5007    pub fn get(&self) -> AssetState<T> {
5008        self.inner.get()
5009    }
5010
5011    pub fn get_ref(&self) -> AssetState<T> {
5012        self.inner.get()
5013    }
5014
5015    pub fn is_loading(&self) -> bool {
5016        matches!(self.get(), AssetState::Loading)
5017    }
5018
5019    pub fn is_ready(&self) -> bool {
5020        matches!(self.get(), AssetState::Ready(_))
5021    }
5022
5023    pub fn is_error(&self) -> bool {
5024        matches!(self.get(), AssetState::Error(_))
5025    }
5026
5027    pub fn ready_value(&self) -> Option<T> {
5028        match self.get() {
5029            AssetState::Ready(value) => Some(value),
5030            _ => None,
5031        }
5032    }
5033
5034    pub fn error_message(&self) -> Option<String> {
5035        match self.get() {
5036            AssetState::Error(message) => Some(message),
5037            _ => None,
5038        }
5039    }
5040
5041    pub fn subscribe<F: Fn(&AssetState<T>) + Send + Sync + 'static>(&self, callback: F) {
5042        self.inner.subscribe(callback)
5043    }
5044
5045    pub fn inner_state(&self) -> &State<AssetState<T>> {
5046        &self.inner
5047    }
5048}
5049
5050impl<T: Clone + Send + Sync + 'static> Clone for Suspense<T> {
5051    fn clone(&self) -> Self {
5052        Self {
5053            inner: self.inner.clone(),
5054        }
5055    }
5056}
5057
5058impl<T: Clone + Send + Sync + 'static> From<T> for Suspense<T> {
5059    fn from(value: T) -> Self {
5060        Self::ready(value)
5061    }
5062}
5063
5064impl<T: Clone + Send + Sync + 'static> From<Result<T, String>> for Suspense<T> {
5065    fn from(result: Result<T, String>) -> Self {
5066        match result {
5067            Ok(value) => Self::ready(value),
5068            Err(error) => Self::error(error),
5069        }
5070    }
5071}
5072
5073#[cfg(test)]
5074mod phase1_test;
5075
5076/// Berserker mode states for the rendering pipeline.
5077#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5078pub enum BerserkerMode {
5079    Normal,
5080    Rage,    // Red tint, slight shake
5081    Frenzy,  // Heavy red tint, motion blur, aggressive shake
5082    GodMode, // Golden aura, lightning arcs
5083}
5084
5085/// Seer trait for AI-assisted UI components.
5086/// Allows components to receive "prophecies" (predictions) from an AI backend.
5087pub trait Seer: Send + Sync {
5088    /// Provide a prediction for the next user action or content.
5089    fn predict(&self, context: &str) -> String;
5090    /// Stream real-time "whispers" (transcriptions/intent).
5091    fn whispers(&self) -> Vec<String>;
5092}
5093
5094#[cfg(test)]
5095mod vili_tests {
5096    use super::*;
5097
5098    struct DummyRenderer;
5099    impl ElapsedTime for DummyRenderer {
5100        fn elapsed_time(&self) -> f32 {
5101            0.0
5102        }
5103        fn delta_time(&self) -> f32 {
5104            0.0
5105        }
5106    }
5107    impl Renderer for DummyRenderer {
5108        fn fill_rect(&mut self, _r: Rect, _c: [f32; 4]) {}
5109        fn fill_rounded_rect(&mut self, _r: Rect, _rad: f32, _c: [f32; 4]) {}
5110        fn fill_ellipse(&mut self, _r: Rect, _c: [f32; 4]) {}
5111        fn stroke_rect(&mut self, _r: Rect, _c: [f32; 4], _w: f32) {}
5112        fn stroke_rounded_rect(&mut self, _r: Rect, _rad: f32, _c: [f32; 4], _w: f32) {}
5113        fn stroke_ellipse(&mut self, _r: Rect, _c: [f32; 4], _w: f32) {}
5114        fn draw_line(&mut self, _x1: f32, _y1: f32, _x2: f32, _y2: f32, _c: [f32; 4], _w: f32) {}
5115        fn draw_text(&mut self, _t: &str, _x: f32, _y: f32, _s: f32, _c: [f32; 4]) {}
5116        fn measure_text(&mut self, _t: &str, _s: f32) -> (f32, f32) {
5117            (0.0, 0.0)
5118        }
5119        fn memoize(&mut self, _id: u64, _hash: u64, _r: &dyn Fn(&mut dyn Renderer)) {}
5120    }
5121
5122    #[test]
5123    fn test_magnetic_warp() {
5124        let renderer = DummyRenderer;
5125        let anchor = Rect {
5126            x: 100.0,
5127            y: 100.0,
5128            width: 50.0,
5129            height: 50.0,
5130        };
5131        // Pointer is near the anchor (distance < 120)
5132        let pointer = [125.0, 50.0];
5133        // distance from center (125, 125) is 75.
5134        // force = (1.0 - 75/120) * strength
5135        let warp = renderer.magnetic_warp(pointer, anchor, 1.0);
5136        // It should pull closer to (125, 125), so Y should be > 50
5137        assert!(warp[1] > 50.0);
5138
5139        // Out of range pointer should remain unchanged
5140        let far_pointer = [500.0, 500.0];
5141        let far_warp = renderer.magnetic_warp(far_pointer, anchor, 1.0);
5142        assert_eq!(far_pointer, far_warp);
5143    }
5144
5145    #[test]
5146    fn test_mani_glow() {
5147        let renderer = DummyRenderer;
5148        let bounds = Rect {
5149            x: 0.0,
5150            y: 0.0,
5151            width: 100.0,
5152            height: 100.0,
5153        };
5154        let pointer_inside = [50.0, 50.0];
5155        let glow_max = renderer.mani_glow_intensity(pointer_inside, bounds, 120.0);
5156        assert_eq!(glow_max, 1.0);
5157
5158        let pointer_edge = [50.0, -10.0];
5159        let glow_partial = renderer.mani_glow_intensity(pointer_edge, bounds, 120.0);
5160        assert!(glow_partial > 0.0 && glow_partial < 1.0);
5161    }
5162
5163    #[test]
5164    fn test_fafnir_evolve() {
5165        let renderer = DummyRenderer;
5166        let bounds = Rect {
5167            x: 0.0,
5168            y: 0.0,
5169            width: 100.0,
5170            height: 100.0,
5171        };
5172        let pointer_inside = [50.0, 50.0];
5173        let scale = renderer.fafnir_evolve(pointer_inside, bounds, 1.2);
5174        assert_eq!(scale, 1.2); // Full scale when hovering center
5175    }
5176
5177    #[test]
5178    fn test_undo_manager_basic() {
5179        let mut manager = UndoManager::new(3, 0.5);
5180        let val = std::sync::Arc::new(std::sync::Mutex::new(0));
5181
5182        let v1 = val.clone();
5183        let v2 = val.clone();
5184        manager.push(
5185            "Add",
5186            move || *v1.lock().unwrap() -= 1,
5187            move || *v2.lock().unwrap() += 1,
5188        );
5189
5190        assert!(manager.can_undo());
5191        assert!(!manager.can_redo());
5192
5193        let undo = manager.undo().unwrap();
5194        undo();
5195        assert_eq!(*val.lock().unwrap(), -1);
5196        assert!(!manager.can_undo());
5197        assert!(manager.can_redo());
5198
5199        let redo = manager.redo().unwrap();
5200        redo();
5201        assert_eq!(*val.lock().unwrap(), 0);
5202    }
5203
5204    #[test]
5205    fn test_undo_manager_depth_limit() {
5206        let mut manager = UndoManager::new(2, 0.5);
5207        manager.push("1", || {}, || {});
5208        manager.push("2", || {}, || {});
5209        manager.push("3", || {}, || {});
5210
5211        assert_eq!(manager.stack.len(), 2);
5212        assert_eq!(manager.position, 2);
5213    }
5214
5215    #[test]
5216    fn test_undo_manager_coalescing() {
5217        let mut manager = UndoManager::new(10, 1.0);
5218        let count = std::sync::Arc::new(std::sync::Mutex::new(0));
5219
5220        let c = count.clone();
5221        manager.push_coalesceable("Type", move || *c.lock().unwrap() -= 1, || {});
5222
5223        let c = count.clone();
5224        manager.push_coalesceable("Type", move || *c.lock().unwrap() -= 1, || {});
5225
5226        assert_eq!(manager.stack.len(), 1);
5227
5228        let undo = manager.undo().unwrap();
5229        undo();
5230        assert_eq!(*count.lock().unwrap(), -2);
5231    }
5232}
5233
5234// =============================================================================
5235// THEME CONTEXT — Thread-local theme access for components
5236// =============================================================================
5237//
5238// Components call `use_theme()` to get the current SemanticColors.
5239// The native renderer sets this via `set_current_theme()` before each frame.
5240// Falls back to dark theme defaults if no theme has been set.
5241//
5242// We store SemanticColors directly (not the full Theme) to avoid depending
5243// on cvkg-themes from cvkg-core. The colors are cloned into thread-local storage.
5244
5245use std::cell::RefCell;
5246
5247thread_local! {
5248    /// Thread-local semantic colors for the current frame.
5249    static THEME_CONTEXT: RefCell<Option<color::SemanticColors>> = const { RefCell::new(None) };
5250}
5251
5252/// Semantic colors extracted from the theme for use by components.
5253/// This is a standalone type defined in cvkg-core so cvkg-components
5254/// can use it without depending on cvkg-themes.
5255///
5256/// Components should access these via `use_theme()` rather than hardcoding RGBA.
5257pub use color::SemanticColors;
5258
5259/// Set the current semantic colors for this thread.
5260/// Called by the native renderer before each frame.
5261pub fn set_current_theme(colors: color::SemanticColors) {
5262    THEME_CONTEXT.with(|cell| {
5263        *cell.borrow_mut() = Some(colors);
5264    });
5265}
5266
5267/// Clear the current theme. Called after each frame.
5268pub fn clear_current_theme() {
5269    THEME_CONTEXT.with(|cell| {
5270        *cell.borrow_mut() = None;
5271    });
5272}
5273
5274/// Access the current semantic colors from within a component's `render()` method.
5275///
5276/// Returns the colors set by the most recent `set_current_theme()` call.
5277/// Falls back to dark theme defaults if no theme has been set.
5278///
5279/// # Example
5280/// ```no_run
5281/// use cvkg_core::{use_theme, Renderer, Rect};
5282///
5283/// fn render_button(renderer: &mut dyn Renderer, rect: Rect) {
5284///     let colors = use_theme();
5285///     renderer.fill_rounded_rect(rect, 8.0, [colors.accent.r, colors.accent.g, colors.accent.b, colors.accent.a]);
5286/// }
5287/// ```
5288pub fn use_theme() -> color::SemanticColors {
5289    THEME_CONTEXT.with(|cell| {
5290        cell.borrow()
5291            .clone()
5292            .unwrap_or_else(color::SemanticColors::dark)
5293    })
5294}
5295
5296// =============================================================================
5297// COLOR MODULE — Standalone semantic colors type
5298// =============================================================================
5299//
5300// This module provides `SemanticColors`, a self-contained color palette that
5301// components can use without depending on `cvkg-themes`. The `use_theme()`
5302// function returns the current `SemanticColors` from thread-local storage.
5303
5304pub mod color {
5305    use super::Color;
5306
5307    /// A complete set of semantic colors for UI components.
5308    ///
5309    /// Each color serves a specific role in the UI. Components should reference
5310    /// these semantic roles rather than hardcoding RGBA values.
5311    ///
5312    /// # Example
5313    /// ```no_run
5314    /// use cvkg_core::{use_theme, Renderer, Rect};
5315    ///
5316    /// fn render_button(renderer: &mut dyn Renderer, rect: Rect) {
5317    ///     let colors = use_theme();
5318    ///     // Use accent color for the button background
5319    ///     renderer.fill_rounded_rect(rect, 8.0,
5320    ///         [colors.accent.r, colors.accent.g, colors.accent.b, colors.accent.a]);
5321    /// }
5322    /// ```
5323    #[derive(Debug, Clone)]
5324    pub struct SemanticColors {
5325        /// Primary brand color — used for key interactive elements.
5326        pub primary: Color,
5327        /// Secondary color — used for less prominent interactive elements.
5328        pub secondary: Color,
5329        /// Accent color — used for highlights, focus rings, CTAs.
5330        pub accent: Color,
5331        /// Page/window background color.
5332        pub background: Color,
5333        /// Surface color — used for cards, panels, sheets.
5334        pub surface: Color,
5335        /// Error color — used for destructive actions, error messages.
5336        pub error: Color,
5337        /// Warning color — used for caution indicators.
5338        pub warning: Color,
5339        /// Success color — used for positive feedback.
5340        pub success: Color,
5341        /// Primary text color.
5342        pub text: Color,
5343        /// Dimmed/disabled text color.
5344        pub text_dim: Color,
5345    }
5346
5347    impl SemanticColors {
5348        /// Dark theme semantic colors (default fallback).
5349        pub fn dark() -> Self {
5350            Self {
5351                primary: Color::new(1.0, 0.84, 0.0, 1.0),      // Viking Gold
5352                secondary: Color::new(1.0, 0.0, 1.0, 1.0),     // Magenta Liquid
5353                accent: Color::new(1.0, 0.0, 0.4, 1.0),        // Crimson Flash
5354                background: Color::new(0.02, 0.02, 0.05, 1.0), // Deep Void
5355                surface: Color::new(0.05, 0.05, 0.07, 1.0),    // Tactical Obsidian
5356                error: Color::new(1.0, 0.2, 0.2, 1.0),         // Red
5357                warning: Color::new(1.0, 0.8, 0.0, 1.0),       // Yellow
5358                success: Color::new(0.0, 1.0, 0.5, 1.0),       // Green
5359                text: Color::new(0.95, 0.95, 1.0, 1.0),        // Near-white
5360                text_dim: Color::new(0.6, 0.6, 0.7, 1.0),      // Gray
5361            }
5362        }
5363
5364        /// Light theme semantic colors.
5365        pub fn light() -> Self {
5366            Self {
5367                primary: Color::new(0.35, 0.30, 0.70, 1.0),
5368                secondary: Color::new(0.30, 0.50, 0.30, 1.0),
5369                accent: Color::new(0.30, 0.35, 0.75, 1.0),
5370                background: Color::new(0.97, 0.97, 0.98, 1.0),
5371                surface: Color::new(0.93, 0.93, 0.95, 1.0),
5372                error: Color::new(0.75, 0.15, 0.15, 1.0),
5373                warning: Color::new(0.80, 0.60, 0.0, 1.0),
5374                success: Color::new(0.15, 0.65, 0.30, 1.0),
5375                text: Color::new(0.08, 0.08, 0.10, 1.0),
5376                text_dim: Color::new(0.40, 0.40, 0.45, 1.0),
5377            }
5378        }
5379
5380        /// Convert the accent color semantic color into interactive state colors.
5381        ///
5382        /// This provides hover/active/focus/disabled variants derived from the
5383        /// accent color, matching the pattern that `cvkg-themes::StateColors` uses.
5384        pub fn accent_states(&self) -> InteractiveColorStates {
5385            InteractiveColorStates::from_color(self.accent)
5386        }
5387
5388        /// Convert the primary color into interactive state colors.
5389        pub fn primary_states(&self) -> InteractiveColorStates {
5390            InteractiveColorStates::from_color(self.primary)
5391        }
5392
5393        /// Convert the error color into interactive state colors.
5394        pub fn error_states(&self) -> InteractiveColorStates {
5395            InteractiveColorStates::from_color(self.error)
5396        }
5397
5398        /// Convert the success color into interactive state colors.
5399        pub fn success_states(&self) -> InteractiveColorStates {
5400            InteractiveColorStates::from_color(self.success)
5401        }
5402    }
5403
5404    /// Interactive state colors derived from a single base color.
5405    ///
5406    /// Provides hover/active/focus/disabled variants for any color,
5407    /// derived via simple lightness adjustments in sRGB space.
5408    #[derive(Debug, Clone)]
5409    pub struct InteractiveColorStates {
5410        pub default: Color,
5411        pub hover: Color,
5412        pub active: Color,
5413        pub focus: Color,
5414        pub disabled: Color,
5415        pub focus_ring: Color,
5416    }
5417
5418    impl InteractiveColorStates {
5419        /// Derive interactive state colors from a base sRGB color.
5420        ///
5421        /// Uses simple lightness adjustments:
5422        /// - Hover: +15% lightness
5423        /// - Active: -15% lightness
5424        /// - Focus: same as default
5425        /// - Disabled: 40% opacity
5426        /// - Focus ring: base color at 70% opacity
5427        pub fn from_color(base: Color) -> Self {
5428            Self {
5429                default: base,
5430                hover: base.lighten(0.15),
5431                active: base.darken(0.15),
5432                focus: base,
5433                disabled: Color::new(base.r, base.g, base.b, base.a * 0.4),
5434                focus_ring: Color::new(base.r, base.g, base.b, base.a * 0.7),
5435            }
5436        }
5437
5438        /// Get the color for a specific interactive state.
5439        pub fn color_for(&self, state: InteractiveState) -> Color {
5440            match state {
5441                InteractiveState::Default => self.default,
5442                InteractiveState::Hover => self.hover,
5443                InteractiveState::Active => self.active,
5444                InteractiveState::Focus => self.focus,
5445                InteractiveState::Disabled => self.disabled,
5446            }
5447        }
5448    }
5449
5450    /// Interactive state for a component.
5451    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5452    pub enum InteractiveState {
5453        Default,
5454        Hover,
5455        Active,
5456        Focus,
5457        Disabled,
5458    }
5459}
5460
5461// =============================================================================
5462// USE_STATE HOOK — Local component state with automatic re-render
5463// =============================================================================
5464//
5465// Components call `use_state(id, initial)` to get a `(getter, setter)` pair.
5466// The setter updates the global system state and triggers a re-render.
5467//
5468// This is the minimal state primitive needed for interactive components.
5469// For complex state, use the global `KnowledgeState` directly.
5470
5471/// Local state hook for components.
5472///
5473/// Returns a `(getter, setter)` pair:
5474/// - `getter()` returns the current value of type `T`
5475/// - `setter(value)` updates the value and triggers a re-render
5476///
5477/// The `id` must be unique per component instance (use a hash of the
5478/// component's label or a generated UUID).
5479pub fn use_state<T: Clone + Send + Sync + 'static>(
5480    id: u64,
5481    initial: T,
5482) -> (impl Fn() -> T, impl Fn(T)) {
5483    // Initialize the state if not already present
5484    let already_exists = load_system_state().get_component_state::<T>(id).is_some();
5485    if !already_exists {
5486        update_system_state(|s| {
5487            let mut ns = s.clone();
5488            ns.set_component_state(id, initial.clone());
5489            ns
5490        });
5491    }
5492
5493    let getter = move || -> T {
5494        load_system_state()
5495            .get_component_state::<T>(id)
5496            .map(|arc_lock| {
5497                arc_lock
5498                    .read()
5499                    .ok()
5500                    .map(|guard| (*guard).clone())
5501                    .unwrap_or_else(|| initial.clone())
5502            })
5503            .unwrap_or_else(|| initial.clone())
5504    };
5505
5506    let setter = {
5507        move |value| {
5508            update_system_state(|s| {
5509                let mut ns = s.clone();
5510                ns.set_component_state(id, value);
5511                ns
5512            });
5513        }
5514    };
5515
5516    (getter, setter)
5517}
5518
5519/// Generate a stable hash ID from a string key.
5520///
5521/// Use this to create unique IDs for `use_state` based on component labels
5522/// or other stable identifiers.
5523///
5524/// # Example
5525/// ```no_run
5526/// use cvkg_core::{use_state, use_state_hash};
5527/// let id = use_state_hash("my-checkbox");
5528/// let (value, set_value) = use_state(id, false);
5529/// ```
5530pub fn use_state_hash(key: &str) -> u64 {
5531    use std::hash::{Hash, Hasher};
5532    let mut s = std::collections::hash_map::DefaultHasher::new();
5533    key.hash(&mut s);
5534    s.finish()
5535}
5536
5537// =============================================================================
5538// ACCESSIBILITY PREFERENCES — System accessibility settings
5539// =============================================================================
5540//
5541// Components and the renderer query these to adapt behavior:
5542// - Reduce Motion: disable non-essential animations
5543// - Reduce Transparency: replace glass materials with opaque surfaces
5544// - Increase Contrast: make borders visible, minimum alpha 0.5
5545
5546thread_local! {
5547    /// Thread-local accessibility preferences.
5548    /// Defaults to no restrictions (all false).
5549    static ACCESSIBILITY_PREFS: std::cell::RefCell<AccessibilityPreferences> =
5550        std::cell::RefCell::new(AccessibilityPreferences::default());
5551}
5552
5553/// System accessibility preferences that components and the renderer must honor.
5554///
5555/// These map to macOS System Settings > Accessibility:
5556/// - `reduce_motion`: Disables non-essential animations (spring, bounce, etc.)
5557/// - `reduce_transparency`: Replaces glass/transparent materials with opaque surfaces
5558/// - `increase_contrast`: Makes all borders visible, minimum alpha 0.5 for all elements
5559#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5560pub struct AccessibilityPreferences {
5561    /// User prefers reduced motion. Animations should be instant or very short.
5562    pub reduce_motion: bool,
5563    /// User prefers reduced transparency. Glass materials should be opaque.
5564    pub reduce_transparency: bool,
5565    /// User prefers increased contrast. Borders must be visible, min alpha 0.5.
5566    pub increase_contrast: bool,
5567}
5568
5569impl AccessibilityPreferences {
5570    /// Detect system accessibility preferences (macOS).
5571    ///
5572    /// On non-macOS platforms, returns defaults (all false).
5573    /// In a production implementation, this would query the OS APIs.
5574    pub fn detect_from_system() -> Self {
5575        #[cfg(target_os = "macos")]
5576        {
5577            // Try to read macOS accessibility preferences via defaults command
5578            let reduce_motion = std::process::Command::new("defaults")
5579                .args(["read", "-g", "com.apple.universalaccess", "reduceMotion"])
5580                .output()
5581                .ok()
5582                .and_then(|o| String::from_utf8(o.stdout).ok())
5583                .map(|s| s.trim() == "1")
5584                .unwrap_or(false);
5585
5586            let reduce_transparency = std::process::Command::new("defaults")
5587                .args([
5588                    "read",
5589                    "-g",
5590                    "com.apple.universalaccess",
5591                    "reduceTransparency",
5592                ])
5593                .output()
5594                .ok()
5595                .and_then(|o| String::from_utf8(o.stdout).ok())
5596                .map(|s| s.trim() == "1")
5597                .unwrap_or(false);
5598
5599            let increase_contrast = std::process::Command::new("defaults")
5600                .args([
5601                    "read",
5602                    "-g",
5603                    "com.apple.universalaccess",
5604                    "increaseContrast",
5605                ])
5606                .output()
5607                .ok()
5608                .and_then(|o| String::from_utf8(o.stdout).ok())
5609                .map(|s| s.trim() == "1")
5610                .unwrap_or(false);
5611
5612            Self {
5613                reduce_motion,
5614                reduce_transparency,
5615                increase_contrast,
5616            }
5617        }
5618        #[cfg(not(target_os = "macos"))]
5619        {
5620            Self::default()
5621        }
5622    }
5623
5624    /// Apply a minimum alpha constraint for increase-contrast mode.
5625    pub fn min_alpha(&self, requested: f32) -> f32 {
5626        if self.increase_contrast {
5627            requested.max(0.5)
5628        } else {
5629            requested
5630        }
5631    }
5632
5633    /// Returns true if glass effects should be replaced with opaque surfaces.
5634    pub fn should_disable_glass(&self) -> bool {
5635        self.reduce_transparency
5636    }
5637
5638    /// Returns true if animations should be instant.
5639    pub fn should_reduce_motion(&self) -> bool {
5640        self.reduce_motion
5641    }
5642
5643    /// Returns true if borders should be made visible.
5644    pub fn should_increase_contrast(&self) -> bool {
5645        self.increase_contrast
5646    }
5647}
5648
5649/// Get the current accessibility preferences for this thread.
5650pub fn accessibility_preferences() -> AccessibilityPreferences {
5651    ACCESSIBILITY_PREFS.with(|p| *p.borrow())
5652}
5653
5654/// Set the accessibility preferences for this thread.
5655///
5656/// The native renderer should call this on startup and when system
5657/// preferences change (via `detect_from_system()`).
5658pub fn set_accessibility_preferences(prefs: AccessibilityPreferences) {
5659    ACCESSIBILITY_PREFS.with(|p| {
5660        *p.borrow_mut() = prefs;
5661    });
5662}
5663
5664// =============================================================================
5665// CLIPBOARD — System clipboard access
5666// =============================================================================
5667
5668/// Trait for clipboard operations.
5669///
5670/// The native renderer implements this via `arboard` on desktop platforms.
5671/// On WASM, it uses the browser Clipboard API.
5672pub trait ClipboardProvider: Send + Sync {
5673    /// Read text from the system clipboard.
5674    fn read_text(&self) -> Option<String>;
5675    /// Write text to the system clipboard.
5676    fn write_text(&self, text: &str);
5677}
5678
5679/// Default clipboard implementation using `arboard`.
5680/// Note: This is only available when the `arboard` feature is enabled.
5681/// The renderer provides the concrete implementation.
5682#[cfg(not(target_arch = "wasm32"))]
5683pub struct SystemClipboard;
5684
5685#[cfg(not(target_arch = "wasm32"))]
5686impl ClipboardProvider for SystemClipboard {
5687    fn read_text(&self) -> Option<String> {
5688        use std::process::Command;
5689        // Fallback: try pbpaste on macOS
5690        Command::new("pbpaste")
5691            .output()
5692            .ok()
5693            .and_then(|o| String::from_utf8(o.stdout).ok())
5694    }
5695
5696    fn write_text(&self, text: &str) {
5697        use std::process::Command;
5698        // Fallback: try pbcopy on macOS
5699        if let Ok(mut child) = Command::new("pbcopy")
5700            .stdin(std::process::Stdio::piped())
5701            .spawn()
5702        {
5703            if let Some(stdin) = child.stdin.as_mut() {
5704                use std::io::Write;
5705                let _ = stdin.write_all(text.as_bytes());
5706            }
5707            let _ = child.wait();
5708        }
5709    }
5710}
5711
5712// =============================================================================
5713// TEXT INPUT — Direction enum for cursor movement
5714// =============================================================================
5715
5716/// Direction for cursor movement in text input.
5717#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5718pub enum TextDirection {
5719    Forward,
5720    Backward,
5721    Up,
5722    Down,
5723    LineStart,
5724    LineEnd,
5725    WordForward,
5726    WordBackward,
5727}
5728
5729/// Text input state managed by the renderer.
5730///
5731/// Components don't store this directly — the renderer maintains it
5732/// and components query/modify it through the Renderer trait methods.
5733#[derive(Debug, Clone, Default)]
5734pub struct TextInputState {
5735    /// The full text content.
5736    pub text: String,
5737    /// Cursor position as byte offset into the text.
5738    pub cursor_pos: usize,
5739    /// Selection anchor. If Some, the selection is from anchor to cursor.
5740    /// If None, there is no selection.
5741    pub selection_anchor: Option<usize>,
5742    /// Whether the input is focused (shows cursor, accepts keyboard).
5743    pub focused: bool,
5744    /// Whether the caret is currently visible (for blinking).
5745    pub caret_visible: bool,
5746    /// Last edit timestamp for undo coalescing.
5747    pub last_edit_time: f32,
5748}
5749
5750impl TextInputState {
5751    /// Create a new TextInputState with the given initial text.
5752    pub fn new(text: impl Into<String>) -> Self {
5753        let text = text.into();
5754        let cursor_pos = text.len();
5755        Self {
5756            text,
5757            cursor_pos,
5758            selection_anchor: None,
5759            focused: false,
5760            caret_visible: true,
5761            last_edit_time: 0.0,
5762        }
5763    }
5764
5765    /// Get the selection range as (start, end) byte offsets.
5766    /// Returns None if there is no selection.
5767    pub fn selection_range(&self) -> Option<(usize, usize)> {
5768        self.selection_anchor.map(|anchor| {
5769            if anchor <= self.cursor_pos {
5770                (anchor, self.cursor_pos)
5771            } else {
5772                (self.cursor_pos, anchor)
5773            }
5774        })
5775    }
5776
5777    /// Get the selected text, or empty string if no selection.
5778    pub fn selected_text(&self) -> String {
5779        self.selection_range()
5780            .map(|(start, end)| self.text[start..end].to_string())
5781            .unwrap_or_default()
5782    }
5783
5784    /// Insert text at the current cursor position, replacing any selection.
5785    pub fn insert(&mut self, new_text: &str) {
5786        if let Some((start, end)) = self.selection_range() {
5787            self.text.replace_range(start..end, new_text);
5788            self.cursor_pos = start + new_text.len();
5789        } else {
5790            self.text.insert_str(self.cursor_pos, new_text);
5791            self.cursor_pos += new_text.len();
5792        }
5793        self.selection_anchor = None;
5794    }
5795
5796    /// Delete characters. If there's a selection, delete it.
5797    /// Otherwise delete `count` characters backward (backspace) or forward (delete).
5798    pub fn delete(&mut self, backward: bool, count: usize) -> String {
5799        if let Some((start, end)) = self.selection_range() {
5800            let deleted = self.text[start..end].to_string();
5801            self.text.replace_range(start..end, "");
5802            self.cursor_pos = start;
5803            self.selection_anchor = None;
5804            return deleted;
5805        }
5806
5807        if backward && self.cursor_pos > 0 {
5808            let start = self.cursor_pos.saturating_sub(count);
5809            let deleted = self.text[start..self.cursor_pos].to_string();
5810            self.text.replace_range(start..self.cursor_pos, "");
5811            self.cursor_pos = start;
5812            deleted
5813        } else if !backward && self.cursor_pos < self.text.len() {
5814            let end = (self.cursor_pos + count).min(self.text.len());
5815            let deleted = self.text[self.cursor_pos..end].to_string();
5816            self.text.replace_range(self.cursor_pos..end, "");
5817            deleted
5818        } else {
5819            String::new()
5820        }
5821    }
5822
5823    /// Move the cursor in the given direction.
5824    pub fn move_cursor(&mut self, direction: TextDirection, extend_selection: bool) {
5825        if !extend_selection {
5826            self.selection_anchor = None;
5827        } else if self.selection_anchor.is_none() {
5828            self.selection_anchor = Some(self.cursor_pos);
5829        }
5830
5831        match direction {
5832            TextDirection::Forward if self.cursor_pos < self.text.len() => {
5833                // Move to next character boundary (UTF-8 safe)
5834                let next = self.text[self.cursor_pos..]
5835                    .char_indices()
5836                    .nth(1)
5837                    .map(|(i, _)| self.cursor_pos + i)
5838                    .unwrap_or(self.text.len());
5839                self.cursor_pos = next;
5840            }
5841            TextDirection::Backward if self.cursor_pos > 0 => {
5842                let prev = self.text[..self.cursor_pos]
5843                    .char_indices()
5844                    .next_back()
5845                    .map(|(i, _)| i)
5846                    .unwrap_or(0);
5847                self.cursor_pos = prev;
5848            }
5849            TextDirection::LineStart => {
5850                self.cursor_pos = 0;
5851            }
5852            TextDirection::LineEnd => {
5853                self.cursor_pos = self.text.len();
5854            }
5855            TextDirection::WordForward => {
5856                // Find next word boundary
5857                let rest = &self.text[self.cursor_pos..];
5858                // Skip current word chars
5859                let after_word = rest
5860                    .char_indices()
5861                    .find(|(_, c)| !c.is_alphanumeric())
5862                    .map(|(i, _)| i)
5863                    .unwrap_or(rest.len());
5864                // Skip whitespace
5865                let after_space = rest[after_word..]
5866                    .char_indices()
5867                    .find(|(_, c)| !c.is_whitespace())
5868                    .map(|(i, _)| after_word + i)
5869                    .unwrap_or(rest.len());
5870                self.cursor_pos = (self.cursor_pos + after_space).min(self.text.len());
5871            }
5872            TextDirection::WordBackward => {
5873                let before = &self.text[..self.cursor_pos];
5874                // Skip whitespace going backward
5875                let before_word = before
5876                    .char_indices()
5877                    .rev()
5878                    .find(|(_, c)| !c.is_whitespace())
5879                    .map(|(i, _)| i)
5880                    .unwrap_or(0);
5881                // Skip word chars going backward
5882                let word_start = before[..before_word]
5883                    .char_indices()
5884                    .rev()
5885                    .find(|(_, c)| !c.is_alphanumeric())
5886                    .map(|(i, _)| i)
5887                    .unwrap_or(0);
5888                self.cursor_pos = word_start;
5889            }
5890            _ => {} // Up/Down handled by multi-line components
5891        }
5892
5893        if !extend_selection {
5894            self.selection_anchor = None;
5895        }
5896    }
5897
5898    /// Select all text.
5899    pub fn select_all(&mut self) {
5900        self.cursor_pos = self.text.len();
5901        self.selection_anchor = Some(0);
5902    }
5903
5904    /// Get the byte offset of the cursor.
5905    pub fn cursor_byte_pos(&self) -> usize {
5906        self.cursor_pos
5907    }
5908}
5909
5910/// Action details for interactive buttons inside a notification.
5911#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
5912pub struct NotificationAction {
5913    /// Unique identifier of the action.
5914    pub id: String,
5915    /// The text label to display on the action button.
5916    pub title: String,
5917    /// Indicates whether the action performs a destructive task (e.g. Delete).
5918    pub is_destructive: bool,
5919}
5920
5921/// Priority tier of a notification.
5922#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
5923pub enum NotificationPriority {
5924    /// Placed silently into the notification center without visual alerts.
5925    Passive,
5926    /// Triggers a visual alert (toast) but does not interrupt focus.
5927    #[default]
5928    Active,
5929    /// Important alert that bypasses standard DND/Focus bounds.
5930    TimeSensitive,
5931}
5932
5933/// A structured notification representation.
5934#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
5935pub struct Notification {
5936    /// Unique identifier for this notification.
5937    pub id: String,
5938    /// App or source identifier spawning this notification.
5939    pub app_name: Option<String>,
5940    /// The bold heading/title text.
5941    pub title: String,
5942    /// The detailed descriptive body text.
5943    pub body: String,
5944    /// Optional URI or path to an icon asset.
5945    pub icon: Option<String>,
5946    /// Optional sound identifier to play when posting.
5947    pub sound: Option<String>,
5948    /// Interactive actions available on this notification.
5949    pub actions: Vec<NotificationAction>,
5950    /// Timer duration in seconds after which the toast auto-dismisses.
5951    pub timeout: Option<f32>,
5952    /// Priority level for delivery logic.
5953    pub priority: NotificationPriority,
5954    /// Time (in seconds since renderer startup) when this notification was posted.
5955    pub timestamp: f32,
5956    /// Whether the notification has been dismissed/read.
5957    pub dismissed: bool,
5958}
5959
5960/// Error type indicating a failure in generating or posting a notification.
5961#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, thiserror::Error)]
5962pub enum NotificationError {
5963    /// Permissions denied.
5964    #[error("Notification permission denied")]
5965    PermissionDenied,
5966    /// Failed to post the notification.
5967    #[error("Failed to post notification")]
5968    PostFailed,
5969}
5970
5971/// State of notification permissions.
5972#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
5973pub enum NotificationPermission {
5974    /// Explicitly allowed.
5975    Granted,
5976    /// Explicitly blocked.
5977    Denied,
5978    /// Prompt has not been shown or decided yet.
5979    #[default]
5980    NotDetermined,
5981}
5982
5983/// Core interface for routing and dispatching notification events.
5984pub trait NotificationHandler: Send + Sync {
5985    /// Posts a new notification.
5986    fn show(&self, notification: Notification) -> Result<(), NotificationError>;
5987    /// Dismisses a notification by ID.
5988    fn dismiss(&self, id: &str) -> Result<(), NotificationError>;
5989    /// Requests delivery permission.
5990    fn request_permission(&self) -> NotificationPermission;
5991}
5992
5993static NEXT_NOTIFICATION_ID: std::sync::atomic::AtomicUsize =
5994    std::sync::atomic::AtomicUsize::new(1);
5995
5996/// Default in-app notification handler that writes state to KnowledgeState.
5997#[derive(Clone, Copy, Debug, Default)]
5998pub struct DefaultNotificationHandler;
5999
6000impl NotificationHandler for DefaultNotificationHandler {
6001    /// Save the notification to the global system state (history) and auto-assign an ID if empty.
6002    fn show(&self, notification: Notification) -> Result<(), NotificationError> {
6003        let mut notif = notification;
6004        if notif.id.is_empty() {
6005            let id = NEXT_NOTIFICATION_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
6006            notif.id = format!("notif_{}", id);
6007        }
6008        update_system_state(|state| {
6009            let mut new_state = state.clone();
6010            new_state.notifications.push(notif.clone());
6011            new_state
6012        });
6013        Ok(())
6014    }
6015
6016    /// Mark a notification as dismissed/read in the global system state.
6017    fn dismiss(&self, id: &str) -> Result<(), NotificationError> {
6018        update_system_state(|state| {
6019            let mut new_state = state.clone();
6020            for notif in &mut new_state.notifications {
6021                if notif.id == id {
6022                    notif.dismissed = true;
6023                }
6024            }
6025            new_state
6026        });
6027        Ok(())
6028    }
6029
6030    /// Returns the permission state (always Granted for internal in-app notifications).
6031    fn request_permission(&self) -> NotificationPermission {
6032        NotificationPermission::Granted
6033    }
6034}
6035
6036static NOTIFICATION_HANDLER: once_cell::sync::OnceCell<std::sync::Arc<dyn NotificationHandler>> =
6037    once_cell::sync::OnceCell::new();
6038
6039/// Sets the global notification handler.
6040pub fn set_notification_handler(handler: std::sync::Arc<dyn NotificationHandler>) {
6041    let _ = NOTIFICATION_HANDLER.set(handler);
6042}
6043
6044/// Gets the global notification handler, fallback to DefaultNotificationHandler.
6045pub fn get_notification_handler() -> std::sync::Arc<dyn NotificationHandler> {
6046    NOTIFICATION_HANDLER
6047        .get_or_init(|| std::sync::Arc::new(DefaultNotificationHandler))
6048        .clone()
6049}
6050
6051/// Filter mapping name to extension list for a file dialog.
6052#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
6053pub struct FileFilter {
6054    /// Friendly name of the filter (e.g. "Images").
6055    pub name: String,
6056    /// List of file extensions (e.g. ["png", "jpg"]).
6057    pub extensions: Vec<String>,
6058}
6059
6060/// The mode/purpose of the file dialog.
6061#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
6062pub enum FileDialogMode {
6063    /// Pick a single or multiple files to open.
6064    #[default]
6065    OpenFile,
6066    /// Pick a directory path.
6067    OpenDirectory,
6068    /// Prompt for a location/name to save a file.
6069    SaveFile,
6070}
6071
6072/// Dialog options for picking files or directories.
6073#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
6074pub struct FileDialog {
6075    /// Title displayed in the dialog window.
6076    pub title: String,
6077    /// Optional starting directory path.
6078    pub default_path: Option<String>,
6079    /// Extensions used to filter selection.
6080    pub filters: Vec<FileFilter>,
6081    /// Open/save mode.
6082    pub mode: FileDialogMode,
6083    /// Allows selecting multiple files if in OpenFile mode.
6084    pub allow_multiple: bool,
6085}
6086
6087/// Errors returned by the file dialog.
6088#[derive(Debug, thiserror::Error)]
6089pub enum FileDialogError {
6090    /// The user closed the dialog without selecting anything.
6091    #[error("File dialog cancelled")]
6092    Cancelled,
6093    /// An input/output error occurred.
6094    #[error("I/O error: {0}")]
6095    Io(#[from] std::io::Error),
6096    /// Platform-specific error.
6097    #[error("Platform error: {0}")]
6098    Platform(String),
6099}
6100
6101impl FileDialog {
6102    /// Creates a new FileDialog with the given mode.
6103    pub fn new(mode: FileDialogMode) -> Self {
6104        Self {
6105            mode,
6106            ..Default::default()
6107        }
6108    }
6109
6110    /// Sets the dialog title.
6111    pub fn title(mut self, title: impl Into<String>) -> Self {
6112        self.title = title.into();
6113        self
6114    }
6115
6116    /// Adds a file filter.
6117    pub fn add_filter(mut self, name: &str, extensions: &[&str]) -> Self {
6118        self.filters.push(FileFilter {
6119            name: name.to_string(),
6120            extensions: extensions.iter().map(|s| s.to_string()).collect(),
6121        });
6122        self
6123    }
6124
6125    /// Sets the default starting directory path.
6126    pub fn default_path(mut self, path: impl Into<String>) -> Self {
6127        self.default_path = Some(path.into());
6128        self
6129    }
6130
6131    /// Sets whether selecting multiple files is allowed.
6132    pub fn allow_multiple(mut self, allow: bool) -> Self {
6133        self.allow_multiple = allow;
6134        self
6135    }
6136}
6137
6138#[cfg(not(target_arch = "wasm32"))]
6139impl FileDialog {
6140    /// Pick file(s) or folder based on current mode configuration.
6141    pub fn pick(self) -> Result<Vec<std::path::PathBuf>, FileDialogError> {
6142        let mut dialog = rfd::FileDialog::new();
6143        dialog = dialog.set_title(&self.title);
6144        if let Some(path) = &self.default_path {
6145            dialog = dialog.set_directory(path);
6146        }
6147        for filter in &self.filters {
6148            let refs: Vec<&str> = filter.extensions.iter().map(|s| s.as_str()).collect();
6149            dialog = dialog.add_filter(&filter.name, &refs);
6150        }
6151
6152        match self.mode {
6153            FileDialogMode::OpenFile => {
6154                if self.allow_multiple {
6155                    dialog.pick_files().ok_or(FileDialogError::Cancelled)
6156                } else {
6157                    Ok(dialog.pick_file().into_iter().collect())
6158                }
6159            }
6160            FileDialogMode::OpenDirectory => Ok(dialog.pick_folder().into_iter().collect()),
6161            FileDialogMode::SaveFile => Ok(dialog.save_file().into_iter().collect()),
6162        }
6163    }
6164
6165    /// Helper to pick a single file/directory, returning None if cancelled.
6166    pub fn pick_single(self) -> Result<Option<std::path::PathBuf>, FileDialogError> {
6167        let results = self.pick()?;
6168        Ok(results.into_iter().next())
6169    }
6170}
6171
6172#[cfg(target_arch = "wasm32")]
6173impl FileDialog {
6174    /// Pick is unsupported/mocked on WASM.
6175    pub fn pick(self) -> Result<Vec<std::path::PathBuf>, FileDialogError> {
6176        Err(FileDialogError::Platform(
6177            "FileDialog is not supported synchronously on WebAssembly".to_string(),
6178        ))
6179    }
6180
6181    /// Helper to pick a single file/directory, returning None if cancelled.
6182    pub fn pick_single(self) -> Result<Option<std::path::PathBuf>, FileDialogError> {
6183        Err(FileDialogError::Platform(
6184            "FileDialog is not supported synchronously on WebAssembly".to_string(),
6185        ))
6186    }
6187}
6188
6189/// Error type representing a failure in Document load/save/parse operations.
6190#[derive(Debug, thiserror::Error)]
6191pub enum DocumentError {
6192    /// An input/output error occurred.
6193    #[error("I/O error: {0}")]
6194    Io(#[from] std::io::Error),
6195    /// Failure during deserialization or parsing.
6196    #[error("Parse error: {0}")]
6197    Parse(String),
6198    /// Failure during serialization.
6199    #[error("Serialization error: {0}")]
6200    Serialize(String),
6201}
6202
6203/// A document interface mapping to local filesystem persistence.
6204pub trait Document: Send + Sync {
6205    /// Loads the document from the specified path.
6206    fn read_from(path: &std::path::Path) -> Result<Self, DocumentError>
6207    where
6208        Self: Sized;
6209
6210    /// Saves the document to the specified path.
6211    fn write_to(&self, path: &std::path::Path) -> Result<(), DocumentError>;
6212
6213    /// Returns true if the document has unsaved modifications.
6214    fn is_dirty(&self) -> bool;
6215
6216    /// Marks the document as clean/saved.
6217    fn mark_clean(&mut self);
6218}
6219
6220/// Periodic auto-save coordinator for open Documents.
6221pub struct AutoSaveManager {
6222    /// Time interval in seconds between auto-saves.
6223    pub interval: f32,
6224    /// Elapsed timer tracker.
6225    pub timer: f32,
6226    /// Registered open documents under management.
6227    pub documents: Vec<(std::path::PathBuf, Box<dyn Document>)>,
6228}
6229
6230impl AutoSaveManager {
6231    /// Creates a new AutoSaveManager with the specified check interval.
6232    pub fn new(interval: f32) -> Self {
6233        Self {
6234            interval,
6235            timer: 0.0,
6236            documents: Vec::new(),
6237        }
6238    }
6239
6240    /// Register a document with its current file path.
6241    pub fn register(&mut self, path: std::path::PathBuf, doc: Box<dyn Document>) {
6242        self.documents.push((path, doc));
6243    }
6244
6245    /// Advance the timer and auto-save any dirty documents when the interval is reached.
6246    pub fn tick(&mut self, dt: f32) {
6247        self.timer += dt;
6248        if self.timer >= self.interval {
6249            self.timer = 0.0;
6250            for (path, doc) in &mut self.documents {
6251                if doc.is_dirty() {
6252                    match doc.write_to(path) {
6253                        Ok(()) => {
6254                            doc.mark_clean();
6255                            log::info!("[AutoSaveManager] Auto-saved document to {:?}", path);
6256                        }
6257                        Err(e) => {
6258                            log::error!(
6259                                "[AutoSaveManager] Failed to auto-save document to {:?}: {:?}",
6260                                path,
6261                                e
6262                            );
6263                        }
6264                    }
6265                }
6266            }
6267        }
6268    }
6269}
6270
6271// ── Menu Bar API ──────────────────────────────────────────────────────────────
6272
6273/// Keyboard modifier flags used by [`KeyboardShortcut`].
6274///
6275/// On macOS, `cmd` maps to the Command (⌘) key.
6276/// On all other platforms, `cmd` maps to the Control key.
6277/// This is enforced at the renderer level, not here; the data model is OS-agnostic.
6278#[derive(Debug, Clone, PartialEq, Eq, Default)]
6279pub struct Modifiers {
6280    /// Command on macOS, Control on Windows/Linux.
6281    pub cmd: bool,
6282    /// Shift key.
6283    pub shift: bool,
6284    /// Alt/Option key.
6285    pub alt: bool,
6286    /// Control key (distinct from cmd on all platforms).
6287    pub ctrl: bool,
6288}
6289
6290/// A keyboard shortcut binding to a menu action.
6291#[derive(Debug, Clone)]
6292pub struct KeyboardShortcut {
6293    /// The key character or name, e.g. `"s"`, `"z"`, `"Return"`.
6294    pub key: String,
6295    /// The required modifier combination.
6296    pub modifiers: Modifiers,
6297}
6298
6299impl KeyboardShortcut {
6300    /// Convenience constructor: cmd (or ctrl on non-macOS) + `key`.
6301    pub fn cmd(key: impl Into<String>) -> Self {
6302        Self {
6303            key: key.into(),
6304            modifiers: Modifiers {
6305                cmd: true,
6306                ..Default::default()
6307            },
6308        }
6309    }
6310
6311    /// Convenience constructor: cmd+Shift + `key`.
6312    pub fn cmd_shift(key: impl Into<String>) -> Self {
6313        Self {
6314            key: key.into(),
6315            modifiers: Modifiers {
6316                cmd: true,
6317                shift: true,
6318                ..Default::default()
6319            },
6320        }
6321    }
6322}
6323
6324/// A single entry in a [`MenuBar`].
6325///
6326/// Actions hold a callback that is invoked when the user activates the item
6327/// (either via the menu UI or via the associated keyboard shortcut).
6328/// Separators provide visual grouping. Submenus allow hierarchical menus.
6329pub enum MenuItem {
6330    /// An activatable menu entry with an optional shortcut and enabled/disabled state.
6331    Action {
6332        label: String,
6333        shortcut: Option<KeyboardShortcut>,
6334        action: std::sync::Arc<dyn Fn() + Send + Sync>,
6335        enabled: bool,
6336    },
6337    /// A nested submenu.
6338    Submenu { label: String, items: Vec<MenuItem> },
6339    /// A visual separator line between groups of items.
6340    Separator,
6341}
6342
6343impl std::fmt::Debug for MenuItem {
6344    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
6345        match self {
6346            Self::Action { label, enabled, .. } => f
6347                .debug_struct("Action")
6348                .field("label", label)
6349                .field("enabled", enabled)
6350                .finish(),
6351            Self::Submenu { label, items } => f
6352                .debug_struct("Submenu")
6353                .field("label", label)
6354                .field("items", items)
6355                .finish(),
6356            Self::Separator => write!(f, "Separator"),
6357        }
6358    }
6359}
6360
6361/// A top-level menu bar containing [`MenuItem`]s.
6362///
6363/// The menu bar is a data model only; rendering it into an OS-native menu is
6364/// handled by the platform renderer (`cvkg-render-native`).
6365pub struct MenuBar {
6366    /// Ordered list of top-level menu items.
6367    pub items: Vec<MenuItem>,
6368}
6369
6370impl MenuBar {
6371    /// Create an empty menu bar.
6372    pub fn new() -> Self {
6373        Self { items: Vec::new() }
6374    }
6375
6376    /// Append a menu item to the bar.
6377    pub fn add_item(&mut self, item: MenuItem) {
6378        self.items.push(item);
6379    }
6380
6381    /// Build the standard CVKG menu structure with all conventional shortcuts.
6382    ///
6383    /// The `cmd` modifier maps to ⌘ on macOS and Ctrl on Windows/Linux — this
6384    /// translation is enforced by the renderer, not here.
6385    ///
6386    /// Menus included:
6387    /// - **File**: New, Open, Save, Close
6388    /// - **Edit**: Undo, Redo, Cut, Copy, Paste, Select All, Find
6389    /// - **View**: Zoom In, Zoom Out, Fullscreen
6390    /// - **Window**: Minimize, Zoom, Bring All to Front
6391    /// - **Help**: Search Help
6392    #[allow(clippy::too_many_arguments)]
6393    pub fn standard(
6394        new_fn: std::sync::Arc<dyn Fn() + Send + Sync>,
6395        open_fn: std::sync::Arc<dyn Fn() + Send + Sync>,
6396        save_fn: std::sync::Arc<dyn Fn() + Send + Sync>,
6397        close_fn: std::sync::Arc<dyn Fn() + Send + Sync>,
6398        quit_fn: std::sync::Arc<dyn Fn() + Send + Sync>,
6399        undo_fn: std::sync::Arc<dyn Fn() + Send + Sync>,
6400        redo_fn: std::sync::Arc<dyn Fn() + Send + Sync>,
6401        cut_fn: std::sync::Arc<dyn Fn() + Send + Sync>,
6402        copy_fn: std::sync::Arc<dyn Fn() + Send + Sync>,
6403        paste_fn: std::sync::Arc<dyn Fn() + Send + Sync>,
6404        select_all_fn: std::sync::Arc<dyn Fn() + Send + Sync>,
6405        find_fn: std::sync::Arc<dyn Fn() + Send + Sync>,
6406    ) -> Self {
6407        let mut bar = Self::new();
6408
6409        // ── File ──────────────────────────────────────────────────────────────
6410        bar.add_item(MenuItem::Submenu {
6411            label: "File".to_string(),
6412            items: vec![
6413                MenuItem::Action {
6414                    label: "New".to_string(),
6415                    shortcut: Some(KeyboardShortcut::cmd("n")),
6416                    action: new_fn,
6417                    enabled: true,
6418                },
6419                MenuItem::Action {
6420                    label: "Open…".to_string(),
6421                    shortcut: Some(KeyboardShortcut::cmd("o")),
6422                    action: open_fn,
6423                    enabled: true,
6424                },
6425                MenuItem::Separator,
6426                MenuItem::Action {
6427                    label: "Save".to_string(),
6428                    shortcut: Some(KeyboardShortcut::cmd("s")),
6429                    action: save_fn,
6430                    enabled: true,
6431                },
6432                MenuItem::Separator,
6433                MenuItem::Action {
6434                    label: "Close".to_string(),
6435                    shortcut: Some(KeyboardShortcut::cmd("w")),
6436                    action: close_fn,
6437                    enabled: true,
6438                },
6439                MenuItem::Separator,
6440                MenuItem::Action {
6441                    label: "Quit".to_string(),
6442                    shortcut: Some(KeyboardShortcut::cmd("q")),
6443                    action: quit_fn,
6444                    enabled: true,
6445                },
6446            ],
6447        });
6448
6449        // ── Edit ──────────────────────────────────────────────────────────────
6450        bar.add_item(MenuItem::Submenu {
6451            label: "Edit".to_string(),
6452            items: vec![
6453                MenuItem::Action {
6454                    label: "Undo".to_string(),
6455                    shortcut: Some(KeyboardShortcut::cmd("z")),
6456                    action: undo_fn,
6457                    enabled: true,
6458                },
6459                MenuItem::Action {
6460                    label: "Redo".to_string(),
6461                    shortcut: Some(KeyboardShortcut::cmd_shift("z")),
6462                    action: redo_fn,
6463                    enabled: true,
6464                },
6465                MenuItem::Separator,
6466                MenuItem::Action {
6467                    label: "Cut".to_string(),
6468                    shortcut: Some(KeyboardShortcut::cmd("x")),
6469                    action: cut_fn,
6470                    enabled: true,
6471                },
6472                MenuItem::Action {
6473                    label: "Copy".to_string(),
6474                    shortcut: Some(KeyboardShortcut::cmd("c")),
6475                    action: copy_fn,
6476                    enabled: true,
6477                },
6478                MenuItem::Action {
6479                    label: "Paste".to_string(),
6480                    shortcut: Some(KeyboardShortcut::cmd("v")),
6481                    action: paste_fn,
6482                    enabled: true,
6483                },
6484                MenuItem::Separator,
6485                MenuItem::Action {
6486                    label: "Select All".to_string(),
6487                    shortcut: Some(KeyboardShortcut::cmd("a")),
6488                    action: select_all_fn,
6489                    enabled: true,
6490                },
6491                MenuItem::Separator,
6492                MenuItem::Action {
6493                    label: "Find…".to_string(),
6494                    shortcut: Some(KeyboardShortcut::cmd("f")),
6495                    action: find_fn,
6496                    enabled: true,
6497                },
6498            ],
6499        });
6500
6501        // ── View ──────────────────────────────────────────────────────────────
6502        // View items carry no application-level callbacks at the model layer;
6503        // zoom and fullscreen are handled by the renderer directly.
6504        let noop: std::sync::Arc<dyn Fn() + Send + Sync> = std::sync::Arc::new(|| {});
6505        bar.add_item(MenuItem::Submenu {
6506            label: "View".to_string(),
6507            items: vec![
6508                MenuItem::Action {
6509                    label: "Zoom In".to_string(),
6510                    shortcut: Some(KeyboardShortcut::cmd("=")),
6511                    action: noop.clone(),
6512                    enabled: true,
6513                },
6514                MenuItem::Action {
6515                    label: "Zoom Out".to_string(),
6516                    shortcut: Some(KeyboardShortcut::cmd("-")),
6517                    action: noop.clone(),
6518                    enabled: true,
6519                },
6520                MenuItem::Separator,
6521                MenuItem::Action {
6522                    label: "Toggle Fullscreen".to_string(),
6523                    shortcut: Some(KeyboardShortcut {
6524                        key: "f".to_string(),
6525                        modifiers: Modifiers {
6526                            ctrl: true,
6527                            ..Default::default()
6528                        },
6529                    }),
6530                    action: noop.clone(),
6531                    enabled: true,
6532                },
6533            ],
6534        });
6535
6536        // ── Window ────────────────────────────────────────────────────────────
6537        bar.add_item(MenuItem::Submenu {
6538            label: "Window".to_string(),
6539            items: vec![
6540                MenuItem::Action {
6541                    label: "Minimize".to_string(),
6542                    shortcut: Some(KeyboardShortcut::cmd("m")),
6543                    action: noop.clone(),
6544                    enabled: true,
6545                },
6546                MenuItem::Action {
6547                    label: "Zoom".to_string(),
6548                    shortcut: None,
6549                    action: noop.clone(),
6550                    enabled: true,
6551                },
6552                MenuItem::Separator,
6553                MenuItem::Action {
6554                    label: "Bring All to Front".to_string(),
6555                    shortcut: None,
6556                    action: noop.clone(),
6557                    enabled: true,
6558                },
6559            ],
6560        });
6561
6562        // ── Help ──────────────────────────────────────────────────────────────
6563        bar.add_item(MenuItem::Submenu {
6564            label: "Help".to_string(),
6565            items: vec![MenuItem::Action {
6566                label: "Search Help".to_string(),
6567                shortcut: None,
6568                action: noop,
6569                enabled: true,
6570            }],
6571        });
6572
6573        bar
6574    }
6575}
6576
6577impl Default for MenuBar {
6578    fn default() -> Self {
6579        Self::new()
6580    }
6581}
6582
6583// =============================================================================
6584// LOCALIZATION — Item 12: Localization / Internationalization
6585// =============================================================================
6586// OS-agnostic: works on all platforms. No platform-specific string loading.
6587
6588use std::sync::RwLock;
6589
6590/// Layout direction for UI elements (LTR or RTL).
6591#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
6592pub enum Direction {
6593    #[default]
6594    LTR,
6595    RTL,
6596    Auto,
6597}
6598
6599impl Direction {
6600    pub fn is_rtl(self) -> bool {
6601        matches!(self, Direction::RTL)
6602    }
6603}
6604#[derive(Clone, Debug)]
6605pub struct L10nBundle {
6606    pub locale: String,
6607    pub strings: HashMap<String, String>,
6608    pub is_rtl: bool,
6609}
6610
6611impl L10nBundle {
6612    pub fn new(locale: impl Into<String>) -> Self {
6613        Self {
6614            locale: locale.into(),
6615            strings: HashMap::new(),
6616            is_rtl: false,
6617        }
6618    }
6619
6620    pub fn add(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
6621        self.strings.insert(key.into(), value.into());
6622        self
6623    }
6624
6625    pub fn from_strings_format(locale: impl Into<String>, input: &str) -> Self {
6626        let mut bundle = Self::new(locale);
6627        for line in input.lines() {
6628            let line = line.trim();
6629            if line.is_empty() || line.starts_with("//") {
6630                continue;
6631            }
6632            if let Some(eq_pos) = line.find(" = ") {
6633                let key = line[..eq_pos].trim_matches('"').to_string();
6634                let val = line[eq_pos + 3..]
6635                    .trim_end_matches(';')
6636                    .trim_matches('"')
6637                    .to_string();
6638                bundle.strings.insert(key, val);
6639            }
6640        }
6641        bundle
6642    }
6643    /// Get a translated string by key. Returns the key itself if not found.
6644    pub fn t(&self, key: &str) -> String {
6645        self.strings
6646            .get(key)
6647            .map(|s| s.to_string())
6648            .unwrap_or_else(|| key.to_string())
6649    }
6650
6651    /// Translate with interpolation. Replaces {0}, {1}, etc. with args.
6652    pub fn tf(&self, key: &str, args: &[&str]) -> String {
6653        let mut result = self.t(key);
6654        for (i, arg) in args.iter().enumerate() {
6655            result = result.replace(&format!("{{{}}}", i), arg);
6656        }
6657        result
6658    }
6659}
6660
6661/// Global localization manager.
6662pub struct L10n {
6663    bundles: HashMap<String, L10nBundle>,
6664    current: String,
6665}
6666
6667impl L10n {
6668    pub fn new(default_locale: &str) -> Self {
6669        Self {
6670            bundles: HashMap::new(),
6671            current: default_locale.to_string(),
6672        }
6673    }
6674
6675    pub fn add_bundle(&mut self, bundle: L10nBundle) {
6676        self.bundles.insert(bundle.locale.clone(), bundle);
6677    }
6678
6679    pub fn set_locale(&mut self, locale: &str) {
6680        self.current = locale.to_string();
6681    }
6682    pub fn current_locale(&self) -> &str {
6683        &self.current
6684    }
6685
6686    pub fn is_rtl(&self) -> bool {
6687        self.bundles
6688            .get(self.current.as_str())
6689            .map(|b| b.is_rtl)
6690            .unwrap_or(false)
6691    }
6692
6693    pub fn t(&self, key: &str) -> String {
6694        self.bundles
6695            .get(self.current.as_str())
6696            .map(|b| b.t(key))
6697            .unwrap_or_else(|| key.to_string())
6698    }
6699
6700    pub fn tf(&self, key: &str, args: &[&str]) -> String {
6701        let mut result = self.t(key);
6702        for (i, arg) in args.iter().enumerate() {
6703            result = result.replace(&format!("{{{}}}", i), arg);
6704        }
6705        result
6706    }
6707
6708    pub fn direction(&self) -> Direction {
6709        if self.is_rtl() {
6710            Direction::RTL
6711        } else {
6712            Direction::LTR
6713        }
6714    }
6715}
6716
6717static L10N: once_cell::sync::Lazy<Arc<RwLock<L10n>>> =
6718    once_cell::sync::Lazy::new(|| Arc::new(RwLock::new(L10n::new("en"))));
6719
6720pub fn init_l10n(l10n: L10n) {
6721    if let Ok(mut guard) = L10N.write() {
6722        *guard = l10n;
6723    }
6724}
6725
6726pub fn l10n() -> Arc<RwLock<L10n>> {
6727    L10N.clone()
6728}
6729
6730pub fn t(key: &str) -> String {
6731    L10N.read()
6732        .map(|g| g.t(key).to_string())
6733        .unwrap_or_else(|_| key.to_string())
6734}
6735
6736pub fn tf(key: &str, args: &[&str]) -> String {
6737    L10N.read()
6738        .map(|g| g.tf(key, args))
6739        .unwrap_or_else(|_| key.to_string())
6740}
6741
6742pub fn set_locale(locale: &str) {
6743    if let Ok(mut guard) = L10N.write() {
6744        guard.set_locale(locale);
6745    }
6746}
6747
6748pub fn current_locale() -> String {
6749    L10N.read()
6750        .map(|g| g.current_locale().to_string())
6751        .unwrap_or_else(|_| "en".to_string())
6752}
6753
6754pub fn is_rtl() -> bool {
6755    L10N.read().map(|g| g.is_rtl()).unwrap_or(false)
6756}
6757
6758// =============================================================================
6759// SYSTEM THEME DETECTION — Dark/Light mode detection
6760// =============================================================================
6761//
6762// OS-agnostic theme detection. Checks the CVKG_THEME environment variable first,
6763// then falls back to dark mode (safe default).
6764//
6765// Platform backends may override this with native OS queries (e.g.,
6766// dark-light crate on desktop, prefers-color-scheme on web).
6767
6768/// The detected system theme.
6769#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
6770pub enum SystemTheme {
6771    /// Dark mode (default).
6772    #[default]
6773    Dark,
6774    /// Light mode.
6775    Light,
6776}
6777
6778/// Detect the current system theme.
6779///
6780/// Checks `CVKG_THEME` environment variable first:
6781/// - `"dark"` → `SystemTheme::Dark`
6782/// - `"light"` → `SystemTheme::Light`
6783/// - unset or any other value → `SystemTheme::Dark` (default)
6784///
6785/// Platform backends can call this and override with native detection
6786/// (e.g., `dark-light` crate on desktop, `prefers-color-scheme` on web).
6787pub fn detect_system_theme() -> SystemTheme {
6788    std::env::var("CVKG_THEME")
6789        .ok()
6790        .and_then(|v| match v.as_str() {
6791            "light" => Some(SystemTheme::Light),
6792            "dark" => Some(SystemTheme::Dark),
6793            _ => None,
6794        })
6795        .unwrap_or(SystemTheme::Dark)
6796}
6797
6798// =============================================================================
6799// AUDIO / HAPTIC — Item 14: Spatial Audio / Haptic Feedback
6800// =============================================================================
6801// OS-agnostic: pure trait abstractions. Platform backends via cfg in renderer.
6802
6803pub mod audio_haptic;
6804pub use audio_haptic::{
6805    AudioEngine, HapticEngine, HapticIntensity, NullAudioEngine, NullHapticEngine, haptic_error,
6806    haptic_impact, haptic_selection, haptic_success, play_sound, set_audio_engine,
6807    set_haptic_engine, sounds,
6808};