Documented

This commit is contained in:
2026-01-31 18:21:46 +01:00
parent 0f5422b66b
commit 73cfc36277
14 changed files with 376 additions and 4 deletions
+45
View File
@@ -1,3 +1,9 @@
//! # Context Module
//!
//! Provides the Context type which is central to component state management.
//! Context holds component state, manages event handlers, and coordinates
//! rendering and updates across the component tree.
use std::{
any::{Any, TypeId},
collections::HashMap,
@@ -16,15 +22,28 @@ use crate::{
use super::{scope::Scope, Component, ComponentImpl};
/// Context represents the runtime state and behavior of a component
///
/// Each component instance has a Context that holds:
/// - The component implementation
/// - The current view (render result)
/// - Event handlers for responding to events
/// - Child scopes for managing child components
pub struct Context {
/// The component implementation
component: AccessCell<Component>,
/// The current rendered view
view: AccessCell<View>,
/// Event handlers grouped by event type
event_handlers: AccessCell<HashMap<TypeId, Vec<EventHandler>>>,
/// Child scopes (component hierarchies)
pub(crate) scopes: Mutex<Vec<Arc<Scope>>>,
/// Command executor for this context's command handling
executor: Arc<dyn CommandExecutor>,
}
impl Context {
/// Creates a new context for the given component
pub fn new<F: ComponentImpl + 'static>(
component: F,
executor: Arc<dyn CommandExecutor>,
@@ -38,6 +57,9 @@ impl Context {
})
}
/// Refreshes the component by re-rendering it
///
/// Clears event handlers and calls the component to produce a new view.
pub fn refresh(self: &Arc<Self>) {
self.event_handlers
.access(|event_handlers| event_handlers.clear());
@@ -53,6 +75,9 @@ impl Context {
});
}
/// Synchronously refreshes the component
///
/// Blocks until the component has finished rendering.
pub fn refresh_sync(self: &Arc<Self>) {
let (tx, rx) = std::sync::mpsc::channel::<()>();
@@ -79,10 +104,15 @@ impl Context {
let _ = rx.recv();
}
/// Gets the current view
pub fn get_view(self: &Arc<Self>) -> View {
self.view.access_ref().clone()
}
/// Registers an event handler for events of type T
///
/// When an event of type T is emitted, the handler is called with
/// the context and a reference to the event.
pub fn on_event<T: Any + 'static, F: Fn(&Arc<Self>, &T) + Send + Sync + 'static>(
self: &Arc<Self>,
handler: F,
@@ -101,6 +131,10 @@ impl Context {
});
}
/// Emits an event to this component and all descendants
///
/// Calls all registered handlers for this event type,
/// then propagates the event to child components.
pub fn emit_event<E: Send + Sync + Any + 'static>(self: &Arc<Self>, event: E) {
let event = Arc::new(event);
let handlers_to_call: Vec<EventHandler> = {
@@ -118,6 +152,10 @@ impl Context {
}
}
/// Emits an event to this component in a spawned thread
///
/// Similar to emit_event but handlers are called in spawned threads
/// for concurrent execution.
pub fn emit_event_threaded<E: Any + Send + Sync + Clone + 'static>(
self: &Arc<Self>,
event: &E,
@@ -141,6 +179,7 @@ impl Context {
}
}
/// Creates a new child scope
pub fn scope(self: &Arc<Self>) -> Arc<Scope> {
let scope = Scope::new(self.executor.clone());
self.scopes.lock().unwrap().push(scope.clone());
@@ -148,6 +187,7 @@ impl Context {
scope
}
/// Creates a dynamic child scope that re-renders when dependencies change
pub fn dyn_scope<F: Fn(&Arc<Scope>) + Send + Sync + 'static>(
self: &Arc<Self>,
drawer: F,
@@ -171,10 +211,12 @@ impl Context {
scope
}
/// Adds a pre-constructed scope as a child
pub fn add_scope(self: &Arc<Self>, scope: Arc<Scope>) {
self.scopes.lock().unwrap().push(scope);
}
/// Renders all child components to the draw context
pub fn draw_children(self: &Arc<Self>, ctx: &mut DrawContext) {
for scope in self.scopes.lock().unwrap().iter() {
for (child, view_wrapper) in scope.children.lock().unwrap().iter() {
@@ -189,15 +231,18 @@ impl Context {
}
}
/// Gets the command executor for this context
pub fn get_executor(self: &Arc<Self>) -> Arc<dyn CommandExecutor> {
self.executor.clone()
}
/// Executes a command
pub fn execute<T: Command + 'static>(self: &Arc<Self>, command: T) -> crate::Result<()> {
self.executor
.execute_command(&(Arc::new(command) as Arc<dyn Command>))
}
/// Stops the application
pub fn stop(self: &Arc<Self>) -> crate::Result<()> {
self.execute(crate::engine::commands::Stop)
}
+11
View File
@@ -1,3 +1,9 @@
//! # Component Module
//!
//! Provides the component system that forms the foundation of OSUI.
//! Components are reusable units of UI that can manage their own state
//! and respond to events.
pub mod context;
pub mod scope;
@@ -10,10 +16,15 @@ use crate::View;
use context::Context;
/// A Component is an implementor of the ComponentImpl trait, wrapped in Arc
pub type Component = Arc<dyn ComponentImpl>;
/// An event handler function stored in a mutex for thread-safe mutation
pub type EventHandler = Arc<Mutex<dyn FnMut(&Arc<Context>, &dyn Any) + Send + Sync>>;
/// Trait implemented by components to render themselves
pub trait ComponentImpl: Send + Sync {
/// Renders the component within the given context, returning a View
fn call(&self, cx: &Arc<Context>) -> View;
}
+17
View File
@@ -1,15 +1,28 @@
//! # Scope Module
//!
//! Provides the Scope type for managing component hierarchies.
//! Scopes group child components and manage their lifecycle.
use std::sync::{Arc, Mutex};
use crate::{engine::CommandExecutor, View, ViewWrapper};
use super::{context::Context, ComponentImpl};
/// A scope groups child components and manages their rendering
///
/// Scopes form the hierarchical structure of a component tree.
/// Each scope contains references to its child components and their
/// optional view wrappers (for layout/styling).
pub struct Scope {
/// Child components with optional view wrappers
pub children: Mutex<Vec<(Arc<Context>, Option<ViewWrapper>)>>,
/// Command executor for this scope's children
executor: Arc<dyn CommandExecutor>,
}
impl Scope {
/// Creates a new scope with the given command executor
pub fn new(executor: Arc<dyn CommandExecutor>) -> Arc<Self> {
Arc::new(Self {
children: Mutex::new(Vec::new()),
@@ -17,6 +30,9 @@ impl Scope {
})
}
/// Adds a child component to this scope
///
/// The view_wrapper is optional and can be used for layout or styling.
pub fn child<F: ComponentImpl + 'static>(
self: &Arc<Self>,
child: F,
@@ -29,6 +45,7 @@ impl Scope {
self.children.lock().unwrap().push((ctx, view_wrapper));
}
/// Adds a view directly to this scope
pub fn view(self: &Arc<Self>, view: View) {
let ctx = Context::new(view, self.executor.clone());
+13
View File
@@ -1,3 +1,7 @@
//! # Benchmark Module
//!
//! Provides performance benchmarking capabilities for rendering engines.
use std::{io::stdout, sync::Arc, time::Instant};
use crossterm::{cursor::MoveTo, execute, terminal::Clear};
@@ -7,18 +11,26 @@ use crate::{render::Area, DrawContext, View};
use super::Engine;
/// Results of a benchmark run
#[derive(Debug, Clone)]
pub struct BenchmarkResult {
/// Average render time in microseconds
pub average: u128,
/// Minimum render time in microseconds
pub min: u128,
/// Maximum render time in microseconds
pub max: u128,
/// Total time spent rendering in microseconds
pub total_render: u128,
/// Total benchmark time including setup in microseconds
pub total: u128,
}
/// Wraps an engine to benchmark its rendering performance
pub struct Benchmark<T: Engine>(T);
impl<T: Engine> Benchmark<T> {
/// Creates a new benchmark wrapper around the given engine
pub fn new(engine: T) -> Self {
Self(engine)
}
@@ -31,6 +43,7 @@ impl<T: Engine> Engine<BenchmarkResult> for Benchmark<T> {
let start = Instant::now();
// Run 40 render cycles and measure each
for _ in 0..40 {
let start = Instant::now();
self.render(&cx);
+5
View File
@@ -1,5 +1,10 @@
//! # Commands Module
//!
//! Defines built-in commands for controlling the engine.
use crate::engine::Command;
/// Command to stop the engine and terminate the application
pub struct Stop;
impl Command for Stop {
+16
View File
@@ -1,3 +1,8 @@
//! # Console Engine Implementation
//!
//! Provides a Console implementation of the Engine trait for rendering
//! to the terminal using crossterm.
use std::{
io::{stdout, Write},
sync::{Arc, Mutex},
@@ -14,16 +19,24 @@ use crate::{
use super::Engine;
/// Executes commands for the console engine
pub struct ConsoleExecutor {
/// Flag indicating whether the application is running
running: Mutex<bool>,
}
/// Console-based rendering engine
///
/// Renders components to the terminal using crossterm for cross-platform support.
pub struct Console {
/// Thread functions to execute
threads: Mutex<Vec<Arc<dyn Fn(Arc<Context>) + Send + Sync>>>,
/// The executor for this console
executor: Arc<ConsoleExecutor>,
}
impl Console {
/// Creates a new console engine
pub fn new() -> Self {
Self {
threads: Mutex::new(Vec::new()),
@@ -33,6 +46,7 @@ impl Console {
}
}
/// Registers a thread function to run alongside the engine
pub fn thread<F: Fn(Arc<Context>) + Send + Sync + 'static>(&self, run: F) {
self.threads.lock().unwrap().push(Arc::new(run));
}
@@ -114,10 +128,12 @@ impl Engine for Console {
}
impl ConsoleExecutor {
/// Checks if the engine is still running
pub fn is_running(self: &Arc<ConsoleExecutor>) -> bool {
*self.running.lock().unwrap()
}
/// Stops the engine
pub fn stop(&self) -> crate::Result<()> {
*self.running.lock()? = false;
Ok(())
+23
View File
@@ -1,3 +1,9 @@
//! # Engine Module
//!
//! Provides the rendering engine and command execution system.
//! The engine is responsible for initializing components, rendering frames,
//! and handling user commands.
pub mod benchmark;
pub mod commands;
pub mod console;
@@ -10,23 +16,40 @@ use std::{any::Any, sync::Arc};
use crate::component::{context::Context, ComponentImpl};
use crate::{render::Area, DrawContext, View};
/// Main engine trait for rendering and running components
pub trait Engine<Output = ()> {
/// Runs a component to completion
fn run<C: ComponentImpl + 'static>(&self, component: C) -> crate::Result<Output>;
/// Initializes a component and returns its context
fn init<C: ComponentImpl + 'static>(&self, component: C) -> Arc<Context>;
/// Renders the current state of a component
fn render(&self, cx: &Arc<Context>);
/// Sleeps between render frames (default 16ms for ~60fps)
fn render_delay(&self) {
crate::sleep(16);
}
/// Renders a view within an area and returns the draw context
fn render_view(&self, area: &Area, view: &View) -> DrawContext;
/// Executes the drawing instructions in a draw context
fn draw_context(&self, ctx: &DrawContext);
/// Returns the command executor for this engine
fn executor(&self) -> Arc<dyn CommandExecutor>;
}
/// Trait for commands that can be executed by the engine
pub trait Command {
/// Returns the command as Any for downcasting
fn as_any(&self) -> &dyn Any;
}
/// Executes commands during the application lifecycle
pub trait CommandExecutor: Send + Sync {
/// Executes the given command
fn execute_command(&self, command: &Arc<dyn Command>) -> crate::Result<()>;
}
+28
View File
@@ -1,34 +1,59 @@
//! # Frontend Module
//!
//! Provides the RSX (React-like Syntax) system for composing components.
//! This module defines the structure for building component hierarchies
//! with static and dynamic scopes, similar to React's JSX.
use std::sync::Arc;
use crate::component::{context::Context, scope::Scope};
use crate::{render::Point, state::HookDependency, View};
/// Trait for converting values to RSX
pub trait ToRsx {
/// Convert to RSX representation
fn to_rsx(&self) -> Rsx;
}
/// Scope types for RSX components
#[derive(Clone)]
pub enum RsxScope {
/// Static scope - executed once and never updated
Static(Arc<dyn Fn(&Arc<Scope>) + Send + Sync>),
/// Dynamic scope - re-executed when dependencies change
Dynamic(
Arc<dyn Fn(&Arc<Scope>) + Send + Sync>,
Vec<Arc<dyn HookDependency>>,
),
/// Child RSX scope for composition
Child(Rsx),
}
/// RSX (React-like Syntax) builder for component hierarchies
///
/// Represents a collection of scopes that define component structure.
/// Scopes can be static (execute once) or dynamic (reactive to changes).
#[derive(Clone)]
pub struct Rsx(Vec<RsxScope>);
impl Rsx {
/// Creates a new empty RSX
pub fn new() -> Self {
Self(Vec::new())
}
/// Adds a static scope to this RSX
///
/// The provided function is executed once during rendering
/// and will not be re-executed on dependency changes.
pub fn static_scope<F: Fn(&Arc<Scope>) + Send + Sync + 'static>(&mut self, scope: F) {
self.0.push(RsxScope::Static(Arc::new(scope)));
}
/// Adds a dynamic scope to this RSX
///
/// The provided function is executed when dependencies change,
/// allowing for reactive updates similar to React hooks.
pub fn dynamic_scope<F: Fn(&Arc<Scope>) + Send + Sync + 'static>(
&mut self,
drawer: F,
@@ -38,10 +63,12 @@ impl Rsx {
.push(RsxScope::Dynamic(Arc::new(drawer), dependencies));
}
/// Adds a child RSX
pub fn child<R: ToRsx>(&mut self, child: R) {
self.0.push(RsxScope::Child(child.to_rsx()));
}
/// Generates child components within the given context
pub fn generate_children(&self, context: &Arc<Context>) {
let executor = context.get_executor();
@@ -64,6 +91,7 @@ impl Rsx {
}
}
/// Converts this RSX to a View
pub fn view(&self, context: &Arc<Context>) -> View {
let context = context.clone();
+50
View File
@@ -1,3 +1,41 @@
//! # OSUI - A TUI Library for Advanced UIs
//!
//! OSUI is a Rust library for building sophisticated Terminal User Interfaces (TUIs).
//! It provides a component-based architecture with state management, event handling,
//! and rendering capabilities for creating interactive console applications.
//!
//! ## Key Features
//!
//! - **Component System**: Build UIs using composable components
//! - **State Management**: React-like hooks for managing component state
//! - **Event Handling**: Type-safe event system with reactive updates
//! - **RSX Syntax**: Macro-based DSL for defining component hierarchies
//! - **Console Engine**: Terminal rendering with crossterm support
//!
//! ## Architecture
//!
//! - [`component`] - Component system and context management
//! - [`state`] - State management with hooks (useState, useEffect, etc.)
//! - [`engine`] - Rendering engine and command execution
//! - [`frontend`] - RSX (React-like Syntax) for component definitions
//! - [`render`] - Low-level rendering primitives
//!
//! ## Example
//!
//! ```rust,no_run
//! use osui::prelude::*;
//! use std::sync::Arc;
//!
//! #[component]
//! pub fn Counter(cx: &Arc<Context>) -> View {
//! let count = use_state(0);
//!
//! Arc::new(move |ctx| {
//! ctx.draw_text(Point { x: 0, y: 0 }, &format!("Count: {}", count.get_dl()));
//! })
//! }
//! ```
use std::sync::Arc;
use crate::render::DrawContext;
@@ -9,6 +47,7 @@ pub mod render;
pub mod state;
pub mod prelude {
//! Prelude module - Re-exports commonly used items for convenience
pub use crate::component::{context::*, scope::*, *};
pub use crate::engine::*;
pub use crate::frontend::*;
@@ -20,12 +59,21 @@ pub mod prelude {
pub use std::sync::{Arc, Mutex};
}
/// A View is an async function that renders content to a DrawContext.
/// It takes a mutable DrawContext and produces drawing instructions.
pub type View = Arc<dyn Fn(&mut DrawContext) + Send + Sync>;
/// A ViewWrapper is a higher-order function that wraps views.
/// It can modify or enhance how a view is rendered.
pub type ViewWrapper = Arc<dyn Fn(&mut DrawContext, View) + Send + Sync>;
/// Result type for OSUI operations
pub type Result<T> = std::result::Result<T, Error>;
/// Error type for OSUI operations
#[derive(Debug, Clone)]
pub enum Error {
/// Error that occurs when a mutex is poisoned
PoisonError,
}
@@ -35,6 +83,8 @@ impl From<std::sync::PoisonError<std::sync::MutexGuard<'_, bool>>> for Error {
}
}
/// Sleep for the specified duration in milliseconds.
/// Useful for controlling render frame rate or delays.
pub fn sleep(delay_ms: u64) {
std::thread::sleep(std::time::Duration::from_millis(delay_ms));
}
+34
View File
@@ -1,40 +1,69 @@
//! # Rendering Module
//!
//! This module provides low-level rendering primitives and data structures
//! for drawing content to the terminal. It includes geometric primitives
//! (Point, Area, Size) and drawing instructions.
use crate::View;
/// Represents a drawing instruction that can be executed by the rendering engine
#[derive(Clone)]
pub enum DrawInstruction {
/// Draw text at a specific point
Text(Point, String),
/// Render a view within a specified area
View(Area, View),
/// Render a child drawing context at an offset
Child(Point, DrawContext),
}
/// Represents the dimensions of a drawable area
#[derive(Clone)]
pub struct Size {
/// Width in terminal columns
pub width: u16,
/// Height in terminal rows
pub height: u16,
}
/// Represents a position in 2D space
#[derive(Clone)]
pub struct Point {
/// X coordinate (column)
pub x: u16,
/// Y coordinate (row)
pub y: u16,
}
/// Represents a rectangular area with position and dimensions
#[derive(Clone)]
pub struct Area {
/// X coordinate (column) of the top-left corner
pub x: u16,
/// Y coordinate (row) of the top-left corner
pub y: u16,
/// Width in terminal columns
pub width: u16,
/// Height in terminal rows
pub height: u16,
}
/// Context for drawing operations
///
/// Accumulates drawing instructions that are executed by the rendering engine.
/// Tracks allocated space within the drawable area.
#[derive(Clone)]
pub struct DrawContext {
/// The total area available for drawing
pub area: Area,
/// The area that has been allocated for drawing (union of all allocations)
pub allocated: Area,
/// List of drawing instructions to execute
pub drawing: Vec<DrawInstruction>,
}
impl DrawContext {
/// Creates a new DrawContext with the specified area
pub fn new(area: Area) -> Self {
Self {
area,
@@ -48,6 +77,8 @@ impl DrawContext {
}
}
/// Allocates space within the drawable area and returns the allocated area
/// Updates the allocated bounds to include this allocation
pub fn allocate(&mut self, x: u16, y: u16, width: u16, height: u16) -> Area {
self.allocated.x = self.allocated.x.min(x);
self.allocated.y = self.allocated.y.min(y);
@@ -62,15 +93,18 @@ impl DrawContext {
}
}
/// Adds a drawing instruction to be executed
pub fn draw(&mut self, inst: DrawInstruction) {
self.drawing.push(inst);
}
/// Draws text at the specified point
pub fn draw_text(&mut self, point: Point, text: &str) {
self.drawing
.push(DrawInstruction::Text(point, text.to_string()));
}
/// Draws a view within the specified area
pub fn draw_view(&mut self, area: Area, view: View) {
self.drawing.push(DrawInstruction::View(area, view));
}
+61 -3
View File
@@ -1,3 +1,8 @@
//! # State Management Module
//!
//! Provides React-like hooks for managing component state and side effects.
//! This module includes useState, useEffect, useMount, and state synchronization hooks.
use std::{
any::Any,
fmt::{Debug, Display, Formatter, Result as FmtResult},
@@ -7,24 +12,42 @@ use std::{
use crate::component::context::Context;
/// Effect callback that can be triggered by state changes
#[derive(Clone)]
pub struct HookEffect(Arc<Mutex<dyn FnMut() + Send + Sync>>);
/// State holder for reactive values
///
/// Similar to React's useState hook. Holds a value and tracks dependents
/// that need to be notified when the value changes.
#[derive(Debug)]
pub struct State<T> {
/// The actual state value
value: Arc<Mutex<T>>,
/// Functions to call when state is updated
dependents: Arc<Mutex<Vec<HookEffect>>>,
}
/// Guard for accessing and potentially modifying state
///
/// Dereferences to the state value. When dropped after modification,
/// automatically triggers all dependent effects.
pub struct Inner<'a, T> {
value: MutexGuard<'a, T>,
dependents: Arc<Mutex<Vec<HookEffect>>>,
updated: bool,
}
/// Mount lifecycle hook
///
/// Tracks whether a component has been mounted and executes
/// any pending mount effects.
#[derive(Debug, Clone)]
pub struct Mount(Arc<Mutex<bool>>, Arc<Mutex<Vec<HookEffect>>>);
/// Creates a new state value
///
/// Returns a State that can be read and written from multiple threads.
pub fn use_state<T>(v: T) -> State<T> {
State {
value: Arc::new(Mutex::new(v)),
@@ -33,14 +56,20 @@ pub fn use_state<T>(v: T) -> State<T> {
}
impl<T: Clone> State<T> {
/// Gets the cloned value, recommended for preventing deadlocks
/// Gets a cloned copy of the state value
///
/// Recommended over `get()` to prevent deadlocks when cloning is acceptable.
/// "dl" stands for "deadlock-less".
pub fn get_dl(&self) -> T {
self.value.lock().unwrap().clone()
}
}
impl<T> State<T> {
/// Gets a lock on the state for read/write access.
/// Acquires a lock on the state for read/write access
///
/// Returns an Inner guard that implements Deref and DerefMut.
/// When dropped after modification, triggers dependent effects.
pub fn get(&self) -> Inner<'_, T> {
Inner {
value: self.value.lock().unwrap(),
@@ -49,18 +78,20 @@ impl<T> State<T> {
}
}
/// Sets the value and marks it as changed.
/// Sets the state value and triggers dependents
pub fn set(&self, v: T) {
*self.value.lock().unwrap() = v;
self.update();
}
/// Notifies all dependents of an update
pub fn update(&self) {
for d in self.dependents.lock().unwrap().iter() {
d.call();
}
}
/// Clones the State handle (not the value)
pub fn clone(&self) -> Self {
Self {
dependents: self.dependents.clone(),
@@ -82,15 +113,19 @@ impl Debug for HookEffect {
}
impl HookEffect {
/// Creates a new effect from a function
pub fn new<F: Fn() + Send + Sync + 'static>(f: F) -> Self {
Self(Arc::new(Mutex::new(f)))
}
/// Executes the effect function
pub fn call(&self) {
(self.0.lock().unwrap())()
}
}
/// Inner implements Deref for read access and DerefMut for write access
/// On drop after mutation, automatically triggers dependent effects
impl<T> Drop for Inner<'_, T> {
fn drop(&mut self) {
if self.updated {
@@ -115,7 +150,9 @@ impl<T> DerefMut for Inner<'_, T> {
}
}
/// Trait for values that can be tracked as dependencies in hooks
pub trait HookDependency: Send + Sync {
/// Register an effect to be triggered on updates
fn on_update(&self, hook: HookEffect);
}
@@ -136,6 +173,7 @@ impl HookDependency for Mount {
}
impl Mount {
/// Mark the component as mounted and execute pending effects
pub fn mount(&self) {
*self.0.lock().unwrap() = true;
for hook_effect in self.1.lock().unwrap().iter() {
@@ -145,6 +183,10 @@ impl Mount {
}
}
/// Executes a function when dependencies change
///
/// Similar to React's useEffect. The provided function is executed
/// when any of the dependencies change.
pub fn use_effect<F: FnMut() + Send + Sync + 'static>(f: F, dependencies: &[&dyn HookDependency]) {
let f = Arc::new(Mutex::new(f));
let hook = HookEffect(Arc::new(Mutex::new({
@@ -160,10 +202,18 @@ pub fn use_effect<F: FnMut() + Send + Sync + 'static>(f: F, dependencies: &[&dyn
}
}
/// Creates a mount lifecycle hook
///
/// Returns a Mount that tracks component lifecycle and executes
/// effects after mounting.
pub fn use_mount() -> Mount {
Mount(Arc::new(Mutex::new(true)), Arc::new(Mutex::new(Vec::new())))
}
/// Creates a manual mount lifecycle hook
///
/// Similar to use_mount but the component starts as unmounted.
/// Must call .mount() to trigger mounted effects.
pub fn use_mount_manual() -> Mount {
Mount(
Arc::new(Mutex::new(false)),
@@ -171,6 +221,10 @@ pub fn use_mount_manual() -> Mount {
)
}
/// Synchronizes state with events from the context
///
/// Creates state that is automatically updated when events are emitted
/// to the context. The decoder function converts events to state values.
pub fn use_sync_state<
T: Send + Sync + 'static,
E: Any + 'static,
@@ -190,6 +244,10 @@ pub fn use_sync_state<
state
}
/// Synchronizes state changes back to the context as events
///
/// Creates an effect that emits an event whenever the state changes.
/// The encoder function converts state values to events.
pub fn use_sync_effect<
T: Send + Sync + 'static,
Ev: Send + Sync + 'static,