1use serde::{Deserialize, Serialize};
35use std::collections::HashMap;
36use std::str::FromStr;
37
38pub mod security;
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42#[serde(untagged)]
43pub enum TokenValue {
44 Single { value: String },
46 Adaptive { light: String, dark: String },
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct YggdrasilTokens {
53 pub color: HashMap<String, TokenValue>,
54 pub font: HashMap<String, TokenValue>,
55 pub spacing: HashMap<String, TokenValue>,
56 pub radius: HashMap<String, TokenValue>,
57 pub shadow: HashMap<String, TokenValue>,
58 pub border: HashMap<String, TokenValue>,
59 pub anim: HashMap<String, TokenValue>,
60 pub bifrost: HashMap<String, TokenValue>,
61 pub gungnir: HashMap<String, TokenValue>,
62 pub mjolnir: HashMap<String, TokenValue>,
63 pub accessibility: HashMap<String, TokenValue>,
64}
65
66impl YggdrasilTokens {
67 pub fn new() -> Self {
68 Self {
69 color: HashMap::new(),
70 font: HashMap::new(),
71 spacing: HashMap::new(),
72 radius: HashMap::new(),
73 shadow: HashMap::new(),
74 border: HashMap::new(),
75 anim: HashMap::new(),
76 bifrost: HashMap::new(),
77 gungnir: HashMap::new(),
78 mjolnir: HashMap::new(),
79 accessibility: HashMap::new(),
80 }
81 }
82
83 pub fn get_color(&self, key: &str, is_dark: bool) -> Option<String> {
85 self.color.get(key).and_then(|token| match token {
86 TokenValue::Single { value } => Some(value.clone()),
87 TokenValue::Adaptive { light, dark } => {
88 if is_dark {
89 Some(dark.clone())
90 } else {
91 Some(light.clone())
92 }
93 }
94 })
95 }
96
97 pub fn get<T: FromStr>(&self, category: &str, key: &str, is_dark: bool) -> Option<T> {
99 let map = match category {
100 "color" => &self.color,
101 "font" => &self.font,
102 "spacing" => &self.spacing,
103 "radius" => &self.radius,
104 "shadow" => &self.shadow,
105 "border" => &self.border,
106 "anim" => &self.anim,
107 "bifrost" => &self.bifrost,
108 "gungnir" => &self.gungnir,
109 "mjolnir" => &self.mjolnir,
110 "accessibility" => &self.accessibility,
111 _ => return None,
112 };
113
114 map.get(key).and_then(|token| match token {
115 TokenValue::Single { value } => value.parse().ok(),
116 TokenValue::Adaptive { light, dark } => {
117 let value = if is_dark { dark } else { light };
118 value.parse().ok()
119 }
120 })
121 }
122}
123
124pub trait View: Sized + Send {
125 type Body: View;
128
129 fn body(self) -> Self::Body;
130
131 fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
134
135 fn intrinsic_size(&self, _renderer: &mut dyn Renderer, _proposal: SizeProposal) -> Size {
138 Size::ZERO
139 }
140
141 fn layout(&self) -> Option<&dyn layout::LayoutView> {
143 None
144 }
145
146 fn flex_weight(&self) -> f32 {
148 0.0
149 }
150
151 fn modifier<M: ViewModifier>(self, m: M) -> ModifiedView<Self, M> {
153 ModifiedView::new(self, m)
154 }
155
156 fn bifrost(
158 self,
159 blur: f32,
160 saturation: f32,
161 opacity: f32,
162 ) -> ModifiedView<Self, BifrostModifier> {
163 self.modifier(BifrostModifier {
164 blur,
165 saturation,
166 opacity,
167 })
168 }
169
170 fn gungnir(
172 self,
173 color: impl Into<String>,
174 radius: f32,
175 intensity: f32,
176 ) -> ModifiedView<Self, GungnirModifier> {
177 self.modifier(GungnirModifier {
178 color: color.into(),
179 radius,
180 intensity,
181 })
182 }
183
184 fn mjolnir_slice(self, angle: f32, offset: f32) -> ModifiedView<Self, MjolnirSliceModifier> {
186 self.modifier(MjolnirSliceModifier { angle, offset })
187 }
188
189 fn mjolnir_shatter(
191 self,
192 pieces: u32,
193 force: f32,
194 ) -> ModifiedView<Self, MjolnirShatterModifier> {
195 self.modifier(MjolnirShatterModifier { pieces, force })
196 }
197
198 fn bifrost_bridge(self, id: impl Into<String>) -> ModifiedView<Self, BifrostBridgeModifier> {
200 self.modifier(BifrostBridgeModifier { id: id.into() })
201 }
202
203 fn background(self, color: [f32; 4]) -> ModifiedView<Self, BackgroundModifier> {
205 self.modifier(BackgroundModifier { color })
206 }
207
208 fn padding(self, amount: f32) -> ModifiedView<Self, PaddingModifier> {
210 self.modifier(PaddingModifier { amount })
211 }
212
213 fn opacity(self, opacity: f32) -> ModifiedView<Self, OpacityModifier> {
215 self.modifier(OpacityModifier {
216 opacity: opacity.clamp(0.0, 1.0),
217 })
218 }
219
220 fn foreground_color(self, color: [f32; 4]) -> ModifiedView<Self, ForegroundColorModifier> {
222 self.modifier(ForegroundColorModifier { color })
223 }
224
225 fn frame(self, width: Option<f32>, height: Option<f32>) -> ModifiedView<Self, FrameModifier> {
227 self.modifier(FrameModifier { width, height })
228 }
229
230 fn flex(self, weight: f32) -> ModifiedView<Self, FlexModifier> {
232 self.modifier(FlexModifier { weight })
233 }
234
235 fn safe_area_padding(self) -> ModifiedView<Self, SafeAreaModifier> {
237 self.modifier(SafeAreaModifier { ignores: false })
238 }
239
240 fn ignores_safe_area(self) -> ModifiedView<Self, SafeAreaModifier> {
242 self.modifier(SafeAreaModifier { ignores: true })
243 }
244
245 fn clip_to_bounds(self) -> ModifiedView<Self, ClipModifier> {
247 self.modifier(ClipModifier)
248 }
249
250 fn border(self, color: [f32; 4], width: f32) -> ModifiedView<Self, BorderModifier> {
252 self.modifier(BorderModifier { color, width })
253 }
254
255 fn elevation(self, level: f32) -> ModifiedView<Self, ElevationModifier> {
257 self.modifier(ElevationModifier { level })
258 }
259
260 fn on_appear<F: Fn() + Send + Sync + 'static>(
262 self,
263 action: F,
264 ) -> ModifiedView<Self, LifecycleModifier> {
265 self.modifier(LifecycleModifier {
266 on_appear: Some(Arc::new(action)),
267 on_disappear: None,
268 })
269 }
270
271 fn on_disappear<F: Fn() + Send + Sync + 'static>(
273 self,
274 action: F,
275 ) -> ModifiedView<Self, LifecycleModifier> {
276 self.modifier(LifecycleModifier {
277 on_appear: None,
278 on_disappear: Some(Arc::new(action)),
279 })
280 }
281
282 fn on_click<F: Fn() + Send + Sync + 'static>(
284 self,
285 action: F,
286 ) -> ModifiedView<Self, OnClickModifier> {
287 self.modifier(OnClickModifier {
288 action: Arc::new(action),
289 })
290 }
291
292 fn on_pointer_enter<F: Fn() + Send + Sync + 'static>(
294 self,
295 action: F,
296 ) -> ModifiedView<Self, OnPointerEnterModifier> {
297 self.modifier(OnPointerEnterModifier {
298 action: Arc::new(action),
299 })
300 }
301
302 fn on_pointer_leave<F: Fn() + Send + Sync + 'static>(
304 self,
305 action: F,
306 ) -> ModifiedView<Self, OnPointerLeaveModifier> {
307 self.modifier(OnPointerLeaveModifier {
308 action: Arc::new(action),
309 })
310 }
311
312 fn on_pointer_move<F: Fn(f32, f32) + Send + Sync + 'static>(
314 self,
315 action: F,
316 ) -> ModifiedView<Self, OnPointerMoveModifier> {
317 self.modifier(OnPointerMoveModifier {
318 action: Arc::new(action),
319 })
320 }
321
322 fn on_pointer_down<F: Fn() + Send + Sync + 'static>(
324 self,
325 action: F,
326 ) -> ModifiedView<Self, OnPointerDownModifier> {
327 self.modifier(OnPointerDownModifier {
328 action: Arc::new(action),
329 })
330 }
331
332 fn on_pointer_up<F: Fn() + Send + Sync + 'static>(
334 self,
335 action: F,
336 ) -> ModifiedView<Self, OnPointerUpModifier> {
337 self.modifier(OnPointerUpModifier {
338 action: Arc::new(action),
339 })
340 }
341
342 fn erase(self) -> AnyView
344 where
345 Self: 'static,
346 {
347 AnyView::new(self)
348 }
349}
350
351pub trait ErasedView: Send {
353 fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect);
354 fn name(&self) -> &'static str;
355 fn flex_weight_erased(&self) -> f32;
356}
357
358impl<V: View + 'static> ErasedView for V {
359 fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect) {
360 self.render(renderer, rect);
361 }
362
363 fn name(&self) -> &'static str {
364 std::any::type_name::<V>()
365 }
366
367 fn flex_weight_erased(&self) -> f32 {
368 self.flex_weight()
369 }
370}
371
372pub struct AnyView {
374 inner: Box<dyn ErasedView>,
375}
376
377impl AnyView {
378 pub fn new<V: View + 'static>(view: V) -> Self {
379 Self {
380 inner: Box::new(view),
381 }
382 }
383}
384
385impl View for AnyView {
386 type Body = Never;
387 fn body(self) -> Self::Body {
388 unreachable!()
389 }
390
391 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
392 renderer.push_vnode(rect, self.inner.name());
393 self.inner.render_erased(renderer, rect);
394 renderer.pop_vnode();
395 }
396
397 fn flex_weight(&self) -> f32 {
398 self.inner.flex_weight_erased()
399 }
400}
401
402#[derive(Debug, Clone, PartialEq)]
406pub struct BifrostBridgeModifier {
407 pub id: String,
408}
409
410impl ViewModifier for BifrostBridgeModifier {
411 fn modify<V: View>(self, content: V) -> impl View {
412 ModifiedView::new(content, self)
413 }
414
415 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
416 renderer.register_shared_element(&self.id, rect);
418 }
419}
420
421#[derive(Debug, Clone, Copy, PartialEq)]
424pub struct MjolnirSliceModifier {
425 pub angle: f32,
426 pub offset: f32,
427}
428
429impl ViewModifier for MjolnirSliceModifier {
430 fn modify<V: View>(self, content: V) -> impl View {
431 ModifiedView::new(content, self)
432 }
433
434 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
435 renderer.push_mjolnir_slice(self.angle, self.offset);
436 }
437
438 fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
439 renderer.pop_mjolnir_slice();
440 }
441}
442
443#[derive(Debug, Clone, Copy, PartialEq)]
446pub struct MjolnirShatterModifier {
447 pub pieces: u32,
448 pub force: f32,
449}
450
451impl ViewModifier for MjolnirShatterModifier {
452 fn modify<V: View>(self, content: V) -> impl View {
453 ModifiedView::new(content, self)
454 }
455
456 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
457 let pieces = self.pieces.max(1);
459 for i in 0..pieces {
460 let progress = i as f32 / pieces as f32;
461 let next_progress = (i + 1) as f32 / pieces as f32;
462
463 let angle_start = progress * 360.0;
464 let angle_end = next_progress * 360.0;
465
466 renderer.push_mjolnir_slice(angle_start, 0.0);
468 renderer.push_mjolnir_slice(angle_end + 180.0, 0.0);
469
470 let mid_angle = (angle_start + angle_end) / 2.0;
472 let rad = mid_angle.to_radians();
473 let dx = rad.cos() * self.force;
474 let dy = rad.sin() * self.force;
475
476 let shard_rect = Rect {
477 x: rect.x + dx,
478 y: rect.y + dy,
479 ..rect
480 };
481
482 view.render(renderer, shard_rect);
483
484 renderer.pop_mjolnir_slice();
485 renderer.pop_mjolnir_slice();
486 }
487 }
488}
489
490#[derive(Debug, Clone, Copy, PartialEq)]
493pub struct BifrostModifier {
494 pub blur: f32,
495 pub saturation: f32,
496 pub opacity: f32,
497}
498
499impl ViewModifier for BifrostModifier {
500 fn modify<V: View>(self, content: V) -> impl View {
501 ModifiedView::new(content, self)
502 }
503
504 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
505 renderer.bifrost(rect, self.blur, self.saturation, self.opacity);
506 }
507}
508
509#[derive(Debug, Clone, Copy, PartialEq)]
511pub struct BackgroundModifier {
512 pub color: [f32; 4],
513}
514
515impl ViewModifier for BackgroundModifier {
516 fn modify<V: View>(self, content: V) -> impl View {
517 ModifiedView::new(content, self)
518 }
519
520 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
521 renderer.fill_rect(rect, self.color);
522 }
523}
524
525#[derive(Debug, Clone, Copy, PartialEq)]
527pub struct PaddingModifier {
528 pub amount: f32,
529}
530
531impl ViewModifier for PaddingModifier {
532 fn modify<V: View>(self, content: V) -> impl View {
533 ModifiedView::new(content, self)
534 }
535
536 fn transform_rect(&self, rect: Rect) -> Rect {
537 Rect {
538 x: rect.x + self.amount,
539 y: rect.y + self.amount,
540 width: (rect.width - 2.0 * self.amount).max(0.0),
541 height: (rect.height - 2.0 * self.amount).max(0.0),
542 }
543 }
544
545 fn transform_proposal(&self, mut proposal: SizeProposal) -> SizeProposal {
546 if let Some(w) = proposal.width {
547 proposal.width = Some((w - 2.0 * self.amount).max(0.0));
548 }
549 if let Some(h) = proposal.height {
550 proposal.height = Some((h - 2.0 * self.amount).max(0.0));
551 }
552 proposal
553 }
554
555 fn transform_size(&self, mut size: Size) -> Size {
556 size.width += 2.0 * self.amount;
557 size.height += 2.0 * self.amount;
558 size
559 }
560}
561
562#[derive(Debug, Clone, PartialEq)]
565pub struct GungnirModifier {
566 pub color: String,
567 pub radius: f32,
568 pub intensity: f32,
569}
570
571impl ViewModifier for GungnirModifier {
572 fn modify<V: View>(self, content: V) -> impl View {
573 ModifiedView::new(content, self)
574 }
575
576 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
577 renderer.stroke_rect(rect, [0.0, 1.0, 1.0, self.intensity], self.radius / 10.0);
579 }
580}
581
582#[derive(Debug, Clone, Copy, PartialEq)]
584pub struct GungnirPulseModifier {
585 pub color: [f32; 4],
586 pub radius: f32,
587 pub speed: f32,
588}
589
590impl ViewModifier for GungnirPulseModifier {
591 fn modify<V: View>(self, content: V) -> impl View {
592 ModifiedView::new(content, self)
593 }
594
595 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
596 let time = std::time::SystemTime::now()
597 .duration_since(std::time::UNIX_EPOCH)
598 .unwrap_or_default()
599 .as_secs_f32();
600
601 let intensity = (time * self.speed).sin() * 0.5 + 0.5;
604 let mut color = self.color;
605 color[3] *= intensity;
606
607 renderer.stroke_rect(rect, color, self.radius);
609 }
610}
611
612#[derive(Debug, Clone, Copy, PartialEq)]
614pub struct SleipnirParams {
615 pub stiffness: f32,
616 pub damping: f32,
617 pub mass: f32,
618}
619
620impl SleipnirParams {
621 pub fn snappy() -> Self { Self { stiffness: 230.0, damping: 22.0, mass: 1.0 } }
622 pub fn fluid() -> Self { Self { stiffness: 170.0, damping: 26.0, mass: 1.0 } }
623 pub fn heavy() -> Self { Self { stiffness: 90.0, damping: 20.0, mass: 1.0 } }
624 pub fn bouncy() -> Self { Self { stiffness: 190.0, damping: 14.0, mass: 1.0 } }
625}
626
627impl Default for SleipnirParams {
628 fn default() -> Self { Self::fluid() }
629}
630
631#[derive(Debug, Clone, Copy, PartialEq)]
632struct SolverState {
633 x: f32,
634 v: f32,
635}
636
637#[derive(Debug, Clone, Copy, PartialEq)]
640pub struct SleipnirSolver {
641 params: SleipnirParams,
642 target: f32,
643 state: SolverState,
644}
645
646impl SleipnirSolver {
647 pub fn new(params: SleipnirParams, target: f32, current: f32) -> Self {
649 Self {
650 params,
651 target,
652 state: SolverState { x: current, v: 0.0 },
653 }
654 }
655
656 pub fn tick(&mut self, dt: f32) -> f32 {
658 if dt <= 0.0 { return self.state.x; }
659
660 let mut remaining = dt;
662 let step = 1.0 / 120.0;
663
664 while remaining > 0.0 {
665 let d = remaining.min(step);
666 self.step(d);
667 remaining -= d;
668 }
669
670 self.state.x
671 }
672
673 fn step(&mut self, dt: f32) {
674 let a = self.evaluate(self.state, 0.0, SolverState { x: 0.0, v: 0.0 });
675 let b = self.evaluate(self.state, dt * 0.5, a);
676 let c = self.evaluate(self.state, dt * 0.5, b);
677 let d = self.evaluate(self.state, dt, c);
678
679 let dxdt = 1.0 / 6.0 * (a.x + 2.0 * (b.x + c.x) + d.x);
680 let dvdt = 1.0 / 6.0 * (a.v + 2.0 * (b.v + c.v) + d.v);
681
682 self.state.x += dxdt * dt;
683 self.state.v += dvdt * dt;
684 }
685
686 fn evaluate(&self, initial: SolverState, dt: f32, d: SolverState) -> SolverState {
687 let state = SolverState {
688 x: initial.x + d.x * dt,
689 v: initial.v + d.v * dt,
690 };
691 let force = -self.params.stiffness * (state.x - self.target) - self.params.damping * state.v;
692 let mass = self.params.mass.max(0.001);
693 SolverState { x: state.v, v: force / mass }
694 }
695
696 pub fn is_settled(&self) -> bool {
697 (self.state.x - self.target).abs() < 0.001 && self.state.v.abs() < 0.001
698 }
699
700 pub fn set_target(&mut self, target: f32) {
701 self.target = target;
702 }
703
704 pub fn current_value(&self) -> f32 {
705 self.state.x
706 }
707}
708
709#[derive(Debug, Clone, PartialEq)]
711pub struct SleipnirModifier {
712 pub id: u64,
713 pub target: f32,
714 pub params: SleipnirParams,
715}
716
717impl ViewModifier for SleipnirModifier {
718 fn modify<V: View>(self, content: V) -> impl View {
719 ModifiedView::new(content, self)
720 }
721
722 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
723 let state = load_system_state();
724
725 let solver_lock_opt = state.get_component_state::<SleipnirSolver>(self.id);
727
728 let current_val;
729
730 if let Some(lock) = solver_lock_opt {
731 let mut solver = lock.write().unwrap();
733 solver.set_target(self.target);
734 current_val = solver.tick(renderer.delta_time());
735
736 if !solver.is_settled() {
738 renderer.request_redraw();
739 }
740 } else {
741 let solver = SleipnirSolver::new(
743 self.params,
744 self.target,
745 self.target );
747
748 get_system_state().rcu(|old| {
750 let mut new_state = (**old).clone();
751 new_state.set_component_state(self.id, solver.clone());
752 new_state
753 });
754
755 current_val = self.target;
756 }
757
758 renderer.push_transform([0.0, current_val], [1.0, 1.0], 0.0);
760 view.render(renderer, rect);
761 renderer.pop_transform();
762 }
763}
764
765#[derive(Debug, Clone, Copy, PartialEq)]
768pub struct TransformModifier {
769 pub translation: [f32; 2],
770 pub scale: [f32; 2],
771 pub rotation: f32,
772}
773
774impl TransformModifier {
775 pub fn new() -> Self {
776 Self {
777 translation: [0.0, 0.0],
778 scale: [1.0, 1.0],
779 rotation: 0.0,
780 }
781 }
782
783 pub fn translate(mut self, x: f32, y: f32) -> Self {
784 self.translation = [x, y];
785 self
786 }
787
788 pub fn scale(mut self, x: f32, y: f32) -> Self {
789 self.scale = [x, y];
790 self
791 }
792
793 pub fn rotate(mut self, radians: f32) -> Self {
794 self.rotation = radians;
795 self
796 }
797}
798
799impl ViewModifier for TransformModifier {
800 fn modify<V: View>(self, content: V) -> impl View {
801 ModifiedView::new(content, self)
802 }
803
804 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
805 renderer.push_transform(self.translation, self.scale, self.rotation);
806 view.render(renderer, rect);
807 renderer.pop_transform();
808 }
809}
810
811#[derive(Clone)]
814pub struct LifecycleModifier {
815 pub on_appear: Option<Arc<dyn Fn() + Send + Sync>>,
816 pub on_disappear: Option<Arc<dyn Fn() + Send + Sync>>,
817}
818
819impl ViewModifier for LifecycleModifier {
820 fn modify<V: View>(self, content: V) -> impl View {
821 ModifiedView::new(content, self)
822 }
823}
824
825#[derive(Debug, Clone, Copy, PartialEq)]
828pub struct OpacityModifier {
829 pub opacity: f32,
830}
831
832impl ViewModifier for OpacityModifier {
833 fn modify<V: View>(self, content: V) -> impl View {
834 ModifiedView::new(content, self)
835 }
836
837 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
838 renderer.push_opacity(self.opacity);
839 }
840
841 fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
842 renderer.pop_opacity();
843 }
844}
845
846#[derive(Clone)]
848pub struct OnClickModifier {
849 pub action: Arc<dyn Fn() + Send + Sync>,
850}
851
852impl ViewModifier for OnClickModifier {
853 fn modify<V: View>(self, content: V) -> impl View {
854 ModifiedView::new(content, self)
855 }
856
857 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
858 let action = self.action.clone();
859 renderer.register_handler(
860 "pointerclick",
861 std::sync::Arc::new(move |event| {
862 if let Event::PointerClick { .. } = event {
863 (action)();
864 }
865 }),
866 );
867 }
868}
869
870#[derive(Clone)]
872pub struct OnPointerEnterModifier {
873 pub action: Arc<dyn Fn() + Send + Sync>,
874}
875
876impl ViewModifier for OnPointerEnterModifier {
877 fn modify<V: View>(self, content: V) -> impl View {
878 ModifiedView::new(content, self)
879 }
880
881 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
882 let action = self.action.clone();
883 renderer.register_handler(
884 "pointerenter",
885 std::sync::Arc::new(move |event| {
886 if let Event::PointerEnter = event {
887 (action)();
888 }
889 }),
890 );
891 }
892}
893
894#[derive(Clone)]
896pub struct OnPointerLeaveModifier {
897 pub action: Arc<dyn Fn() + Send + Sync>,
898}
899
900impl ViewModifier for OnPointerLeaveModifier {
901 fn modify<V: View>(self, content: V) -> impl View {
902 ModifiedView::new(content, self)
903 }
904
905 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
906 let action = self.action.clone();
907 renderer.register_handler(
908 "pointerleave",
909 std::sync::Arc::new(move |event| {
910 if let Event::PointerLeave = event {
911 (action)();
912 }
913 }),
914 );
915 }
916}
917
918#[derive(Clone)]
920pub struct OnPointerMoveModifier {
921 pub action: Arc<dyn Fn(f32, f32) + Send + Sync>,
922}
923
924impl ViewModifier for OnPointerMoveModifier {
925 fn modify<V: View>(self, content: V) -> impl View {
926 ModifiedView::new(content, self)
927 }
928
929 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
930 let action = self.action.clone();
931 renderer.register_handler(
932 "pointermove",
933 std::sync::Arc::new(move |event| {
934 if let Event::PointerMove { x, y } = event {
935 (action)(x, y);
936 }
937 }),
938 );
939 }
940}
941
942#[derive(Clone)]
944pub struct OnPointerDownModifier {
945 pub action: Arc<dyn Fn() + Send + Sync>,
946}
947
948impl ViewModifier for OnPointerDownModifier {
949 fn modify<V: View>(self, content: V) -> impl View {
950 ModifiedView::new(content, self)
951 }
952
953 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
954 let action = self.action.clone();
955 renderer.register_handler(
956 "pointerdown",
957 std::sync::Arc::new(move |event| {
958 if let Event::PointerDown { .. } = event {
959 (action)();
960 }
961 }),
962 );
963 }
964}
965
966#[derive(Clone)]
968pub struct OnPointerUpModifier {
969 pub action: Arc<dyn Fn() + Send + Sync>,
970}
971
972impl ViewModifier for OnPointerUpModifier {
973 fn modify<V: View>(self, content: V) -> impl View {
974 ModifiedView::new(content, self)
975 }
976
977 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
978 let action = self.action.clone();
979 renderer.register_handler(
980 "pointerup",
981 std::sync::Arc::new(move |event| {
982 if let Event::PointerUp { .. } = event {
983 (action)();
984 }
985 }),
986 );
987 }
988}
989
990#[derive(Debug, Clone, Copy, PartialEq)]
993pub struct ForegroundColorModifier {
994 pub color: [f32; 4],
995}
996
997impl ViewModifier for ForegroundColorModifier {
998 fn modify<V: View>(self, content: V) -> impl View {
999 ModifiedView::new(content, self)
1000 }
1001}
1002
1003#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1006pub struct ClipModifier;
1007
1008impl ViewModifier for ClipModifier {
1009 fn modify<V: View>(self, content: V) -> impl View {
1010 ModifiedView::new(content, self)
1011 }
1012
1013 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1014 renderer.push_clip_rect(rect);
1015 }
1016
1017 fn post_render(&self, renderer: &mut dyn Renderer, _rect: Rect) {
1018 renderer.pop_clip_rect();
1019 }
1020}
1021
1022#[derive(Debug, Clone, Copy, PartialEq)]
1024pub struct BorderModifier {
1025 pub color: [f32; 4],
1026 pub width: f32,
1027}
1028
1029impl ViewModifier for BorderModifier {
1030 fn modify<V: View>(self, content: V) -> impl View {
1031 ModifiedView::new(content, self)
1032 }
1033
1034 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1035 renderer.stroke_rect(rect, self.color, self.width);
1036 }
1037}
1038
1039#[doc(hidden)]
1041pub enum Never {}
1042
1043impl View for Never {
1044 type Body = Never;
1045 fn body(self) -> Never {
1046 unreachable!()
1047 }
1048}
1049
1050pub struct ModifiedView<V, M> {
1054 view: V,
1055 modifier: M,
1056}
1057
1058impl<V: View, M: ViewModifier> ModifiedView<V, M> {
1059 #[doc(hidden)]
1060 pub fn new(view: V, modifier: M) -> Self {
1061 Self { view, modifier }
1062 }
1063}
1064
1065impl<V: View, M: ViewModifier> View for ModifiedView<V, M> {
1066 type Body = ModifiedView<V::Body, M>;
1067
1068 fn body(self) -> Self::Body {
1069 ModifiedView {
1070 view: self.view.body(),
1071 modifier: self.modifier.clone(),
1072 }
1073 }
1074
1075 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
1076 self.modifier.render_view(&self.view, renderer, rect);
1077 }
1078
1079 fn intrinsic_size(&self, renderer: &mut dyn Renderer, proposal: SizeProposal) -> Size {
1080 self.modifier.measure_view(&self.view, renderer, proposal)
1081 }
1082
1083 fn flex_weight(&self) -> f32 {
1084 self.modifier.child_flex_weight(&self.view)
1085 }
1086}
1087
1088pub trait ViewModifier: Send + Clone {
1089 fn modify<V: View>(self, content: V) -> impl View;
1090
1091 fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
1093
1094 fn post_render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
1096
1097 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
1100 self.render(renderer, rect);
1101 let child_rect = self.transform_rect(rect);
1102 view.render(renderer, child_rect);
1103 self.post_render(renderer, rect);
1104 }
1105
1106 fn transform_rect(&self, rect: Rect) -> Rect {
1107 rect
1108 }
1109
1110 fn transform_proposal(&self, proposal: SizeProposal) -> SizeProposal {
1112 proposal
1113 }
1114
1115 fn transform_size(&self, size: Size) -> Size {
1117 size
1118 }
1119
1120 fn measure_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, proposal: SizeProposal) -> Size {
1122 let child_proposal = self.transform_proposal(proposal);
1123 let child_size = view.intrinsic_size(renderer, child_proposal);
1124 self.transform_size(child_size)
1125 }
1126
1127 fn child_flex_weight<V: View>(&self, view: &V) -> f32 {
1129 view.flex_weight()
1130 }
1131}
1132
1133#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
1135pub struct TelemetryData {
1136 pub frame_time_ms: f32,
1137
1138 pub input_time_ms: f32,
1140 pub state_flush_time_ms: f32,
1141 pub layout_time_ms: f32,
1142 pub draw_time_ms: f32,
1143 pub gpu_submit_time_ms: f32,
1144
1145 pub draw_calls: u32,
1146 pub vertices: u32,
1147
1148 pub vram_usage_mb: f32,
1150 pub vram_textures_mb: f32,
1151 pub vram_buffers_mb: f32,
1152 pub vram_pipelines_mb: f32,
1153}
1154
1155#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1157pub struct FrameBudget {
1158 pub target_ms: f32,
1160 pub allow_degradation: bool,
1163}
1164
1165impl Default for FrameBudget {
1166 fn default() -> Self {
1167 Self {
1168 target_ms: 16.0,
1169 allow_degradation: true,
1170 }
1171 }
1172}
1173
1174pub trait Renderer: Send {
1182 fn delta_time(&self) -> f32;
1184
1185 fn request_redraw(&mut self) {}
1188
1189 fn fill_rect(&mut self, rect: Rect, color: [f32; 4]);
1191 fn fill_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4]);
1192 fn fill_ellipse(&mut self, rect: Rect, color: [f32; 4]);
1194
1195 fn stroke_rect(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
1197 fn stroke_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4], stroke_width: f32);
1198 fn stroke_ellipse(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
1200 fn draw_line(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: [f32; 4], stroke_width: f32);
1202
1203 fn draw_text(&mut self, text: &str, x: f32, y: f32, size: f32, color: [f32; 4]);
1205 fn measure_text(&mut self, text: &str, size: f32) -> (f32, f32);
1207
1208 fn draw_texture(&mut self, texture_id: u32, rect: Rect);
1211 fn draw_image(&mut self, image_name: &str, rect: Rect);
1213 fn load_image(&mut self, name: &str, data: &[u8]);
1215
1216 fn upload_data_texture(&mut self, _id: &str, _data: &[f32], _width: u32, _height: u32) {}
1219 fn draw_heatmap(&mut self, _texture_id: &str, _rect: Rect, _palette: &str) {}
1221
1222 fn draw_mesh(&mut self, _mesh: &Mesh, _color: [f32; 4], _transform: glam::Mat4) {}
1225
1226 fn draw_linear_gradient(
1229 &mut self,
1230 _rect: Rect,
1231 _start_color: [f32; 4],
1232 _end_color: [f32; 4],
1233 _angle: f32,
1234 ) {
1235 }
1236 fn draw_radial_gradient(
1238 &mut self,
1239 _rect: Rect,
1240 _inner_color: [f32; 4],
1241 _outer_color: [f32; 4],
1242 ) {
1243 }
1244 fn draw_drop_shadow(
1246 &mut self,
1247 _rect: Rect,
1248 _radius: f32,
1249 _color: [f32; 4],
1250 _blur: f32,
1251 _spread: f32,
1252 ) {
1253 }
1254 fn stroke_dashed_rounded_rect(
1256 &mut self,
1257 _rect: Rect,
1258 _radius: f32,
1259 _color: [f32; 4],
1260 _width: f32,
1261 _dash: f32,
1262 _gap: f32,
1263 ) {
1264 }
1265 fn draw_9slice(
1267 &mut self,
1268 _image_name: &str,
1269 _rect: Rect,
1270 _left: f32,
1271 _top: f32,
1272 _right: f32,
1273 _bottom: f32,
1274 ) {
1275 }
1276
1277 fn push_clip_rect(&mut self, rect: Rect);
1281 fn pop_clip_rect(&mut self);
1283
1284 fn push_opacity(&mut self, opacity: f32);
1288 fn pop_opacity(&mut self);
1290
1291 fn set_theme(&mut self, _theme: ColorTheme) {}
1293 fn set_rage(&mut self, _rage: f32) {}
1294 fn trigger_shatter_event(&mut self, _origin: [f32; 2], _force: f32) {}
1295
1296 fn bifrost(&mut self, rect: Rect, blur: f32, saturation: f32, opacity: f32);
1299 fn push_mjolnir_slice(&mut self, angle: f32, offset: f32);
1301 fn pop_mjolnir_slice(&mut self);
1303 fn mjolnir_shatter(&mut self, _rect: Rect, _pieces: u32, _force: f32, _color: [f32; 4]) {}
1305 fn mjolnir_fluid_shatter(&mut self, _rect: Rect, _pieces: u32, _force: f32, _color: [f32; 4]) {}
1306 fn draw_mjolnir_bolt(&mut self, _from: [f32; 2], _to: [f32; 2], _color: [f32; 4]) {}
1308
1309 fn set_aria_role(&mut self, _role: &str) {}
1311 fn set_aria_label(&mut self, _label: &str) {}
1312
1313 fn register_shared_element(&mut self, _id: &str, _rect: Rect) {}
1315
1316 fn set_key(&mut self, _key: &str) {}
1318
1319
1320 fn get_telemetry(&self) -> TelemetryData {
1323 TelemetryData::default()
1324 }
1325
1326 fn push_shadow(&mut self, _radius: f32, _color: [f32; 4], _offset: [f32; 2]) {}
1329 fn pop_shadow(&mut self) {}
1331
1332 fn push_vnode(&mut self, _rect: Rect, _name: &'static str) {}
1335 fn pop_vnode(&mut self) {}
1337 fn register_handler(
1339 &mut self,
1340 _event_type: &str,
1341 _handler: std::sync::Arc<dyn Fn(Event) + Send + Sync>,
1342 ) {
1343 }
1344
1345 fn set_z_index(&mut self, _z: f32) {}
1349 fn get_z_index(&self) -> f32 {
1351 0.0
1352 }
1353
1354 fn load_svg(&mut self, _name: &str, _svg_data: &[u8]) {}
1357 fn draw_svg(&mut self, _name: &str, _rect: Rect) {}
1359
1360 fn push_transform(&mut self, _translation: [f32; 2], _scale: [f32; 2], _rotation: f32) {}
1365 fn pop_transform(&mut self) {}
1367
1368 fn query_layout(&self, _node_id: scene_graph::NodeId) -> Option<Rect> {
1371 None
1372 }
1373
1374 fn set_debug_layout(&mut self, _enabled: bool) {}
1376 fn get_debug_layout(&self) -> bool {
1378 false
1379 }
1380}
1381
1382#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
1384pub enum RenderTier {
1385 Tier1GPU = 0,
1387 Tier2GPU = 1,
1389 Tier3Fallback = 2,
1391}
1392
1393use bytemuck::{Pod, Zeroable};
1398
1399#[repr(C)]
1401#[derive(Copy, Clone, Debug, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
1402pub struct ColorTheme {
1403 pub primary_neon: [f32; 4], pub shatter_neon: [f32; 4],
1405 pub glass_base: [f32; 4],
1406 pub glass_edge: [f32; 4],
1407 pub rune_glow: [f32; 4],
1408 pub ember_core: [f32; 4],
1409 pub background_deep: [f32; 4],
1410 pub glass_blur_strength: f32,
1411 pub shatter_edge_width: f32,
1412 pub neon_bloom_radius: f32,
1413 pub rune_opacity: f32, pub _pad: [f32; 3], pub _pad2: f32,
1417}
1418
1419impl ColorTheme {
1420 pub fn cyberpunk_viking() -> Self {
1421 Self {
1422 primary_neon: [0.0, 1.0, 0.95, 1.2],
1423 shatter_neon: [1.0, 0.0, 0.75, 1.5],
1424 glass_base: [0.04, 0.04, 0.06, 0.82],
1425 glass_edge: [0.0, 0.45, 0.55, 0.6],
1426 rune_glow: [0.75, 0.98, 1.0, 0.9],
1427 ember_core: [0.95, 0.12, 0.12, 1.0],
1428 background_deep: [0.01, 0.01, 0.03, 1.0],
1429 glass_blur_strength: 0.6,
1430 shatter_edge_width: 1.8,
1431 neon_bloom_radius: 0.022,
1432 rune_opacity: 0.55,
1433 _pad: [0.0; 3],
1434 _pad2: 0.0,
1435 }
1436 }
1437
1438 pub fn vibrant_glass() -> Self {
1439 Self {
1440 primary_neon: [0.0, 1.0, 0.95, 1.2],
1441 shatter_neon: [1.0, 0.0, 0.75, 1.5],
1442 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],
1445 ember_core: [1.0, 0.4, 0.1, 1.0],
1446 background_deep: [0.05, 0.05, 0.1, 1.0],
1447 glass_blur_strength: 0.9,
1448 shatter_edge_width: 1.8,
1449 neon_bloom_radius: 0.022,
1450 rune_opacity: 0.55,
1451 _pad: [0.0; 3],
1452 _pad2: 0.0,
1453 }
1454 }
1455}
1456
1457impl Default for ColorTheme {
1458 fn default() -> Self {
1459 Self::vibrant_glass()
1460 }
1461}
1462
1463#[repr(C)]
1465#[derive(Copy, Clone, Debug, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
1466pub struct SceneUniforms {
1467 pub view: glam::Mat4,
1468 pub proj: glam::Mat4,
1469 pub time: f32,
1470 pub delta_time: f32,
1471 pub resolution: [f32; 2],
1472 pub mouse: [f32; 2],
1473 pub mouse_velocity: [f32; 2],
1474 pub shatter_origin: [f32; 2],
1475 pub shatter_time: f32,
1476 pub shatter_force: f32,
1477 pub berzerker_rage: f32,
1478 pub scroll_offset: f32,
1479 pub scale_factor: f32,
1480 pub _pad: [f32; 1],
1482}
1483
1484impl SceneUniforms {
1485 pub fn new(width: f32, height: f32) -> Self {
1486 Self {
1487 view: glam::Mat4::IDENTITY,
1488 proj: glam::Mat4::orthographic_lh(0.0, width, height, 0.0, -100.0, 100.0),
1489 time: 0.0,
1490 delta_time: 0.016,
1491 resolution: [width, height],
1492 mouse: [0.5, 0.5],
1493 mouse_velocity: [0.0, 0.0],
1494 shatter_origin: [0.5, 0.5],
1495 shatter_time: -100.0,
1496 shatter_force: 0.0,
1497 berzerker_rage: 0.0,
1498 scroll_offset: 0.0,
1499 scale_factor: 1.0,
1500 _pad: [0.0; 1],
1501 }
1502 }
1503}
1504
1505#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1507pub struct Mesh {
1508 pub vertices: Vec<[f32; 3]>,
1509 pub normals: Vec<[f32; 3]>,
1510 pub indices: Vec<u32>,
1511}
1512
1513impl Mesh {
1514 pub fn from_obj(data: &[u8]) -> anyhow::Result<Vec<Self>> {
1515 let mut cursor = std::io::Cursor::new(data);
1516 let (models, _) = tobj::load_obj_buf(&mut cursor, &tobj::LoadOptions::default(), |_| {
1517 Ok((Vec::new(), Default::default()))
1518 })?;
1519
1520 let mut meshes = Vec::new();
1521 for m in models {
1522 let mesh = m.mesh;
1523 let vertices: Vec<[f32; 3]> = mesh
1524 .positions
1525 .chunks(3)
1526 .map(|c| [c[0], c[1], c[2]])
1527 .collect();
1528 let normals = if mesh.normals.is_empty() {
1529 vec![[0.0, 0.0, 1.0]; vertices.len()]
1530 } else {
1531 mesh.normals.chunks(3).map(|c| [c[0], c[1], c[2]]).collect()
1532 };
1533 meshes.push(Mesh {
1534 vertices,
1535 normals,
1536 indices: mesh.indices,
1537 });
1538 }
1539 Ok(meshes)
1540 }
1541
1542 pub fn from_stl(data: &[u8]) -> anyhow::Result<Self> {
1543 let mut cursor = std::io::Cursor::new(data);
1544 let stl = stl_io::read_stl(&mut cursor)?;
1545
1546 let vertices: Vec<[f32; 3]> = stl.vertices.iter().map(|v| [v[0], v[1], v[2]]).collect();
1547 let mut indices = Vec::new();
1548 for face in stl.faces {
1549 indices.push(face.vertices[0] as u32);
1550 indices.push(face.vertices[1] as u32);
1551 indices.push(face.vertices[2] as u32);
1552 }
1553
1554 let normals = vec![[0.0, 0.0, 1.0]; vertices.len()];
1555
1556 Ok(Mesh {
1557 vertices,
1558 normals,
1559 indices,
1560 })
1561 }
1562}
1563
1564pub trait FrameRenderer<E = ()>: Renderer {
1567 fn begin_frame(&mut self) -> E;
1568 fn end_frame(&mut self, encoder: E);
1569}
1570
1571use std::sync::Arc;
1572
1573#[derive(Clone)]
1575pub struct State<T: Clone + Send + Sync + 'static> {
1576 swap: Arc<arc_swap::ArcSwap<T>>,
1577 metadata_swap: Arc<arc_swap::ArcSwap<Option<agents::MutationMetadata>>>,
1578 #[cfg(not(target_arch = "wasm32"))]
1579 tvar: Arc<stm::TVar<T>>,
1580 #[cfg(not(target_arch = "wasm32"))]
1581 metadata_tvar: Arc<stm::TVar<Option<agents::MutationMetadata>>>,
1582 subscribers: Arc<std::sync::Mutex<Vec<Box<dyn Fn(&T) + Send + Sync>>>>,
1583 version: Arc<std::sync::atomic::AtomicU64>,
1584 resolution: agents::ConflictResolution,
1585}
1586
1587impl<T: Clone + Send + Sync + 'static> State<T> {
1588 pub fn new(value: T) -> Self {
1590 #[cfg(not(target_arch = "wasm32"))]
1591 let tvar = Arc::new(stm::TVar::new(value.clone()));
1592 #[cfg(not(target_arch = "wasm32"))]
1593 let metadata_tvar = Arc::new(stm::TVar::new(None));
1594
1595 Self {
1596 swap: Arc::new(arc_swap::ArcSwap::from_pointee(value)),
1597 metadata_swap: Arc::new(arc_swap::ArcSwap::new(Arc::new(None))),
1598 #[cfg(not(target_arch = "wasm32"))]
1599 tvar,
1600 #[cfg(not(target_arch = "wasm32"))]
1601 metadata_tvar,
1602 subscribers: Arc::new(std::sync::Mutex::new(Vec::new())),
1603 version: Arc::new(std::sync::atomic::AtomicU64::new(0)),
1604 resolution: agents::ConflictResolution::default(),
1605 }
1606 }
1607
1608 pub fn with_resolution(mut self, resolution: agents::ConflictResolution) -> Self {
1610 self.resolution = resolution;
1611 self
1612 }
1613
1614 pub fn get(&self) -> T {
1616 (**self.swap.load()).clone()
1617 }
1618
1619 pub fn set(&self, value: T) {
1621 #[cfg(not(target_arch = "wasm32"))]
1622 let (was_skipped, final_val, final_meta) = stm::atomically(|tx| {
1623 let new_meta = agents::get_current_mutation_metadata();
1624 let existing_meta = self.metadata_tvar.read(tx)?;
1625
1626 let mut skip = false;
1627 if self.resolution == agents::ConflictResolution::PriorityWins {
1628 if let (Some(new_m), Some(old_m)) = (new_meta, existing_meta) {
1629 if new_m.priority < old_m.priority {
1630 skip = true;
1631 }
1632 }
1633 }
1634
1635 if !skip {
1636 self.tvar.write(tx, value.clone())?;
1637 self.metadata_tvar.write(tx, new_meta)?;
1638 Ok((false, value.clone(), new_meta))
1639 } else {
1640 Ok((true, self.tvar.read(tx)?, existing_meta))
1641 }
1642 });
1643
1644 #[cfg(target_arch = "wasm32")]
1645 let (was_skipped, final_val, final_meta) = (false, value, agents::get_current_mutation_metadata());
1646
1647 if was_skipped {
1648 if let (Some(new_m), Some(old_m)) = (agents::get_current_mutation_metadata(), final_meta) {
1649 agents::notify_conflict(agents::ConflictEvent {
1650 agent_id: new_m.agent_id,
1651 priority: new_m.priority,
1652 existing_agent_id: old_m.agent_id,
1653 existing_priority: old_m.priority,
1654 timestamp_ms: new_m.timestamp_ms,
1655 });
1656 }
1657 return;
1658 }
1659
1660 self.swap.store(Arc::new(final_val.clone()));
1661 self.metadata_swap.store(Arc::new(final_meta));
1662 self.version.fetch_add(1, std::sync::atomic::Ordering::Release);
1663
1664 let subs = Arc::clone(&self.subscribers);
1665 if crate::is_batching() {
1666 crate::enqueue_batch_task(Box::new(move || {
1667 let s = subs.lock().unwrap();
1668 for cb in s.iter() {
1669 cb(&final_val);
1670 }
1671 }));
1672 } else {
1673 let s = subs.lock().unwrap();
1674 for cb in s.iter() {
1675 cb(&final_val);
1676 }
1677 }
1678 }
1679
1680 pub fn mutate<F: Fn(&T) -> T>(&self, f: F) {
1681 #[cfg(not(target_arch = "wasm32"))]
1682 {
1683 let (was_skipped, final_val, final_meta) = stm::atomically(|tx| {
1684 let new_meta = agents::get_current_mutation_metadata();
1685 let existing_meta = self.metadata_tvar.read(tx)?;
1686
1687 let mut skip = false;
1688 if self.resolution == agents::ConflictResolution::PriorityWins {
1689 if let (Some(new_m), Some(old_m)) = (new_meta, existing_meta) {
1690 if new_m.priority < old_m.priority {
1691 skip = true;
1692 }
1693 }
1694 }
1695
1696 if !skip {
1697 let current = self.tvar.read(tx)?;
1698 let next = f(¤t);
1699 self.tvar.write(tx, next.clone())?;
1700 self.metadata_tvar.write(tx, new_meta)?;
1701 Ok((false, next, new_meta))
1702 } else {
1703 Ok((true, self.tvar.read(tx)?, existing_meta))
1704 }
1705 });
1706
1707 if was_skipped {
1708 if let (Some(new_m), Some(old_m)) = (agents::get_current_mutation_metadata(), final_meta) {
1709 agents::notify_conflict(agents::ConflictEvent {
1710 agent_id: new_m.agent_id,
1711 priority: new_m.priority,
1712 existing_agent_id: old_m.agent_id,
1713 existing_priority: old_m.priority,
1714 timestamp_ms: new_m.timestamp_ms,
1715 });
1716 }
1717 return;
1718 }
1719
1720 self.swap.store(Arc::new(final_val.clone()));
1721 self.metadata_swap.store(Arc::new(final_meta));
1722 self.version.fetch_add(1, std::sync::atomic::Ordering::Release);
1723
1724 let subs = Arc::clone(&self.subscribers);
1725 if crate::is_batching() {
1726 crate::enqueue_batch_task(Box::new(move || {
1727 let s = subs.lock().unwrap();
1728 for cb in s.iter() {
1729 cb(&final_val);
1730 }
1731 }));
1732 } else {
1733 let s = subs.lock().unwrap();
1734 for cb in s.iter() {
1735 cb(&final_val);
1736 }
1737 }
1738 }
1739 #[cfg(target_arch = "wasm32")]
1740 {
1741 self.set(f(&self.get()));
1742 }
1743 }
1744
1745 pub fn version(&self) -> u64 {
1747 self.version.load(std::sync::atomic::Ordering::Acquire)
1748 }
1749
1750 pub fn subscribe<F: Fn(&T) + Send + Sync + 'static>(&self, callback: F) {
1752 self.subscribers.lock().unwrap().push(Box::new(callback));
1753 }
1754}
1755
1756#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
1760pub struct ComponentErrorState {
1761 pub has_error: bool,
1762 pub error_message: Option<String>,
1763 pub error_location: Option<String>,
1764}
1765
1766impl ComponentErrorState {
1767 pub fn clear() -> Self {
1769 Self::default()
1770 }
1771
1772 pub fn error(message: impl Into<String>, location: impl Into<String>) -> Self {
1774 Self {
1775 has_error: true,
1776 error_message: Some(message.into()),
1777 error_location: Some(location.into()),
1778 }
1779 }
1780}
1781
1782#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
1784pub struct KnowledgeFragment {
1785 pub id: String,
1787 pub summary: String,
1789 pub source: String,
1791 pub created_at: u64,
1793 pub accessed_count: u32,
1795 pub content: Option<String>,
1797}
1798
1799#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
1802pub struct KnowledgeState {
1803 #[serde(skip)]
1805 pub component_states: std::collections::HashMap<u64, Arc<dyn std::any::Any + Send + Sync>>,
1806
1807 pub fragments: HashMap<String, KnowledgeFragment>,
1809
1810 pub last_query_results: Vec<String>,
1812}
1813
1814use crate::runtime::NodeStateSnapshot;
1815use std::sync::atomic::{AtomicBool, Ordering};
1816use std::sync::OnceLock;
1817
1818pub static SYSTEM_STATE: OnceLock<Arc<arc_swap::ArcSwap<KnowledgeState>>> = OnceLock::new();
1820
1821#[cfg(not(target_arch = "wasm32"))]
1822static KNOWLEDGE_TVAR: OnceLock<stm::TVar<KnowledgeState>> = OnceLock::new();
1823
1824static IS_BATCHING: AtomicBool = AtomicBool::new(false);
1825pub static IS_RENDERING: AtomicBool = AtomicBool::new(false);
1826pub static LAYOUT_DIRTY: AtomicBool = AtomicBool::new(false);
1827static BATCH_QUEUE: OnceLock<std::sync::Mutex<Vec<Box<dyn FnOnce() + Send + Sync>>>> = OnceLock::new();
1828
1829pub fn is_batching() -> bool {
1831 IS_BATCHING.load(Ordering::Acquire)
1832}
1833
1834pub fn is_rendering() -> bool {
1836 IS_RENDERING.load(Ordering::Acquire)
1837}
1838
1839pub fn begin_render_phase() {
1841 IS_RENDERING.store(true, Ordering::Release);
1842}
1843
1844pub fn end_render_phase() {
1846 IS_RENDERING.store(false, Ordering::Release);
1847}
1848
1849pub fn enqueue_batch_task(task: Box<dyn FnOnce() + Send + Sync>) {
1851 let mut queue = BATCH_QUEUE
1852 .get_or_init(|| std::sync::Mutex::new(Vec::new()))
1853 .lock()
1854 .unwrap();
1855 queue.push(task);
1856}
1857
1858pub fn batch<F: FnOnce()>(f: F) {
1862 if IS_BATCHING.swap(true, Ordering::AcqRel) {
1863 f();
1865 return;
1866 }
1867
1868 f();
1869
1870 IS_BATCHING.store(false, Ordering::Release);
1871
1872 let mut queue = BATCH_QUEUE
1873 .get_or_init(|| std::sync::Mutex::new(Vec::new()))
1874 .lock()
1875 .unwrap();
1876 let tasks: Vec<_> = queue.drain(..).collect();
1877 drop(queue);
1878
1879 for task in tasks {
1880 task();
1881 }
1882}
1883
1884pub fn get_system_state() -> Arc<arc_swap::ArcSwap<KnowledgeState>> {
1886 SYSTEM_STATE
1887 .get_or_init(|| Arc::new(arc_swap::ArcSwap::from_pointee(KnowledgeState::default())))
1888 .clone()
1889}
1890
1891pub fn load_system_state() -> arc_swap::Guard<Arc<KnowledgeState>> {
1892 get_system_state().load()
1893}
1894
1895pub fn update_system_state<F>(f: F)
1896where
1897 F: Fn(&KnowledgeState) -> KnowledgeState,
1898{
1899 if is_rendering() {
1900 log::warn!("LAYOUT THRASH DETECTED: System state mutated during render phase. This may trigger redundant layout passes and impact performance.");
1901 }
1902
1903 LAYOUT_DIRTY.store(true, Ordering::SeqCst);
1904
1905 let swap = get_system_state();
1906 let current = swap.load();
1907 let new_state = Arc::new(f(¤t));
1908 swap.store(Arc::clone(&new_state));
1909
1910 #[cfg(not(target_arch = "wasm32"))]
1911 {
1912 let tvar = KNOWLEDGE_TVAR
1913 .get_or_init(|| stm::TVar::new((*new_state).clone()));
1914 let _ = stm::atomically(|tx| tvar.write(tx, (*new_state).clone()));
1915 }
1916}
1917
1918pub fn transact_system_state<F>(f: F)
1919where
1920 F: Fn(&KnowledgeState) -> KnowledgeState,
1921{
1922 #[cfg(not(target_arch = "wasm32"))]
1923 {
1924 if is_rendering() {
1925 log::warn!("LAYOUT THRASH DETECTED: System state mutated during render phase. This may trigger redundant layout passes and impact performance.");
1926 }
1927 let tvar = KNOWLEDGE_TVAR
1928 .get_or_init(|| {
1929 stm::TVar::new((**get_system_state().load()).clone())
1930 })
1931 .clone();
1932 let new_state = stm::atomically(move |tx| {
1933 let current = tvar.read(tx)?;
1934 let next = f(¤t);
1935 tvar.write(tx, next.clone())?;
1936 Ok(next)
1937 });
1938 get_system_state().store(Arc::new(new_state));
1939 }
1940 #[cfg(target_arch = "wasm32")]
1941 {
1942 if is_rendering() {
1943 log::warn!("LAYOUT THRASH DETECTED: System state mutated during render phase. This may trigger redundant layout passes and impact performance.");
1944 }
1945 update_system_state(f);
1946 }
1947}
1948
1949impl KnowledgeState {
1950 pub fn new() -> Self {
1952 Self::default()
1953 }
1954
1955 pub fn set_component_state<T: 'static + Send + Sync>(&mut self, id: u64, state: T) {
1957 self.component_states
1958 .insert(id, Arc::new(std::sync::RwLock::new(state)));
1959 }
1960
1961 pub fn get_component_state<T: 'static + Send + Sync>(
1963 &self,
1964 id: u64,
1965 ) -> Option<Arc<std::sync::RwLock<T>>> {
1966 let lock = self.component_states.get(&id)?;
1967 lock.clone().downcast::<std::sync::RwLock<T>>().ok()
1968 }
1969
1970 pub fn remember(&mut self, fragment: KnowledgeFragment) {
1972 self.fragments.insert(fragment.id.clone(), fragment);
1973 }
1974
1975 pub fn process_query(&mut self, query: &str) {
1977 let query_lower = query.to_lowercase();
1978 let mut results: Vec<(f32, String)> = self
1979 .fragments
1980 .iter()
1981 .map(|(id, frag)| {
1982 let mut score = 0.0;
1983 if frag.summary.to_lowercase().contains(&query_lower) {
1984 score += 1.0;
1985 }
1986 if frag.source.to_lowercase().contains(&query_lower) {
1987 score += 0.5;
1988 }
1989 (score, id.clone())
1990 })
1991 .filter(|(score, _)| *score > 0.0)
1992 .collect();
1993
1994 results.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
1996
1997 self.last_query_results = results.into_iter().map(|(_, id)| id).take(5).collect();
1998 }
1999
2000 pub fn snapshot(&self) -> Vec<NodeStateSnapshot> {
2002 let mut snapshots = Vec::new();
2003
2004 for (_id, frag) in &self.fragments {
2006 if let Ok(val) = serde_json::to_value(frag) {
2007 snapshots.push(NodeStateSnapshot { id: 0, state: val });
2008 }
2009 }
2010
2011 snapshots
2012 }
2013}
2014
2015#[derive(Clone)]
2017pub struct Binding<T: Clone + Send + Sync + 'static> {
2018 swap: Arc<arc_swap::ArcSwap<T>>,
2019 #[cfg(not(target_arch = "wasm32"))]
2020 tvar: Arc<stm::TVar<T>>,
2021 version: Arc<std::sync::atomic::AtomicU64>,
2022}
2023
2024impl<T: Clone + Send + Sync + 'static> Binding<T> {
2025 pub fn from_state(state: &State<T>) -> Self {
2027 Self {
2028 swap: Arc::clone(&state.swap),
2029 #[cfg(not(target_arch = "wasm32"))]
2030 tvar: Arc::clone(&state.tvar),
2031 version: Arc::clone(&state.version),
2032 }
2033 }
2034
2035 pub fn get(&self) -> T {
2037 (**self.swap.load()).clone()
2038 }
2039
2040 pub fn set(&self, value: T) {
2042 self.swap.store(Arc::new(value.clone()));
2043 #[cfg(not(target_arch = "wasm32"))]
2044 {
2045 let tvar = Arc::clone(&self.tvar);
2046 let v = value.clone();
2047 let _ = stm::atomically(move |tx| tvar.write(tx, v.clone()));
2048 }
2049 self.version.fetch_add(1, std::sync::atomic::Ordering::Release);
2050 }
2051
2052 pub fn version(&self) -> u64 {
2054 self.version.load(std::sync::atomic::Ordering::Acquire)
2055 }
2056}
2057
2058#[cfg(not(target_arch = "wasm32"))]
2059pub fn transact_pair<A, B, F>(state_a: &State<A>, state_b: &State<B>, f: F)
2060where
2061 A: Clone + Send + Sync + 'static,
2062 B: Clone + Send + Sync + 'static,
2063 F: Fn(&A, &B) -> (A, B),
2064{
2065 let tvar_a = Arc::clone(&state_a.tvar);
2066 let tvar_b = Arc::clone(&state_b.tvar);
2067 let (new_a, new_b) = stm::atomically(move |tx| {
2068 let a = tvar_a.read(tx)?;
2069 let b = tvar_b.read(tx)?;
2070 let (na, nb) = f(&a, &b);
2071 tvar_a.write(tx, na.clone())?;
2072 tvar_b.write(tx, nb.clone())?;
2073 Ok((na, nb))
2074 });
2075 state_a.swap.store(Arc::new(new_a.clone()));
2076 state_b.swap.store(Arc::new(new_b.clone()));
2077 state_a.version.fetch_add(1, std::sync::atomic::Ordering::Release);
2078 state_b.version.fetch_add(1, std::sync::atomic::Ordering::Release);
2079
2080 let subs_a = Arc::clone(&state_a.subscribers);
2081 let subs_b = Arc::clone(&state_b.subscribers);
2082
2083 if crate::is_batching() {
2084 crate::enqueue_batch_task(Box::new(move || {
2085 {
2086 let s = subs_a.lock().unwrap();
2087 for cb in s.iter() { cb(&new_a); }
2088 }
2089 {
2090 let s = subs_b.lock().unwrap();
2091 for cb in s.iter() { cb(&new_b); }
2092 }
2093 }));
2094 } else {
2095 {
2096 let s = subs_a.lock().unwrap();
2097 for cb in s.iter() { cb(&new_a); }
2098 }
2099 {
2100 let s = subs_b.lock().unwrap();
2101 for cb in s.iter() { cb(&new_b); }
2102 }
2103 }
2104}
2105
2106use std::any::TypeId;
2107use std::sync::Mutex;
2108
2109pub(crate) static ENVIRONMENT: OnceLock<
2111 Mutex<HashMap<TypeId, Box<dyn std::any::Any + Send + Sync>>>,
2112> = OnceLock::new();
2113
2114pub trait EnvKey: 'static + Send + Sync {
2118 type Value: Clone + Send + Sync + 'static;
2120
2121 fn default_value() -> Self::Value;
2123}
2124
2125pub struct YggdrasilKey;
2127
2128impl EnvKey for YggdrasilKey {
2129 type Value = YggdrasilTokens;
2130 fn default_value() -> Self::Value {
2131 default_tokens()
2132 }
2133}
2134
2135pub struct AssetKey;
2137
2138impl EnvKey for AssetKey {
2139 type Value = Arc<dyn AssetManager>;
2140 fn default_value() -> Self::Value {
2141 Arc::new(DefaultAssetManager::new())
2142 }
2143}
2144
2145#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2147pub enum Appearance {
2148 Light,
2149 Dark,
2150}
2151
2152#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2154pub enum Orientation {
2155 Horizontal,
2156 Vertical,
2157}
2158
2159#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
2161pub enum Alignment {
2162 #[default]
2163 Center,
2164 Leading,
2165 Trailing,
2166 Top,
2167 Bottom,
2168}
2169
2170#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
2172pub enum Distribution {
2173 #[default]
2174 Fill,
2175 Center,
2176 Leading,
2177 Trailing,
2178 SpaceBetween,
2179 SpaceAround,
2180 SpaceEvenly,
2181}
2182
2183#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
2185pub struct Color {
2186 pub r: f32,
2187 pub g: f32,
2188 pub b: f32,
2189 pub a: f32,
2190}
2191
2192impl Color {
2193 pub const BLACK: Color = Color {
2194 r: 0.0,
2195 g: 0.0,
2196 b: 0.0,
2197 a: 1.0,
2198 };
2199 pub const WHITE: Color = Color {
2200 r: 1.0,
2201 g: 1.0,
2202 b: 1.0,
2203 a: 1.0,
2204 };
2205 pub const TRANSPARENT: Color = Color {
2206 r: 0.0,
2207 g: 0.0,
2208 b: 0.0,
2209 a: 0.0,
2210 };
2211 pub const RED: Color = Color {
2212 r: 1.0,
2213 g: 0.0,
2214 b: 0.0,
2215 a: 1.0,
2216 };
2217 pub const GREEN: Color = Color {
2218 r: 0.0,
2219 g: 1.0,
2220 b: 0.0,
2221 a: 1.0,
2222 };
2223 pub const BLUE: Color = Color {
2224 r: 0.0,
2225 g: 0.0,
2226 b: 1.0,
2227 a: 1.0,
2228 };
2229
2230 pub fn relative_luminance(&self) -> f32 {
2232 fn res(c: f32) -> f32 {
2233 if c <= 0.03928 {
2234 c / 12.92
2235 } else {
2236 ((c + 0.055) / 1.055).powf(2.4)
2237 }
2238 }
2239 0.2126 * res(self.r) + 0.7152 * res(self.g) + 0.0722 * res(self.b)
2240 }
2241
2242 pub fn contrast_ratio(&self, other: &Color) -> f32 {
2244 let l1 = self.relative_luminance();
2245 let l2 = other.relative_luminance();
2246 if l1 > l2 {
2247 (l1 + 0.05) / (l2 + 0.05)
2248 } else {
2249 (l2 + 0.05) / (l1 + 0.05)
2250 }
2251 }
2252
2253 pub const CYAN: Color = Color {
2254 r: 0.0,
2255 g: 1.0,
2256 b: 1.0,
2257 a: 1.0,
2258 };
2259 pub const YELLOW: Color = Color {
2260 r: 1.0,
2261 g: 1.0,
2262 b: 0.0,
2263 a: 1.0,
2264 };
2265 pub const MAGENTA: Color = Color {
2266 r: 1.0,
2267 g: 0.0,
2268 b: 1.0,
2269 a: 1.0,
2270 };
2271 pub const GRAY: Color = Color {
2272 r: 0.5,
2273 g: 0.5,
2274 b: 0.5,
2275 a: 1.0,
2276 };
2277
2278 pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
2280 Self { r, g, b, a }
2281 }
2282
2283 pub fn as_array(&self) -> [f32; 4] {
2285 [self.r, self.g, self.b, self.a]
2286 }
2287}
2288
2289impl View for Color {
2290 type Body = Never;
2291 fn body(self) -> Self::Body {
2292 unreachable!()
2293 }
2294
2295 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
2296 renderer.fill_rect(rect, self.as_array());
2297 }
2298}
2299
2300pub struct AppearanceKey;
2302
2303impl EnvKey for AppearanceKey {
2304 type Value = Appearance;
2305 fn default_value() -> Self::Value {
2306 Appearance::Dark }
2308}
2309
2310pub struct StyleResolver;
2312
2313impl StyleResolver {
2314 pub fn color(key: &str) -> String {
2316 let tokens = Environment::<YggdrasilKey>::new().get();
2317 let appearance = Environment::<AppearanceKey>::new().get();
2318 let is_dark = appearance == Appearance::Dark;
2319
2320 tokens
2321 .get_color(key, is_dark)
2322 .unwrap_or_else(|| "#FF00FF".to_string()) }
2324
2325 pub fn get<T: FromStr>(category: &str, key: &str) -> Option<T> {
2327 let tokens = Environment::<YggdrasilKey>::new().get();
2328 let appearance = Environment::<AppearanceKey>::new().get();
2329 let is_dark = appearance == Appearance::Dark;
2330
2331 tokens.get(category, key, is_dark)
2332 }
2333}
2334
2335pub fn default_tokens() -> YggdrasilTokens {
2337 let mut tokens = YggdrasilTokens::new();
2338
2339 tokens.color.insert(
2341 "background".to_string(),
2342 TokenValue::Single {
2343 value: "#000000".to_string(), },
2345 );
2346
2347 tokens.color.insert(
2348 "primary".to_string(),
2349 TokenValue::Single {
2350 value: "#00FFFF".to_string(), },
2352 );
2353
2354 tokens.color.insert(
2355 "secondary".to_string(),
2356 TokenValue::Single {
2357 value: "#FF00FF".to_string(), },
2359 );
2360
2361 tokens.color.insert(
2362 "surface".to_string(),
2363 TokenValue::Adaptive {
2364 light: "#FFFFFF".to_string(),
2365 dark: "#121212".to_string(),
2366 },
2367 );
2368
2369 tokens.color.insert(
2370 "text".to_string(),
2371 TokenValue::Adaptive {
2372 light: "#000000".to_string(),
2373 dark: "#FFFFFF".to_string(),
2374 },
2375 );
2376
2377 tokens.bifrost.insert(
2379 "blur".to_string(),
2380 TokenValue::Single {
2381 value: "25.0".to_string(),
2382 },
2383 );
2384 tokens.bifrost.insert(
2385 "saturation".to_string(),
2386 TokenValue::Single {
2387 value: "1.2".to_string(),
2388 },
2389 );
2390 tokens.bifrost.insert(
2391 "opacity".to_string(),
2392 TokenValue::Single {
2393 value: "0.65".to_string(),
2394 },
2395 );
2396
2397 tokens.gungnir.insert(
2399 "intensity".to_string(),
2400 TokenValue::Single {
2401 value: "1.0".to_string(),
2402 },
2403 );
2404 tokens.gungnir.insert(
2405 "radius".to_string(),
2406 TokenValue::Single {
2407 value: "15.0".to_string(),
2408 },
2409 );
2410
2411 tokens.mjolnir.insert(
2413 "clip_angle".to_string(),
2414 TokenValue::Single {
2415 value: "12.0".to_string(),
2416 },
2417 );
2418 tokens.mjolnir.insert(
2419 "border_width".to_string(),
2420 TokenValue::Single {
2421 value: "2.0".to_string(),
2422 },
2423 );
2424
2425 tokens.anim.insert(
2427 "stiffness".to_string(),
2428 TokenValue::Single {
2429 value: "170.0".to_string(),
2430 },
2431 );
2432 tokens.anim.insert(
2433 "damping".to_string(),
2434 TokenValue::Single {
2435 value: "26.0".to_string(),
2436 },
2437 );
2438 tokens.anim.insert(
2439 "mass".to_string(),
2440 TokenValue::Single {
2441 value: "1.0".to_string(),
2442 },
2443 );
2444
2445 tokens.accessibility.insert(
2447 "reduce_motion".to_string(),
2448 TokenValue::Single {
2449 value: "false".to_string(),
2450 },
2451 );
2452
2453 tokens
2454}
2455
2456pub struct Environment<K: EnvKey> {
2458 _marker: std::marker::PhantomData<K>,
2459}
2460
2461impl<K: EnvKey> Environment<K> {
2462 pub fn new() -> Self {
2464 Self {
2465 _marker: std::marker::PhantomData,
2466 }
2467 }
2468
2469 pub fn get(&self) -> K::Value {
2471 if let Some(env_store) = ENVIRONMENT.get() {
2472 let env_lock = env_store.lock().unwrap();
2473 if let Some(val) = env_lock.get(&std::any::TypeId::of::<K>()) {
2474 if let Some(typed_val) = val.downcast_ref::<K::Value>() {
2475 return typed_val.clone();
2476 }
2477 }
2478 }
2479 K::default_value()
2480 }
2481}
2482
2483pub mod env {
2485
2486 pub fn insert<K: super::EnvKey>(value: K::Value) {
2488 if let Some(store) = super::ENVIRONMENT.get() {
2489 let mut env_map = store.lock().unwrap();
2490 env_map.insert(std::any::TypeId::of::<K>(), Box::new(value));
2491 }
2492 }
2493
2494 pub fn remove<K: super::EnvKey>() {
2496 if let Some(store) = super::ENVIRONMENT.get() {
2497 let mut env_map = store.lock().unwrap();
2498 env_map.remove(&std::any::TypeId::of::<K>());
2499 }
2500 }
2501}
2502
2503#[derive(Debug, Clone, Copy, PartialEq)]
2507pub struct Size {
2508 pub width: f32,
2509 pub height: f32,
2510}
2511
2512impl Size {
2513 pub const ZERO: Self = Self { width: 0.0, height: 0.0 };
2514
2515 pub fn new(width: f32, height: f32) -> Self {
2516 Self { width, height }
2517 }
2518}
2519
2520#[derive(Debug, Clone, Copy, PartialEq)]
2522pub struct EdgeInsets {
2523 pub top: f32,
2524 pub leading: f32,
2525 pub bottom: f32,
2526 pub trailing: f32,
2527}
2528
2529impl EdgeInsets {
2530 pub fn all(value: f32) -> Self {
2532 Self {
2533 top: value,
2534 leading: value,
2535 bottom: value,
2536 trailing: value,
2537 }
2538 }
2539
2540 pub fn vertical(value: f32) -> Self {
2542 Self {
2543 top: value,
2544 leading: 0.0,
2545 bottom: value,
2546 trailing: 0.0,
2547 }
2548 }
2549
2550 pub fn horizontal(value: f32) -> Self {
2552 Self {
2553 top: 0.0,
2554 leading: value,
2555 bottom: 0.0,
2556 trailing: value,
2557 }
2558 }
2559}
2560
2561#[derive(Debug, Clone, Copy, PartialEq)]
2563pub struct FrameModifier {
2564 pub width: Option<f32>,
2565 pub height: Option<f32>,
2566}
2567
2568impl FrameModifier {
2569 pub fn new() -> Self {
2570 Self {
2571 width: None,
2572 height: None,
2573 }
2574 }
2575
2576 pub fn width(mut self, width: f32) -> Self {
2577 self.width = Some(width);
2578 self
2579 }
2580
2581 pub fn height(mut self, height: f32) -> Self {
2582 self.height = Some(height);
2583 self
2584 }
2585
2586 pub fn size(mut self, width: f32, height: f32) -> Self {
2587 self.width = Some(width);
2588 self.height = Some(height);
2589 self
2590 }
2591}
2592
2593impl ViewModifier for FrameModifier {
2594 fn modify<V: View>(self, content: V) -> impl View {
2595 ModifiedView::new(content, self)
2596 }
2597}
2598
2599#[derive(Debug, Clone, Copy, PartialEq)]
2601pub struct FlexModifier {
2602 pub weight: f32,
2603}
2604
2605impl ViewModifier for FlexModifier {
2606 fn modify<V: View>(self, content: V) -> impl View {
2607 ModifiedView::new(content, self)
2608 }
2609
2610 fn child_flex_weight<V: View>(&self, _view: &V) -> f32 {
2611 self.weight
2612 }
2613}
2614
2615#[derive(Debug, Clone, Copy, PartialEq)]
2617pub struct OffsetModifier {
2618 pub x: f32,
2619 pub y: f32,
2620}
2621
2622impl OffsetModifier {
2623 pub fn new(x: f32, y: f32) -> Self {
2624 Self { x, y }
2625 }
2626}
2627
2628impl ViewModifier for OffsetModifier {
2629 fn modify<V: View>(self, content: V) -> impl View {
2630 ModifiedView::new(content, self)
2631 }
2632}
2633
2634#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2636pub struct ZIndexModifier {
2637 pub z_index: i32,
2638}
2639
2640impl ZIndexModifier {
2641 pub fn new(z_index: i32) -> Self {
2642 Self { z_index }
2643 }
2644}
2645
2646impl ViewModifier for ZIndexModifier {
2647 fn modify<V: View>(self, content: V) -> impl View {
2648 ModifiedView::new(content, self)
2649 }
2650}
2651
2652#[derive(Debug, Clone, Copy, PartialEq)]
2654pub struct LayoutConstraints {
2655 pub min_width: Option<f32>,
2656 pub max_width: Option<f32>,
2657 pub min_height: Option<f32>,
2658 pub max_height: Option<f32>,
2659}
2660
2661impl Default for LayoutConstraints {
2662 fn default() -> Self {
2663 Self {
2664 min_width: None,
2665 max_width: None,
2666 min_height: None,
2667 max_height: None,
2668 }
2669 }
2670}
2671
2672#[derive(Debug, Clone, Copy, PartialEq)]
2674pub struct LayoutModifier {
2675 pub constraints: LayoutConstraints,
2676}
2677
2678impl LayoutModifier {
2679 pub fn new(constraints: LayoutConstraints) -> Self {
2680 Self { constraints }
2681 }
2682}
2683
2684impl ViewModifier for LayoutModifier {
2685 fn modify<V: View>(self, content: V) -> impl View {
2686 ModifiedView::new(content, self)
2687 }
2688}
2689
2690#[derive(Debug, Clone, Copy, PartialEq)]
2692pub struct SafeAreaModifier {
2693 pub ignores: bool,
2694}
2695
2696impl ViewModifier for SafeAreaModifier {
2697 fn modify<V: View>(self, content: V) -> impl View {
2698 ModifiedView::new(content, self)
2699 }
2700}
2701
2702#[derive(Debug, Clone, Copy, PartialEq)]
2704pub struct ElevationModifier {
2705 pub level: f32,
2706}
2707
2708impl ViewModifier for ElevationModifier {
2709 fn modify<V: View>(self, content: V) -> impl View {
2710 ModifiedView::new(content, self)
2711 }
2712
2713 fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
2714 if self.level > 0.0 {
2715 let radius = self.level * 2.0;
2716 let offset_y = self.level * 0.5;
2717 let shadow_color = [0.0, 0.0, 0.0, 0.3];
2718 renderer.push_shadow(radius, shadow_color, [0.0, offset_y]);
2719 view.render(renderer, rect);
2720 renderer.pop_shadow();
2721 } else {
2722 view.render(renderer, rect);
2723 }
2724 }
2725}
2726
2727pub mod layout {
2729 use super::*;
2730
2731 pub struct LayoutCache {
2733 pub safe_area: SafeArea,
2734 size_cache: HashMap<(u64, u32, u32), Size>, }
2736
2737 impl LayoutCache {
2738 pub fn new() -> Self {
2739 Self {
2740 safe_area: SafeArea::default(),
2741 size_cache: HashMap::new(),
2742 }
2743 }
2744
2745 pub fn clear(&mut self) {
2746 self.safe_area = SafeArea::default();
2747 self.size_cache.clear();
2748 }
2749
2750 pub fn get_size(&self, view_hash: u64, proposal: SizeProposal) -> Option<Size> {
2751 let pw = (proposal.width.unwrap_or(-1.0) * 100.0) as u32;
2752 let ph = (proposal.height.unwrap_or(-1.0) * 100.0) as u32;
2753 self.size_cache.get(&(view_hash, pw, ph)).copied()
2754 }
2755
2756 pub fn set_size(&mut self, view_hash: u64, proposal: SizeProposal, size: Size) {
2757 let pw = (proposal.width.unwrap_or(-1.0) * 100.0) as u32;
2758 let ph = (proposal.height.unwrap_or(-1.0) * 100.0) as u32;
2759 self.size_cache.insert((view_hash, pw, ph), size);
2760 }
2761
2762 pub fn invalidate_view(&mut self, view_hash: u64) {
2764 self.size_cache.retain(|&(hash, _, _), _| hash != view_hash);
2765 }
2766 }
2767
2768 #[derive(Debug, Clone, Copy, PartialEq)]
2770 pub struct SizeProposal {
2771 pub width: Option<f32>,
2772 pub height: Option<f32>,
2773 }
2774
2775 impl SizeProposal {
2776 pub fn unspecified() -> Self {
2777 Self {
2778 width: None,
2779 height: None,
2780 }
2781 }
2782
2783 pub fn width(width: f32) -> Self {
2784 Self {
2785 width: Some(width),
2786 height: None,
2787 }
2788 }
2789
2790 pub fn height(height: f32) -> Self {
2791 Self {
2792 width: None,
2793 height: Some(height),
2794 }
2795 }
2796
2797 pub fn tight(width: f32, height: f32) -> Self {
2798 Self {
2799 width: Some(width),
2800 height: Some(height),
2801 }
2802 }
2803
2804 pub fn new(width: Option<f32>, height: Option<f32>) -> Self {
2805 Self { width, height }
2806 }
2807 }
2808
2809 pub trait LayoutView: Send {
2811 fn size_that_fits(
2813 &self,
2814 proposal: SizeProposal,
2815 subviews: &[&dyn LayoutView],
2816 cache: &mut LayoutCache,
2817 ) -> Size;
2818
2819 fn place_subviews(
2821 &self,
2822 bounds: Rect,
2823 subviews: &mut [&mut dyn LayoutView],
2824 cache: &mut LayoutCache,
2825 );
2826
2827 fn flex_weight(&self) -> f32 {
2829 0.0
2830 }
2831 }
2832 #[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
2834 pub struct EdgeInsets {
2835 pub top: f32,
2836 pub leading: f32,
2837 pub bottom: f32,
2838 pub trailing: f32,
2839 }
2840
2841 impl EdgeInsets {
2842 pub fn new(top: f32, leading: f32, bottom: f32, trailing: f32) -> Self {
2843 Self { top, leading, bottom, trailing }
2844 }
2845
2846 pub fn all(value: f32) -> Self {
2847 Self {
2848 top: value,
2849 leading: value,
2850 bottom: value,
2851 trailing: value,
2852 }
2853 }
2854 }
2855
2856 #[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
2858 pub struct SafeArea {
2859 pub insets: EdgeInsets,
2860 }
2861
2862 #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
2864 pub struct Rect {
2865 pub x: f32,
2866 pub y: f32,
2867 pub width: f32,
2868 pub height: f32,
2869 }
2870
2871 impl Rect {
2872 pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
2873 Self {
2874 x,
2875 y,
2876 width,
2877 height,
2878 }
2879 }
2880
2881 pub fn zero() -> Self {
2882 Self {
2883 x: 0.0,
2884 y: 0.0,
2885 width: 0.0,
2886 height: 0.0,
2887 }
2888 }
2889
2890 pub fn size(&self) -> Size {
2891 Size {
2892 width: self.width,
2893 height: self.height,
2894 }
2895 }
2896
2897 pub fn split_horizontal(&self, n: usize) -> Vec<Rect> {
2899 if n == 0 {
2900 return vec![];
2901 }
2902 let item_width = self.width / n as f32;
2903 (0..n)
2904 .map(|i| Rect {
2905 x: self.x + i as f32 * item_width,
2906 y: self.y,
2907 width: item_width,
2908 height: self.height,
2909 })
2910 .collect()
2911 }
2912
2913 pub fn split_vertical(&self, n: usize) -> Vec<Rect> {
2915 if n == 0 {
2916 return vec![];
2917 }
2918 let item_height = self.height / n as f32;
2919 (0..n)
2920 .map(|i| Rect {
2921 x: self.x,
2922 y: self.y + i as f32 * item_height,
2923 width: self.width,
2924 height: item_height,
2925 })
2926 .collect()
2927 }
2928 }
2929}
2930
2931pub use layout::{LayoutCache, LayoutView, Rect, SizeProposal};
2933pub mod runtime;
2936pub mod scene_graph;
2937pub mod agents;
2938pub mod material;
2939
2940
2941pub use scene_graph::{NodeId, bifrost_registry};
2942
2943#[derive(Debug, Clone, PartialEq)]
2945pub enum AssetState<T> {
2946 Loading,
2947 Ready(T),
2948 Error(String),
2949}
2950
2951pub trait AssetManager: Send + Sync {
2953 fn load_image(&self, url: &str) -> AssetState<Arc<Vec<u8>>>;
2955
2956 fn preload_image(&self, url: &str);
2958}
2959
2960#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
2962pub enum Event {
2963 PointerDown { x: f32, y: f32 },
2964 PointerUp { x: f32, y: f32 },
2965 PointerMove { x: f32, y: f32 },
2966 PointerClick { x: f32, y: f32 },
2967 PointerEnter,
2968 PointerLeave,
2969 KeyDown { key: String },
2970 KeyUp { key: String },
2971 Ime(String),
2973}
2974
2975impl Event {
2976 pub fn name(&self) -> &'static str {
2978 match self {
2979 Self::PointerDown { .. } => "pointerdown",
2980 Self::PointerUp { .. } => "pointerup",
2981 Self::PointerMove { .. } => "pointermove",
2982 Self::PointerClick { .. } => "pointerclick",
2983 Self::PointerEnter => "pointerenter",
2984 Self::PointerLeave => "pointerleave",
2985 Self::KeyDown { .. } => "keydown",
2986 Self::KeyUp { .. } => "keyup",
2987 Self::Ime(_) => "ime",
2988 }
2989 }
2990}
2991
2992#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2994pub enum EventResponse {
2995 Handled,
2996 Ignored,
2997}
2998
2999pub struct DefaultAssetManager {
3001 cache: Arc<arc_swap::ArcSwap<HashMap<String, AssetState<Arc<Vec<u8>>>>>>,
3002}
3003
3004impl DefaultAssetManager {
3005 pub fn new() -> Self {
3006 Self {
3007 cache: Arc::new(arc_swap::ArcSwap::from_pointee(HashMap::new())),
3008 }
3009 }
3010}
3011
3012impl AssetManager for DefaultAssetManager {
3013 fn load_image(&self, url: &str) -> AssetState<Arc<Vec<u8>>> {
3014 if let Some(state) = self.cache.load().get(url) {
3015 return state.clone();
3016 }
3017
3018 self.cache.rcu(|map| {
3019 let mut m = (**map).clone();
3020 m.entry(url.to_string()).or_insert(AssetState::Loading);
3021 m
3022 });
3023 AssetState::Loading
3024 }
3025
3026 fn preload_image(&self, _url: &str) {}
3027}
3028
3029use std::future::Future;
3030
3031pub struct Suspense<T: Clone + Send + Sync + 'static> {
3034 inner: State<AssetState<T>>,
3035}
3036
3037impl<T: Clone + Send + Sync + 'static> Suspense<T> {
3038 pub fn new() -> Self {
3039 Self {
3040 inner: State::new(AssetState::Loading),
3041 }
3042 }
3043
3044 pub fn new_async<F>(future: F) -> Self
3045 where
3046 F: Future<Output = Result<T, String>> + Send + 'static,
3047 {
3048 let suspense = Self::new();
3049 let _suspense_clone = suspense.clone();
3050
3051 #[cfg(not(target_arch = "wasm32"))]
3052 {
3053 std::thread::spawn(move || {
3054 let _rt = std::sync::Arc::new(std::sync::Mutex::new(future));
3058 });
3060 }
3061 #[cfg(target_arch = "wasm32")]
3062 {
3063 }
3066
3067 suspense
3068 }
3069
3070 pub fn ready(value: T) -> Self {
3071 Self {
3072 inner: State::new(AssetState::Ready(value)),
3073 }
3074 }
3075
3076 pub fn error(message: impl Into<String>) -> Self {
3077 Self {
3078 inner: State::new(AssetState::Error(message.into())),
3079 }
3080 }
3081
3082 pub fn get(&self) -> AssetState<T> {
3083 self.inner.get()
3084 }
3085
3086 pub fn get_ref(&self) -> AssetState<T> {
3087 self.inner.get()
3088 }
3089
3090 pub fn is_loading(&self) -> bool {
3091 matches!(self.get(), AssetState::Loading)
3092 }
3093
3094 pub fn is_ready(&self) -> bool {
3095 matches!(self.get(), AssetState::Ready(_))
3096 }
3097
3098 pub fn is_error(&self) -> bool {
3099 matches!(self.get(), AssetState::Error(_))
3100 }
3101
3102 pub fn ready_value(&self) -> Option<T> {
3103 match self.get() {
3104 AssetState::Ready(value) => Some(value),
3105 _ => None,
3106 }
3107 }
3108
3109 pub fn error_message(&self) -> Option<String> {
3110 match self.get() {
3111 AssetState::Error(message) => Some(message),
3112 _ => None,
3113 }
3114 }
3115
3116 pub fn subscribe<F: Fn(&AssetState<T>) + Send + Sync + 'static>(&self, callback: F) {
3117 self.inner.subscribe(callback)
3118 }
3119
3120 pub fn inner_state(&self) -> &State<AssetState<T>> {
3121 &self.inner
3122 }
3123}
3124
3125impl<T: Clone + Send + Sync + 'static> Clone for Suspense<T> {
3126 fn clone(&self) -> Self {
3127 Self {
3128 inner: self.inner.clone(),
3129 }
3130 }
3131}
3132
3133impl<T: Clone + Send + Sync + 'static> From<T> for Suspense<T> {
3134 fn from(value: T) -> Self {
3135 Self::ready(value)
3136 }
3137}
3138
3139impl<T: Clone + Send + Sync + 'static> From<Result<T, String>> for Suspense<T> {
3140 fn from(result: Result<T, String>) -> Self {
3141 match result {
3142 Ok(value) => Self::ready(value),
3143 Err(error) => Self::error(error),
3144 }
3145 }
3146}
3147
3148#[cfg(test)]
3149mod phase1_test;