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