use bytemuck::{Pod, Zeroable};
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
pub struct ColorTheme {
pub primary_neon: [f32; 4], pub shatter_neon: [f32; 4],
pub glass_base: [f32; 4],
pub glass_edge: [f32; 4],
pub rune_glow: [f32; 4],
pub ember_core: [f32; 4],
pub background_deep: [f32; 4],
pub mani_glow: [f32; 4], pub glass_blur_strength: f32,
pub shatter_edge_width: f32,
pub neon_bloom_radius: f32,
pub rune_opacity: f32,
pub glass_tint_adapt: f32,
pub glass_ior: f32,
pub color_space: u32,
pub _pad0: f32,
pub _pad1: f32,
pub _pad2: f32,
pub _pad3: f32,
pub _pad4: f32,
}
const _: () = assert!(
std::mem::size_of::<ColorTheme>() == 176,
"ColorTheme Rust/WGSL layout mismatch: expected 176 bytes"
);
impl ColorTheme {
pub fn asgard() -> Self {
Self {
primary_neon: [0.0, 1.0, 0.95, 1.2],
shatter_neon: [1.0, 0.0, 0.75, 1.5],
glass_base: [0.04, 0.04, 0.06, 0.82],
glass_edge: [0.0, 0.45, 0.55, 0.6],
rune_glow: [0.75, 0.98, 1.0, 0.9],
ember_core: [0.95, 0.12, 0.12, 1.0],
background_deep: [0.01, 0.01, 0.03, 1.0],
mani_glow: [0.7, 0.9, 1.0, 0.05],
glass_blur_strength: 0.6,
shatter_edge_width: 1.8,
neon_bloom_radius: 0.022,
rune_opacity: 0.55,
glass_tint_adapt: 0.35,
glass_ior: 1.45,
color_space: 0,
_pad0: 0.0,
_pad1: 0.0,
_pad2: 0.0,
_pad3: 0.0,
_pad4: 0.0,
}
}
pub fn midgard() -> Self {
Self {
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],
background_deep: [0.05, 0.05, 0.07, 1.0],
mani_glow: [0.0, 0.0, 0.0, 0.0], glass_blur_strength: 0.0, shatter_edge_width: 1.0,
neon_bloom_radius: 0.0,
rune_opacity: 0.0,
glass_tint_adapt: 0.0,
glass_ior: 1.0,
color_space: 0,
_pad0: 0.0,
_pad1: 0.0,
_pad2: 0.0,
_pad3: 0.0,
_pad4: 0.0,
}
}
pub fn cyberpunk_viking() -> Self {
Self::asgard()
}
pub fn vibrant_glass() -> Self {
Self {
primary_neon: [0.0, 1.0, 0.95, 1.2],
shatter_neon: [1.0, 0.0, 0.75, 1.5],
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],
ember_core: [1.0, 0.4, 0.1, 1.0],
background_deep: [0.05, 0.05, 0.1, 1.0],
mani_glow: [0.7, 0.9, 1.0, 0.05],
glass_blur_strength: 0.9,
shatter_edge_width: 1.8,
neon_bloom_radius: 0.022,
rune_opacity: 0.55,
glass_tint_adapt: 0.65,
glass_ior: 1.45,
color_space: 0,
_pad0: 0.0,
_pad1: 0.0,
_pad2: 0.0,
_pad3: 0.0,
_pad4: 0.0,
}
}
pub fn berserker() -> Self {
Self {
primary_neon: [1.0, 0.08, 0.12, 1.8],
shatter_neon: [0.95, 0.92, 0.88, 1.6],
glass_base: [0.03, 0.02, 0.02, 0.88],
glass_edge: [0.8, 0.35, 0.08, 0.7],
rune_glow: [0.9, 0.72, 0.3, 1.0],
ember_core: [0.98, 0.25, 0.05, 1.0],
background_deep: [0.01, 0.005, 0.005, 1.0],
mani_glow: [0.8, 0.2, 0.05, 0.08],
glass_blur_strength: 0.85,
shatter_edge_width: 2.8,
neon_bloom_radius: 0.035,
rune_opacity: 0.85,
glass_tint_adapt: 0.15,
glass_ior: 1.85,
color_space: 0,
_pad0: 0.0,
_pad1: 0.0,
_pad2: 0.0,
_pad3: 0.0,
_pad4: 0.0,
}
}
}
impl Default for ColorTheme {
fn default() -> Self {
Self::vibrant_glass()
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
pub struct SceneUniforms {
pub view: glam::Mat4,
pub proj: glam::Mat4,
pub time: f32,
pub delta_time: f32,
pub resolution: [f32; 2],
pub mouse: [f32; 2],
pub mouse_velocity: [f32; 2],
pub shatter_origin: [f32; 2],
pub shatter_time: f32,
pub shatter_force: f32,
pub berzerker_rage: f32,
pub berzerker_mode: u32,
pub scroll_offset: f32,
pub scale_factor: f32,
pub scene_type: u32,
pub _pad_vec2_align: [u32; 1], pub fireball_pos: [f32; 2],
pub _pad: [f32; 4], }
pub const SCENE_AURORA: u32 = 0;
pub const SCENE_VOID: u32 = 1;
pub const SCENE_NEBULA: u32 = 2;
pub const SCENE_GLITCH: u32 = 3;
pub const SCENE_YGGDRASIL: u32 = 4;
pub fn resolve_scene_by_name(name: &str) -> Option<u32> {
let normalized = name.to_lowercase().replace(['-', '_', ' ', '.'], "");
match normalized.as_str() {
"aurora" => Some(SCENE_AURORA),
"void" | "empty" | "none" | "blank" => Some(SCENE_VOID),
"nebula" => Some(SCENE_NEBULA),
"glitch" => Some(SCENE_GLITCH),
"yggdrasil" | "worldtree" | "tree" => Some(SCENE_YGGDRASIL),
_ => None,
}
}
impl SceneUniforms {
pub fn new(width: f32, height: f32) -> Self {
Self {
view: glam::Mat4::IDENTITY,
proj: glam::Mat4::orthographic_lh(0.0, width, height, 0.0, -100.0, 100.0),
time: 0.0,
delta_time: 0.016,
resolution: [width, height],
mouse: [0.5, 0.5],
mouse_velocity: [0.0, 0.0],
shatter_origin: [0.5, 0.5],
shatter_time: -100.0,
shatter_force: 0.0,
berzerker_rage: 0.0,
berzerker_mode: 0,
scroll_offset: 0.0,
scale_factor: 1.0,
scene_type: SCENE_AURORA,
_pad_vec2_align: [0],
fireball_pos: [0.0, 0.0],
_pad: [0.0; 4],
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Mesh {
pub vertices: Vec<[f32; 3]>,
pub normals: Vec<[f32; 3]>,
pub indices: Vec<u32>,
}
impl Mesh {
pub fn from_obj(data: &[u8]) -> anyhow::Result<Vec<Self>> {
let mut cursor = std::io::Cursor::new(data);
let (models, _) = tobj::load_obj_buf(&mut cursor, &tobj::LoadOptions::default(), |_| {
Ok((Vec::new(), Default::default()))
})?;
let mut meshes = Vec::new();
for m in models {
let mesh = m.mesh;
let vertices: Vec<[f32; 3]> = mesh
.positions
.chunks_exact(3)
.map(|c| [c[0], c[1], c[2]])
.collect();
let normals = if mesh.normals.is_empty() {
vec![[0.0, 0.0, 1.0]; vertices.len()]
} else {
mesh.normals.chunks(3).map(|c| [c[0], c[1], c[2]]).collect()
};
meshes.push(Mesh {
vertices,
normals,
indices: mesh.indices,
});
}
Ok(meshes)
}
pub fn from_stl(data: &[u8]) -> anyhow::Result<Self> {
let mut cursor = std::io::Cursor::new(data);
let stl = stl_io::read_stl(&mut cursor)?;
let vertices: Vec<[f32; 3]> = stl.vertices.iter().map(|v| [v[0], v[1], v[2]]).collect();
let mut indices = Vec::new();
for face in stl.faces {
indices.push(face.vertices[0] as u32);
indices.push(face.vertices[1] as u32);
indices.push(face.vertices[2] as u32);
}
let normals = vec![[0.0, 0.0, 1.0]; vertices.len()];
Ok(Mesh {
vertices,
normals,
indices,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Transform3D {
pub position: glam::Vec3,
pub rotation: glam::Quat,
pub scale: glam::Vec3,
}
impl Default for Transform3D {
fn default() -> Self {
Self {
position: glam::Vec3::ZERO,
rotation: glam::Quat::IDENTITY,
scale: glam::Vec3::ONE,
}
}
}
impl Transform3D {
pub fn to_matrix(&self) -> glam::Mat4 {
glam::Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.position)
}
pub fn from_2d(x: f32, y: f32, rotation: f32) -> Self {
Self {
position: glam::Vec3::new(x, y, 0.0),
rotation: glam::Quat::from_rotation_z(rotation),
scale: glam::Vec3::ONE,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Camera3D {
pub position: glam::Vec3,
pub target: glam::Vec3,
pub up: glam::Vec3,
pub fov_y: f32,
pub near: f32,
pub far: f32,
pub perspective: bool,
pub aspect: f32,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Material3D {
pub base_color: [f32; 4],
pub metallic: f32,
pub roughness: f32,
pub emissive: [f32; 3],
pub opacity: f32,
}
impl Default for Material3D {
fn default() -> Self {
Self {
base_color: [1.0, 1.0, 1.0, 1.0],
metallic: 0.0,
roughness: 0.5,
emissive: [0.0, 0.0, 0.0],
opacity: 1.0,
}
}
}
impl Material3D {
pub fn unlit(color: [f32; 4]) -> Self {
Self {
base_color: color,
metallic: 0.0,
roughness: 1.0,
emissive: [0.0, 0.0, 0.0],
opacity: color[3],
}
}
pub fn metallic(color: [f32; 4], roughness: f32) -> Self {
Self {
base_color: color,
metallic: 1.0,
roughness: roughness.clamp(0.0, 1.0),
emissive: [0.0, 0.0, 0.0],
opacity: color[3],
}
}
}
impl Default for Camera3D {
fn default() -> Self {
Self {
position: glam::Vec3::new(0.0, 0.0, 10.0),
target: glam::Vec3::ZERO,
up: glam::Vec3::Y,
fov_y: 45.0f32.to_radians(),
near: 0.1,
far: 1000.0,
perspective: true,
aspect: 16.0 / 9.0,
}
}
}
impl Camera3D {
pub fn view_matrix(&self) -> glam::Mat4 {
glam::Mat4::look_at_lh(self.position, self.target, self.up)
}
pub fn projection_matrix(&self) -> glam::Mat4 {
if self.perspective {
glam::Mat4::perspective_lh(self.fov_y, self.aspect, self.near, self.far)
} else {
let top = self.fov_y;
let right = top * self.aspect;
glam::Mat4::orthographic_lh(-right, right, -top, top, self.near, self.far)
}
}
pub fn view_projection(&self) -> glam::Mat4 {
self.projection_matrix() * self.view_matrix()
}
}
pub trait FrameRenderer<E = ()>: Renderer {
fn begin_frame(&mut self) -> E;
fn render_frame(&mut self) {
}
fn end_frame(&mut self, encoder: E);
}
use std::sync::Arc;
type SubscriberList<T> = Arc<std::sync::Mutex<Vec<Box<dyn Fn(&T) + Send + Sync>>>>;
fn invoke_subscribers_safely<T>(subs: &SubscriberList<T>, val: &T) -> usize
where
{
let guard = match subs.lock() {
Ok(g) => g,
Err(poisoned) => {
log::warn!(
"[State] subscriber list mutex was poisoned; recovering"
);
poisoned.into_inner()
}
};
let mut invoked = 0usize;
for cb in guard.iter() {
let cb_ref: &(dyn Fn(&T) + Send + Sync) = &**cb;
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
cb_ref(val);
}));
if let Err(payload) = result {
let msg = if let Some(s) = payload.downcast_ref::<&'static str>() {
(*s).to_string()
} else if let Some(s) = payload.downcast_ref::<String>() {
s.clone()
} else {
"unknown panic payload".to_string()
};
log::error!("[State] subscriber callback panicked: {msg}");
} else {
invoked += 1;
}
}
invoked
}
#[derive(Clone)]
pub struct State<T: Clone + Send + Sync + 'static> {
swap: Arc<arc_swap::ArcSwap<T>>,
metadata_swap: Arc<arc_swap::ArcSwap<Option<agents::MutationMetadata>>>,
#[cfg(not(target_arch = "wasm32"))]
tvar: Arc<stm::TVar<T>>,
#[cfg(not(target_arch = "wasm32"))]
metadata_tvar: Arc<stm::TVar<Option<agents::MutationMetadata>>>,
subscribers: SubscriberList<T>,
version: Arc<std::sync::atomic::AtomicU64>,
resolution: agents::ConflictResolution,
}
impl<T: Clone + Send + Sync + 'static> State<T> {
pub fn new(value: T) -> Self {
#[cfg(not(target_arch = "wasm32"))]
let tvar = Arc::new(stm::TVar::new(value.clone()));
#[cfg(not(target_arch = "wasm32"))]
let metadata_tvar = Arc::new(stm::TVar::new(None));
Self {
swap: Arc::new(arc_swap::ArcSwap::from_pointee(value)),
metadata_swap: Arc::new(arc_swap::ArcSwap::new(Arc::new(None))),
#[cfg(not(target_arch = "wasm32"))]
tvar,
#[cfg(not(target_arch = "wasm32"))]
metadata_tvar,
subscribers: Arc::new(std::sync::Mutex::new(Vec::new())),
version: Arc::new(std::sync::atomic::AtomicU64::new(0)),
resolution: agents::ConflictResolution::default(),
}
}
pub fn with_resolution(mut self, resolution: agents::ConflictResolution) -> Self {
self.resolution = resolution;
self
}
pub fn get(&self) -> T {
(**self.swap.load()).clone()
}
pub fn set(&self, value: T) {
#[cfg(not(target_arch = "wasm32"))]
let (was_skipped, final_val, final_meta) = stm::atomically(|tx| {
let new_meta = agents::get_current_mutation_metadata();
let existing_meta = self.metadata_tvar.read(tx)?;
let mut skip = false;
if self.resolution == agents::ConflictResolution::PriorityWins
&& let (Some(new_m), Some(old_m)) = (new_meta, existing_meta)
&& new_m.priority < old_m.priority
{
skip = true;
}
if !skip {
self.tvar.write(tx, value.clone())?;
self.metadata_tvar.write(tx, new_meta)?;
Ok((false, value.clone(), new_meta))
} else {
Ok((true, self.tvar.read(tx)?, existing_meta))
}
});
#[cfg(target_arch = "wasm32")]
let (was_skipped, final_val, final_meta) =
(false, value, agents::get_current_mutation_metadata());
if was_skipped {
if let (Some(new_m), Some(old_m)) =
(agents::get_current_mutation_metadata(), final_meta)
{
agents::notify_conflict(agents::ConflictEvent {
agent_id: new_m.agent_id,
priority: new_m.priority,
existing_agent_id: old_m.agent_id,
existing_priority: old_m.priority,
timestamp_ms: new_m.timestamp_ms,
});
}
return;
}
self.swap.store(Arc::new(final_val.clone()));
self.metadata_swap.store(Arc::new(final_meta));
self.version
.fetch_add(1, std::sync::atomic::Ordering::Release);
let subs = Arc::clone(&self.subscribers);
if crate::is_batching() {
crate::enqueue_batch_task(Box::new(move || {
let _ = invoke_subscribers_safely(&subs, &final_val);
}));
} else {
let _ = invoke_subscribers_safely(&subs, &final_val);
}
}
pub fn set_direct(&self, value: T) {
self.swap.store(Arc::new(value.clone()));
let new_meta = agents::get_current_mutation_metadata();
self.metadata_swap.store(Arc::new(new_meta));
self.version
.fetch_add(1, std::sync::atomic::Ordering::Release);
#[cfg(not(target_arch = "wasm32"))]
{
let _ = stm::atomically(|tx| {
self.tvar.write(tx, value.clone())?;
let meta = agents::get_current_mutation_metadata();
self.metadata_tvar.write(tx, meta)?;
Ok(())
});
}
let subs = Arc::clone(&self.subscribers);
if crate::is_batching() {
crate::enqueue_batch_task(Box::new(move || {
let _ = invoke_subscribers_safely(&subs, &value);
}));
} else {
let _ = invoke_subscribers_safely(&subs, &value);
}
}
pub fn mutate<F: Fn(&T) -> T>(&self, f: F) {
#[cfg(not(target_arch = "wasm32"))]
{
let (was_skipped, final_val, final_meta) = stm::atomically(|tx| {
let new_meta = agents::get_current_mutation_metadata();
let existing_meta = self.metadata_tvar.read(tx)?;
let mut skip = false;
if self.resolution == agents::ConflictResolution::PriorityWins
&& let (Some(new_m), Some(old_m)) = (new_meta, existing_meta)
&& new_m.priority < old_m.priority
{
skip = true;
}
if !skip {
let current = self.tvar.read(tx)?;
let next = f(¤t);
self.tvar.write(tx, next.clone())?;
self.metadata_tvar.write(tx, new_meta)?;
Ok((false, next, new_meta))
} else {
Ok((true, self.tvar.read(tx)?, existing_meta))
}
});
if was_skipped {
if let (Some(new_m), Some(old_m)) =
(agents::get_current_mutation_metadata(), final_meta)
{
agents::notify_conflict(agents::ConflictEvent {
agent_id: new_m.agent_id,
priority: new_m.priority,
existing_agent_id: old_m.agent_id,
existing_priority: old_m.priority,
timestamp_ms: new_m.timestamp_ms,
});
}
return;
}
self.swap.store(Arc::new(final_val.clone()));
self.metadata_swap.store(Arc::new(final_meta));
self.version
.fetch_add(1, std::sync::atomic::Ordering::Release);
let subs = Arc::clone(&self.subscribers);
if crate::is_batching() {
crate::enqueue_batch_task(Box::new(move || {
let _ = invoke_subscribers_safely(&subs, &final_val);
}));
} else {
let _ = invoke_subscribers_safely(&subs, &final_val);
}
}
#[cfg(target_arch = "wasm32")]
{
self.set(f(&self.get()));
}
}
pub fn version(&self) -> u64 {
self.version.load(std::sync::atomic::Ordering::Acquire)
}
pub fn subscribe<F: Fn(&T) + Send + Sync + 'static>(&self, callback: F) {
self.subscribers.lock().unwrap_or_else(|p| p.into_inner()).push(Box::new(callback));
}
}
use crate::runtime::NodeStateSnapshot;
use std::sync::OnceLock;
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "wasm32"))]
static FALLBACK_RUNTIME: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
#[cfg(not(target_arch = "wasm32"))]
static FALLBACK_WORKER_COUNT: OnceLock<usize> = OnceLock::new();
#[cfg(not(target_arch = "wasm32"))]
fn fallback_runtime() -> &'static tokio::runtime::Runtime {
FALLBACK_RUNTIME.get_or_init(|| {
let worker_count = *FALLBACK_WORKER_COUNT.get_or_init(|| {
let available = std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(2);
available.saturating_sub(1).clamp(1, 8)
});
tokio::runtime::Builder::new_current_thread()
.worker_threads(worker_count)
.thread_name("cvkg-fallback-rt")
.enable_all()
.build()
.expect("failed to build fallback tokio runtime")
})
}
pub static SYSTEM_STATE: OnceLock<Arc<arc_swap::ArcSwap<AppState>>> = OnceLock::new();
#[cfg(not(target_arch = "wasm32"))]
static KNOWLEDGE_TVAR: OnceLock<stm::TVar<AppState>> = OnceLock::new();
static IS_BATCHING: AtomicBool = AtomicBool::new(false);
pub static IS_RENDERING: AtomicBool = AtomicBool::new(false);
pub static LAYOUT_DIRTY: AtomicBool = AtomicBool::new(false);
type BatchQueue = OnceLock<std::sync::Mutex<Vec<Box<dyn FnOnce() + Send + Sync>>>>;
static BATCH_QUEUE: BatchQueue = OnceLock::new();
static STATE_WRITE_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
pub fn is_batching() -> bool {
IS_BATCHING.load(Ordering::Acquire)
}
pub fn is_rendering() -> bool {
IS_RENDERING.load(Ordering::Acquire)
}
pub fn begin_render_phase() {
IS_RENDERING.store(true, Ordering::Release);
}
pub fn end_render_phase() {
IS_RENDERING.store(false, Ordering::Release);
}
pub fn enqueue_batch_task(task: Box<dyn FnOnce() + Send + Sync>) {
let mut queue = BATCH_QUEUE
.get_or_init(|| std::sync::Mutex::new(Vec::new()))
.lock()
.unwrap_or_else(|p| p.into_inner());
queue.push(task);
}
pub fn batch<F: FnOnce()>(f: F) {
if IS_BATCHING.swap(true, Ordering::AcqRel) {
f();
return;
}
f();
IS_BATCHING.store(false, Ordering::Release);
let mut queue = BATCH_QUEUE
.get_or_init(|| std::sync::Mutex::new(Vec::new()))
.lock()
.unwrap();
let tasks: Vec<_> = queue.drain(..).collect();
drop(queue);
for task in tasks {
task();
}
}
pub fn get_system_state() -> Arc<arc_swap::ArcSwap<AppState>> {
SYSTEM_STATE
.get_or_init(|| Arc::new(arc_swap::ArcSwap::from_pointee(AppState::default())))
.clone()
}
pub fn load_system_state() -> arc_swap::Guard<Arc<AppState>> {
get_system_state().load()
}
pub fn update_system_state<F>(f: F)
where
F: FnOnce(&AppState) -> AppState,
{
let _lock = STATE_WRITE_MUTEX.lock().unwrap_or_else(|p| p.into_inner());
if is_rendering() {
log::warn!(
"LAYOUT THRASH DETECTED: System state mutated during render phase. This may trigger redundant layout passes and impact performance."
);
}
LAYOUT_DIRTY.store(true, Ordering::SeqCst);
let swap = get_system_state();
let current = swap.load();
let new_state = Arc::new(f(¤t));
swap.store(Arc::clone(&new_state));
#[cfg(not(target_arch = "wasm32"))]
{
let tvar = KNOWLEDGE_TVAR.get_or_init(|| stm::TVar::new((*new_state).clone()));
stm::atomically(|tx| tvar.write(tx, (*new_state).clone()));
}
}
pub fn transact_system_state<F>(f: F)
where
F: Fn(&AppState) -> AppState,
{
let _lock = STATE_WRITE_MUTEX.lock().unwrap_or_else(|p| p.into_inner());
#[cfg(not(target_arch = "wasm32"))]
{
if is_rendering() {
log::warn!(
"LAYOUT THRASH DETECTED: System state mutated during render phase. This may trigger redundant layout passes and impact performance."
);
}
let tvar = KNOWLEDGE_TVAR
.get_or_init(|| stm::TVar::new((**get_system_state().load()).clone()))
.clone();
let new_state = stm::atomically(move |tx| {
let current = tvar.read(tx)?;
let next = f(¤t);
tvar.write(tx, next.clone())?;
Ok(next)
});
get_system_state().store(Arc::new(new_state));
}
#[cfg(target_arch = "wasm32")]
{
if is_rendering() {
log::warn!(
"LAYOUT THRASH DETECTED: System state mutated during render phase. This may trigger redundant layout passes and impact performance."
);
}
update_system_state(f);
}
}
impl AppState {
pub fn new() -> Self {
Self::default()
}
pub fn set_component_state<T: 'static + Send + Sync>(&mut self, id: u64, state: T) {
self.component_states
.insert(id, Arc::new(std::sync::RwLock::new(state)));
}
pub fn get_component_state<T: 'static + Send + Sync>(
&self,
id: u64,
) -> Option<Arc<std::sync::RwLock<T>>> {
let stored = self.component_states.get(&id)?;
let any_ref = stored.read().ok()?;
let _verified: &T = any_ref.downcast_ref::<T>()?;
drop(any_ref);
let raw = Arc::into_raw(stored.clone());
Some(unsafe { Arc::from_raw(raw as *const std::sync::RwLock<T>) })
}
pub fn remember(&mut self, fragment: KnowledgeFragment) {
self.fragments.insert(fragment.id.clone(), fragment);
}
pub fn process_query(&mut self, query: &str) {
let query_lower = query.to_lowercase();
let mut results: Vec<(f32, String)> = self
.fragments
.iter()
.map(|(id, frag)| {
let mut score = 0.0;
if frag.summary.to_lowercase().contains(&query_lower) {
score += 1.0;
}
if frag.source.to_lowercase().contains(&query_lower) {
score += 0.5;
}
(score, id.clone())
})
.filter(|(score, _)| *score > 0.0)
.collect();
results.sort_by(|a, b| b.0.total_cmp(&a.0));
self.last_query_results = results.into_iter().map(|(_, id)| id).take(5).collect();
}
pub fn snapshot(&self) -> Vec<NodeStateSnapshot> {
let mut snapshots = Vec::new();
for frag in self.fragments.values() {
if let Ok(val) = serde_json::to_value(frag) {
snapshots.push(NodeStateSnapshot { id: 0, state: val });
}
}
snapshots
}
}
#[derive(Clone)]
pub struct Binding<T: Clone + Send + Sync + 'static> {
swap: Arc<arc_swap::ArcSwap<T>>,
#[cfg(not(target_arch = "wasm32"))]
tvar: Arc<stm::TVar<T>>,
version: Arc<std::sync::atomic::AtomicU64>,
}
impl<T: Clone + Send + Sync + 'static> Binding<T> {
pub fn from_state(state: &State<T>) -> Self {
Self {
swap: Arc::clone(&state.swap),
#[cfg(not(target_arch = "wasm32"))]
tvar: Arc::clone(&state.tvar),
version: Arc::clone(&state.version),
}
}
pub fn get(&self) -> T {
(**self.swap.load()).clone()
}
pub fn set(&self, value: T) {
self.swap.store(Arc::new(value.clone()));
#[cfg(not(target_arch = "wasm32"))]
{
let tvar = Arc::clone(&self.tvar);
let v = value.clone();
stm::atomically(move |tx| tvar.write(tx, v.clone()));
}
self.version
.fetch_add(1, std::sync::atomic::Ordering::Release);
}
pub fn version(&self) -> u64 {
self.version.load(std::sync::atomic::Ordering::Acquire)
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn transact_pair<A, B, F>(state_a: &State<A>, state_b: &State<B>, f: F)
where
A: Clone + Send + Sync + 'static,
B: Clone + Send + Sync + 'static,
F: Fn(&A, &B) -> (A, B),
{
let tvar_a = Arc::clone(&state_a.tvar);
let tvar_b = Arc::clone(&state_b.tvar);
let (new_a, new_b) = stm::atomically(move |tx| {
let a = tvar_a.read(tx)?;
let b = tvar_b.read(tx)?;
let (na, nb) = f(&a, &b);
tvar_a.write(tx, na.clone())?;
tvar_b.write(tx, nb.clone())?;
Ok((na, nb))
});
state_a.swap.store(Arc::new(new_a.clone()));
state_b.swap.store(Arc::new(new_b.clone()));
state_a
.version
.fetch_add(1, std::sync::atomic::Ordering::Release);
state_b
.version
.fetch_add(1, std::sync::atomic::Ordering::Release);
let subs_a = Arc::clone(&state_a.subscribers);
let subs_b = Arc::clone(&state_b.subscribers);
if crate::is_batching() {
crate::enqueue_batch_task(Box::new(move || {
{
let s = subs_a.lock().unwrap_or_else(|p| p.into_inner());
for cb in s.iter() {
cb(&new_a);
}
}
{
let s = subs_b.lock().unwrap_or_else(|p| p.into_inner());
for cb in s.iter() {
cb(&new_b);
}
}
}));
} else {
{
let s = subs_a.lock().unwrap_or_else(|p| p.into_inner());
for cb in s.iter() {
cb(&new_a);
}
}
{
let s = subs_b.lock().unwrap_or_else(|p| p.into_inner());
for cb in s.iter() {
cb(&new_b);
}
}
}
}
use std::any::TypeId;
use std::sync::Mutex;
pub(crate) static ENVIRONMENT: OnceLock<
Mutex<HashMap<TypeId, Box<dyn std::any::Any + Send + Sync>>>,
> = OnceLock::new();
pub trait EnvKey: 'static + Send + Sync {
type Value: Clone + Send + Sync + 'static;
fn default_value() -> Self::Value;
}
pub struct YggdrasilKey;
impl EnvKey for YggdrasilKey {
type Value = DesignTokens;
fn default_value() -> Self::Value {
default_tokens()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Appearance {
Light,
Dark,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Orientation {
Horizontal,
Vertical,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct GridPlacement {
pub column: i32,
pub column_span: u32,
pub row: i32,
pub row_span: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum Alignment {
#[default]
Center,
Leading,
Trailing,
Top,
Bottom,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum Distribution {
#[default]
Fill,
Center,
Leading,
Trailing,
SpaceBetween,
SpaceAround,
SpaceEvenly,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Color {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl Color {
pub const BLACK: Color = Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 1.0,
};
pub const WHITE: Color = Color {
r: 1.0,
g: 1.0,
b: 1.0,
a: 1.0,
};
pub const TRANSPARENT: Color = Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
};
pub const RED: Color = Color {
r: 1.0,
g: 0.0,
b: 0.0,
a: 1.0,
};
pub const GREEN: Color = Color {
r: 0.0,
g: 1.0,
b: 0.0,
a: 1.0,
};
pub const BLUE: Color = Color {
r: 0.0,
g: 0.0,
b: 1.0,
a: 1.0,
};
pub const VIKING_GOLD: Color = Color {
r: 1.0,
g: 0.84,
b: 0.0,
a: 1.0,
};
pub const MAGENTA_LIQUID: Color = Color {
r: 1.0,
g: 0.0,
b: 1.0,
a: 1.0,
};
pub const TACTICAL_OBSIDIAN: Color = Color {
r: 0.05,
g: 0.05,
b: 0.07,
a: 1.0,
};
pub fn relative_luminance(&self) -> f32 {
fn res(c: f32) -> f32 {
if c <= 0.03928 {
c / 12.92
} else {
((c + 0.055) / 1.055).powf(2.4)
}
}
0.2126 * res(self.r) + 0.7152 * res(self.g) + 0.0722 * res(self.b)
}
pub fn contrast_ratio(&self, other: &Color) -> f32 {
let l1 = self.relative_luminance();
let l2 = other.relative_luminance();
if l1 > l2 {
(l1 + 0.05) / (l2 + 0.05)
} else {
(l2 + 0.05) / (l1 + 0.05)
}
}
pub const CYAN: Color = Color {
r: 0.0,
g: 1.0,
b: 1.0,
a: 1.0,
};
pub const YELLOW: Color = Color {
r: 1.0,
g: 1.0,
b: 0.0,
a: 1.0,
};
pub const MAGENTA: Color = Color {
r: 1.0,
g: 0.0,
b: 1.0,
a: 1.0,
};
pub const GRAY: Color = Color {
r: 0.5,
g: 0.5,
b: 0.5,
a: 1.0,
};
pub fn from_hex(hex: &str) -> Option<Self> {
let hex = hex.strip_prefix('#').unwrap_or(hex);
if hex.len() != 6 {
return None;
}
let r = u8::from_str_radix(&hex[0..2], 16).ok()? as f32 / 255.0;
let g = u8::from_str_radix(&hex[2..4], 16).ok()? as f32 / 255.0;
let b = u8::from_str_radix(&hex[4..6], 16).ok()? as f32 / 255.0;
Some(Color { r, g, b, a: 1.0 })
}
pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
Self { r, g, b, a }
}
pub fn as_array(&self) -> [f32; 4] {
[self.r, self.g, self.b, self.a]
}
pub fn lighten(&self, amount: f32) -> Self {
Self {
r: (self.r + amount).clamp(0.0, 1.0),
g: (self.g + amount).clamp(0.0, 1.0),
b: (self.b + amount).clamp(0.0, 1.0),
a: self.a,
}
}
pub fn darken(&self, amount: f32) -> Self {
Self {
r: (self.r - amount).clamp(0.0, 1.0),
g: (self.g - amount).clamp(0.0, 1.0),
b: (self.b - amount).clamp(0.0, 1.0),
a: self.a,
}
}
}
impl View for Color {
type Body = Never;
fn body(self) -> Self::Body {
unreachable!()
}
fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
renderer.fill_rect(rect, self.as_array());
}
}
pub struct AppearanceKey;
impl EnvKey for AppearanceKey {
type Value = Appearance;
fn default_value() -> Self::Value {
Appearance::Dark }
}
pub struct DirectionKey;
impl EnvKey for DirectionKey {
type Value = Direction;
fn default_value() -> Self::Value {
Direction::LTR
}
}
pub struct StyleResolver;
impl StyleResolver {
pub fn color(key: &str) -> String {
let tokens = Environment::<YggdrasilKey>::new().get();
let appearance = Environment::<AppearanceKey>::new().get();
let is_dark = appearance == Appearance::Dark;
tokens
.get_color(key, is_dark)
.unwrap_or_else(|| "#FF00FF".to_string()) }
pub fn get<T: FromStr>(category: &str, key: &str) -> Option<T> {
let tokens = Environment::<YggdrasilKey>::new().get();
let appearance = Environment::<AppearanceKey>::new().get();
let is_dark = appearance == Appearance::Dark;
tokens.get(category, key, is_dark)
}
pub fn color_array(key: &str) -> [f32; 4] {
let hex = Self::color(key);
parse_hex_color(&hex)
}
}
fn parse_hex_color(hex: &str) -> [f32; 4] {
let hex = hex.trim_start_matches('#');
if hex.len() >= 6 {
let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(255) as f32 / 255.0;
let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(0) as f32 / 255.0;
let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(255) as f32 / 255.0;
let a = if hex.len() >= 8 {
u8::from_str_radix(&hex[6..8], 16).unwrap_or(255) as f32 / 255.0
} else {
1.0
};
[r, g, b, a]
} else {
[1.0, 0.0, 1.0, 1.0] }
}
pub fn default_tokens() -> DesignTokens {
let mut tokens = DesignTokens::new();
tokens.color.insert(
"background".to_string(),
TokenValue::Adaptive {
light: "#FFFFFF".to_string(), dark: "#000000".to_string(), },
);
tokens.color.insert(
"primary".to_string(),
TokenValue::Adaptive {
light: "#007B8A".to_string(), dark: "#00FFFF".to_string(), },
);
tokens.color.insert(
"secondary".to_string(),
TokenValue::Adaptive {
light: "#8A008A".to_string(), dark: "#FF00FF".to_string(), },
);
tokens.color.insert(
"surface".to_string(),
TokenValue::Adaptive {
light: "#FFFFFF".to_string(),
dark: "#121212".to_string(),
},
);
tokens.color.insert(
"text".to_string(),
TokenValue::Adaptive {
light: "#000000".to_string(),
dark: "#FFFFFF".to_string(),
},
);
tokens.color.insert(
"surface_elevated".to_string(),
TokenValue::Adaptive {
light: "#FFFFFF".to_string(),
dark: "#1A1A24".to_string(),
},
);
tokens.color.insert(
"surface_overlay".to_string(),
TokenValue::Adaptive {
light: "#FFFFFF".to_string(),
dark: "#1E1E2E".to_string(),
},
);
tokens.color.insert(
"border".to_string(),
TokenValue::Adaptive {
light: "#D0D0D8".to_string(),
dark: "#2A2A3A".to_string(),
},
);
tokens.color.insert(
"border_strong".to_string(),
TokenValue::Adaptive {
light: "#A0A0B0".to_string(),
dark: "#3A3A50".to_string(),
},
);
tokens.color.insert(
"text_muted".to_string(),
TokenValue::Adaptive {
light: "#606070".to_string(),
dark: "#8080A0".to_string(),
},
);
tokens.color.insert(
"text_dim".to_string(),
TokenValue::Adaptive {
light: "#9090A0".to_string(),
dark: "#505070".to_string(),
},
);
tokens.color.insert(
"accent".to_string(),
TokenValue::Adaptive {
light: "#007B8A".to_string(), dark: "#00FFFF".to_string(), },
);
tokens.color.insert(
"accent_hover".to_string(),
TokenValue::Adaptive {
light: "#00A0B0".to_string(), dark: "#33FFFF".to_string(), },
);
tokens.color.insert(
"success".to_string(),
TokenValue::Single {
value: "#00E676".to_string(),
},
);
tokens.color.insert(
"warning".to_string(),
TokenValue::Single {
value: "#FFB300".to_string(),
},
);
tokens.color.insert(
"error".to_string(),
TokenValue::Single {
value: "#FF5252".to_string(),
},
);
tokens.color.insert(
"info".to_string(),
TokenValue::Single {
value: "#448AFF".to_string(),
},
);
tokens.color.insert(
"hover".to_string(),
TokenValue::Adaptive {
light: "#F0F0F5".to_string(),
dark: "#252535".to_string(),
},
);
tokens.color.insert(
"active".to_string(),
TokenValue::Adaptive {
light: "#E0E0EB".to_string(),
dark: "#303045".to_string(),
},
);
tokens.color.insert(
"disabled".to_string(),
TokenValue::Adaptive {
light: "#E8E8F0".to_string(),
dark: "#1A1A28".to_string(),
},
);
tokens.color.insert(
"disabled_text".to_string(),
TokenValue::Adaptive {
light: "#B0B0C0".to_string(),
dark: "#404060".to_string(),
},
);
tokens.color.insert(
"focus_ring".to_string(),
TokenValue::Single {
value: "#00FFFF".to_string(),
},
);
tokens.color.insert(
"shadow".to_string(),
TokenValue::Adaptive {
light: "#00000020".to_string(),
dark: "#00000060".to_string(),
},
);
tokens.color.insert(
"code_bg".to_string(),
TokenValue::Adaptive {
light: "#F5F5FA".to_string(),
dark: "#0D0D18".to_string(),
},
);
tokens.bifrost.insert(
"blur".to_string(),
TokenValue::Single {
value: "25.0".to_string(),
},
);
tokens.bifrost.insert(
"saturation".to_string(),
TokenValue::Single {
value: "1.2".to_string(),
},
);
tokens.bifrost.insert(
"opacity".to_string(),
TokenValue::Single {
value: "0.65".to_string(),
},
);
tokens.gungnir.insert(
"intensity".to_string(),
TokenValue::Single {
value: "1.0".to_string(),
},
);
tokens.gungnir.insert(
"radius".to_string(),
TokenValue::Single {
value: "15.0".to_string(),
},
);
tokens.mjolnir.insert(
"clip_angle".to_string(),
TokenValue::Single {
value: "12.0".to_string(),
},
);
tokens.mjolnir.insert(
"border_width".to_string(),
TokenValue::Single {
value: "2.0".to_string(),
},
);
tokens.anim.insert(
"stiffness".to_string(),
TokenValue::Single {
value: "170.0".to_string(),
},
);
tokens.anim.insert(
"damping".to_string(),
TokenValue::Single {
value: "26.0".to_string(),
},
);
tokens.anim.insert(
"mass".to_string(),
TokenValue::Single {
value: "1.0".to_string(),
},
);
tokens.accessibility.insert(
"reduce_motion".to_string(),
TokenValue::Single {
value: "false".to_string(),
},
);
tokens
}
pub struct Environment<K: EnvKey> {
_marker: std::marker::PhantomData<K>,
}
impl<K: EnvKey> Default for Environment<K> {
fn default() -> Self {
Self::new()
}
}
impl<K: EnvKey> Environment<K> {
pub fn new() -> Self {
Self {
_marker: std::marker::PhantomData,
}
}
pub fn get(&self) -> K::Value {
if let Some(env_store) = ENVIRONMENT.get() {
let env_lock = env_store.lock().unwrap_or_else(|p| p.into_inner());
if let Some(val) = env_lock.get(&std::any::TypeId::of::<K>()) {
if let Some(typed_val) = val.downcast_ref::<K::Value>() {
return typed_val.clone();
} else {
log::warn!(
"Environment: Downcast failed for key type {:?}",
std::any::type_name::<K>()
);
}
} else {
log::trace!(
"Environment: Key not found: {:?}. Returning default.",
std::any::type_name::<K>()
);
}
} else {
log::trace!(
"Environment: Store not initialized. Key: {:?}. Returning default.",
std::any::type_name::<K>()
);
}
K::default_value()
}
}
pub mod env {
pub fn insert<K: super::EnvKey>(value: K::Value) {
let store = super::ENVIRONMENT
.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()));
let mut env_map = store.lock().unwrap_or_else(|p| p.into_inner());
env_map.insert(std::any::TypeId::of::<K>(), Box::new(value));
}
pub fn remove<K: super::EnvKey>() {
if let Some(store) = super::ENVIRONMENT.get() {
let mut env_map = store.lock().unwrap_or_else(|p| p.into_inner());
env_map.remove(&std::any::TypeId::of::<K>());
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Size {
pub width: f32,
pub height: f32,
}
impl Size {
pub const ZERO: Self = Self {
width: 0.0,
height: 0.0,
};
pub fn new(width: f32, height: f32) -> Self {
Self { width, height }
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct EdgeInsets {
pub top: f32,
pub leading: f32,
pub bottom: f32,
pub trailing: f32,
}
impl EdgeInsets {
pub fn all(value: f32) -> Self {
Self {
top: value,
leading: value,
bottom: value,
trailing: value,
}
}
pub fn vertical(value: f32) -> Self {
Self {
top: value,
leading: 0.0,
bottom: value,
trailing: 0.0,
}
}
pub fn horizontal(value: f32) -> Self {
Self {
top: 0.0,
leading: value,
bottom: 0.0,
trailing: value,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct FrameModifier {
pub width: Option<f32>,
pub height: Option<f32>,
pub min_width: Option<f32>,
pub max_width: Option<f32>,
pub min_height: Option<f32>,
pub max_height: Option<f32>,
pub alignment: Alignment,
}
impl Default for FrameModifier {
fn default() -> Self {
Self::new()
}
}
impl FrameModifier {
pub fn new() -> Self {
Self {
width: None,
height: None,
min_width: None,
max_width: None,
min_height: None,
max_height: None,
alignment: Alignment::Center,
}
}
pub fn width(mut self, width: f32) -> Self {
self.width = Some(width);
self
}
pub fn height(mut self, height: f32) -> Self {
self.height = Some(height);
self
}
pub fn size(mut self, width: f32, height: f32) -> Self {
self.width = Some(width);
self.height = Some(height);
self
}
pub fn min_width(mut self, min_width: f32) -> Self {
self.min_width = Some(min_width);
self
}
pub fn max_width(mut self, max_width: f32) -> Self {
self.max_width = Some(max_width);
self
}
pub fn min_height(mut self, min_height: f32) -> Self {
self.min_height = Some(min_height);
self
}
pub fn max_height(mut self, max_height: f32) -> Self {
self.max_height = Some(max_height);
self
}
pub fn alignment(mut self, alignment: Alignment) -> Self {
self.alignment = alignment;
self
}
}
impl ViewModifier for FrameModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
fn transform_proposal(&self, proposal: SizeProposal) -> SizeProposal {
let w = if let Some(width) = self.width {
Some(width)
} else {
proposal.width.map(|pw| {
pw.clamp(
self.min_width.unwrap_or(0.0),
self.max_width.unwrap_or(f32::INFINITY),
)
})
};
let h = if let Some(height) = self.height {
Some(height)
} else {
proposal.height.map(|ph| {
ph.clamp(
self.min_height.unwrap_or(0.0),
self.max_height.unwrap_or(f32::INFINITY),
)
})
};
SizeProposal {
width: w,
height: h,
}
}
fn transform_size(&self, child_size: Size) -> Size {
let w = if let Some(width) = self.width {
width
} else {
child_size.width.clamp(
self.min_width.unwrap_or(0.0),
self.max_width.unwrap_or(f32::INFINITY),
)
};
let h = if let Some(height) = self.height {
height
} else {
child_size.height.clamp(
self.min_height.unwrap_or(0.0),
self.max_height.unwrap_or(f32::INFINITY),
)
};
Size {
width: w,
height: h,
}
}
fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
self.render(renderer, rect);
let child_proposal =
self.transform_proposal(SizeProposal::new(Some(rect.width), Some(rect.height)));
let child_size = view.intrinsic_size(renderer, child_proposal);
let mut child_x = rect.x;
let mut child_y = rect.y;
match self.alignment {
Alignment::Leading => {
child_y = rect.y + (rect.height - child_size.height) / 2.0;
}
Alignment::Trailing => {
child_x = rect.x + rect.width - child_size.width;
child_y = rect.y + (rect.height - child_size.height) / 2.0;
}
Alignment::Top => {
child_x = rect.x + (rect.width - child_size.width) / 2.0;
}
Alignment::Bottom => {
child_x = rect.x + (rect.width - child_size.width) / 2.0;
child_y = rect.y + rect.height - child_size.height;
}
Alignment::Center => {
child_x = rect.x + (rect.width - child_size.width) / 2.0;
child_y = rect.y + (rect.height - child_size.height) / 2.0;
}
}
let child_rect = Rect {
x: child_x,
y: child_y,
width: child_size.width,
height: child_size.height,
};
view.render(renderer, child_rect);
self.post_render(renderer, rect);
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct FlexModifier {
pub weight: f32,
}
impl ViewModifier for FlexModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
fn child_flex_weight<V: View>(&self, _view: &V) -> f32 {
self.weight
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GridPlacementModifier {
pub placement: GridPlacement,
}
impl ViewModifier for GridPlacementModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
fn get_grid_placement(&self) -> Option<GridPlacement> {
Some(self.placement)
}
}
#[derive(Clone)]
pub struct OverlayModifier {
pub overlay: AnyView,
pub alignment: Alignment,
pub offset: [f32; 2],
pub on_dismiss: Option<Arc<dyn Fn() + Send + Sync>>,
}
impl ViewModifier for OverlayModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
view.render(renderer, rect);
let overlay_size = self
.overlay
.intrinsic_size(renderer, SizeProposal::unspecified());
let mut overlay_x;
let mut overlay_y;
match self.alignment {
Alignment::Leading => {
overlay_x = rect.x - overlay_size.width;
overlay_y = rect.y + (rect.height - overlay_size.height) / 2.0;
}
Alignment::Trailing => {
overlay_x = rect.x + rect.width;
overlay_y = rect.y + (rect.height - overlay_size.height) / 2.0;
}
Alignment::Top => {
overlay_x = rect.x + (rect.width - overlay_size.width) / 2.0;
overlay_y = rect.y - overlay_size.height;
}
Alignment::Bottom => {
overlay_x = rect.x + (rect.width - overlay_size.width) / 2.0;
overlay_y = rect.y + rect.height;
}
Alignment::Center => {
overlay_x = rect.x + (rect.width - overlay_size.width) / 2.0;
overlay_y = rect.y + (rect.height - overlay_size.height) / 2.0;
}
}
overlay_x += self.offset[0];
overlay_y += self.offset[1];
let overlay_rect = Rect {
x: overlay_x,
y: overlay_y,
width: overlay_size.width,
height: overlay_size.height,
};
if let Some(on_dismiss) = &self.on_dismiss {
let dismiss = on_dismiss.clone();
renderer.register_handler(
"pointerdown",
Arc::new(move |event| {
if let Event::PointerDown { x, y, .. } = event {
let click_inside = x >= overlay_rect.x
&& x <= overlay_rect.x + overlay_rect.width
&& y >= overlay_rect.y
&& y <= overlay_rect.y + overlay_rect.height;
if !click_inside {
dismiss();
}
}
}),
);
}
self.overlay.render(renderer, overlay_rect);
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct OffsetModifier {
pub x: f32,
pub y: f32,
}
impl OffsetModifier {
pub fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
}
impl ViewModifier for OffsetModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ZIndexModifier {
pub z_index: i32,
}
impl ZIndexModifier {
pub fn new(z_index: i32) -> Self {
Self { z_index }
}
}
impl ViewModifier for ZIndexModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct LayoutConstraints {
pub min_width: Option<f32>,
pub max_width: Option<f32>,
pub min_height: Option<f32>,
pub max_height: Option<f32>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LayoutModifier {
pub constraints: LayoutConstraints,
}
impl LayoutModifier {
pub fn new(constraints: LayoutConstraints) -> Self {
Self { constraints }
}
}
impl ViewModifier for LayoutModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SafeAreaModifier {
pub ignores: bool,
}
impl ViewModifier for SafeAreaModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ElevationModifier {
pub level: f32,
}
impl ViewModifier for ElevationModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
fn render_view<V: View>(&self, view: &V, renderer: &mut dyn Renderer, rect: Rect) {
if self.level > 0.0 {
let radius = self.level * 2.0;
let offset_y = self.level * 0.5;
let shadow_color = [0.0, 0.0, 0.0, 0.3];
renderer.push_shadow(radius, shadow_color, [0.0, offset_y]);
view.render(renderer, rect);
renderer.pop_shadow();
} else {
view.render(renderer, rect);
}
}
}
#[derive(Clone)]
pub struct PositionModifier {
pub x: f32,
pub y: f32,
}
impl ViewModifier for PositionModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
fn transform_rect(&self, rect: Rect) -> Rect {
Rect {
x: rect.x + self.x,
y: rect.y + self.y,
width: rect.width,
height: rect.height,
}
}
}
pub mod layout {
use super::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct LayoutKey {
pub view_hash: u64,
pub generation: u64,
}
pub struct LayoutCache {
pub safe_area: SafeArea,
pub delta_time: f32,
pub scale_factor: f32,
pub viewport: Option<Rect>,
pub layout_time_budget: std::time::Duration,
pub layout_start_time: Option<std::time::Instant>,
size_cache: HashMap<(u64, u32, u32), Size>, pub parent_map: HashMap<u64, u64>,
generation: u64,
pub engine: Option<Box<dyn std::any::Any + Send + Sync>>,
pub animators: Option<Box<dyn std::any::Any + Send + Sync>>,
pub previous_rects: HashMap<u64, Rect>,
pub eviction_generation: u64,
pub previous_rects_generation: HashMap<u64, u64>,
eviction_threshold: u64,
}
thread_local! {
static LAYOUT_BUDGET_DEADLINE: std::cell::RefCell<Option<std::time::Instant>> =
const { std::cell::RefCell::new(None) };
}
impl Default for LayoutCache {
fn default() -> Self {
Self::new()
}
}
impl LayoutCache {
pub fn new() -> Self {
Self {
safe_area: SafeArea::default(),
delta_time: 0.016,
scale_factor: 1.0,
viewport: None,
layout_time_budget: std::time::Duration::from_millis(4),
layout_start_time: None,
size_cache: HashMap::new(),
parent_map: HashMap::new(),
generation: 0,
engine: None,
animators: None,
previous_rects: HashMap::new(),
eviction_generation: 0,
previous_rects_generation: HashMap::new(),
eviction_threshold: 300, }
}
pub fn generation(&self) -> u64 {
self.generation
}
pub fn evict_stale_entries(&mut self) {
self.eviction_generation += 1;
let threshold = self.eviction_threshold;
let current_gen = self.eviction_generation;
self.previous_rects.retain(|hash, _| {
self.previous_rects_generation
.get(hash)
.map_or(false, |g| current_gen - *g < threshold)
});
self.previous_rects_generation
.retain(|hash, _| self.previous_rects.contains_key(hash));
}
pub fn is_over_budget(&self) -> bool {
let deadline_red = LAYOUT_BUDGET_DEADLINE.with(|deadline| {
deadline.borrow().as_ref().is_some_and(|deadline| std::time::Instant::now() >= *deadline)
});
if deadline_red {
return true;
}
if let Some(start) = self.layout_start_time {
start.elapsed() > self.layout_time_budget
} else {
false
}
}
pub fn set_layout_budget_deadline(deadline: Option<std::time::Instant>) {
LAYOUT_BUDGET_DEADLINE.with(|slot| {
*slot.borrow_mut() = deadline;
});
}
pub fn clear_layout_budget_deadline() {
Self::set_layout_budget_deadline(None);
}
pub fn invalidate(&mut self) {
self.generation = self.generation.wrapping_add(1);
}
pub fn is_valid(&self, key: LayoutKey, current_gen: u64) -> bool {
key.generation == current_gen && key.generation == self.generation
}
pub fn clear(&mut self) {
self.safe_area = SafeArea::default();
self.viewport = None;
self.layout_start_time = None;
self.size_cache.clear();
self.parent_map.clear();
}
pub fn get_size(&self, view_hash: u64, proposal: SizeProposal) -> Option<Size> {
let pw = (proposal.width.unwrap_or(-1.0) * 100.0) as u32;
let ph = (proposal.height.unwrap_or(-1.0) * 100.0) as u32;
self.size_cache.get(&(view_hash, pw, ph)).copied()
}
pub fn set_size(&mut self, view_hash: u64, proposal: SizeProposal, size: Size) {
let pw = (proposal.width.unwrap_or(-1.0) * 100.0) as u32;
let ph = (proposal.height.unwrap_or(-1.0) * 100.0) as u32;
self.size_cache.insert((view_hash, pw, ph), size);
}
pub fn register_parent(&mut self, child_hash: u64, parent_hash: u64) {
if child_hash != 0 && parent_hash != 0 {
self.parent_map.insert(child_hash, parent_hash);
}
}
pub fn invalidate_view(&mut self, view_hash: u64) {
let mut to_invalidate = vec![view_hash];
let mut visited = std::collections::HashSet::new();
while let Some(hash) = to_invalidate.pop() {
if !visited.insert(hash) {
continue;
}
self.size_cache.retain(|&(h, _, _), _| h != hash);
if let Some(&parent) = self.parent_map.get(&hash) {
to_invalidate.push(parent);
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SizeProposal {
pub width: Option<f32>,
pub height: Option<f32>,
}
impl SizeProposal {
pub fn unspecified() -> Self {
Self {
width: None,
height: None,
}
}
pub fn width(width: f32) -> Self {
Self {
width: Some(width),
height: None,
}
}
pub fn height(height: f32) -> Self {
Self {
width: None,
height: Some(height),
}
}
pub fn tight(width: f32, height: f32) -> Self {
Self {
width: Some(width),
height: Some(height),
}
}
pub fn new(width: Option<f32>, height: Option<f32>) -> Self {
Self { width, height }
}
}
pub trait LayoutView: Send {
fn size_that_fits(
&self,
proposal: SizeProposal,
subviews: &[&dyn LayoutView],
cache: &mut LayoutCache,
) -> Size;
fn place_subviews(
&self,
bounds: Rect,
subviews: &mut [&mut dyn LayoutView],
cache: &mut LayoutCache,
);
fn flex_weight(&self) -> f32 {
0.0
}
fn view_hash(&self) -> u64 {
0
}
fn changed(&self) -> bool {
true
}
fn debug_layout(&self, indent: usize) -> String {
let prefix = " ".repeat(indent);
format!("{}LayoutView", prefix)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
pub struct EdgeInsets {
pub top: f32,
pub leading: f32,
pub bottom: f32,
pub trailing: f32,
}
impl EdgeInsets {
pub fn new(top: f32, leading: f32, bottom: f32, trailing: f32) -> Self {
Self {
top,
leading,
bottom,
trailing,
}
}
pub fn all(value: f32) -> Self {
Self {
top: value,
leading: value,
bottom: value,
trailing: value,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
pub struct SafeArea {
pub insets: EdgeInsets,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum SdfShape {
Rect(Rect),
RoundedRect { rect: Rect, radius: f32 },
Circle { center: [f32; 2], radius: f32 },
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Rect {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
impl Rect {
pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
Self {
x,
y,
width,
height,
}
}
pub fn inset(&self, amount: f32) -> Self {
Self {
x: self.x + amount,
y: self.y + amount,
width: (self.width - amount * 2.0).max(0.0),
height: (self.height - amount * 2.0).max(0.0),
}
}
pub fn offset(&self, dx: f32, dy: f32) -> Self {
Self {
x: self.x + dx,
y: self.y + dy,
..*self
}
}
pub fn zero() -> Self {
Self {
x: 0.0,
y: 0.0,
width: 0.0,
height: 0.0,
}
}
pub fn contains(&self, x: f32, y: f32) -> bool {
x >= self.x && x <= self.x + self.width && y >= self.y && y <= self.y + self.height
}
pub fn intersects(&self, other: &Rect) -> bool {
self.x < other.x + other.width
&& self.x + self.width > other.x
&& self.y < other.y + other.height
&& self.y + self.height > other.y
}
pub fn size(&self) -> Size {
Size {
width: self.width,
height: self.height,
}
}
pub fn split_horizontal(&self, n: usize) -> Vec<Rect> {
if n == 0 {
return vec![];
}
let item_width = self.width / n as f32;
(0..n)
.map(|i| Rect {
x: self.x + i as f32 * item_width,
y: self.y,
width: item_width,
height: self.height,
})
.collect()
}
pub fn split_vertical(&self, n: usize) -> Vec<Rect> {
if n == 0 {
return vec![];
}
let item_height = self.height / n as f32;
(0..n)
.map(|i| Rect {
x: self.x,
y: self.y + i as f32 * item_height,
width: self.width,
height: item_height,
})
.collect()
}
}
}
pub mod agents;
pub mod animation;
pub mod gpu;
pub mod material;
pub mod runtime;
pub mod scene_graph;
pub mod sdf_shadow;
pub use layout::{LayoutCache, LayoutKey, LayoutView, Rect, SizeProposal};
pub use material::DrawMaterial;
pub use scene_graph::{NodeId, bifrost_registry};
pub use color::SemanticColors;
pub trait AssetManager: Send + Sync {
fn load_image(&self, url: &str) -> AssetState<Arc<Vec<u8>>>;
fn preload_image(&self, url: &str);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum TouchPhase {
Began,
Moved,
Ended,
Cancelled,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum Event {
PointerDown {
x: f32,
y: f32,
button: u32,
proximity_field: f32,
tilt: Option<f32>,
azimuth: Option<f32>,
pressure: Option<f32>,
barrel_rotation: Option<f32>,
pointer_precision: f32,
},
PointerUp {
x: f32,
y: f32,
button: u32,
tilt: Option<f32>,
azimuth: Option<f32>,
pressure: Option<f32>,
barrel_rotation: Option<f32>,
pointer_precision: f32,
},
PointerMove {
x: f32,
y: f32,
proximity_field: f32,
tilt: Option<f32>,
azimuth: Option<f32>,
pressure: Option<f32>,
barrel_rotation: Option<f32>,
pointer_precision: f32,
},
PointerClick {
x: f32,
y: f32,
button: u32,
tilt: Option<f32>,
azimuth: Option<f32>,
pressure: Option<f32>,
barrel_rotation: Option<f32>,
pointer_precision: f32,
},
PointerEnter,
PointerLeave,
PointerWheel {
x: f32,
y: f32,
delta_x: f32,
delta_y: f32,
pointer_precision: f32,
},
PointerDoubleClick {
x: f32,
y: f32,
button: u32,
pointer_precision: f32,
},
DragStart {
x: f32,
y: f32,
button: u32,
pointer_precision: f32,
},
DragMove {
x: f32,
y: f32,
pointer_precision: f32,
},
DragEnd {
x: f32,
y: f32,
pointer_precision: f32,
},
KeyDown {
key: String,
modifiers: KeyModifiers,
},
KeyUp {
key: String,
modifiers: KeyModifiers,
},
FocusIn,
FocusOut,
Copy,
Cut,
Paste(String),
Ime(String),
TouchStart {
x: f32,
y: f32,
touch_id: u64,
},
TouchMove {
x: f32,
y: f32,
touch_id: u64,
},
TouchEnd {
x: f32,
y: f32,
touch_id: u64,
},
TouchCancel {
touch_id: u64,
},
GesturePinch {
center: [f32; 2],
scale: f32,
velocity: f32,
phase: TouchPhase,
},
GestureSwipe {
direction: [f32; 2],
velocity: f32,
phase: TouchPhase,
},
FileDrop {
x: f32,
y: f32,
path: String,
},
}
impl Event {
pub fn pointer_precision(&self) -> f32 {
match self {
Self::PointerDown {
pointer_precision, ..
}
| Self::PointerUp {
pointer_precision, ..
}
| Self::PointerMove {
pointer_precision, ..
}
| Self::PointerClick {
pointer_precision, ..
}
| Self::PointerWheel {
pointer_precision, ..
}
| Self::PointerDoubleClick {
pointer_precision, ..
}
| Self::DragStart {
pointer_precision, ..
}
| Self::DragMove {
pointer_precision, ..
}
| Self::DragEnd {
pointer_precision, ..
} => *pointer_precision,
_ => 0.0,
}
}
pub fn name(&self) -> &'static str {
match self {
Self::PointerDown { .. } => "pointerdown",
Self::PointerUp { .. } => "pointerup",
Self::PointerMove { .. } => "pointermove",
Self::PointerClick { .. } => "pointerclick",
Self::PointerEnter => "pointerenter",
Self::PointerLeave => "pointerleave",
Self::PointerWheel { .. } => "pointerwheel",
Self::PointerDoubleClick { .. } => "pointerdoubleclick",
Self::DragStart { .. } => "dragstart",
Self::DragMove { .. } => "dragmove",
Self::DragEnd { .. } => "dragend",
Self::KeyDown { .. } => "keydown",
Self::KeyUp { .. } => "keyup",
Self::FocusIn => "focusin",
Self::FocusOut => "focusout",
Self::Copy => "copy",
Self::Cut => "cut",
Self::Paste(_) => "paste",
Self::Ime(_) => "ime",
Self::TouchStart { .. } => "touchstart",
Self::TouchMove { .. } => "touchmove",
Self::TouchEnd { .. } => "touchend",
Self::TouchCancel { .. } => "touchcancel",
Self::GesturePinch { .. } => "gesturepinch",
Self::GestureSwipe { .. } => "gestureswipe",
Self::FileDrop { .. } => "filedrop",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EventResponse {
Handled,
Ignored,
}