1use serde::{Deserialize, Serialize};
35use std::collections::HashMap;
36use std::str::FromStr;
37
38pub mod error_types;
39
40pub mod security;
41
42#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
44pub struct ComponentErrorState {
45 pub has_error: bool,
46 pub error_message: Option<String>,
47 pub error_location: Option<String>,
48}
49impl ComponentErrorState {
50 pub fn clear() -> Self {
51 Self::default()
52 }
53
54 pub fn error(message: impl Into<String>, location: impl Into<String>) -> Self {
55 Self {
56 has_error: true,
57 error_message: Some(message.into()),
58 error_location: Some(location.into()),
59 }
60 }
61}
62
63#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
65pub struct KnowledgeState {
66 pub thoughts: Vec<String>,
67 pub actions: Vec<String>,
68 pub context: HashMap<String, String>,
69 pub last_query_results: Vec<KnowledgeId>,
70 #[serde(alias = "items")]
71 pub fragments: std::collections::HashMap<KnowledgeId, KnowledgeFragment>,
72 pub nodes: Vec<TemporalNode>,
74 pub edges: Vec<TemporalEdge>,
76 pub realm: Realm,
78 pub last_pointer_pos: [f32; 2],
80 pub pointer_velocity: [f32; 2],
82 pub odin_focus: Option<String>,
84 pub agent_attention: HashMap<String, f32>,
86 #[serde(skip)]
88 pub component_states: HashMap<u64, Arc<std::sync::RwLock<dyn std::any::Any + Send + Sync>>>,
89}
90
91impl KnowledgeState {
92 pub fn apply_decay(&mut self, decay_factor: f32) {
96 for node in &mut self.nodes {
97 node.weight *= decay_factor;
98 }
99
100 for state in self.component_states.values() {
102 if let Ok(mut lock) = state.write()
103 && let Some(v) = lock.downcast_mut::<f32>()
104 {
105 *v = (*v * decay_factor).max(1.0);
106 }
107 }
108 }
109
110 pub fn reinforce(&mut self, node_ids: &[String], boost: f32) {
112 for node in &mut self.nodes {
113 if node_ids.contains(&node.id) {
114 node.weight += boost;
115 }
116 }
117 }
118
119 pub fn update_pointer(&mut self, new_pos: [f32; 2]) {
121 self.pointer_velocity = [
122 new_pos[0] - self.last_pointer_pos[0],
123 new_pos[1] - self.last_pointer_pos[1],
124 ];
125 self.last_pointer_pos = new_pos;
126 }
127}
128pub type KnowledgeId = String;
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct KnowledgeFragment {
135 pub id: String,
137 pub summary: String,
139 pub source: String,
141 pub created_at: u64,
143 pub accessed_count: u32,
145 pub content: Option<String>,
147}
148
149impl KnowledgeFragment {
150 pub fn new(id: String, summary: String, source: String) -> Self {
151 Self {
152 id,
153 summary,
154 source,
155 created_at: 0,
156 accessed_count: 0,
157 content: None,
158 }
159 }
160}
161
162#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
164pub enum MemoryLayer {
165 Episodic,
167 Semantic,
169 Procedural,
171}
172
173#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
177pub enum Realm {
178 Midgard,
179 #[default]
180 Asgard,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct TemporalNode {
186 pub id: String,
188 pub fragment_id: KnowledgeId,
190 pub timestamp: u64,
192 pub layer: MemoryLayer,
194 pub weight: f32,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct TemporalEdge {
201 pub source: String,
203 pub target: String,
205 pub relation: String,
207 pub weight: f32,
209}
210
211#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
212pub struct AssetKey(pub String);
213
214impl EnvKey for AssetKey {
215 type Value = Arc<dyn AssetManager>;
216 fn default_value() -> Self::Value {
217 Arc::new(DefaultAssetManager::new())
218 }
219}
220
221#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
223pub enum AssetState<T> {
224 Loading,
225 Ready(T),
226 Error(String),
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
231#[serde(untagged)]
232pub enum TokenValue {
233 Single { value: String },
235 Adaptive { light: String, dark: String },
237}
238
239#[derive(Debug, Clone, Serialize, Deserialize)]
241pub struct YggdrasilTokens {
242 pub color: HashMap<String, TokenValue>,
243 pub font: HashMap<String, TokenValue>,
244 pub spacing: HashMap<String, TokenValue>,
245 pub radius: HashMap<String, TokenValue>,
246 pub shadow: HashMap<String, TokenValue>,
247 pub border: HashMap<String, TokenValue>,
248 pub anim: HashMap<String, TokenValue>,
249 pub bifrost: HashMap<String, TokenValue>,
250 pub gungnir: HashMap<String, TokenValue>,
251 pub mjolnir: HashMap<String, TokenValue>,
252 pub accessibility: HashMap<String, TokenValue>,
253}
254
255impl Default for YggdrasilTokens {
256 fn default() -> Self {
257 Self::new()
258 }
259}
260
261impl YggdrasilTokens {
262 pub fn new() -> Self {
263 Self {
264 color: HashMap::new(),
265 font: HashMap::new(),
266 spacing: HashMap::new(),
267 radius: HashMap::new(),
268 shadow: HashMap::new(),
269 border: HashMap::new(),
270 anim: HashMap::new(),
271 bifrost: HashMap::new(),
272 gungnir: HashMap::new(),
273 mjolnir: HashMap::new(),
274 accessibility: HashMap::new(),
275 }
276 }
277
278 pub fn get_color(&self, key: &str, is_dark: bool) -> Option<String> {
280 self.color.get(key).map(|token| match token {
281 TokenValue::Single { value } => value.clone(),
282 TokenValue::Adaptive { light, dark } => {
283 if is_dark {
284 dark.clone()
285 } else {
286 light.clone()
287 }
288 }
289 })
290 }
291
292 pub fn get<T: FromStr>(&self, category: &str, key: &str, is_dark: bool) -> Option<T> {
294 let map = match category {
295 "color" => &self.color,
296 "font" => &self.font,
297 "spacing" => &self.spacing,
298 "radius" => &self.radius,
299 "shadow" => &self.shadow,
300 "border" => &self.border,
301 "anim" => &self.anim,
302 "bifrost" => &self.bifrost,
303 "gungnir" => &self.gungnir,
304 "mjolnir" => &self.mjolnir,
305 "accessibility" => &self.accessibility,
306 _ => return None,
307 };
308
309 map.get(key).and_then(|token| match token {
310 TokenValue::Single { value } => value.parse().ok(),
311 TokenValue::Adaptive { light, dark } => {
312 let value = if is_dark { dark } else { light };
313 value.parse().ok()
314 }
315 })
316 }
317}
318
319pub trait View: Sized + Send {
320 type Body: View;
323
324 fn body(self) -> Self::Body;
325
326 fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
329
330 fn intrinsic_size(&self, _renderer: &mut dyn Renderer, _proposal: SizeProposal) -> Size {
333 Size::ZERO
334 }
335
336 fn layout(&self) -> Option<&dyn layout::LayoutView> {
338 None
339 }
340
341 fn flex_weight(&self) -> f32 {
343 0.0
344 }
345
346 fn modifier<M: ViewModifier>(self, m: M) -> ModifiedView<Self, M> {
348 ModifiedView::new(self, m)
349 }
350
351 fn bifrost(
353 self,
354 blur: f32,
355 saturation: f32,
356 opacity: f32,
357 ) -> ModifiedView<Self, BifrostModifier> {
358 self.modifier(BifrostModifier {
359 blur,
360 saturation,
361 opacity,
362 })
363 }
364
365 fn gungnir(
367 self,
368 color: impl Into<String>,
369 radius: f32,
370 intensity: f32,
371 ) -> ModifiedView<Self, GungnirModifier> {
372 self.modifier(GungnirModifier {
373 color: color.into(),
374 radius,
375 intensity,
376 })
377 }
378
379 fn mjolnir_slice(self, angle: f32, offset: f32) -> ModifiedView<Self, MjolnirSliceModifier> {
381 self.modifier(MjolnirSliceModifier { angle, offset })
382 }
383
384 fn mjolnir_shatter(
386 self,
387 pieces: u32,
388 force: f32,
389 ) -> ModifiedView<Self, MjolnirShatterModifier> {
390 self.modifier(MjolnirShatterModifier { pieces, force })
391 }
392
393 fn bifrost_bridge(self, id: impl Into<String>) -> ModifiedView<Self, BifrostBridgeModifier> {
395 self.modifier(BifrostBridgeModifier { id: id.into() })
396 }
397
398 fn background(self, color: [f32; 4]) -> ModifiedView<Self, BackgroundModifier> {
400 self.modifier(BackgroundModifier { color })
401 }
402
403 fn padding(self, amount: f32) -> ModifiedView<Self, PaddingModifier> {
405 self.modifier(PaddingModifier { amount })
406 }
407
408 fn opacity(self, opacity: f32) -> ModifiedView<Self, OpacityModifier> {
410 self.modifier(OpacityModifier {
411 opacity: opacity.clamp(0.0, 1.0),
412 })
413 }
414
415 fn foreground_color(self, color: [f32; 4]) -> ModifiedView<Self, ForegroundColorModifier> {
417 self.modifier(ForegroundColorModifier { color })
418 }
419
420 fn frame(self, width: Option<f32>, height: Option<f32>) -> ModifiedView<Self, FrameModifier> {
422 self.modifier(FrameModifier { width, height })
423 }
424
425 fn flex(self, weight: f32) -> ModifiedView<Self, FlexModifier> {
427 self.modifier(FlexModifier { weight })
428 }
429
430 fn safe_area_padding(self) -> ModifiedView<Self, SafeAreaModifier> {
432 self.modifier(SafeAreaModifier { ignores: false })
433 }
434
435 fn ignores_safe_area(self) -> ModifiedView<Self, SafeAreaModifier> {
437 self.modifier(SafeAreaModifier { ignores: true })
438 }
439
440 fn clip_to_bounds(self) -> ModifiedView<Self, ClipModifier> {
442 self.modifier(ClipModifier)
443 }
444
445 fn border(self, color: [f32; 4], width: f32) -> ModifiedView<Self, BorderModifier> {
447 self.modifier(BorderModifier { color, width })
448 }
449
450 fn elevation(self, level: f32) -> ModifiedView<Self, ElevationModifier> {
452 self.modifier(ElevationModifier { level })
453 }
454
455 fn magnetic(self, radius: f32, intensity: f32) -> ModifiedView<Self, MagneticModifier> {
457 self.modifier(MagneticModifier { radius, intensity })
458 }
459
460 fn mani_glow(self, color: [f32; 4], radius: f32) -> ModifiedView<Self, ManiGlowModifier> {
462 self.modifier(ManiGlowModifier { color, radius })
463 }
464
465 fn memory_layer(self, layer: MemoryLayer) -> ModifiedView<Self, BifrostLayerModifier> {
467 self.modifier(BifrostLayerModifier { layer })
468 }
469
470 fn fafnir_evolve(self, id: u64) -> ModifiedView<Self, FafnirModifier> {
472 self.modifier(FafnirModifier { id })
473 }
474
475 fn mimir_intent(self) -> ModifiedView<Self, MimirIntentModifier> {
477 self.modifier(MimirIntentModifier)
478 }
479
480 fn kvasir_vibes(self, complexity: f32) -> ModifiedView<Self, KvasirVibeModifier> {
482 self.modifier(KvasirVibeModifier { complexity })
483 }
484
485 fn odins_eye(self) -> ModifiedView<Self, OdinsEyeModifier> {
487 self.modifier(OdinsEyeModifier)
488 }
489
490 fn on_appear<F: Fn() + Send + Sync + 'static>(
492 self,
493 action: F,
494 ) -> ModifiedView<Self, LifecycleModifier> {
495 self.modifier(LifecycleModifier {
496 on_appear: Some(Arc::new(action)),
497 on_disappear: None,
498 })
499 }
500
501 fn on_disappear<F: Fn() + Send + Sync + 'static>(
503 self,
504 action: F,
505 ) -> ModifiedView<Self, LifecycleModifier> {
506 self.modifier(LifecycleModifier {
507 on_appear: None,
508 on_disappear: Some(Arc::new(action)),
509 })
510 }
511
512 fn on_click<F: Fn() + Send + Sync + 'static>(
514 self,
515 action: F,
516 ) -> ModifiedView<Self, OnClickModifier> {
517 self.modifier(OnClickModifier {
518 action: Arc::new(action),
519 })
520 }
521
522 fn on_pointer_enter<F: Fn() + Send + Sync + 'static>(
524 self,
525 action: F,
526 ) -> ModifiedView<Self, OnPointerEnterModifier> {
527 self.modifier(OnPointerEnterModifier {
528 action: Arc::new(action),
529 })
530 }
531
532 fn on_pointer_leave<F: Fn() + Send + Sync + 'static>(
534 self,
535 action: F,
536 ) -> ModifiedView<Self, OnPointerLeaveModifier> {
537 self.modifier(OnPointerLeaveModifier {
538 action: Arc::new(action),
539 })
540 }
541
542 fn on_pointer_move<F: Fn(f32, f32) + Send + Sync + 'static>(
544 self,
545 action: F,
546 ) -> ModifiedView<Self, OnPointerMoveModifier> {
547 self.modifier(OnPointerMoveModifier {
548 action: Arc::new(action),
549 })
550 }
551
552 fn on_pointer_down<F: Fn() + Send + Sync + 'static>(
554 self,
555 action: F,
556 ) -> ModifiedView<Self, OnPointerDownModifier> {
557 self.modifier(OnPointerDownModifier {
558 action: Arc::new(action),
559 })
560 }
561
562 fn on_pointer_up<F: Fn() + Send + Sync + 'static>(
564 self,
565 action: F,
566 ) -> ModifiedView<Self, OnPointerUpModifier> {
567 self.modifier(OnPointerUpModifier {
568 action: Arc::new(action),
569 })
570 }
571
572 fn erase(self) -> AnyView
574 where
575 Self: Clone + 'static,
576 {
577 AnyView::new(self)
578 }
579}
580
581pub trait ErasedView: Send {
583 fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect);
584 fn name(&self) -> &'static str;
585 fn flex_weight_erased(&self) -> f32;
586 fn layout_erased(&self) -> Option<&dyn layout::LayoutView>;
587 fn clone_box(&self) -> Box<dyn ErasedView>;
588}
589
590impl<V: View + Clone + 'static> ErasedView for V {
591 fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect) {
592 self.render(renderer, rect);
593 }
594
595 fn name(&self) -> &'static str {
596 std::any::type_name::<V>()
597 }
598
599 fn flex_weight_erased(&self) -> f32 {
600 self.flex_weight()
601 }
602
603 fn layout_erased(&self) -> Option<&dyn layout::LayoutView> {
604 self.layout()
605 }
606
607 fn clone_box(&self) -> Box<dyn ErasedView> {
608 Box::new(self.clone())
609 }
610}
611
612pub struct MemoView<V, F> {
615 id: u64,
616 data_hash: u64,
617 builder: F,
618 _v: std::marker::PhantomData<V>,
619}
620
621impl<V: View, F: Fn() -> V + Send + Sync> MemoView<V, F> {
622 pub fn new(id: u64, data_hash: u64, builder: F) -> Self {
624 Self {
625 id,
626 data_hash,
627 builder,
628 _v: std::marker::PhantomData,
629 }
630 }
631}
632
633impl<V: View + 'static, F: Fn() -> V + Send + Sync + 'static> View for MemoView<V, F> {
634 type Body = Never;
635 fn body(self) -> Self::Body {
636 unreachable!("MemoView does not have a body")
637 }
638
639 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
640 renderer.memoize(self.id, self.data_hash, &|r| {
641 let view = (self.builder)();
642 view.render(r, rect);
643 });
644 }
645}
646
647pub struct AnyView {
649 inner: Box<dyn ErasedView>,
650}
651
652impl Clone for AnyView {
653 fn clone(&self) -> Self {
654 Self {
655 inner: self.inner.clone_box(),
656 }
657 }
658}
659
660impl AnyView {
661 pub fn new<V: View + Clone + 'static>(view: V) -> Self {
662 Self {
663 inner: Box::new(view),
664 }
665 }
666}
667
668impl View for AnyView {
669 type Body = Never;
670 fn body(self) -> Self::Body {
671 unreachable!()
672 }
673
674 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
675 renderer.push_vnode(rect, self.inner.name());
676 self.inner.render_erased(renderer, rect);
677 renderer.pop_vnode();
678 }
679
680 fn flex_weight(&self) -> f32 {
681 self.inner.flex_weight_erased()
682 }
683
684 fn layout(&self) -> Option<&dyn layout::LayoutView> {
685 self.inner.layout_erased()
686 }
687}
688
689#[derive(Debug, Clone, PartialEq)]
693pub struct BifrostBridgeModifier {
694 pub id: String,
695}
696
697impl ViewModifier for BifrostBridgeModifier {
698 fn modify<V: View>(self, content: V) -> impl View {
699 ModifiedView::new(content, self)
700 }
701
702 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
703 renderer.register_shared_element(&self.id, rect);
705 }
706}
707
708#[derive(Debug, Clone, Copy, PartialEq)]
711pub struct MjolnirSliceModifier {
712 pub angle: f32,
713 pub offset: f32,
714}
715
716impl ViewModifier for MjolnirSliceModifier {
717 fn modify<V: View>(self, content: V) -> impl View {
718 ModifiedView::new(content, self)
719 }
720
721 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
722 renderer.push_mjolnir_slice(self.angle, self.offset);
723 }
724
725 fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
726 renderer.pop_mjolnir_slice();
727 }
728}
729
730#[derive(Debug, Clone, Copy, PartialEq)]
733pub struct MjolnirShatterModifier {
734 pub pieces: u32,
735 pub force: f32,
736}
737
738impl ViewModifier for MjolnirShatterModifier {
739 fn modify<V: View>(self, content: V) -> impl View {
740 ModifiedView::new(content, self)
741 }
742
743 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
744 let pieces = self.pieces.max(1);
746 for i in 0..pieces {
747 let progress = i as f32 / pieces as f32;
748 let next_progress = (i + 1) as f32 / pieces as f32;
749
750 let angle_start = progress * 360.0;
751 let angle_end = next_progress * 360.0;
752
753 renderer.push_mjolnir_slice(angle_start, 0.0);
755 renderer.push_mjolnir_slice(angle_end + 180.0, 0.0);
756
757 let mid_angle = (angle_start + angle_end) / 2.0;
759 let rad = mid_angle.to_radians();
760 let dx = rad.cos() * self.force;
761 let dy = rad.sin() * self.force;
762
763 let shard_rect = Rect {
764 x: rect.x + dx,
765 y: rect.y + dy,
766 ..rect
767 };
768
769 view.render(renderer, shard_rect);
770
771 renderer.pop_mjolnir_slice();
772 renderer.pop_mjolnir_slice();
773 }
774 }
775}
776
777#[derive(Debug, Clone, Copy, PartialEq)]
780pub struct BifrostModifier {
781 pub blur: f32,
782 pub saturation: f32,
783 pub opacity: f32,
784}
785
786impl ViewModifier for BifrostModifier {
787 fn modify<V: View>(self, content: V) -> impl View {
788 ModifiedView::new(content, self)
789 }
790
791 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
792 if renderer.is_over_budget() {
793 renderer.bifrost(rect, self.blur * 0.5, self.saturation, self.opacity);
795 } else {
796 renderer.bifrost(rect, self.blur, self.saturation, self.opacity);
797 }
798 }
799}
800
801#[derive(Debug, Clone, Copy, PartialEq)]
803pub struct BackgroundModifier {
804 pub color: [f32; 4],
805}
806
807impl ViewModifier for BackgroundModifier {
808 fn modify<V: View>(self, content: V) -> impl View {
809 ModifiedView::new(content, self)
810 }
811
812 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
813 renderer.fill_rect(rect, self.color);
814 }
815}
816
817#[derive(Debug, Clone, Copy, PartialEq)]
819pub struct PaddingModifier {
820 pub amount: f32,
821}
822
823impl ViewModifier for PaddingModifier {
824 fn modify<V: View>(self, content: V) -> impl View {
825 ModifiedView::new(content, self)
826 }
827
828 fn transform_rect(&self, rect: Rect) -> Rect {
829 Rect {
830 x: rect.x + self.amount,
831 y: rect.y + self.amount,
832 width: (rect.width - 2.0 * self.amount).max(0.0),
833 height: (rect.height - 2.0 * self.amount).max(0.0),
834 }
835 }
836
837 fn transform_proposal(&self, mut proposal: SizeProposal) -> SizeProposal {
838 if let Some(w) = proposal.width {
839 proposal.width = Some((w - 2.0 * self.amount).max(0.0));
840 }
841 if let Some(h) = proposal.height {
842 proposal.height = Some((h - 2.0 * self.amount).max(0.0));
843 }
844 proposal
845 }
846
847 fn transform_size(&self, mut size: Size) -> Size {
848 size.width += 2.0 * self.amount;
849 size.height += 2.0 * self.amount;
850 size
851 }
852}
853
854#[derive(Debug, Clone, PartialEq)]
857pub struct GungnirModifier {
858 pub color: String,
859 pub radius: f32,
860 pub intensity: f32,
861}
862
863impl ViewModifier for GungnirModifier {
864 fn modify<V: View>(self, content: V) -> impl View {
865 ModifiedView::new(content, self)
866 }
867
868 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
869 renderer.stroke_rect(rect, [0.0, 1.0, 1.0, self.intensity], self.radius / 10.0);
871 }
872}
873
874#[derive(Debug, Clone, Copy, PartialEq)]
876pub struct GungnirPulseModifier {
877 pub color: [f32; 4],
878 pub radius: f32,
879 pub speed: f32,
880}
881
882impl ViewModifier for GungnirPulseModifier {
883 fn modify<V: View>(self, content: V) -> impl View {
884 ModifiedView::new(content, self)
885 }
886
887 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
888 let time = std::time::SystemTime::now()
889 .duration_since(std::time::UNIX_EPOCH)
890 .unwrap_or_default()
891 .as_secs_f32();
892
893 let intensity = (time * self.speed).sin() * 0.5 + 0.5;
896 let mut color = self.color;
897 color[3] *= intensity;
898
899 renderer.stroke_rect(rect, color, self.radius);
901 }
902}
903
904#[derive(Debug, Clone, Copy, PartialEq)]
906pub struct MagneticModifier {
907 pub radius: f32,
908 pub intensity: f32,
909}
910
911impl ViewModifier for MagneticModifier {
912 fn modify<V: View>(self, content: V) -> impl View {
913 ModifiedView::new(content, self)
914 }
915
916 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
917 let [px, py] = renderer.get_pointer_position();
918 let center_x = rect.x + rect.width / 2.0;
919 let center_y = rect.y + rect.height / 2.0;
920
921 let dx = px - center_x;
922 let dy = py - center_y;
923 let dist = (dx * dx + dy * dy).sqrt();
924
925 let mut offset_x = 0.0;
926 let mut offset_y = 0.0;
927
928 if dist < self.radius && dist > 0.0 {
929 let force = (1.0 - dist / self.radius) * self.intensity;
930 offset_x = dx * force;
931 offset_y = dy * force;
932 }
933
934 let magnetic_rect = Rect {
935 x: rect.x + offset_x,
936 y: rect.y + offset_y,
937 ..rect
938 };
939
940 view.render(renderer, magnetic_rect);
941 }
942}
943
944#[derive(Debug, Clone, Copy, PartialEq)]
947pub struct ManiGlowModifier {
948 pub color: [f32; 4],
949 pub radius: f32,
950}
951
952impl ViewModifier for ManiGlowModifier {
953 fn modify<V: View>(self, content: V) -> impl View {
954 ModifiedView::new(content, self)
955 }
956
957 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
958 if crate::load_system_state().realm == Realm::Asgard {
959 renderer.mani_glow(rect, self.color, self.radius);
960 }
961 view.render(renderer, rect);
962 }
963}
964
965#[derive(Debug, Clone, Copy, PartialEq)]
970pub struct BifrostLayerModifier {
971 pub layer: MemoryLayer,
972}
973
974impl ViewModifier for BifrostLayerModifier {
975 fn modify<V: View>(self, content: V) -> impl View {
976 ModifiedView::new(content, self)
977 }
978
979 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
980 let realm = crate::load_system_state().realm;
981 match self.layer {
982 MemoryLayer::Episodic => {
983 if realm == Realm::Asgard {
984 renderer.bifrost(rect, 40.0, 1.2, 0.7);
985 } else {
986 renderer.fill_rect(rect, [0.1, 0.12, 0.15, 0.8]);
987 }
988 }
989 MemoryLayer::Semantic => {
990 if realm == Realm::Asgard {
991 renderer.gungnir(rect, [1.0, 0.84, 0.0, 1.0], 15.0, 0.6);
992 } else {
993 renderer.stroke_rect(rect, [0.4, 0.4, 0.4, 1.0], 1.5);
994 }
995 }
996 MemoryLayer::Procedural => {
997 renderer.fill_rect(rect, [0.05, 0.05, 0.07, 0.95]);
998 let stroke_color = if realm == Realm::Asgard {
999 [0.3, 0.3, 0.3, 1.0]
1000 } else {
1001 [0.2, 0.2, 0.2, 1.0]
1002 };
1003 renderer.stroke_rect(rect, stroke_color, 2.0);
1004 }
1005 }
1006 view.render(renderer, rect);
1007 }
1008}
1009
1010#[derive(Debug, Clone, Copy, PartialEq)]
1014pub struct FafnirModifier {
1015 pub id: u64,
1017}
1018
1019impl ViewModifier for FafnirModifier {
1020 fn modify<V: View>(self, content: V) -> impl View {
1021 ModifiedView::new(content, self)
1022 }
1023
1024 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1025 let state = crate::load_system_state();
1026 let vitality = state
1027 .get_component_state::<f32>(self.id)
1028 .map(|v| *v.read().unwrap())
1029 .unwrap_or(1.0);
1030
1031 let growth = (vitality - 1.0).clamp(0.0, 4.0);
1034 let scale = 1.0 + growth * 0.12;
1035 let glow_intensity = growth * 0.25;
1036
1037 let id = self.id;
1039 renderer.register_handler(
1040 "pointermove",
1041 std::sync::Arc::new(move |_| {
1042 crate::update_system_state(|s| {
1043 let mut s = s.clone();
1044 let v = s
1045 .get_component_state::<f32>(id)
1046 .map(|v| *v.read().unwrap())
1047 .unwrap_or(1.0);
1048 s.set_component_state(id, (v + 0.05).min(5.0)); s
1050 });
1051 }),
1052 );
1053
1054 if scale > 1.01 {
1055 renderer.push_transform([0.0, 0.0], [scale, scale], 0.0);
1056 }
1057
1058 if glow_intensity > 0.1 && state.realm == Realm::Asgard {
1059 renderer.gungnir(rect, [1.0, 0.84, 0.0, 1.0], 15.0 * vitality, glow_intensity);
1060 }
1061
1062 view.render(renderer, rect);
1063
1064 if scale > 1.01 {
1065 renderer.pop_transform();
1066 }
1067 }
1068}
1069
1070#[derive(Debug, Clone, Copy, PartialEq)]
1072pub struct MimirIntentModifier;
1073
1074impl ViewModifier for MimirIntentModifier {
1075 fn modify<V: View>(self, content: V) -> impl View {
1076 ModifiedView::new(content, self)
1077 }
1078
1079 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1080 let state = crate::load_system_state();
1081 let pos = state.last_pointer_pos;
1082 let vel = state.pointer_velocity;
1083
1084 let center = [rect.x + rect.width / 2.0, rect.y + rect.height / 2.0];
1086 let dx = center[0] - pos[0];
1087 let dy = center[1] - pos[1];
1088
1089 let dot = vel[0] * dx + vel[1] * dy;
1091 let speed_sq = vel[0] * vel[0] + vel[1] * vel[1];
1092 let dist_sq = dx * dx + dy * dy;
1093
1094 if dot > 0.0 && dist_sq < 250.0 * 250.0 && speed_sq > 0.5 && state.realm == Realm::Asgard {
1095 let intent_strength = (dot / (speed_sq.sqrt() * dist_sq.sqrt())).clamp(0.0, 1.0);
1097 renderer.stroke_rect(rect, [0.0, 0.9, 1.0, 0.3 * intent_strength], 1.5);
1098 }
1099
1100 view.render(renderer, rect);
1101 }
1102}
1103
1104#[derive(Debug, Clone, Copy, PartialEq)]
1106pub struct KvasirVibeModifier {
1107 pub complexity: f32,
1108}
1109
1110impl ViewModifier for KvasirVibeModifier {
1111 fn modify<V: View>(self, content: V) -> impl View {
1112 ModifiedView::new(content, self)
1113 }
1114
1115 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1116 if crate::load_system_state().realm == Realm::Asgard {
1117 let t = renderer.elapsed_time();
1118 let c = self.complexity.clamp(0.0, 1.0);
1119
1120 let blur = 20.0 + c * 40.0;
1123 let turbulence_x = (t * (1.0 + c * 2.0)).sin() * 8.0 * c;
1124 let turbulence_y = (t * (0.8 + c * 1.5)).cos() * 5.0 * c;
1125 renderer.bifrost(
1126 rect.offset(turbulence_x, turbulence_y),
1127 blur,
1128 0.8 + c * 0.4,
1129 0.25,
1130 );
1131
1132 if c > 0.2 {
1134 let pulse = (t * (3.0 + c * 5.0)).sin().abs() * c;
1135 let color = [0.0, 0.9, 1.0, 0.4 * pulse]; renderer.gungnir(rect, color, 12.0 + c * 24.0, 0.6 * pulse);
1137 }
1138
1139 if c > 0.7 {
1141 let instability = (t * 15.0).cos().abs() * (c - 0.7) * 3.3;
1142 let warning_color = [1.0, 0.0, 0.4, 0.12 * instability];
1143 renderer.fill_rect(rect, warning_color);
1144 renderer.stroke_rect(rect, [1.0, 0.0, 0.2, 0.45 * instability], 1.8);
1145 }
1146 }
1147 view.render(renderer, rect);
1148 }
1149}
1150
1151#[derive(Debug, Clone, Copy, PartialEq)]
1153pub struct OdinsEyeModifier;
1154
1155impl ViewModifier for OdinsEyeModifier {
1156 fn modify<V: View>(self, content: V) -> impl View {
1157 ModifiedView::new(content, self)
1158 }
1159
1160 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1161 let state = crate::load_system_state();
1162 let t = renderer.elapsed_time();
1163
1164 view.render(renderer, rect);
1166
1167 if state.realm == Realm::Asgard {
1168 let eye_pulse = (t * 0.5).sin().abs() * 0.05;
1171 renderer.draw_radial_gradient(
1172 rect,
1173 [0.0, 0.6, 0.8, 0.08 + eye_pulse], [0.0, 0.0, 0.0, 0.0], );
1176
1177 let hugin_rect = Rect {
1179 x: rect.x + 20.0,
1180 y: rect.y + 40.0,
1181 width: 200.0,
1182 height: rect.height - 80.0,
1183 };
1184 renderer.draw_text(
1185 "HUGIN: THOUGHT",
1186 hugin_rect.x,
1187 hugin_rect.y,
1188 10.0,
1189 [0.0, 1.0, 1.0, 0.6],
1190 );
1191 for (i, thought) in state.thoughts.iter().rev().take(10).enumerate() {
1192 renderer.draw_text(
1193 thought,
1194 hugin_rect.x,
1195 hugin_rect.y + 20.0 + i as f32 * 14.0,
1196 9.0,
1197 [1.0, 1.0, 1.0, 0.4],
1198 );
1199 }
1200
1201 let munin_rect = Rect {
1203 x: rect.x + rect.width - 220.0,
1204 y: rect.y + 40.0,
1205 width: 200.0,
1206 height: rect.height - 80.0,
1207 };
1208 renderer.draw_text(
1209 "MUNIN: MEMORY",
1210 munin_rect.x,
1211 munin_rect.y,
1212 10.0,
1213 [1.0, 0.84, 0.0, 0.6],
1214 );
1215 for (i, node) in state.nodes.iter().take(10).enumerate() {
1216 let opacity = (node.weight.min(1.0)) * 0.5;
1217 renderer.draw_text(
1218 &node.id,
1219 munin_rect.x,
1220 munin_rect.y + 20.0 + i as f32 * 14.0,
1221 9.0,
1222 [1.0, 1.0, 1.0, opacity],
1223 );
1224 }
1225
1226 if let Some(focus_id) = &state.odin_focus {
1228 renderer.draw_text(
1230 &format!("EYE FOCUS: {}", focus_id),
1231 rect.x + rect.width / 2.0 - 50.0,
1232 rect.y + 20.0,
1233 12.0,
1234 [0.0, 1.0, 1.0, 0.8],
1235 );
1236
1237 renderer.gungnir(
1240 Rect {
1241 x: rect.x + rect.width / 2.0 - 1.0,
1242 y: rect.y,
1243 width: 2.0,
1244 height: rect.height,
1245 },
1246 [0.0, 1.0, 1.0, 1.0],
1247 20.0,
1248 0.4,
1249 );
1250 }
1251 }
1252 }
1253}
1254
1255#[derive(Debug, Clone, Copy, PartialEq)]
1257pub struct SleipnirParams {
1258 pub stiffness: f32,
1259 pub damping: f32,
1260 pub mass: f32,
1261}
1262
1263impl SleipnirParams {
1264 pub fn snappy() -> Self {
1265 Self {
1266 stiffness: 230.0,
1267 damping: 22.0,
1268 mass: 1.0,
1269 }
1270 }
1271 pub fn fluid() -> Self {
1272 Self {
1273 stiffness: 170.0,
1274 damping: 26.0,
1275 mass: 1.0,
1276 }
1277 }
1278 pub fn heavy() -> Self {
1279 Self {
1280 stiffness: 90.0,
1281 damping: 20.0,
1282 mass: 1.0,
1283 }
1284 }
1285 pub fn bouncy() -> Self {
1286 Self {
1287 stiffness: 190.0,
1288 damping: 14.0,
1289 mass: 1.0,
1290 }
1291 }
1292}
1293
1294impl Default for SleipnirParams {
1295 fn default() -> Self {
1296 Self::fluid()
1297 }
1298}
1299
1300#[derive(Debug, Clone, Copy, PartialEq)]
1301struct SolverState {
1302 x: f32,
1303 v: f32,
1304}
1305
1306#[derive(Debug, Clone, Copy, PartialEq)]
1309pub struct SleipnirSolver {
1310 params: SleipnirParams,
1311 target: f32,
1312 state: SolverState,
1313}
1314
1315impl SleipnirSolver {
1316 pub fn new(params: SleipnirParams, target: f32, current: f32) -> Self {
1318 Self {
1319 params,
1320 target,
1321 state: SolverState { x: current, v: 0.0 },
1322 }
1323 }
1324
1325 pub fn tick(&mut self, dt: f32) -> f32 {
1327 if dt <= 0.0 {
1328 return self.state.x;
1329 }
1330
1331 let mut remaining = dt;
1333 let step = 1.0 / 120.0;
1334
1335 while remaining > 0.0 {
1336 let d = remaining.min(step);
1337 self.step(d);
1338 remaining -= d;
1339 }
1340
1341 self.state.x
1342 }
1343
1344 fn step(&mut self, dt: f32) {
1345 let a = self.evaluate(self.state, 0.0, SolverState { x: 0.0, v: 0.0 });
1346 let b = self.evaluate(self.state, dt * 0.5, a);
1347 let c = self.evaluate(self.state, dt * 0.5, b);
1348 let d = self.evaluate(self.state, dt, c);
1349
1350 let dxdt = 1.0 / 6.0 * (a.x + 2.0 * (b.x + c.x) + d.x);
1351 let dvdt = 1.0 / 6.0 * (a.v + 2.0 * (b.v + c.v) + d.v);
1352
1353 self.state.x += dxdt * dt;
1354 self.state.v += dvdt * dt;
1355 }
1356
1357 fn evaluate(&self, initial: SolverState, dt: f32, d: SolverState) -> SolverState {
1358 let state = SolverState {
1359 x: initial.x + d.x * dt,
1360 v: initial.v + d.v * dt,
1361 };
1362 let force =
1363 -self.params.stiffness * (state.x - self.target) - self.params.damping * state.v;
1364 let mass = self.params.mass.max(0.001);
1365 SolverState {
1366 x: state.v,
1367 v: force / mass,
1368 }
1369 }
1370
1371 pub fn is_settled(&self) -> bool {
1372 (self.state.x - self.target).abs() < 0.001 && self.state.v.abs() < 0.001
1373 }
1374
1375 pub fn set_target(&mut self, target: f32) {
1376 self.target = target;
1377 }
1378
1379 pub fn current_value(&self) -> f32 {
1380 self.state.x
1381 }
1382}
1383
1384#[derive(Debug, Clone, PartialEq)]
1386pub struct SleipnirModifier {
1387 pub id: u64,
1388 pub target: f32,
1389 pub params: SleipnirParams,
1390}
1391
1392impl ViewModifier for SleipnirModifier {
1393 fn modify<V: View>(self, content: V) -> impl View {
1394 ModifiedView::new(content, self)
1395 }
1396
1397 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1398 let state = load_system_state();
1399
1400 let solver_lock_opt = state.get_component_state::<SleipnirSolver>(self.id);
1402
1403 let current_val;
1404
1405 if let Some(lock) = solver_lock_opt {
1406 let mut solver = lock.write().unwrap();
1408 solver.set_target(self.target);
1409 current_val = solver.tick(renderer.delta_time());
1410
1411 if !solver.is_settled() {
1413 renderer.request_redraw();
1414 }
1415 } else {
1416 let solver = SleipnirSolver::new(
1418 self.params,
1419 self.target,
1420 self.target, );
1422
1423 get_system_state().rcu(|old| {
1425 let mut new_state = (**old).clone();
1426 new_state.set_component_state(self.id, solver);
1427 new_state
1428 });
1429
1430 current_val = self.target;
1431 }
1432
1433 renderer.push_transform([0.0, current_val], [1.0, 1.0], 0.0);
1435 view.render(renderer, rect);
1436 renderer.pop_transform();
1437 }
1438}
1439
1440#[derive(Debug, Clone, Copy, PartialEq)]
1443pub struct TransformModifier {
1444 pub translation: [f32; 2],
1445 pub scale: [f32; 2],
1446 pub rotation: f32,
1447}
1448
1449impl Default for TransformModifier {
1450 fn default() -> Self {
1451 Self::new()
1452 }
1453}
1454
1455impl TransformModifier {
1456 pub fn new() -> Self {
1457 Self {
1458 translation: [0.0, 0.0],
1459 scale: [1.0, 1.0],
1460 rotation: 0.0,
1461 }
1462 }
1463
1464 pub fn translate(mut self, x: f32, y: f32) -> Self {
1465 self.translation = [x, y];
1466 self
1467 }
1468
1469 pub fn scale(mut self, x: f32, y: f32) -> Self {
1470 self.scale = [x, y];
1471 self
1472 }
1473
1474 pub fn rotate(mut self, radians: f32) -> Self {
1475 self.rotation = radians;
1476 self
1477 }
1478}
1479
1480impl ViewModifier for TransformModifier {
1481 fn modify<V: View>(self, content: V) -> impl View {
1482 ModifiedView::new(content, self)
1483 }
1484
1485 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1486 renderer.push_transform(self.translation, self.scale, self.rotation);
1487 view.render(renderer, rect);
1488 renderer.pop_transform();
1489 }
1490}
1491
1492#[derive(Clone)]
1495pub struct LifecycleModifier {
1496 pub on_appear: Option<Arc<dyn Fn() + Send + Sync>>,
1497 pub on_disappear: Option<Arc<dyn Fn() + Send + Sync>>,
1498}
1499
1500impl ViewModifier for LifecycleModifier {
1501 fn modify<V: View>(self, content: V) -> impl View {
1502 ModifiedView::new(content, self)
1503 }
1504}
1505
1506#[derive(Debug, Clone, Copy, PartialEq)]
1509pub struct OpacityModifier {
1510 pub opacity: f32,
1511}
1512
1513impl ViewModifier for OpacityModifier {
1514 fn modify<V: View>(self, content: V) -> impl View {
1515 ModifiedView::new(content, self)
1516 }
1517
1518 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1519 renderer.push_opacity(self.opacity);
1520 }
1521
1522 fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1523 renderer.pop_opacity();
1524 }
1525}
1526
1527#[derive(Clone)]
1529pub struct OnClickModifier {
1530 pub action: Arc<dyn Fn() + Send + Sync>,
1531}
1532
1533impl ViewModifier for OnClickModifier {
1534 fn modify<V: View>(self, content: V) -> impl View {
1535 ModifiedView::new(content, self)
1536 }
1537
1538 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1539 let action = self.action.clone();
1540 renderer.register_handler(
1541 "pointerclick",
1542 std::sync::Arc::new(move |event| {
1543 if let Event::PointerClick { .. } = event {
1544 (action)();
1545 }
1546 }),
1547 );
1548 }
1549}
1550
1551#[derive(Clone)]
1553pub struct OnPointerEnterModifier {
1554 pub action: Arc<dyn Fn() + Send + Sync>,
1555}
1556
1557impl ViewModifier for OnPointerEnterModifier {
1558 fn modify<V: View>(self, content: V) -> impl View {
1559 ModifiedView::new(content, self)
1560 }
1561
1562 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1563 let action = self.action.clone();
1564 renderer.register_handler(
1565 "pointerenter",
1566 std::sync::Arc::new(move |event| {
1567 if let Event::PointerEnter = event {
1568 (action)();
1569 }
1570 }),
1571 );
1572 }
1573}
1574
1575#[derive(Clone)]
1577pub struct OnPointerLeaveModifier {
1578 pub action: Arc<dyn Fn() + Send + Sync>,
1579}
1580
1581impl ViewModifier for OnPointerLeaveModifier {
1582 fn modify<V: View>(self, content: V) -> impl View {
1583 ModifiedView::new(content, self)
1584 }
1585
1586 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1587 let action = self.action.clone();
1588 renderer.register_handler(
1589 "pointerleave",
1590 std::sync::Arc::new(move |event| {
1591 if let Event::PointerLeave = event {
1592 (action)();
1593 }
1594 }),
1595 );
1596 }
1597}
1598
1599#[derive(Clone)]
1601pub struct OnPointerMoveModifier {
1602 pub action: Arc<dyn Fn(f32, f32) + Send + Sync>,
1603}
1604
1605impl ViewModifier for OnPointerMoveModifier {
1606 fn modify<V: View>(self, content: V) -> impl View {
1607 ModifiedView::new(content, self)
1608 }
1609
1610 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1611 let action = self.action.clone();
1612 renderer.register_handler(
1613 "pointermove",
1614 std::sync::Arc::new(move |event| {
1615 if let Event::PointerMove { x, y, .. } = event {
1616 (action)(x, y);
1617 }
1618 }),
1619 );
1620 }
1621}
1622
1623#[derive(Clone)]
1625pub struct OnPointerDownModifier {
1626 pub action: Arc<dyn Fn() + Send + Sync>,
1627}
1628
1629impl ViewModifier for OnPointerDownModifier {
1630 fn modify<V: View>(self, content: V) -> impl View {
1631 ModifiedView::new(content, self)
1632 }
1633
1634 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1635 let action = self.action.clone();
1636 renderer.register_handler(
1637 "pointerdown",
1638 std::sync::Arc::new(move |event| {
1639 if let Event::PointerDown { .. } = event {
1640 (action)();
1641 }
1642 }),
1643 );
1644 }
1645}
1646
1647#[derive(Clone)]
1649pub struct OnPointerUpModifier {
1650 pub action: Arc<dyn Fn() + Send + Sync>,
1651}
1652
1653impl ViewModifier for OnPointerUpModifier {
1654 fn modify<V: View>(self, content: V) -> impl View {
1655 ModifiedView::new(content, self)
1656 }
1657
1658 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1659 let action = self.action.clone();
1660 renderer.register_handler(
1661 "pointerup",
1662 std::sync::Arc::new(move |event| {
1663 if let Event::PointerUp { .. } = event {
1664 (action)();
1665 }
1666 }),
1667 );
1668 }
1669}
1670
1671#[derive(Debug, Clone, Copy, PartialEq)]
1674pub struct ForegroundColorModifier {
1675 pub color: [f32; 4],
1676}
1677
1678impl ViewModifier for ForegroundColorModifier {
1679 fn modify<V: View>(self, content: V) -> impl View {
1680 ModifiedView::new(content, self)
1681 }
1682}
1683
1684#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1687pub struct ClipModifier;
1688
1689impl ViewModifier for ClipModifier {
1690 fn modify<V: View>(self, content: V) -> impl View {
1691 ModifiedView::new(content, self)
1692 }
1693
1694 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1695 renderer.push_clip_rect(rect);
1696 }
1697
1698 fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1699 renderer.pop_clip_rect();
1700 }
1701}
1702
1703#[derive(Debug, Clone, Copy, PartialEq)]
1705pub struct BorderModifier {
1706 pub color: [f32; 4],
1707 pub width: f32,
1708}
1709
1710impl ViewModifier for BorderModifier {
1711 fn modify<V: View>(self, content: V) -> impl View {
1712 ModifiedView::new(content, self)
1713 }
1714
1715 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1716 renderer.stroke_rect(rect, self.color, self.width);
1717 }
1718}
1719
1720#[doc(hidden)]
1722pub enum Never {}
1723
1724impl View for Never {
1725 type Body = Never;
1726 fn body(self) -> Never {
1727 unreachable!()
1728 }
1729}
1730
1731#[derive(Debug, Clone, Copy, Default)]
1733pub struct EmptyView;
1734
1735impl View for EmptyView {
1736 type Body = Never;
1737 fn body(self) -> Self::Body {
1738 unreachable!()
1739 }
1740 fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
1741 fn intrinsic_size(&self, _renderer: &mut dyn Renderer, _proposal: SizeProposal) -> Size {
1742 Size {
1743 width: 0.0,
1744 height: 0.0,
1745 }
1746 }
1747}
1748
1749#[derive(Clone)]
1752pub struct ModifiedView<V, M> {
1753 view: V,
1754 modifier: M,
1755}
1756
1757impl<V: View, M: ViewModifier> ModifiedView<V, M> {
1758 #[doc(hidden)]
1759 pub fn new(view: V, modifier: M) -> Self {
1760 Self { view, modifier }
1761 }
1762}
1763
1764impl<V: View, M: ViewModifier> View for ModifiedView<V, M> {
1765 type Body = ModifiedView<V::Body, M>;
1766
1767 fn body(self) -> Self::Body {
1768 ModifiedView {
1769 view: self.view.body(),
1770 modifier: self.modifier.clone(),
1771 }
1772 }
1773
1774 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1775 self.modifier.render_view(&self.view, renderer, rect);
1776 }
1777
1778 fn intrinsic_size(&self, renderer: &mut dyn Renderer, proposal: SizeProposal) -> Size {
1779 self.modifier.measure_view(&self.view, renderer, proposal)
1780 }
1781
1782 fn flex_weight(&self) -> f32 {
1783 self.modifier.child_flex_weight(&self.view)
1784 }
1785
1786 fn layout(&self) -> Option<&dyn layout::LayoutView> {
1787 self.modifier.layout().or_else(|| self.view.layout())
1788 }
1789}
1790
1791pub trait ViewModifier: Send + Clone {
1792 fn modify<V: View>(self, content: V) -> impl View;
1793
1794 fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
1796
1797 fn post_render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
1799
1800 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1803 self.render(renderer, rect);
1804 let child_rect = self.transform_rect(rect);
1805 view.render(renderer, child_rect);
1806 self.post_render(renderer, rect);
1807 }
1808
1809 fn transform_rect(&self, rect: Rect) -> Rect {
1810 rect
1811 }
1812
1813 fn transform_proposal(&self, proposal: SizeProposal) -> SizeProposal {
1815 proposal
1816 }
1817
1818 fn transform_size(&self, size: Size) -> Size {
1820 size
1821 }
1822
1823 fn measure_view<V: View>(
1825 &self,
1826 view: &V,
1827 renderer: &mut dyn Renderer,
1828 proposal: SizeProposal,
1829 ) -> Size {
1830 let child_proposal = self.transform_proposal(proposal);
1831 let child_size = view.intrinsic_size(renderer, child_proposal);
1832 self.transform_size(child_size)
1833 }
1834
1835 fn child_flex_weight<V: View>(&self, view: &V) -> f32 {
1837 view.flex_weight()
1838 }
1839
1840 fn layout(&self) -> Option<&dyn layout::LayoutView> {
1841 None
1842 }
1843}
1844
1845#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
1847pub struct TelemetryData {
1848 pub frame_time_ms: f32,
1849 pub p99_frame_time_ms: f32,
1851 pub frame_jitter_ms: f32,
1853 pub hardware_stall_detected: bool,
1855
1856 pub input_time_ms: f32,
1858 pub state_flush_time_ms: f32,
1859 pub layout_time_ms: f32,
1860 pub draw_time_ms: f32,
1861 pub gpu_submit_time_ms: f32,
1862
1863 pub draw_calls: u32,
1864 pub vertices: u32,
1865
1866 pub berserker_rage: f32,
1868
1869 pub vram_usage_mb: f32,
1871 pub vram_textures_mb: f32,
1872 pub vram_buffers_mb: f32,
1873 pub vram_pipelines_mb: f32,
1874 pub vram_exhausted: bool,
1876}
1877
1878#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1880pub struct FrameBudget {
1881 pub target_ms: f32,
1883 pub allow_degradation: bool,
1886}
1887
1888impl Default for FrameBudget {
1889 fn default() -> Self {
1890 Self {
1891 target_ms: 16.0,
1892 allow_degradation: true,
1893 }
1894 }
1895}
1896
1897pub trait ElapsedTime {
1905 fn elapsed_time(&self) -> f32;
1907
1908 fn delta_time(&self) -> f32;
1910}
1911
1912pub trait Renderer: ElapsedTime + Send {
1919 fn request_redraw(&mut self) {}
1922
1923 fn is_over_budget(&self) -> bool {
1926 false
1927 }
1928
1929 fn fill_rect(&mut self, rect: Rect, color: [f32; 4]);
1931 fn fill_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4]);
1932 fn fill_ellipse(&mut self, rect: Rect, color: [f32; 4]);
1934
1935 fn draw_3d_cube(&mut self, _rect: Rect, _color: [f32; 4], _rotation: [f32; 3]) {}
1938
1939 fn stroke_rect(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
1941 fn stroke_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4], stroke_width: f32);
1942 fn stroke_ellipse(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
1944 fn draw_line(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: [f32; 4], stroke_width: f32);
1946 fn fill_polygon(&mut self, _vertices: &[[f32; 2]], _color: [f32; 4]) {}
1948 fn stroke_polygon(&mut self, _vertices: &[[f32; 2]], _color: [f32; 4], _stroke_width: f32) {}
1950
1951 fn draw_text(&mut self, text: &str, x: f32, y: f32, size: f32, color: [f32; 4]);
1953 fn measure_text(&mut self, text: &str, size: f32) -> (f32, f32);
1955
1956 fn shape_rich_text(
1957 &mut self,
1958 _spans: &[cvkg_runic_text::TextSpan],
1959 _max_width: Option<f32>,
1960 _align: cvkg_runic_text::TextAlign,
1961 _overflow: cvkg_runic_text::TextOverflow,
1962 ) -> Option<cvkg_runic_text::ShapedText> {
1963 None
1964 }
1965
1966 fn draw_shaped_text(&mut self, _text: &cvkg_runic_text::ShapedText, _x: f32, _y: f32) {}
1967
1968 fn draw_texture(&mut self, _texture_id: u32, _rect: Rect) {}
1971 fn draw_image(&mut self, _image_name: &str, _rect: Rect) {}
1973 fn load_image(&mut self, _name: &str, _data: &[u8]) {}
1975 fn prewarm_vram(&mut self, _assets: Vec<(String, Vec<u8>)>) {}
1978
1979 fn get_pointer_position(&self) -> [f32; 2] {
1981 [0.0, 0.0]
1982 }
1983
1984 fn upload_data_texture(&mut self, _id: &str, _data: &[f32], _width: u32, _height: u32) {}
1987 fn draw_heatmap(&mut self, _texture_id: &str, _rect: Rect, _palette: &str) {}
1989
1990 fn draw_mesh(&mut self, _mesh: &Mesh, _color: [f32; 4], _transform: glam::Mat4) {}
1993
1994 fn draw_linear_gradient(
1997 &mut self,
1998 _rect: Rect,
1999 _start_color: [f32; 4],
2000 _end_color: [f32; 4],
2001 _angle: f32,
2002 ) {
2003 }
2004 fn draw_radial_gradient(
2006 &mut self,
2007 _rect: Rect,
2008 _inner_color: [f32; 4],
2009 _outer_color: [f32; 4],
2010 ) {
2011 }
2012 fn draw_drop_shadow(
2014 &mut self,
2015 _rect: Rect,
2016 _radius: f32,
2017 _color: [f32; 4],
2018 _blur: f32,
2019 _spread: f32,
2020 ) {
2021 }
2022 fn stroke_dashed_rounded_rect(
2024 &mut self,
2025 _rect: Rect,
2026 _radius: f32,
2027 _color: [f32; 4],
2028 _width: f32,
2029 _dash: f32,
2030 _gap: f32,
2031 ) {
2032 }
2033 fn draw_9slice(
2035 &mut self,
2036 _image_name: &str,
2037 _rect: Rect,
2038 _left: f32,
2039 _top: f32,
2040 _right: f32,
2041 _bottom: f32,
2042 ) {
2043 }
2044
2045 fn push_clip_rect(&mut self, _rect: Rect) {}
2049 fn pop_clip_rect(&mut self) {}
2051 fn current_clip_rect(&self) -> Rect {
2054 Rect::new(-10000.0, -10000.0, 20000.0, 20000.0)
2055 }
2056
2057 fn push_opacity(&mut self, _opacity: f32) {}
2061 fn pop_opacity(&mut self) {}
2063
2064 fn set_theme(&mut self, _theme: ColorTheme) {}
2066 fn set_rage(&mut self, _rage: f32) {}
2067 fn set_berserker_mode(&mut self, _state: BerserkerMode) {}
2068 fn trigger_shatter_event(&mut self, _origin: [f32; 2], _force: f32) {}
2069 fn set_scene(&mut self, _scene: &str) {}
2071
2072 fn capture_png(&mut self) -> Vec<u8> {
2075 Vec::new()
2076 }
2077 fn print(&mut self) {}
2079
2080 fn set_scene_preset(&mut self, _preset: u32) {}
2081
2082 fn bifrost(&mut self, _rect: Rect, _blur: f32, _saturation: f32, _opacity: f32) {}
2085 fn gungnir(&mut self, _rect: Rect, _color: [f32; 4], _radius: f32, _intensity: f32) {}
2087 fn mani_glow(&mut self, _rect: Rect, _color: [f32; 4], _radius: f32) {}
2089 fn push_mjolnir_slice(&mut self, _angle: f32, _offset: f32) {}
2091 fn pop_mjolnir_slice(&mut self) {}
2092 fn memoize(&mut self, id: u64, data_hash: u64, render_fn: &dyn Fn(&mut dyn Renderer));
2096 fn mjolnir_shatter(&mut self, _rect: Rect, _pieces: u32, _force: f32, _color: [f32; 4]) {}
2098 fn mjolnir_fluid_shatter(&mut self, _rect: Rect, _pieces: u32, _force: f32, _color: [f32; 4]) {}
2099 fn draw_mjolnir_bolt(&mut self, _from: [f32; 2], _to: [f32; 2], _color: [f32; 4]) {}
2101
2102 fn set_aria_role(&mut self, _role: &str) {}
2104 fn set_aria_label(&mut self, _label: &str) {}
2105
2106 fn register_shared_element(&mut self, _id: &str, _rect: Rect) {}
2108
2109 fn set_key(&mut self, _key: &str) {}
2111
2112 fn get_telemetry(&self) -> TelemetryData {
2115 TelemetryData::default()
2116 }
2117
2118 fn push_shadow(&mut self, _radius: f32, _color: [f32; 4], _offset: [f32; 2]) {}
2121 fn pop_shadow(&mut self) {}
2123
2124 fn push_vnode(&mut self, _rect: Rect, _name: &'static str) {}
2127 fn pop_vnode(&mut self) {}
2129 fn register_handler(
2131 &mut self,
2132 _event_type: &str,
2133 _handler: std::sync::Arc<dyn Fn(Event) + Send + Sync>,
2134 ) {
2135 }
2136
2137 fn set_z_index(&mut self, _z: f32) {}
2141 fn get_z_index(&self) -> f32 {
2143 0.0
2144 }
2145
2146 fn load_svg(&mut self, _name: &str, _svg_data: &[u8]) {}
2149 fn draw_svg(&mut self, _name: &str, _rect: Rect) {}
2151 fn serialize_svg(&mut self, _name: &str) -> Result<String, String> {
2155 Err("SVG serialization not supported by this renderer".into())
2156 }
2157 fn apply_svg_filter(
2161 &mut self,
2162 _name: &str,
2163 _filter_id: &str,
2164 _region: Rect,
2165 ) -> Result<String, String> {
2166 Err("SVG filter not supported by this renderer".into())
2167 }
2168
2169 fn push_transform(&mut self, _translation: [f32; 2], _scale: [f32; 2], _rotation: f32) {}
2174 fn push_affine(&mut self, _transform: [f32; 6]) {}
2177 fn pop_transform(&mut self) {}
2179 fn query_layout(&self, _node_id: scene_graph::NodeId) -> Option<Rect> {
2181 None
2182 }
2183 fn set_debug_layout(&mut self, _enabled: bool) {}
2185 fn get_debug_layout(&self) -> bool {
2187 false
2188 }
2189
2190 fn set_material(&mut self, _material: crate::material::DrawMaterial) {}
2194 fn current_material(&self) -> crate::material::DrawMaterial {
2196 crate::material::DrawMaterial::Opaque
2197 }
2198
2199 fn mimir_intent(&self) -> [f32; 2] {
2202 [0.0, 0.0]
2203 }
2204 fn magnetic_warp(&self, pointer: [f32; 2], anchor_rect: Rect, strength: f32) -> [f32; 2] {
2206 if strength <= 0.0 { return pointer; }
2207 let cx = anchor_rect.x + anchor_rect.width / 2.0;
2208 let cy = anchor_rect.y + anchor_rect.height / 2.0;
2209 let dx = pointer[0] - cx;
2210 let dy = pointer[1] - cy;
2211 let dist = (dx * dx + dy * dy).sqrt();
2212 let radius = 120.0;
2213 if dist < radius && dist > 0.0 {
2214 let force = (1.0 - dist / radius) * strength;
2215 [pointer[0] - dx * force, pointer[1] - dy * force]
2216 } else {
2217 pointer
2218 }
2219 }
2220 fn mani_glow_intensity(&self, pointer: [f32; 2], bounds: Rect, radius: f32) -> f32 {
2222 let cx = bounds.x + bounds.width / 2.0;
2223 let cy = bounds.y + bounds.height / 2.0;
2224 let dist = ((pointer[0] - cx).powi(2) + (pointer[1] - cy).powi(2)).sqrt();
2225 if dist < radius { (1.0 - dist / radius).clamp(0.0, 1.0) } else { 0.0 }
2226 }
2227 fn fafnir_evolve(&self, pointer: [f32; 2], bounds: Rect, max_scale: f32) -> f32 {
2229 let prox = self.mani_glow_intensity(pointer, bounds, 120.0);
2230 1.0 + (max_scale - 1.0) * prox
2231 }
2232 fn set_sdf_shape(&mut self, _shape: crate::layout::SdfShape) {}
2234}
2235
2236pub mod accessibility {
2238 pub fn relative_luminance(color: [f32; 4]) -> f32 {
2240 let f = |c: f32| {
2241 if c <= 0.03928 {
2242 c / 12.92
2243 } else {
2244 ((c + 0.055) / 1.055).powf(2.4)
2245 }
2246 };
2247 0.2126 * f(color[0]) + 0.7152 * f(color[1]) + 0.0722 * f(color[2])
2248 }
2249
2250 pub fn contrast_ratio(c1: [f32; 4], c2: [f32; 4]) -> f32 {
2252 let l1 = relative_luminance(c1);
2253 let l2 = relative_luminance(c2);
2254 let (light, dark) = if l1 > l2 { (l1, l2) } else { (l2, l1) };
2255 (light + 0.05) / (dark + 0.05)
2256 }
2257}
2258#[derive(
2260 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
2261)]
2262pub enum RenderTier {
2263 Tier1GPU = 0,
2265 Tier2GPU = 1,
2267 Tier3Fallback = 2,
2269}
2270use bytemuck::{Pod, Zeroable};
2274#[repr(C)]
2276#[derive(Copy, Clone, Debug, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
2277pub struct ColorTheme {
2278 pub primary_neon: [f32; 4], pub shatter_neon: [f32; 4],
2280 pub glass_base: [f32; 4],
2281 pub glass_edge: [f32; 4],
2282 pub rune_glow: [f32; 4],
2283 pub ember_core: [f32; 4],
2284 pub background_deep: [f32; 4],
2285 pub mani_glow: [f32; 4], pub glass_blur_strength: f32,
2287 pub shatter_edge_width: f32,
2288 pub neon_bloom_radius: f32,
2289 pub rune_opacity: f32,
2290}
2291impl ColorTheme {
2292 pub fn asgard() -> Self {
2294 Self {
2295 primary_neon: [0.0, 1.0, 0.95, 1.2],
2296 shatter_neon: [1.0, 0.0, 0.75, 1.5],
2297 glass_base: [0.04, 0.04, 0.06, 0.82],
2298 glass_edge: [0.0, 0.45, 0.55, 0.6],
2299 rune_glow: [0.75, 0.98, 1.0, 0.9],
2300 ember_core: [0.95, 0.12, 0.12, 1.0],
2301 background_deep: [0.01, 0.01, 0.03, 1.0],
2302 mani_glow: [0.7, 0.9, 1.0, 0.05],
2303 glass_blur_strength: 0.6,
2304 shatter_edge_width: 1.8,
2305 neon_bloom_radius: 0.022,
2306 rune_opacity: 0.55,
2307 }
2308 }
2309
2310 pub fn midgard() -> Self {
2312 Self {
2313 primary_neon: [0.2, 0.4, 0.6, 1.0], shatter_neon: [0.5, 0.5, 0.5, 1.0], glass_base: [0.1, 0.12, 0.15, 1.0], glass_edge: [0.3, 0.35, 0.4, 1.0], rune_glow: [0.8, 0.8, 0.8, 0.0], ember_core: [0.5, 0.5, 0.5, 1.0],
2319 background_deep: [0.05, 0.05, 0.07, 1.0],
2320 mani_glow: [0.0, 0.0, 0.0, 0.0], glass_blur_strength: 0.0, shatter_edge_width: 1.0,
2323 neon_bloom_radius: 0.0,
2324 rune_opacity: 0.0,
2325 }
2326 }
2327
2328 pub fn cyberpunk_viking() -> Self {
2329 Self::asgard()
2330 }
2331 pub fn vibrant_glass() -> Self {
2332 Self {
2333 primary_neon: [0.0, 1.0, 0.95, 1.2],
2334 shatter_neon: [1.0, 0.0, 0.75, 1.5],
2335 glass_base: [0.55, 0.6, 0.7, 0.08], glass_edge: [0.7, 0.85, 1.0, 0.45], rune_glow: [0.75, 0.98, 1.0, 0.9],
2338 ember_core: [1.0, 0.4, 0.1, 1.0],
2339 background_deep: [0.05, 0.05, 0.1, 1.0],
2340 mani_glow: [0.7, 0.9, 1.0, 0.05],
2341 glass_blur_strength: 0.9,
2342 shatter_edge_width: 1.8,
2343 neon_bloom_radius: 0.022,
2344 rune_opacity: 0.55,
2345 }
2346 }
2347}
2348impl Default for ColorTheme {
2349 fn default() -> Self {
2350 Self::vibrant_glass()
2351 }
2352}
2353#[repr(C)]
2355#[derive(Copy, Clone, Debug, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
2356pub struct SceneUniforms {
2357 pub view: glam::Mat4,
2358 pub proj: glam::Mat4,
2359 pub time: f32,
2360 pub delta_time: f32,
2361 pub resolution: [f32; 2],
2362 pub mouse: [f32; 2],
2363 pub mouse_velocity: [f32; 2],
2364 pub shatter_origin: [f32; 2],
2365 pub shatter_time: f32,
2366 pub shatter_force: f32,
2367 pub berzerker_rage: f32,
2368 pub berzerker_mode: u32,
2369 pub scroll_offset: f32,
2370 pub scale_factor: f32,
2371 pub scene_type: u32,
2372 pub _pad: [f32; 3], }
2374
2375pub const SCENE_AURORA: u32 = 0;
2376pub const SCENE_VOID: u32 = 1;
2377pub const SCENE_NEBULA: u32 = 2;
2378pub const SCENE_GLITCH: u32 = 3;
2379pub const SCENE_YGGDRASIL: u32 = 4;
2380
2381impl SceneUniforms {
2382 pub fn new(width: f32, height: f32) -> Self {
2383 Self {
2384 view: glam::Mat4::IDENTITY,
2385 proj: glam::Mat4::orthographic_lh(0.0, width, height, 0.0, -100.0, 100.0),
2386 time: 0.0,
2387 delta_time: 0.016,
2388 resolution: [width, height],
2389 mouse: [0.5, 0.5],
2390 mouse_velocity: [0.0, 0.0],
2391 shatter_origin: [0.5, 0.5],
2392 shatter_time: -100.0,
2393 shatter_force: 0.0,
2394 berzerker_rage: 0.0,
2395 berzerker_mode: 0,
2396 scroll_offset: 0.0,
2397 scale_factor: 1.0,
2398 scene_type: SCENE_AURORA,
2399 _pad: [0.0; 3],
2400 }
2401 }
2402}
2403#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
2405pub struct Mesh {
2406 pub vertices: Vec<[f32; 3]>,
2407 pub normals: Vec<[f32; 3]>,
2408 pub indices: Vec<u32>,
2409}
2410impl Mesh {
2411 pub fn from_obj(data: &[u8]) -> anyhow::Result<Vec<Self>> {
2412 let mut cursor = std::io::Cursor::new(data);
2413 let (models, _) = tobj::load_obj_buf(&mut cursor, &tobj::LoadOptions::default(), |_| {
2414 Ok((Vec::new(), Default::default()))
2415 })?;
2416 let mut meshes = Vec::new();
2417 for m in models {
2418 let mesh = m.mesh;
2419 let vertices: Vec<[f32; 3]> = mesh
2420 .positions
2421 .chunks(3)
2422 .map(|c| [c[0], c[1], c[2]])
2423 .collect();
2424 let normals = if mesh.normals.is_empty() {
2425 vec![[0.0, 0.0, 1.0]; vertices.len()]
2426 } else {
2427 mesh.normals.chunks(3).map(|c| [c[0], c[1], c[2]]).collect()
2428 };
2429 meshes.push(Mesh {
2430 vertices,
2431 normals,
2432 indices: mesh.indices,
2433 });
2434 }
2435 Ok(meshes)
2436 }
2437 pub fn from_stl(data: &[u8]) -> anyhow::Result<Self> {
2438 let mut cursor = std::io::Cursor::new(data);
2439 let stl = stl_io::read_stl(&mut cursor)?;
2440 let vertices: Vec<[f32; 3]> = stl.vertices.iter().map(|v| [v[0], v[1], v[2]]).collect();
2441 let mut indices = Vec::new();
2442 for face in stl.faces {
2443 indices.push(face.vertices[0] as u32);
2444 indices.push(face.vertices[1] as u32);
2445 indices.push(face.vertices[2] as u32);
2446 }
2447 let normals = vec![[0.0, 0.0, 1.0]; vertices.len()];
2448 Ok(Mesh {
2449 vertices,
2450 normals,
2451 indices,
2452 })
2453 }
2454}
2455pub trait FrameRenderer<E = ()>: Renderer {
2458 fn begin_frame(&mut self) -> E;
2459 fn render_frame(&mut self) {
2460 }
2462 fn end_frame(&mut self, encoder: E);
2463}
2464use std::sync::Arc;
2465type SubscriberList<T> = Arc<std::sync::Mutex<Vec<Box<dyn Fn(&T) + Send + Sync>>>>;
2466#[derive(Clone)]
2468pub struct State<T: Clone + Send + Sync + 'static> {
2469 swap: Arc<arc_swap::ArcSwap<T>>,
2470 metadata_swap: Arc<arc_swap::ArcSwap<Option<agents::MutationMetadata>>>,
2471 #[cfg(not(target_arch = "wasm32"))]
2472 tvar: Arc<stm::TVar<T>>,
2473 #[cfg(not(target_arch = "wasm32"))]
2474 metadata_tvar: Arc<stm::TVar<Option<agents::MutationMetadata>>>,
2475 subscribers: SubscriberList<T>,
2476 version: Arc<std::sync::atomic::AtomicU64>,
2477 resolution: agents::ConflictResolution,
2478}
2479impl<T: Clone + Send + Sync + 'static> State<T> {
2480 pub fn new(value: T) -> Self {
2482 #[cfg(not(target_arch = "wasm32"))]
2483 let tvar = Arc::new(stm::TVar::new(value.clone()));
2484 #[cfg(not(target_arch = "wasm32"))]
2485 let metadata_tvar = Arc::new(stm::TVar::new(None));
2486 Self {
2487 swap: Arc::new(arc_swap::ArcSwap::from_pointee(value)),
2488 metadata_swap: Arc::new(arc_swap::ArcSwap::new(Arc::new(None))),
2489 #[cfg(not(target_arch = "wasm32"))]
2490 tvar,
2491 #[cfg(not(target_arch = "wasm32"))]
2492 metadata_tvar,
2493 subscribers: Arc::new(std::sync::Mutex::new(Vec::new())),
2494 version: Arc::new(std::sync::atomic::AtomicU64::new(0)),
2495 resolution: agents::ConflictResolution::default(),
2496 }
2497 }
2498 pub fn with_resolution(mut self, resolution: agents::ConflictResolution) -> Self {
2500 self.resolution = resolution;
2501 self
2502 }
2503 pub fn get(&self) -> T {
2505 (**self.swap.load()).clone()
2506 }
2507 pub fn set(&self, value: T) {
2509 #[cfg(not(target_arch = "wasm32"))]
2510 let (was_skipped, final_val, final_meta) = stm::atomically(|tx| {
2511 let new_meta = agents::get_current_mutation_metadata();
2512 let existing_meta = self.metadata_tvar.read(tx)?;
2513 let mut skip = false;
2514 if self.resolution == agents::ConflictResolution::PriorityWins
2515 && let (Some(new_m), Some(old_m)) = (new_meta, existing_meta)
2516 && new_m.priority < old_m.priority
2517 {
2518 skip = true;
2519 }
2520 if !skip {
2521 self.tvar.write(tx, value.clone())?;
2522 self.metadata_tvar.write(tx, new_meta)?;
2523 Ok((false, value.clone(), new_meta))
2524 } else {
2525 Ok((true, self.tvar.read(tx)?, existing_meta))
2526 }
2527 });
2528 #[cfg(target_arch = "wasm32")]
2529 let (was_skipped, final_val, final_meta) =
2530 (false, value, agents::get_current_mutation_metadata());
2531 if was_skipped {
2532 if let (Some(new_m), Some(old_m)) =
2533 (agents::get_current_mutation_metadata(), final_meta)
2534 {
2535 agents::notify_conflict(agents::ConflictEvent {
2536 agent_id: new_m.agent_id,
2537 priority: new_m.priority,
2538 existing_agent_id: old_m.agent_id,
2539 existing_priority: old_m.priority,
2540 timestamp_ms: new_m.timestamp_ms,
2541 });
2542 }
2543 return;
2544 }
2545 self.swap.store(Arc::new(final_val.clone()));
2546 self.metadata_swap.store(Arc::new(final_meta));
2547 self.version
2548 .fetch_add(1, std::sync::atomic::Ordering::Release);
2549 let subs = Arc::clone(&self.subscribers);
2550 if crate::is_batching() {
2551 crate::enqueue_batch_task(Box::new(move || {
2552 let s = subs.lock().unwrap();
2553 for cb in s.iter() {
2554 cb(&final_val);
2555 }
2556 }));
2557 } else {
2558 let s = subs.lock().unwrap();
2559 for cb in s.iter() {
2560 cb(&final_val);
2561 }
2562 }
2563 }
2564 pub fn mutate<F: Fn(&T) -> T>(&self, f: F) {
2565 #[cfg(not(target_arch = "wasm32"))]
2566 {
2567 let (was_skipped, final_val, final_meta) = stm::atomically(|tx| {
2568 let new_meta = agents::get_current_mutation_metadata();
2569 let existing_meta = self.metadata_tvar.read(tx)?;
2570 let mut skip = false;
2571 if self.resolution == agents::ConflictResolution::PriorityWins
2572 && let (Some(new_m), Some(old_m)) = (new_meta, existing_meta)
2573 && new_m.priority < old_m.priority
2574 {
2575 skip = true;
2576 }
2577 if !skip {
2578 let current = self.tvar.read(tx)?;
2579 let next = f(¤t);
2580 self.tvar.write(tx, next.clone())?;
2581 self.metadata_tvar.write(tx, new_meta)?;
2582 Ok((false, next, new_meta))
2583 } else {
2584 Ok((true, self.tvar.read(tx)?, existing_meta))
2585 }
2586 });
2587 if was_skipped {
2588 if let (Some(new_m), Some(old_m)) =
2589 (agents::get_current_mutation_metadata(), final_meta)
2590 {
2591 agents::notify_conflict(agents::ConflictEvent {
2592 agent_id: new_m.agent_id,
2593 priority: new_m.priority,
2594 existing_agent_id: old_m.agent_id,
2595 existing_priority: old_m.priority,
2596 timestamp_ms: new_m.timestamp_ms,
2597 });
2598 }
2599 return;
2600 }
2601 self.swap.store(Arc::new(final_val.clone()));
2602 self.metadata_swap.store(Arc::new(final_meta));
2603 self.version
2604 .fetch_add(1, std::sync::atomic::Ordering::Release);
2605 let subs = Arc::clone(&self.subscribers);
2606 if crate::is_batching() {
2607 crate::enqueue_batch_task(Box::new(move || {
2608 let s = subs.lock().unwrap();
2609 for cb in s.iter() {
2610 cb(&final_val);
2611 }
2612 }));
2613 } else {
2614 let s = subs.lock().unwrap();
2615 for cb in s.iter() {
2616 cb(&final_val);
2617 }
2618 }
2619 }
2620 #[cfg(target_arch = "wasm32")]
2621 {
2622 self.set(f(&self.get()));
2623 }
2624 }
2625 pub fn version(&self) -> u64 {
2627 self.version.load(std::sync::atomic::Ordering::Acquire)
2628 }
2629 pub fn subscribe<F: Fn(&T) + Send + Sync + 'static>(&self, callback: F) {
2631 self.subscribers.lock().unwrap().push(Box::new(callback));
2632 }
2633}
2634use crate::runtime::NodeStateSnapshot;
2635use std::sync::OnceLock;
2636use std::sync::atomic::{AtomicBool, Ordering};
2637pub static SYSTEM_STATE: OnceLock<Arc<arc_swap::ArcSwap<KnowledgeState>>> = OnceLock::new();
2639#[cfg(not(target_arch = "wasm32"))]
2640static KNOWLEDGE_TVAR: OnceLock<stm::TVar<KnowledgeState>> = OnceLock::new();
2641static IS_BATCHING: AtomicBool = AtomicBool::new(false);
2642pub static IS_RENDERING: AtomicBool = AtomicBool::new(false);
2643pub static LAYOUT_DIRTY: AtomicBool = AtomicBool::new(false);
2644type BatchQueue = OnceLock<std::sync::Mutex<Vec<Box<dyn FnOnce() + Send + Sync>>>>;
2645static BATCH_QUEUE: BatchQueue = OnceLock::new();
2646static STATE_WRITE_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
2649pub fn is_batching() -> bool {
2651 IS_BATCHING.load(Ordering::Acquire)
2652}
2653pub fn is_rendering() -> bool {
2655 IS_RENDERING.load(Ordering::Acquire)
2656}
2657pub fn begin_render_phase() {
2659 IS_RENDERING.store(true, Ordering::Release);
2660}
2661pub fn end_render_phase() {
2663 IS_RENDERING.store(false, Ordering::Release);
2664}
2665pub fn enqueue_batch_task(task: Box<dyn FnOnce() + Send + Sync>) {
2667 let mut queue = BATCH_QUEUE
2668 .get_or_init(|| std::sync::Mutex::new(Vec::new()))
2669 .lock()
2670 .unwrap();
2671 queue.push(task);
2672}
2673pub fn batch<F: FnOnce()>(f: F) {
2677 if IS_BATCHING.swap(true, Ordering::AcqRel) {
2678 f();
2680 return;
2681 }
2682 f();
2683 IS_BATCHING.store(false, Ordering::Release);
2684 let mut queue = BATCH_QUEUE
2685 .get_or_init(|| std::sync::Mutex::new(Vec::new()))
2686 .lock()
2687 .unwrap();
2688 let tasks: Vec<_> = queue.drain(..).collect();
2689 drop(queue);
2690 for task in tasks {
2691 task();
2692 }
2693}
2694pub fn get_system_state() -> Arc<arc_swap::ArcSwap<KnowledgeState>> {
2696 SYSTEM_STATE
2697 .get_or_init(|| Arc::new(arc_swap::ArcSwap::from_pointee(KnowledgeState::default())))
2698 .clone()
2699}
2700pub fn load_system_state() -> arc_swap::Guard<Arc<KnowledgeState>> {
2701 get_system_state().load()
2702}
2703pub fn update_system_state<F>(f: F)
2704where
2705 F: Fn(&KnowledgeState) -> KnowledgeState,
2706{
2707 let _lock = STATE_WRITE_MUTEX.lock().unwrap();
2708 if is_rendering() {
2709 log::warn!(
2710 "LAYOUT THRASH DETECTED: System state mutated during render phase. This may trigger redundant layout passes and impact performance."
2711 );
2712 }
2713 LAYOUT_DIRTY.store(true, Ordering::SeqCst);
2714 let swap = get_system_state();
2715 let current = swap.load();
2716 let new_state = Arc::new(f(¤t));
2717 swap.store(Arc::clone(&new_state));
2718 #[cfg(not(target_arch = "wasm32"))]
2719 {
2720 let tvar = KNOWLEDGE_TVAR.get_or_init(|| stm::TVar::new((*new_state).clone()));
2721 stm::atomically(|tx| tvar.write(tx, (*new_state).clone()));
2722 }
2723}
2724pub fn transact_system_state<F>(f: F)
2725where
2726 F: Fn(&KnowledgeState) -> KnowledgeState,
2727{
2728 let _lock = STATE_WRITE_MUTEX.lock().unwrap();
2729 #[cfg(not(target_arch = "wasm32"))]
2730 {
2731 if is_rendering() {
2732 log::warn!(
2733 "LAYOUT THRASH DETECTED: System state mutated during render phase. This may trigger redundant layout passes and impact performance."
2734 );
2735 }
2736 let tvar = KNOWLEDGE_TVAR
2737 .get_or_init(|| stm::TVar::new((**get_system_state().load()).clone()))
2738 .clone();
2739 let new_state = stm::atomically(move |tx| {
2740 let current = tvar.read(tx)?;
2741 let next = f(¤t);
2742 tvar.write(tx, next.clone())?;
2743 Ok(next)
2744 });
2745 get_system_state().store(Arc::new(new_state));
2746 }
2747 #[cfg(target_arch = "wasm32")]
2748 {
2749 if is_rendering() {
2750 log::warn!(
2751 "LAYOUT THRASH DETECTED: System state mutated during render phase. This may trigger redundant layout passes and impact performance."
2752 );
2753 }
2754 update_system_state(f);
2755 }
2756}
2757impl KnowledgeState {
2758 pub fn new() -> Self {
2760 Self::default()
2761 }
2762 pub fn set_component_state<T: 'static + Send + Sync>(&mut self, id: u64, state: T) {
2764 self.component_states
2765 .insert(id, Arc::new(std::sync::RwLock::new(state)));
2766 }
2767 pub fn get_component_state<T: 'static + Send + Sync>(
2769 &self,
2770 id: u64,
2771 ) -> Option<Arc<std::sync::RwLock<T>>> {
2772 let lock = self.component_states.get(&id)?;
2773 let any_ref = lock.read().ok()?;
2777 if any_ref.is::<T>() {
2778 drop(any_ref);
2780 let cloned: Arc<std::sync::RwLock<dyn std::any::Any + Send + Sync>> = Arc::clone(lock);
2781 Some(unsafe {
2784 let raw = Arc::into_raw(cloned);
2785 Arc::from_raw(raw as *const std::sync::RwLock<T>)
2786 })
2787 } else {
2788 None
2789 }
2790 }
2791 pub fn remember(&mut self, fragment: KnowledgeFragment) {
2793 self.fragments.insert(fragment.id.clone(), fragment);
2794 }
2795 pub fn process_query(&mut self, query: &str) {
2797 let query_lower = query.to_lowercase();
2798 let mut results: Vec<(f32, String)> = self
2799 .fragments
2800 .iter()
2801 .map(|(id, frag)| {
2802 let mut score = 0.0;
2803 if frag.summary.to_lowercase().contains(&query_lower) {
2804 score += 1.0;
2805 }
2806 if frag.source.to_lowercase().contains(&query_lower) {
2807 score += 0.5;
2808 }
2809 (score, id.clone())
2810 })
2811 .filter(|(score, _)| *score > 0.0)
2812 .collect();
2813 results.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
2815 self.last_query_results = results.into_iter().map(|(_, id)| id).take(5).collect();
2816 }
2817 pub fn snapshot(&self) -> Vec<NodeStateSnapshot> {
2819 let mut snapshots = Vec::new();
2820 for frag in self.fragments.values() {
2822 if let Ok(val) = serde_json::to_value(frag) {
2823 snapshots.push(NodeStateSnapshot { id: 0, state: val });
2824 }
2825 }
2826 snapshots
2827 }
2828}
2829#[derive(Clone)]
2831pub struct Binding<T: Clone + Send + Sync + 'static> {
2832 swap: Arc<arc_swap::ArcSwap<T>>,
2833 #[cfg(not(target_arch = "wasm32"))]
2834 tvar: Arc<stm::TVar<T>>,
2835 version: Arc<std::sync::atomic::AtomicU64>,
2836}
2837impl<T: Clone + Send + Sync + 'static> Binding<T> {
2838 pub fn from_state(state: &State<T>) -> Self {
2840 Self {
2841 swap: Arc::clone(&state.swap),
2842 #[cfg(not(target_arch = "wasm32"))]
2843 tvar: Arc::clone(&state.tvar),
2844 version: Arc::clone(&state.version),
2845 }
2846 }
2847 pub fn get(&self) -> T {
2849 (**self.swap.load()).clone()
2850 }
2851 pub fn set(&self, value: T) {
2853 self.swap.store(Arc::new(value.clone()));
2854 #[cfg(not(target_arch = "wasm32"))]
2855 {
2856 let tvar = Arc::clone(&self.tvar);
2857 let v = value.clone();
2858 stm::atomically(move |tx| tvar.write(tx, v.clone()));
2859 }
2860 self.version
2861 .fetch_add(1, std::sync::atomic::Ordering::Release);
2862 }
2863 pub fn version(&self) -> u64 {
2865 self.version.load(std::sync::atomic::Ordering::Acquire)
2866 }
2867}
2868#[cfg(not(target_arch = "wasm32"))]
2869pub fn transact_pair<A, B, F>(state_a: &State<A>, state_b: &State<B>, f: F)
2870where
2871 A: Clone + Send + Sync + 'static,
2872 B: Clone + Send + Sync + 'static,
2873 F: Fn(&A, &B) -> (A, B),
2874{
2875 let tvar_a = Arc::clone(&state_a.tvar);
2876 let tvar_b = Arc::clone(&state_b.tvar);
2877 let (new_a, new_b) = stm::atomically(move |tx| {
2878 let a = tvar_a.read(tx)?;
2879 let b = tvar_b.read(tx)?;
2880 let (na, nb) = f(&a, &b);
2881 tvar_a.write(tx, na.clone())?;
2882 tvar_b.write(tx, nb.clone())?;
2883 Ok((na, nb))
2884 });
2885 state_a.swap.store(Arc::new(new_a.clone()));
2886 state_b.swap.store(Arc::new(new_b.clone()));
2887 state_a
2888 .version
2889 .fetch_add(1, std::sync::atomic::Ordering::Release);
2890 state_b
2891 .version
2892 .fetch_add(1, std::sync::atomic::Ordering::Release);
2893 let subs_a = Arc::clone(&state_a.subscribers);
2894 let subs_b = Arc::clone(&state_b.subscribers);
2895 if crate::is_batching() {
2896 crate::enqueue_batch_task(Box::new(move || {
2897 {
2898 let s = subs_a.lock().unwrap();
2899 for cb in s.iter() {
2900 cb(&new_a);
2901 }
2902 }
2903 {
2904 let s = subs_b.lock().unwrap();
2905 for cb in s.iter() {
2906 cb(&new_b);
2907 }
2908 }
2909 }));
2910 } else {
2911 {
2912 let s = subs_a.lock().unwrap();
2913 for cb in s.iter() {
2914 cb(&new_a);
2915 }
2916 }
2917 {
2918 let s = subs_b.lock().unwrap();
2919 for cb in s.iter() {
2920 cb(&new_b);
2921 }
2922 }
2923 }
2924}
2925use std::any::TypeId;
2926use std::sync::Mutex;
2927pub(crate) static ENVIRONMENT: OnceLock<
2929 Mutex<HashMap<TypeId, Box<dyn std::any::Any + Send + Sync>>>,
2930> = OnceLock::new();
2931pub trait EnvKey: 'static + Send + Sync {
2934 type Value: Clone + Send + Sync + 'static;
2936 fn default_value() -> Self::Value;
2938}
2939pub struct YggdrasilKey;
2941impl EnvKey for YggdrasilKey {
2942 type Value = YggdrasilTokens;
2943 fn default_value() -> Self::Value {
2944 default_tokens()
2945 }
2946}
2947#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2950pub enum Appearance {
2951 Light,
2952 Dark,
2953}
2954#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2956pub enum Orientation {
2957 Horizontal,
2958 Vertical,
2959}
2960#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
2962pub enum Alignment {
2963 #[default]
2964 Center,
2965 Leading,
2966 Trailing,
2967 Top,
2968 Bottom,
2969}
2970#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
2972pub enum Distribution {
2973 #[default]
2974 Fill,
2975 Center,
2976 Leading,
2977 Trailing,
2978 SpaceBetween,
2979 SpaceAround,
2980 SpaceEvenly,
2981}
2982#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
2984pub struct Color {
2985 pub r: f32,
2986 pub g: f32,
2987 pub b: f32,
2988 pub a: f32,
2989}
2990impl Color {
2991 pub const BLACK: Color = Color {
2992 r: 0.0,
2993 g: 0.0,
2994 b: 0.0,
2995 a: 1.0,
2996 };
2997 pub const WHITE: Color = Color {
2998 r: 1.0,
2999 g: 1.0,
3000 b: 1.0,
3001 a: 1.0,
3002 };
3003 pub const TRANSPARENT: Color = Color {
3004 r: 0.0,
3005 g: 0.0,
3006 b: 0.0,
3007 a: 0.0,
3008 };
3009 pub const RED: Color = Color {
3010 r: 1.0,
3011 g: 0.0,
3012 b: 0.0,
3013 a: 1.0,
3014 };
3015 pub const GREEN: Color = Color {
3016 r: 0.0,
3017 g: 1.0,
3018 b: 0.0,
3019 a: 1.0,
3020 };
3021 pub const BLUE: Color = Color {
3022 r: 0.0,
3023 g: 0.0,
3024 b: 1.0,
3025 a: 1.0,
3026 };
3027 pub const VIKING_GOLD: Color = Color {
3028 r: 1.0,
3029 g: 0.84,
3030 b: 0.0,
3031 a: 1.0,
3032 };
3033 pub const MAGENTA_LIQUID: Color = Color {
3034 r: 1.0,
3035 g: 0.0,
3036 b: 1.0,
3037 a: 1.0,
3038 };
3039 pub const TACTICAL_OBSIDIAN: Color = Color {
3040 r: 0.05,
3041 g: 0.05,
3042 b: 0.07,
3043 a: 1.0,
3044 };
3045 pub fn relative_luminance(&self) -> f32 {
3047 fn res(c: f32) -> f32 {
3048 if c <= 0.03928 {
3049 c / 12.92
3050 } else {
3051 ((c + 0.055) / 1.055).powf(2.4)
3052 }
3053 }
3054 0.2126 * res(self.r) + 0.7152 * res(self.g) + 0.0722 * res(self.b)
3055 }
3056 pub fn contrast_ratio(&self, other: &Color) -> f32 {
3058 let l1 = self.relative_luminance();
3059 let l2 = other.relative_luminance();
3060 if l1 > l2 {
3061 (l1 + 0.05) / (l2 + 0.05)
3062 } else {
3063 (l2 + 0.05) / (l1 + 0.05)
3064 }
3065 }
3066 pub const CYAN: Color = Color {
3067 r: 0.0,
3068 g: 1.0,
3069 b: 1.0,
3070 a: 1.0,
3071 };
3072 pub const YELLOW: Color = Color {
3073 r: 1.0,
3074 g: 1.0,
3075 b: 0.0,
3076 a: 1.0,
3077 };
3078 pub const MAGENTA: Color = Color {
3079 r: 1.0,
3080 g: 0.0,
3081 b: 1.0,
3082 a: 1.0,
3083 };
3084 pub const GRAY: Color = Color {
3085 r: 0.5,
3086 g: 0.5,
3087 b: 0.5,
3088 a: 1.0,
3089 };
3090 pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
3092 Self { r, g, b, a }
3093 }
3094 pub fn as_array(&self) -> [f32; 4] {
3096 [self.r, self.g, self.b, self.a]
3097 }
3098}
3099impl View for Color {
3100 type Body = Never;
3101 fn body(self) -> Self::Body {
3102 unreachable!()
3103 }
3104 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
3105 renderer.fill_rect(rect, self.as_array());
3106 }
3107}
3108pub struct AppearanceKey;
3110impl EnvKey for AppearanceKey {
3111 type Value = Appearance;
3112 fn default_value() -> Self::Value {
3113 Appearance::Dark }
3115}
3116pub struct StyleResolver;
3118impl StyleResolver {
3119 pub fn color(key: &str) -> String {
3121 let tokens = Environment::<YggdrasilKey>::new().get();
3122 let appearance = Environment::<AppearanceKey>::new().get();
3123 let is_dark = appearance == Appearance::Dark;
3124 tokens
3125 .get_color(key, is_dark)
3126 .unwrap_or_else(|| "#FF00FF".to_string()) }
3128 pub fn get<T: FromStr>(category: &str, key: &str) -> Option<T> {
3130 let tokens = Environment::<YggdrasilKey>::new().get();
3131 let appearance = Environment::<AppearanceKey>::new().get();
3132 let is_dark = appearance == Appearance::Dark;
3133 tokens.get(category, key, is_dark)
3134 }
3135 pub fn color_array(key: &str) -> [f32; 4] {
3139 let hex = Self::color(key);
3140 parse_hex_color(&hex)
3141 }
3142}
3143
3144fn parse_hex_color(hex: &str) -> [f32; 4] {
3146 let hex = hex.trim_start_matches('#');
3147 if hex.len() >= 6 {
3148 let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(255) as f32 / 255.0;
3149 let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(0) as f32 / 255.0;
3150 let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(255) as f32 / 255.0;
3151 let a = if hex.len() >= 8 {
3152 u8::from_str_radix(&hex[6..8], 16).unwrap_or(255) as f32 / 255.0
3153 } else {
3154 1.0
3155 };
3156 [r, g, b, a]
3157 } else {
3158 [1.0, 0.0, 1.0, 1.0] }
3160}
3161
3162pub fn default_tokens() -> YggdrasilTokens {
3164 let mut tokens = YggdrasilTokens::new();
3165 tokens.color.insert(
3167 "background".to_string(),
3168 TokenValue::Single {
3169 value: "#000000".to_string(), },
3171 );
3172 tokens.color.insert(
3173 "primary".to_string(),
3174 TokenValue::Single {
3175 value: "#00FFFF".to_string(), },
3177 );
3178 tokens.color.insert(
3179 "secondary".to_string(),
3180 TokenValue::Single {
3181 value: "#FF00FF".to_string(), },
3183 );
3184 tokens.color.insert(
3185 "surface".to_string(),
3186 TokenValue::Adaptive {
3187 light: "#FFFFFF".to_string(),
3188 dark: "#121212".to_string(),
3189 },
3190 );
3191 tokens.color.insert(
3192 "text".to_string(),
3193 TokenValue::Adaptive {
3194 light: "#000000".to_string(),
3195 dark: "#FFFFFF".to_string(),
3196 },
3197 );
3198 tokens.color.insert(
3200 "surface_elevated".to_string(),
3201 TokenValue::Adaptive {
3202 light: "#FFFFFF".to_string(),
3203 dark: "#1A1A24".to_string(),
3204 },
3205 );
3206 tokens.color.insert(
3207 "surface_overlay".to_string(),
3208 TokenValue::Adaptive {
3209 light: "#FFFFFF".to_string(),
3210 dark: "#1E1E2E".to_string(),
3211 },
3212 );
3213 tokens.color.insert(
3214 "border".to_string(),
3215 TokenValue::Adaptive {
3216 light: "#D0D0D8".to_string(),
3217 dark: "#2A2A3A".to_string(),
3218 },
3219 );
3220 tokens.color.insert(
3221 "border_strong".to_string(),
3222 TokenValue::Adaptive {
3223 light: "#A0A0B0".to_string(),
3224 dark: "#3A3A50".to_string(),
3225 },
3226 );
3227 tokens.color.insert(
3228 "text_muted".to_string(),
3229 TokenValue::Adaptive {
3230 light: "#606070".to_string(),
3231 dark: "#8080A0".to_string(),
3232 },
3233 );
3234 tokens.color.insert(
3235 "text_dim".to_string(),
3236 TokenValue::Adaptive {
3237 light: "#9090A0".to_string(),
3238 dark: "#505070".to_string(),
3239 },
3240 );
3241 tokens.color.insert(
3242 "accent".to_string(),
3243 TokenValue::Single {
3244 value: "#00FFFF".to_string(), },
3246 );
3247 tokens.color.insert(
3248 "accent_hover".to_string(),
3249 TokenValue::Single {
3250 value: "#33FFFF".to_string(),
3251 },
3252 );
3253 tokens.color.insert(
3254 "success".to_string(),
3255 TokenValue::Single {
3256 value: "#00E676".to_string(),
3257 },
3258 );
3259 tokens.color.insert(
3260 "warning".to_string(),
3261 TokenValue::Single {
3262 value: "#FFB300".to_string(),
3263 },
3264 );
3265 tokens.color.insert(
3266 "error".to_string(),
3267 TokenValue::Single {
3268 value: "#FF5252".to_string(),
3269 },
3270 );
3271 tokens.color.insert(
3272 "info".to_string(),
3273 TokenValue::Single {
3274 value: "#448AFF".to_string(),
3275 },
3276 );
3277 tokens.color.insert(
3278 "hover".to_string(),
3279 TokenValue::Adaptive {
3280 light: "#F0F0F5".to_string(),
3281 dark: "#252535".to_string(),
3282 },
3283 );
3284 tokens.color.insert(
3285 "active".to_string(),
3286 TokenValue::Adaptive {
3287 light: "#E0E0EB".to_string(),
3288 dark: "#303045".to_string(),
3289 },
3290 );
3291 tokens.color.insert(
3292 "disabled".to_string(),
3293 TokenValue::Adaptive {
3294 light: "#E8E8F0".to_string(),
3295 dark: "#1A1A28".to_string(),
3296 },
3297 );
3298 tokens.color.insert(
3299 "disabled_text".to_string(),
3300 TokenValue::Adaptive {
3301 light: "#B0B0C0".to_string(),
3302 dark: "#404060".to_string(),
3303 },
3304 );
3305 tokens.color.insert(
3306 "focus_ring".to_string(),
3307 TokenValue::Single {
3308 value: "#00FFFF".to_string(),
3309 },
3310 );
3311 tokens.color.insert(
3312 "shadow".to_string(),
3313 TokenValue::Adaptive {
3314 light: "#00000020".to_string(),
3315 dark: "#00000060".to_string(),
3316 },
3317 );
3318 tokens.color.insert(
3319 "code_bg".to_string(),
3320 TokenValue::Adaptive {
3321 light: "#F5F5FA".to_string(),
3322 dark: "#0D0D18".to_string(),
3323 },
3324 );
3325 tokens.bifrost.insert(
3327 "blur".to_string(),
3328 TokenValue::Single {
3329 value: "25.0".to_string(),
3330 },
3331 );
3332 tokens.bifrost.insert(
3333 "saturation".to_string(),
3334 TokenValue::Single {
3335 value: "1.2".to_string(),
3336 },
3337 );
3338 tokens.bifrost.insert(
3339 "opacity".to_string(),
3340 TokenValue::Single {
3341 value: "0.65".to_string(),
3342 },
3343 );
3344 tokens.gungnir.insert(
3346 "intensity".to_string(),
3347 TokenValue::Single {
3348 value: "1.0".to_string(),
3349 },
3350 );
3351 tokens.gungnir.insert(
3352 "radius".to_string(),
3353 TokenValue::Single {
3354 value: "15.0".to_string(),
3355 },
3356 );
3357 tokens.mjolnir.insert(
3359 "clip_angle".to_string(),
3360 TokenValue::Single {
3361 value: "12.0".to_string(),
3362 },
3363 );
3364 tokens.mjolnir.insert(
3365 "border_width".to_string(),
3366 TokenValue::Single {
3367 value: "2.0".to_string(),
3368 },
3369 );
3370 tokens.anim.insert(
3372 "stiffness".to_string(),
3373 TokenValue::Single {
3374 value: "170.0".to_string(),
3375 },
3376 );
3377 tokens.anim.insert(
3378 "damping".to_string(),
3379 TokenValue::Single {
3380 value: "26.0".to_string(),
3381 },
3382 );
3383 tokens.anim.insert(
3384 "mass".to_string(),
3385 TokenValue::Single {
3386 value: "1.0".to_string(),
3387 },
3388 );
3389 tokens.accessibility.insert(
3391 "reduce_motion".to_string(),
3392 TokenValue::Single {
3393 value: "false".to_string(),
3394 },
3395 );
3396 tokens
3397}
3398pub struct Environment<K: EnvKey> {
3400 _marker: std::marker::PhantomData<K>,
3401}
3402impl<K: EnvKey> Default for Environment<K> {
3403 fn default() -> Self {
3404 Self::new()
3405 }
3406}
3407impl<K: EnvKey> Environment<K> {
3408 pub fn new() -> Self {
3410 Self {
3411 _marker: std::marker::PhantomData,
3412 }
3413 }
3414 pub fn get(&self) -> K::Value {
3416 if let Some(env_store) = ENVIRONMENT.get() {
3417 let env_lock = env_store.lock().unwrap();
3418 if let Some(val) = env_lock.get(&std::any::TypeId::of::<K>()) {
3419 if let Some(typed_val) = val.downcast_ref::<K::Value>() {
3420 return typed_val.clone();
3421 } else {
3422 log::warn!(
3423 "Environment: Downcast failed for key type {:?}",
3424 std::any::type_name::<K>()
3425 );
3426 }
3427 } else {
3428 log::debug!(
3429 "Environment: Key not found: {:?}. Returning default.",
3430 std::any::type_name::<K>()
3431 );
3432 }
3433 } else {
3434 log::debug!(
3435 "Environment: Store not initialized. Key: {:?}. Returning default.",
3436 std::any::type_name::<K>()
3437 );
3438 }
3439 K::default_value()
3440 }
3441}
3442pub mod env {
3444 pub fn insert<K: super::EnvKey>(value: K::Value) {
3446 let store = super::ENVIRONMENT
3447 .get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()));
3448 let mut env_map = store.lock().unwrap();
3449 env_map.insert(std::any::TypeId::of::<K>(), Box::new(value));
3450 }
3451 pub fn remove<K: super::EnvKey>() {
3453 if let Some(store) = super::ENVIRONMENT.get() {
3454 let mut env_map = store.lock().unwrap();
3455 env_map.remove(&std::any::TypeId::of::<K>());
3456 }
3457 }
3458}
3459#[derive(Debug, Clone, Copy, PartialEq)]
3462pub struct Size {
3463 pub width: f32,
3464 pub height: f32,
3465}
3466
3467impl Size {
3468 pub const ZERO: Self = Self {
3469 width: 0.0,
3470 height: 0.0,
3471 };
3472
3473 pub fn new(width: f32, height: f32) -> Self {
3474 Self { width, height }
3475 }
3476}
3477
3478#[derive(Debug, Clone, Copy, PartialEq)]
3480pub struct EdgeInsets {
3481 pub top: f32,
3482 pub leading: f32,
3483 pub bottom: f32,
3484 pub trailing: f32,
3485}
3486
3487impl EdgeInsets {
3488 pub fn all(value: f32) -> Self {
3490 Self {
3491 top: value,
3492 leading: value,
3493 bottom: value,
3494 trailing: value,
3495 }
3496 }
3497
3498 pub fn vertical(value: f32) -> Self {
3500 Self {
3501 top: value,
3502 leading: 0.0,
3503 bottom: value,
3504 trailing: 0.0,
3505 }
3506 }
3507
3508 pub fn horizontal(value: f32) -> Self {
3510 Self {
3511 top: 0.0,
3512 leading: value,
3513 bottom: 0.0,
3514 trailing: value,
3515 }
3516 }
3517}
3518
3519#[derive(Debug, Clone, Copy, PartialEq)]
3521pub struct FrameModifier {
3522 pub width: Option<f32>,
3523 pub height: Option<f32>,
3524}
3525
3526impl Default for FrameModifier {
3527 fn default() -> Self {
3528 Self::new()
3529 }
3530}
3531
3532impl FrameModifier {
3533 pub fn new() -> Self {
3534 Self {
3535 width: None,
3536 height: None,
3537 }
3538 }
3539
3540 pub fn width(mut self, width: f32) -> Self {
3541 self.width = Some(width);
3542 self
3543 }
3544
3545 pub fn height(mut self, height: f32) -> Self {
3546 self.height = Some(height);
3547 self
3548 }
3549
3550 pub fn size(mut self, width: f32, height: f32) -> Self {
3551 self.width = Some(width);
3552 self.height = Some(height);
3553 self
3554 }
3555}
3556
3557impl ViewModifier for FrameModifier {
3558 fn modify<V: View>(self, content: V) -> impl View {
3559 ModifiedView::new(content, self)
3560 }
3561}
3562
3563#[derive(Debug, Clone, Copy, PartialEq)]
3565pub struct FlexModifier {
3566 pub weight: f32,
3567}
3568
3569impl ViewModifier for FlexModifier {
3570 fn modify<V: View>(self, content: V) -> impl View {
3571 ModifiedView::new(content, self)
3572 }
3573
3574 fn child_flex_weight<V: View>(&self, _view: &V) -> f32 {
3575 self.weight
3576 }
3577}
3578
3579#[derive(Debug, Clone, Copy, PartialEq)]
3581pub struct OffsetModifier {
3582 pub x: f32,
3583 pub y: f32,
3584}
3585
3586impl OffsetModifier {
3587 pub fn new(x: f32, y: f32) -> Self {
3588 Self { x, y }
3589 }
3590}
3591
3592impl ViewModifier for OffsetModifier {
3593 fn modify<V: View>(self, content: V) -> impl View {
3594 ModifiedView::new(content, self)
3595 }
3596}
3597
3598#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3600pub struct ZIndexModifier {
3601 pub z_index: i32,
3602}
3603
3604impl ZIndexModifier {
3605 pub fn new(z_index: i32) -> Self {
3606 Self { z_index }
3607 }
3608}
3609
3610impl ViewModifier for ZIndexModifier {
3611 fn modify<V: View>(self, content: V) -> impl View {
3612 ModifiedView::new(content, self)
3613 }
3614}
3615
3616#[derive(Debug, Clone, Copy, PartialEq, Default)]
3618pub struct LayoutConstraints {
3619 pub min_width: Option<f32>,
3620 pub max_width: Option<f32>,
3621 pub min_height: Option<f32>,
3622 pub max_height: Option<f32>,
3623}
3624
3625#[derive(Debug, Clone, Copy, PartialEq)]
3627pub struct LayoutModifier {
3628 pub constraints: LayoutConstraints,
3629}
3630
3631impl LayoutModifier {
3632 pub fn new(constraints: LayoutConstraints) -> Self {
3633 Self { constraints }
3634 }
3635}
3636
3637impl ViewModifier for LayoutModifier {
3638 fn modify<V: View>(self, content: V) -> impl View {
3639 ModifiedView::new(content, self)
3640 }
3641}
3642
3643#[derive(Debug, Clone, Copy, PartialEq)]
3645pub struct SafeAreaModifier {
3646 pub ignores: bool,
3647}
3648
3649impl ViewModifier for SafeAreaModifier {
3650 fn modify<V: View>(self, content: V) -> impl View {
3651 ModifiedView::new(content, self)
3652 }
3653}
3654
3655#[derive(Debug, Clone, Copy, PartialEq)]
3657pub struct ElevationModifier {
3658 pub level: f32,
3659}
3660
3661impl ViewModifier for ElevationModifier {
3662 fn modify<V: View>(self, content: V) -> impl View {
3663 ModifiedView::new(content, self)
3664 }
3665
3666 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
3667 if self.level > 0.0 {
3668 let radius = self.level * 2.0;
3669 let offset_y = self.level * 0.5;
3670 let shadow_color = [0.0, 0.0, 0.0, 0.3];
3671 renderer.push_shadow(radius, shadow_color, [0.0, offset_y]);
3672 view.render(renderer, rect);
3673 renderer.pop_shadow();
3674 } else {
3675 view.render(renderer, rect);
3676 }
3677 }
3678}
3679
3680pub mod layout {
3682 use super::*;
3683
3684 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3687 pub struct LayoutKey {
3688 pub view_hash: u64,
3689 pub generation: u64,
3690 }
3691
3692 pub struct LayoutCache {
3694 pub safe_area: SafeArea,
3695 size_cache: HashMap<(u64, u32, u32), Size>, generation: u64,
3700 }
3701
3702 impl Default for LayoutCache {
3703 fn default() -> Self {
3704 Self::new()
3705 }
3706 }
3707
3708 impl LayoutCache {
3709 pub fn new() -> Self {
3710 Self {
3711 safe_area: SafeArea::default(),
3712 size_cache: HashMap::new(),
3713 generation: 0,
3714 }
3715 }
3716
3717 pub fn generation(&self) -> u64 {
3719 self.generation
3720 }
3721
3722 pub fn invalidate(&mut self) {
3726 self.generation = self.generation.wrapping_add(1);
3727 }
3728
3729 pub fn is_valid(&self, key: LayoutKey, current_gen: u64) -> bool {
3732 key.generation == current_gen && key.generation == self.generation
3733 }
3734
3735 pub fn clear(&mut self) {
3736 self.safe_area = SafeArea::default();
3737 self.size_cache.clear();
3738 }
3739
3740 pub fn get_size(&self, view_hash: u64, proposal: SizeProposal) -> Option<Size> {
3741 let pw = (proposal.width.unwrap_or(-1.0) * 100.0) as u32;
3742 let ph = (proposal.height.unwrap_or(-1.0) * 100.0) as u32;
3743 self.size_cache.get(&(view_hash, pw, ph)).copied()
3744 }
3745
3746 pub fn set_size(&mut self, view_hash: u64, proposal: SizeProposal, size: Size) {
3747 let pw = (proposal.width.unwrap_or(-1.0) * 100.0) as u32;
3748 let ph = (proposal.height.unwrap_or(-1.0) * 100.0) as u32;
3749 self.size_cache.insert((view_hash, pw, ph), size);
3750 }
3751
3752 pub fn invalidate_view(&mut self, view_hash: u64) {
3754 self.size_cache.retain(|&(hash, _, _), _| hash != view_hash);
3755 }
3756 }
3757
3758 #[derive(Debug, Clone, Copy, PartialEq)]
3760 pub struct SizeProposal {
3761 pub width: Option<f32>,
3762 pub height: Option<f32>,
3763 }
3764
3765 impl SizeProposal {
3766 pub fn unspecified() -> Self {
3767 Self {
3768 width: None,
3769 height: None,
3770 }
3771 }
3772
3773 pub fn width(width: f32) -> Self {
3774 Self {
3775 width: Some(width),
3776 height: None,
3777 }
3778 }
3779
3780 pub fn height(height: f32) -> Self {
3781 Self {
3782 width: None,
3783 height: Some(height),
3784 }
3785 }
3786
3787 pub fn tight(width: f32, height: f32) -> Self {
3788 Self {
3789 width: Some(width),
3790 height: Some(height),
3791 }
3792 }
3793
3794 pub fn new(width: Option<f32>, height: Option<f32>) -> Self {
3795 Self { width, height }
3796 }
3797 }
3798
3799 pub trait LayoutView: Send {
3801 fn size_that_fits(
3803 &self,
3804 proposal: SizeProposal,
3805 subviews: &[&dyn LayoutView],
3806 cache: &mut LayoutCache,
3807 ) -> Size;
3808
3809 fn place_subviews(
3811 &self,
3812 bounds: Rect,
3813 subviews: &mut [&mut dyn LayoutView],
3814 cache: &mut LayoutCache,
3815 );
3816
3817 fn flex_weight(&self) -> f32 {
3819 0.0
3820 }
3821
3822 fn debug_layout(&self, indent: usize) -> String {
3825 let prefix = " ".repeat(indent);
3826 format!("{}LayoutView", prefix)
3827 }
3828 }
3829 #[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
3831 pub struct EdgeInsets {
3832 pub top: f32,
3833 pub leading: f32,
3834 pub bottom: f32,
3835 pub trailing: f32,
3836 }
3837
3838 impl EdgeInsets {
3839 pub fn new(top: f32, leading: f32, bottom: f32, trailing: f32) -> Self {
3840 Self {
3841 top,
3842 leading,
3843 bottom,
3844 trailing,
3845 }
3846 }
3847
3848 pub fn all(value: f32) -> Self {
3849 Self {
3850 top: value,
3851 leading: value,
3852 bottom: value,
3853 trailing: value,
3854 }
3855 }
3856 }
3857
3858 #[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
3860 pub struct SafeArea {
3861 pub insets: EdgeInsets,
3862 }
3863
3864 #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
3866 pub enum SdfShape {
3867 Rect(Rect),
3868 RoundedRect { rect: Rect, radius: f32 },
3869 Circle { center: [f32; 2], radius: f32 },
3870 }
3871
3872 #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
3874 pub struct Rect {
3875 pub x: f32,
3876 pub y: f32,
3877 pub width: f32,
3878 pub height: f32,
3879 }
3880
3881 impl Rect {
3882 pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
3883 Self {
3884 x,
3885 y,
3886 width,
3887 height,
3888 }
3889 }
3890
3891 pub fn inset(&self, amount: f32) -> Self {
3892 Self {
3893 x: self.x + amount,
3894 y: self.y + amount,
3895 width: (self.width - amount * 2.0).max(0.0),
3896 height: (self.height - amount * 2.0).max(0.0),
3897 }
3898 }
3899
3900 pub fn offset(&self, dx: f32, dy: f32) -> Self {
3901 Self {
3902 x: self.x + dx,
3903 y: self.y + dy,
3904 ..*self
3905 }
3906 }
3907
3908 pub fn zero() -> Self {
3909 Self {
3910 x: 0.0,
3911 y: 0.0,
3912 width: 0.0,
3913 height: 0.0,
3914 }
3915 }
3916
3917 pub fn contains(&self, x: f32, y: f32) -> bool {
3918 x >= self.x && x <= self.x + self.width && y >= self.y && y <= self.y + self.height
3919 }
3920
3921 pub fn size(&self) -> Size {
3922 Size {
3923 width: self.width,
3924 height: self.height,
3925 }
3926 }
3927
3928 pub fn split_horizontal(&self, n: usize) -> Vec<Rect> {
3930 if n == 0 {
3931 return vec![];
3932 }
3933 let item_width = self.width / n as f32;
3934 (0..n)
3935 .map(|i| Rect {
3936 x: self.x + i as f32 * item_width,
3937 y: self.y,
3938 width: item_width,
3939 height: self.height,
3940 })
3941 .collect()
3942 }
3943
3944 pub fn split_vertical(&self, n: usize) -> Vec<Rect> {
3946 if n == 0 {
3947 return vec![];
3948 }
3949 let item_height = self.height / n as f32;
3950 (0..n)
3951 .map(|i| Rect {
3952 x: self.x,
3953 y: self.y + i as f32 * item_height,
3954 width: self.width,
3955 height: item_height,
3956 })
3957 .collect()
3958 }
3959 }
3960}
3961
3962pub use layout::{LayoutCache, LayoutKey, LayoutView, Rect, SizeProposal};
3964pub mod agents;
3967pub mod animation;
3968pub mod gpu;
3969pub mod material;
3970pub mod runtime;
3971pub mod scene_graph;
3972pub mod sdf_shadow;
3973
3974pub use scene_graph::{NodeId, bifrost_registry};
3975pub use material::DrawMaterial;
3976
3977pub trait AssetManager: Send + Sync {
3981 fn load_image(&self, url: &str) -> AssetState<Arc<Vec<u8>>>;
3983
3984 fn preload_image(&self, url: &str);
3986}
3987
3988#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
3990pub enum Event {
3991 PointerDown {
3992 x: f32,
3993 y: f32,
3994 button: u32,
3995 proximity_field: f32,
3996 },
3997 PointerUp {
3998 x: f32,
3999 y: f32,
4000 button: u32,
4001 },
4002 PointerMove {
4003 x: f32,
4004 y: f32,
4005 proximity_field: f32,
4006 },
4007 PointerClick {
4008 x: f32,
4009 y: f32,
4010 button: u32,
4011 },
4012 PointerEnter,
4013 PointerLeave,
4014 PointerWheel {
4017 x: f32,
4018 y: f32,
4019 delta_x: f32,
4020 delta_y: f32,
4021 },
4022 PointerDoubleClick {
4024 x: f32,
4025 y: f32,
4026 button: u32,
4027 },
4028 DragStart {
4030 x: f32,
4031 y: f32,
4032 button: u32,
4033 },
4034 DragMove {
4036 x: f32,
4037 y: f32,
4038 },
4039 DragEnd {
4041 x: f32,
4042 y: f32,
4043 },
4044 KeyDown {
4045 key: String,
4046 },
4047 KeyUp {
4048 key: String,
4049 },
4050 FocusIn,
4052 FocusOut,
4054 Copy,
4056 Cut,
4058 Paste(String),
4060 Ime(String),
4062 TouchStart {
4064 x: f32,
4065 y: f32,
4066 touch_id: u64,
4067 },
4068 TouchMove {
4070 x: f32,
4071 y: f32,
4072 touch_id: u64,
4073 },
4074 TouchEnd {
4076 x: f32,
4077 y: f32,
4078 touch_id: u64,
4079 },
4080 TouchCancel {
4082 touch_id: u64,
4083 },
4084 GesturePinch {
4086 scale: f32,
4087 velocity: f32,
4088 },
4089 GestureSwipe {
4091 dx: f32,
4092 dy: f32,
4093 velocity_x: f32,
4094 velocity_y: f32,
4095 },
4096}
4097
4098impl Event {
4099 pub fn name(&self) -> &'static str {
4101 match self {
4102 Self::PointerDown { .. } => "pointerdown",
4103 Self::PointerUp { .. } => "pointerup",
4104 Self::PointerMove { .. } => "pointermove",
4105 Self::PointerClick { .. } => "pointerclick",
4106 Self::PointerEnter => "pointerenter",
4107 Self::PointerLeave => "pointerleave",
4108 Self::PointerWheel { .. } => "pointerwheel",
4109 Self::PointerDoubleClick { .. } => "pointerdoubleclick",
4110 Self::DragStart { .. } => "dragstart",
4111 Self::DragMove { .. } => "dragmove",
4112 Self::DragEnd { .. } => "dragend",
4113 Self::KeyDown { .. } => "keydown",
4114 Self::KeyUp { .. } => "keyup",
4115 Self::FocusIn => "focusin",
4116 Self::FocusOut => "focusout",
4117 Self::Copy => "copy",
4118 Self::Cut => "cut",
4119 Self::Paste(_) => "paste",
4120 Self::Ime(_) => "ime",
4121 Self::TouchStart { .. } => "touchstart",
4122 Self::TouchMove { .. } => "touchmove",
4123 Self::TouchEnd { .. } => "touchend",
4124 Self::TouchCancel { .. } => "touchcancel",
4125 Self::GesturePinch { .. } => "gesturepinch",
4126 Self::GestureSwipe { .. } => "gestureswipe",
4127 }
4128 }
4129}
4130
4131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4133pub enum EventResponse {
4134 Handled,
4135 Ignored,
4136}
4137
4138pub struct DefaultAssetManager {
4140 cache: AssetCache,
4141}
4142type AssetCache = Arc<arc_swap::ArcSwap<HashMap<String, AssetState<Arc<Vec<u8>>>>>>;
4143
4144impl Default for DefaultAssetManager {
4145 fn default() -> Self {
4146 Self::new()
4147 }
4148}
4149
4150impl DefaultAssetManager {
4151 pub fn new() -> Self {
4152 Self {
4153 cache: Arc::new(arc_swap::ArcSwap::from_pointee(HashMap::new())),
4154 }
4155 }
4156}
4157
4158impl AssetManager for DefaultAssetManager {
4159 fn load_image(&self, url: &str) -> AssetState<Arc<Vec<u8>>> {
4160 if let Some(state) = self.cache.load().get(url) {
4161 return state.clone();
4162 }
4163
4164 self.cache.rcu(|map| {
4165 let mut m = (**map).clone();
4166 m.entry(url.to_string()).or_insert(AssetState::Loading);
4167 m
4168 });
4169 AssetState::Loading
4170 }
4171
4172 fn preload_image(&self, _url: &str) {}
4173}
4174
4175use std::future::Future;
4176
4177pub struct Suspense<T: Clone + Send + Sync + 'static> {
4180 inner: State<AssetState<T>>,
4181}
4182
4183impl<T: Clone + Send + Sync + 'static> Default for Suspense<T> {
4184 fn default() -> Self {
4185 Self::new()
4186 }
4187}
4188
4189impl<T: Clone + Send + Sync + 'static> Suspense<T> {
4190 pub fn new() -> Self {
4191 Self {
4192 inner: State::new(AssetState::Loading),
4193 }
4194 }
4195
4196 pub fn new_async<F>(future: F) -> Self
4197 where
4198 F: Future<Output = Result<T, String>> + Send + 'static,
4199 {
4200 let suspense = Self::new();
4201 let suspense_clone = suspense.clone();
4202
4203 #[cfg(not(target_arch = "wasm32"))]
4204 {
4205 if let Ok(handle) = tokio::runtime::Handle::try_current() {
4207 handle.spawn(async move {
4208 let result = future.await;
4209 match result {
4210 Ok(val) => suspense_clone.inner.set(AssetState::Ready(val)),
4211 Err(err) => suspense_clone.inner.set(AssetState::Error(err)),
4212 }
4213 });
4214 } else {
4215 std::thread::spawn(move || {
4216 let rt = tokio::runtime::Builder::new_current_thread()
4217 .enable_all()
4218 .build()
4219 .unwrap();
4220 rt.block_on(async {
4221 let result = future.await;
4222 match result {
4223 Ok(val) => suspense_clone.inner.set(AssetState::Ready(val)),
4224 Err(err) => suspense_clone.inner.set(AssetState::Error(err)),
4225 }
4226 });
4227 });
4228 }
4229 }
4230 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
4231 {
4232 wasm_bindgen_futures::spawn_local(async move {
4233 let result = future.await;
4234 match result {
4235 Ok(val) => suspense_clone.inner.set(AssetState::Ready(val)),
4236 Err(err) => suspense_clone.inner.set(AssetState::Error(err)),
4237 }
4238 });
4239 }
4240
4241 suspense
4242 }
4243
4244 pub fn ready(value: T) -> Self {
4245 Self {
4246 inner: State::new(AssetState::Ready(value)),
4247 }
4248 }
4249
4250 pub fn error(message: impl Into<String>) -> Self {
4251 Self {
4252 inner: State::new(AssetState::Error(message.into())),
4253 }
4254 }
4255
4256 pub fn get(&self) -> AssetState<T> {
4257 self.inner.get()
4258 }
4259
4260 pub fn get_ref(&self) -> AssetState<T> {
4261 self.inner.get()
4262 }
4263
4264 pub fn is_loading(&self) -> bool {
4265 matches!(self.get(), AssetState::Loading)
4266 }
4267
4268 pub fn is_ready(&self) -> bool {
4269 matches!(self.get(), AssetState::Ready(_))
4270 }
4271
4272 pub fn is_error(&self) -> bool {
4273 matches!(self.get(), AssetState::Error(_))
4274 }
4275
4276 pub fn ready_value(&self) -> Option<T> {
4277 match self.get() {
4278 AssetState::Ready(value) => Some(value),
4279 _ => None,
4280 }
4281 }
4282
4283 pub fn error_message(&self) -> Option<String> {
4284 match self.get() {
4285 AssetState::Error(message) => Some(message),
4286 _ => None,
4287 }
4288 }
4289
4290 pub fn subscribe<F: Fn(&AssetState<T>) + Send + Sync + 'static>(&self, callback: F) {
4291 self.inner.subscribe(callback)
4292 }
4293
4294 pub fn inner_state(&self) -> &State<AssetState<T>> {
4295 &self.inner
4296 }
4297}
4298
4299impl<T: Clone + Send + Sync + 'static> Clone for Suspense<T> {
4300 fn clone(&self) -> Self {
4301 Self {
4302 inner: self.inner.clone(),
4303 }
4304 }
4305}
4306
4307impl<T: Clone + Send + Sync + 'static> From<T> for Suspense<T> {
4308 fn from(value: T) -> Self {
4309 Self::ready(value)
4310 }
4311}
4312
4313impl<T: Clone + Send + Sync + 'static> From<Result<T, String>> for Suspense<T> {
4314 fn from(result: Result<T, String>) -> Self {
4315 match result {
4316 Ok(value) => Self::ready(value),
4317 Err(error) => Self::error(error),
4318 }
4319 }
4320}
4321
4322#[cfg(test)]
4323mod phase1_test;
4324
4325#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4327pub enum BerserkerMode {
4328 Normal,
4329 Rage, Frenzy, GodMode, }
4333
4334pub trait Seer: Send + Sync {
4337 fn predict(&self, context: &str) -> String;
4339 fn whispers(&self) -> Vec<String>;
4341}
4342
4343#[cfg(test)]
4344mod vili_tests {
4345 use super::*;
4346
4347 struct DummyRenderer;
4348 impl ElapsedTime for DummyRenderer {
4349 fn elapsed_time(&self) -> f32 { 0.0 }
4350 fn delta_time(&self) -> f32 { 0.0 }
4351 }
4352 impl Renderer for DummyRenderer {
4353 fn fill_rect(&mut self, _r: Rect, _c: [f32; 4]) {}
4354 fn fill_rounded_rect(&mut self, _r: Rect, _rad: f32, _c: [f32; 4]) {}
4355 fn fill_ellipse(&mut self, _r: Rect, _c: [f32; 4]) {}
4356 fn stroke_rect(&mut self, _r: Rect, _c: [f32; 4], _w: f32) {}
4357 fn stroke_rounded_rect(&mut self, _r: Rect, _rad: f32, _c: [f32; 4], _w: f32) {}
4358 fn stroke_ellipse(&mut self, _r: Rect, _c: [f32; 4], _w: f32) {}
4359 fn draw_line(&mut self, _x1: f32, _y1: f32, _x2: f32, _y2: f32, _c: [f32; 4], _w: f32) {}
4360 fn draw_text(&mut self, _t: &str, _x: f32, _y: f32, _s: f32, _c: [f32; 4]) {}
4361 fn measure_text(&mut self, _t: &str, _s: f32) -> (f32, f32) { (0.0, 0.0) }
4362 fn memoize(&mut self, _id: u64, _hash: u64, _r: &dyn Fn(&mut dyn Renderer)) {}
4363 }
4364
4365 #[test]
4366 fn test_magnetic_warp() {
4367 let renderer = DummyRenderer;
4368 let anchor = Rect { x: 100.0, y: 100.0, width: 50.0, height: 50.0 };
4369 let pointer = [125.0, 50.0];
4371 let warp = renderer.magnetic_warp(pointer, anchor, 1.0);
4374 assert!(warp[1] > 50.0);
4376
4377 let far_pointer = [500.0, 500.0];
4379 let far_warp = renderer.magnetic_warp(far_pointer, anchor, 1.0);
4380 assert_eq!(far_pointer, far_warp);
4381 }
4382
4383 #[test]
4384 fn test_mani_glow() {
4385 let renderer = DummyRenderer;
4386 let bounds = Rect { x: 0.0, y: 0.0, width: 100.0, height: 100.0 };
4387 let pointer_inside = [50.0, 50.0];
4388 let glow_max = renderer.mani_glow_intensity(pointer_inside, bounds, 120.0);
4389 assert_eq!(glow_max, 1.0);
4390
4391 let pointer_edge = [50.0, -10.0];
4392 let glow_partial = renderer.mani_glow_intensity(pointer_edge, bounds, 120.0);
4393 assert!(glow_partial > 0.0 && glow_partial < 1.0);
4394 }
4395
4396 #[test]
4397 fn test_fafnir_evolve() {
4398 let renderer = DummyRenderer;
4399 let bounds = Rect { x: 0.0, y: 0.0, width: 100.0, height: 100.0 };
4400 let pointer_inside = [50.0, 50.0];
4401 let scale = renderer.fafnir_evolve(pointer_inside, bounds, 1.2);
4402 assert_eq!(scale, 1.2); }
4404}