Skip to main content

cranpose_core/
lib.rs

1#![doc = r"Core runtime pieces for the Cranpose experiment."]
2
3pub extern crate self as cranpose_core;
4
5pub mod composer_context;
6mod frame_clock;
7mod launched_effect;
8pub mod owned;
9pub mod platform;
10pub mod runtime;
11pub mod snapshot_double_index_heap;
12pub mod snapshot_id_set;
13pub mod snapshot_pinning;
14pub mod snapshot_state_observer;
15pub mod snapshot_v2;
16mod snapshot_weak_set;
17mod state;
18pub mod subcompose;
19
20#[cfg(feature = "internal")]
21#[doc(hidden)]
22pub mod internal {
23    pub use crate::frame_clock::{FrameCallbackRegistration, FrameClock};
24}
25pub use launched_effect::{
26    CancelToken, LaunchedEffectScope, __launched_effect_async_impl, __launched_effect_impl,
27};
28pub use owned::Owned;
29pub use platform::{Clock, RuntimeScheduler};
30pub use runtime::{
31    current_runtime_handle, schedule_frame, schedule_node_update, DefaultScheduler, Runtime,
32    RuntimeHandle, StateId, TaskHandle,
33};
34pub use snapshot_state_observer::SnapshotStateObserver;
35
36/// Runs the provided closure inside a mutable snapshot and applies the result.
37///
38/// Use this function when you need to update `MutableState` from outside the
39/// composition or layout phase, typically in event handlers or async tasks.
40///
41/// # Why is this needed?
42/// Cranpose uses a snapshot system (MVCC) to isolate state changes. Modifications
43/// made to `MutableState` are only visible to the current thread's active snapshot.
44/// To make changes visible to the rest of the system (and trigger recomposition),
45/// they must be "applied" by committing the snapshot. This helper handles that
46/// lifecycle for you.
47///
48/// # Example
49///
50/// ```ignore
51/// // Inside a button click handler
52/// run_in_mutable_snapshot(|| {
53///     count.set(count.value() + 1);
54/// });
55/// ```
56///
57/// # Important
58/// ALL UI event handlers (keyboard, mouse, touch, animations) that modify state
59/// MUST use this function or [`dispatch_ui_event`].
60pub fn run_in_mutable_snapshot<T>(block: impl FnOnce() -> T) -> Result<T, &'static str> {
61    let snapshot = snapshot_v2::take_mutable_snapshot(None, None);
62
63    // Mark that we're in an applied snapshot context
64    IN_APPLIED_SNAPSHOT.with(|c| c.set(true));
65    let value = snapshot.enter(block);
66    IN_APPLIED_SNAPSHOT.with(|c| c.set(false));
67
68    match snapshot.apply() {
69        snapshot_v2::SnapshotApplyResult::Success => Ok(value),
70        snapshot_v2::SnapshotApplyResult::Failure => Err("Snapshot apply failed"),
71    }
72}
73
74/// Dispatches a UI event in a proper snapshot context.
75///
76/// This is a convenience wrapper around [`run_in_mutable_snapshot`] that returns
77/// `Option<T>` instead of `Result<T, &str>`.
78///
79/// # Example
80/// ```ignore
81/// // In a keyboard event handler:
82/// dispatch_ui_event(|| {
83///     text_field_state.edit(|buffer| {
84///         buffer.insert("a");
85///     });
86/// });
87/// ```
88pub fn dispatch_ui_event<T>(block: impl FnOnce() -> T) -> Option<T> {
89    run_in_mutable_snapshot(block).ok()
90}
91
92// ─── Event Handler Context Tracking ─────────────────────────────────────────
93//
94// These thread-locals track whether code is running in an event handler context
95// and whether it's properly wrapped in run_in_mutable_snapshot. This allows
96// debug-mode warnings when state is modified without proper snapshot handling.
97
98thread_local! {
99    /// Tracks if we're in a UI event handler context (keyboard, mouse, etc.)
100    pub(crate) static IN_EVENT_HANDLER: Cell<bool> = const { Cell::new(false) };
101    /// Tracks if we're in a properly-applied mutable snapshot
102    pub(crate) static IN_APPLIED_SNAPSHOT: Cell<bool> = const { Cell::new(false) };
103}
104
105/// Marks the start of an event handler context.
106/// Call this at the start of keyboard/mouse/touch event handling.
107/// Use `run_in_mutable_snapshot` or `dispatch_ui_event` inside to ensure proper snapshot handling.
108pub fn enter_event_handler() {
109    IN_EVENT_HANDLER.with(|c| c.set(true));
110}
111
112/// Marks the end of an event handler context.
113pub fn exit_event_handler() {
114    IN_EVENT_HANDLER.with(|c| c.set(false));
115}
116
117/// Returns true if currently in an event handler context.
118pub fn in_event_handler() -> bool {
119    IN_EVENT_HANDLER.with(|c| c.get())
120}
121
122/// Returns true if currently in an applied snapshot context.
123pub fn in_applied_snapshot() -> bool {
124    IN_APPLIED_SNAPSHOT.with(|c| c.get())
125}
126
127// ─── Cached Debug Flag ─────────────────────────────────────────────────────
128//
129// Caches the COMPOSE_DEBUG environment variable check to avoid repeated
130// syscalls in hot paths. The value is checked once on first access.
131
132#[cfg(not(target_arch = "wasm32"))]
133fn compose_debug_enabled() -> bool {
134    use std::sync::OnceLock;
135    static COMPOSE_DEBUG: OnceLock<bool> = OnceLock::new();
136    *COMPOSE_DEBUG.get_or_init(|| std::env::var_os("COMPOSE_DEBUG").is_some())
137}
138
139#[cfg(not(target_arch = "wasm32"))]
140fn input_debug_enabled() -> bool {
141    use std::sync::OnceLock;
142    static INPUT_DEBUG: OnceLock<bool> = OnceLock::new();
143    *INPUT_DEBUG.get_or_init(|| std::env::var_os("CRANPOSE_INPUT_DEBUG").is_some())
144}
145
146#[cfg(target_arch = "wasm32")]
147fn input_debug_enabled() -> bool {
148    false
149}
150
151#[cfg(test)]
152pub use runtime::{TestRuntime, TestScheduler};
153
154use crate::collections::map::HashMap;
155use crate::state::{NeverEqual, SnapshotMutableState, UpdateScope};
156use std::any::Any;
157use std::cell::{Cell, Ref, RefCell, RefMut};
158use std::fmt;
159use std::hash::{Hash, Hasher};
160use std::marker::PhantomData;
161use std::ops::{Deref, DerefMut};
162use std::rc::{Rc, Weak}; // FUTURE(no_std): replace Rc/Weak with arena-managed handles.
163use std::sync::atomic::{AtomicUsize, Ordering};
164use std::sync::Arc;
165
166pub type Key = u64;
167pub type NodeId = usize;
168
169/// Stable identifier for a slot in the slot table.
170///
171/// Anchors provide positional stability: they maintain their identity even when
172/// the slot table is reorganized (e.g., during conditional rendering or group moves).
173/// This prevents effect states from being prematurely removed during recomposition.
174#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Default)]
175pub struct AnchorId(usize);
176
177impl AnchorId {
178    /// Invalid anchor that represents no anchor.
179    pub(crate) const INVALID: AnchorId = AnchorId(0);
180
181    /// Create a new anchor ID from a raw value.
182    pub(crate) fn new(id: usize) -> Self {
183        Self(id)
184    }
185
186    /// Check if this anchor is valid (non-zero).
187    pub fn is_valid(&self) -> bool {
188        self.0 != 0
189    }
190}
191
192pub(crate) type ScopeId = usize;
193type LocalKey = usize;
194pub(crate) type FrameCallbackId = u64;
195type LocalStackSnapshot = Rc<Vec<LocalContext>>;
196
197static NEXT_SCOPE_ID: AtomicUsize = AtomicUsize::new(1);
198static NEXT_LOCAL_KEY: AtomicUsize = AtomicUsize::new(1);
199
200fn next_scope_id() -> ScopeId {
201    NEXT_SCOPE_ID.fetch_add(1, Ordering::Relaxed)
202}
203
204fn next_local_key() -> LocalKey {
205    NEXT_LOCAL_KEY.fetch_add(1, Ordering::Relaxed)
206}
207
208pub(crate) struct RecomposeScopeInner {
209    id: ScopeId,
210    runtime: RuntimeHandle,
211    invalid: Cell<bool>,
212    enqueued: Cell<bool>,
213    active: Cell<bool>,
214    pending_recompose: Cell<bool>,
215    force_reuse: Cell<bool>,
216    force_recompose: Cell<bool>,
217    parent_hint: Cell<Option<NodeId>>,
218    recompose: RefCell<Option<RecomposeCallback>>,
219    local_stack: RefCell<LocalStackSnapshot>,
220    slots_host: RefCell<Option<Weak<SlotsHost>>>,
221}
222
223impl RecomposeScopeInner {
224    fn new(runtime: RuntimeHandle) -> Self {
225        Self {
226            id: next_scope_id(),
227            runtime,
228            invalid: Cell::new(false),
229            enqueued: Cell::new(false),
230            active: Cell::new(true),
231            pending_recompose: Cell::new(false),
232            force_reuse: Cell::new(false),
233            force_recompose: Cell::new(false),
234            parent_hint: Cell::new(None),
235            recompose: RefCell::new(None),
236            local_stack: RefCell::new(Rc::new(Vec::new())),
237            slots_host: RefCell::new(None),
238        }
239    }
240}
241
242type RecomposeCallback = Box<dyn FnMut(&Composer) + 'static>;
243
244#[derive(Clone)]
245pub struct RecomposeScope {
246    inner: Rc<RecomposeScopeInner>, // FUTURE(no_std): replace Rc with arena-managed scope handles.
247}
248
249impl PartialEq for RecomposeScope {
250    fn eq(&self, other: &Self) -> bool {
251        Rc::ptr_eq(&self.inner, &other.inner)
252    }
253}
254
255impl Eq for RecomposeScope {}
256
257impl RecomposeScope {
258    fn new(runtime: RuntimeHandle) -> Self {
259        Self {
260            inner: Rc::new(RecomposeScopeInner::new(runtime)),
261        }
262    }
263
264    pub fn id(&self) -> ScopeId {
265        self.inner.id
266    }
267
268    pub fn is_invalid(&self) -> bool {
269        self.inner.invalid.get()
270    }
271
272    pub fn is_active(&self) -> bool {
273        self.inner.active.get()
274    }
275
276    fn invalidate(&self) {
277        if input_debug_enabled() {
278            eprintln!(
279                "[CRANPOSE_INPUT_DEBUG] scope invalidate id={} active={} enqueued={}",
280                self.inner.id,
281                self.inner.active.get(),
282                self.inner.enqueued.get()
283            );
284        }
285        self.inner.invalid.set(true);
286        if !self.inner.active.get() {
287            return;
288        }
289        if !self.inner.enqueued.replace(true) {
290            self.inner
291                .runtime
292                .register_invalid_scope(self.inner.id, Rc::downgrade(&self.inner));
293        }
294    }
295
296    fn mark_recomposed(&self) {
297        self.inner.invalid.set(false);
298        self.inner.force_reuse.set(false);
299        self.inner.force_recompose.set(false);
300        if self.inner.enqueued.replace(false) {
301            self.inner.runtime.mark_scope_recomposed(self.inner.id);
302        }
303        let pending = self.inner.pending_recompose.replace(false);
304        if pending {
305            if self.inner.active.get() {
306                self.invalidate();
307            } else {
308                self.inner.invalid.set(true);
309            }
310        }
311    }
312
313    fn downgrade(&self) -> Weak<RecomposeScopeInner> {
314        Rc::downgrade(&self.inner)
315    }
316
317    fn set_recompose(&self, callback: RecomposeCallback) {
318        *self.inner.recompose.borrow_mut() = Some(callback);
319    }
320
321    fn run_recompose(&self, composer: &Composer) {
322        let mut callback_cell = self.inner.recompose.borrow_mut();
323        if let Some(mut callback) = callback_cell.take() {
324            drop(callback_cell);
325            callback(composer);
326        }
327    }
328
329    fn snapshot_locals(&self, stack: LocalStackSnapshot) {
330        *self.inner.local_stack.borrow_mut() = stack;
331    }
332
333    fn local_stack(&self) -> LocalStackSnapshot {
334        self.inner.local_stack.borrow().clone()
335    }
336
337    fn set_parent_hint(&self, parent: Option<NodeId>) {
338        self.inner.parent_hint.set(parent);
339    }
340
341    fn parent_hint(&self) -> Option<NodeId> {
342        self.inner.parent_hint.get()
343    }
344
345    fn set_slots_host(&self, host: Weak<SlotsHost>) {
346        *self.inner.slots_host.borrow_mut() = Some(host);
347    }
348
349    fn slots_host(&self) -> Option<Rc<SlotsHost>> {
350        self.inner
351            .slots_host
352            .borrow()
353            .as_ref()
354            .and_then(|weak| weak.upgrade())
355    }
356
357    pub fn deactivate(&self) {
358        if !self.inner.active.replace(false) {
359            return;
360        }
361        if self.inner.enqueued.replace(false) {
362            self.inner.runtime.mark_scope_recomposed(self.inner.id);
363        }
364    }
365
366    pub fn reactivate(&self) {
367        if self.inner.active.replace(true) {
368            return;
369        }
370        if self.inner.invalid.get() && !self.inner.enqueued.replace(true) {
371            self.inner
372                .runtime
373                .register_invalid_scope(self.inner.id, Rc::downgrade(&self.inner));
374        }
375    }
376
377    pub fn force_reuse(&self) {
378        self.inner.force_reuse.set(true);
379        self.inner.force_recompose.set(false);
380        self.inner.pending_recompose.set(true);
381    }
382
383    pub fn force_recompose(&self) {
384        self.inner.force_recompose.set(true);
385        self.inner.force_reuse.set(false);
386        self.inner.pending_recompose.set(false);
387    }
388
389    pub fn should_recompose(&self) -> bool {
390        if self.inner.force_recompose.replace(false) {
391            self.inner.force_reuse.set(false);
392            return true;
393        }
394        if self.inner.force_reuse.replace(false) {
395            return false;
396        }
397        self.is_invalid()
398    }
399}
400
401#[cfg(test)]
402impl RecomposeScope {
403    pub(crate) fn new_for_test(runtime: RuntimeHandle) -> Self {
404        Self::new(runtime)
405    }
406}
407
408#[derive(Debug, Clone, Copy, Default)]
409pub struct RecomposeOptions {
410    pub force_reuse: bool,
411    pub force_recompose: bool,
412}
413
414#[derive(Debug, Clone, PartialEq, Eq)]
415pub enum NodeError {
416    Missing { id: NodeId },
417    TypeMismatch { id: NodeId, expected: &'static str },
418    MissingContext { id: NodeId, reason: &'static str },
419    AlreadyExists { id: NodeId },
420}
421
422impl std::fmt::Display for NodeError {
423    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
424        match self {
425            NodeError::Missing { id } => write!(f, "node {id} missing"),
426            NodeError::TypeMismatch { id, expected } => {
427                write!(f, "node {id} type mismatch; expected {expected}")
428            }
429            NodeError::MissingContext { id, reason } => {
430                write!(f, "missing context for node {id}: {reason}")
431            }
432            NodeError::AlreadyExists { id } => {
433                write!(f, "node {id} already exists")
434            }
435        }
436    }
437}
438
439impl std::error::Error for NodeError {}
440
441pub use subcompose::{
442    ContentTypeReusePolicy, DefaultSlotReusePolicy, SlotId, SlotReusePolicy, SubcomposeState,
443};
444
445#[derive(Copy, Clone, Debug, PartialEq, Eq)]
446pub enum Phase {
447    Compose,
448    Measure,
449    Layout,
450}
451
452pub use composer_context::with_composer as with_current_composer;
453
454#[allow(non_snake_case)]
455pub fn withCurrentComposer<R>(f: impl FnOnce(&Composer) -> R) -> R {
456    composer_context::with_composer(f)
457}
458
459fn with_current_composer_opt<R>(f: impl FnOnce(&Composer) -> R) -> Option<R> {
460    composer_context::try_with_composer(f)
461}
462
463pub fn with_key<K: Hash>(key: &K, content: impl FnOnce()) {
464    with_current_composer(|composer| composer.with_key(key, |_| content()));
465}
466
467#[allow(non_snake_case)]
468pub fn withKey<K: Hash>(key: &K, content: impl FnOnce()) {
469    with_key(key, content)
470}
471
472pub fn remember<T: 'static>(init: impl FnOnce() -> T) -> Owned<T> {
473    with_current_composer(|composer| composer.remember(init))
474}
475
476/// Returns a [`MutableState`] that always holds the latest value.
477///
478/// The state **reference** is stable across recompositions; only the **value** updates.
479/// This allows closures to capture a stable reference while reading fresh values.
480///
481/// # Use Case
482/// Use when a `remember`ed closure needs to read a value that changes each recomposition
483/// without recreating the closure itself.
484///
485/// # Example
486/// ```rust,ignore
487/// let config = build_config(); // Rebuilt each recomposition
488/// let config_state = rememberUpdatedState(config);
489///
490/// // This closure is created once, reads latest config via state
491/// let callback = remember(|| {
492///     let cfg = config_state;
493///     Rc::new(move || do_something(&cfg.value()))
494/// }).with(|c| c.clone());
495/// ```
496///
497/// # JC Equivalent
498/// ```kotlin
499/// @Composable
500/// fun <T> rememberUpdatedState(newValue: T): State<T> =
501///     remember { mutableStateOf(newValue) }.apply { value = newValue }
502/// ```
503#[allow(non_snake_case)]
504pub fn rememberUpdatedState<T: Clone + 'static>(value: T) -> MutableState<T> {
505    with_current_composer(|composer| {
506        let runtime = composer.runtime_handle();
507        let state = composer.remember(|| OwnedMutableState::with_runtime(value.clone(), runtime));
508        state.with(|s| {
509            s.set(value);
510            s.handle()
511        })
512    })
513}
514
515#[cfg(feature = "internal")]
516#[allow(non_snake_case)]
517pub fn withFrameNanos(callback: impl FnOnce(u64) + 'static) -> internal::FrameCallbackRegistration {
518    with_current_composer(|composer| {
519        composer
520            .runtime_handle()
521            .frame_clock()
522            .with_frame_nanos(callback)
523    })
524}
525
526#[cfg(feature = "internal")]
527#[allow(non_snake_case)]
528pub fn withFrameMillis(
529    callback: impl FnOnce(u64) + 'static,
530) -> internal::FrameCallbackRegistration {
531    with_current_composer(|composer| {
532        composer
533            .runtime_handle()
534            .frame_clock()
535            .with_frame_millis(callback)
536    })
537}
538
539/// Creates a new `MutableState` initialized with the given value.
540///
541/// `MutableState` is a cheap copyable observable handle. Reads are tracked by the
542/// current composer or snapshot, and writes trigger recomposition of scopes that
543/// read it.
544///
545/// # When to use
546/// Use `mutableStateOf` when:
547/// 1.  You are creating state properties inside a struct or class (not a composable function).
548/// 2.  You are implementing a custom state management solution.
549///
550/// **If you are inside a `#[composable]` function, use [`useState`] instead.**
551/// `useState` wraps `mutableStateOf` in `remember`, ensuring the state persists
552/// across recompositions. Using `mutableStateOf` directly in a composable will
553/// recreated the state on every frame, losing data.
554///
555/// # Example
556///
557/// ```rust,ignore
558/// struct MyViewModel {
559///     name: MutableState<String>,
560/// }
561///
562/// impl MyViewModel {
563///     fn new() -> Self {
564///         Self {
565///             name: mutableStateOf("Alice".into()),
566///         }
567///     }
568/// }
569/// ```
570///
571/// This creates a runtime-owned persistent state. If you need the state lifetime
572/// tied to a Rust owner instead, store an [`OwnedMutableState`] or call
573/// [`MutableState::retain`] on a handle returned by [`useState`].
574#[allow(non_snake_case)]
575pub fn mutableStateOf<T: Clone + 'static>(initial: T) -> MutableState<T> {
576    // Get runtime handle from current composer if available, otherwise from global registry.
577    // IMPORTANT: We always use with_runtime() (not composer.mutable_state_of) because
578    // slot-based storage doesn't work for state objects that are cloned and shared
579    // across different composition contexts (like TextFieldState).
580    let runtime = with_current_composer_opt(|composer| composer.runtime_handle())
581        .or_else(runtime::current_runtime_handle)
582        .expect("mutableStateOf requires an active runtime. Create state inside a composition or after a Runtime is created.");
583    runtime.alloc_persistent_state(initial)
584}
585
586#[allow(non_snake_case)]
587pub fn ownedMutableStateOf<T: Clone + 'static>(initial: T) -> OwnedMutableState<T> {
588    let runtime = with_current_composer_opt(|composer| composer.runtime_handle())
589        .or_else(runtime::current_runtime_handle)
590        .expect("ownedMutableStateOf requires an active runtime. Create state inside a composition or after a Runtime is created.");
591    OwnedMutableState::with_runtime(initial, runtime)
592}
593
594/// Like [`mutableStateOf`] but returns `None` if no runtime is available.
595///
596/// Use this when you want to lazily initialize reactive state and gracefully
597/// handle the case where the runtime isn't yet available.
598#[allow(non_snake_case)]
599pub fn try_mutableStateOf<T: Clone + 'static>(initial: T) -> Option<MutableState<T>> {
600    let runtime = with_current_composer_opt(|composer| composer.runtime_handle())
601        .or_else(runtime::current_runtime_handle)?;
602    Some(runtime.alloc_persistent_state(initial))
603}
604
605#[allow(non_snake_case)]
606pub fn mutableStateListOf<T, I>(values: I) -> SnapshotStateList<T>
607where
608    T: Clone + 'static,
609    I: IntoIterator<Item = T>,
610{
611    with_current_composer(move |composer| composer.mutable_state_list_of(values))
612}
613
614#[allow(non_snake_case)]
615pub fn mutableStateList<T: Clone + 'static>() -> SnapshotStateList<T> {
616    mutableStateListOf(std::iter::empty::<T>())
617}
618
619#[allow(non_snake_case)]
620pub fn mutableStateMapOf<K, V, I>(pairs: I) -> SnapshotStateMap<K, V>
621where
622    K: Clone + Eq + Hash + 'static,
623    V: Clone + 'static,
624    I: IntoIterator<Item = (K, V)>,
625{
626    with_current_composer(move |composer| composer.mutable_state_map_of(pairs))
627}
628
629#[allow(non_snake_case)]
630pub fn mutableStateMap<K, V>() -> SnapshotStateMap<K, V>
631where
632    K: Clone + Eq + Hash + 'static,
633    V: Clone + 'static,
634{
635    mutableStateMapOf(std::iter::empty::<(K, V)>())
636}
637
638/// A composable hook that creates and remembers a `MutableState`.
639///
640/// This is the primary way to define local state in a composable function.
641/// It combines `remember` and `mutableStateOf`.
642///
643/// # Arguments
644///
645/// * `init` - A closure that provides the initial value. This is only called once
646///   when the composable enters the composition.
647///
648/// # Example
649///
650/// ```rust,ignore
651/// #[composable]
652/// fn Counter() {
653///     // "count" persists across recompositions.
654///     // If we used mutableStateOf directly, it would reset to 0 every frame.
655///     let count = useState(|| 0);
656///
657///     Button(
658///         onClick = move || count.set(count.value() + 1),
659///         || Text(format!("Count: {}", count.value()))
660///     );
661/// }
662/// ```
663#[allow(non_snake_case)]
664pub fn useState<T: Clone + 'static>(init: impl FnOnce() -> T) -> MutableState<T> {
665    with_current_composer(|composer| {
666        let runtime = composer.runtime_handle();
667        composer
668            .remember(|| OwnedMutableState::with_runtime(init(), runtime))
669            .with(|state| state.handle())
670    })
671}
672
673#[allow(deprecated)]
674#[deprecated(
675    since = "0.1.0",
676    note = "use useState(|| value) instead of use_state(|| value)"
677)]
678pub fn use_state<T: Clone + 'static>(init: impl FnOnce() -> T) -> MutableState<T> {
679    useState(init)
680}
681
682#[allow(non_snake_case)]
683pub fn derivedStateOf<T: 'static + Clone>(compute: impl Fn() -> T + 'static) -> State<T> {
684    with_current_composer(|composer| {
685        let key = location_key(file!(), line!(), column!());
686        composer.with_group(key, |composer| {
687            let should_recompute = composer
688                .current_recranpose_scope()
689                .map(|scope| scope.should_recompose())
690                .unwrap_or(true);
691            let runtime = composer.runtime_handle();
692            let compute_rc: Rc<dyn Fn() -> T> = Rc::new(compute); // FUTURE(no_std): replace Rc with arena-managed callbacks.
693            let derived =
694                composer.remember(|| DerivedState::new(runtime.clone(), compute_rc.clone()));
695            derived.update(|derived| {
696                derived.set_compute(compute_rc.clone());
697                if should_recompute {
698                    derived.recompute();
699                }
700            });
701            derived.with(|derived| derived.state.as_state())
702        })
703    })
704}
705
706pub struct ProvidedValue {
707    key: LocalKey,
708    #[allow(clippy::type_complexity)] // Closure returns trait object for flexible local values
709    apply: Box<dyn Fn(&Composer) -> Rc<dyn Any>>, // FUTURE(no_std): return arena-backed local storage pointer.
710}
711
712impl ProvidedValue {
713    fn into_entry(self, composer: &Composer) -> (LocalKey, Rc<dyn Any>) {
714        // FUTURE(no_std): avoid Rc allocation per entry.
715        let ProvidedValue { key, apply } = self;
716        let entry = apply(composer);
717        (key, entry)
718    }
719}
720
721#[allow(non_snake_case)]
722pub fn CompositionLocalProvider(
723    values: impl IntoIterator<Item = ProvidedValue>,
724    content: impl FnOnce(),
725) {
726    with_current_composer(|composer| {
727        let provided: Vec<ProvidedValue> = values.into_iter().collect(); // FUTURE(no_std): replace Vec with stack-allocated small vec.
728        composer.with_composition_locals(provided, |_composer| content());
729    })
730}
731
732struct LocalStateEntry<T: Clone + 'static> {
733    state: OwnedMutableState<T>,
734}
735
736impl<T: Clone + 'static> LocalStateEntry<T> {
737    fn new(initial: T, runtime: RuntimeHandle) -> Self {
738        Self {
739            state: OwnedMutableState::with_runtime(initial, runtime),
740        }
741    }
742
743    fn set(&self, value: T) {
744        self.state.replace(value);
745    }
746
747    fn value(&self) -> T {
748        self.state.value()
749    }
750}
751
752struct StaticLocalEntry<T: Clone + 'static> {
753    value: RefCell<T>,
754}
755
756impl<T: Clone + 'static> StaticLocalEntry<T> {
757    fn new(value: T) -> Self {
758        Self {
759            value: RefCell::new(value),
760        }
761    }
762
763    fn set(&self, value: T) {
764        *self.value.borrow_mut() = value;
765    }
766
767    fn value(&self) -> T {
768        self.value.borrow().clone()
769    }
770}
771
772#[derive(Clone)]
773pub struct CompositionLocal<T: Clone + 'static> {
774    key: LocalKey,
775    default: Rc<dyn Fn() -> T>, // FUTURE(no_std): store default provider in arena-managed cell.
776}
777
778impl<T: Clone + 'static> PartialEq for CompositionLocal<T> {
779    fn eq(&self, other: &Self) -> bool {
780        self.key == other.key
781    }
782}
783
784impl<T: Clone + 'static> Eq for CompositionLocal<T> {}
785
786impl<T: Clone + 'static> CompositionLocal<T> {
787    pub fn provides(&self, value: T) -> ProvidedValue {
788        let key = self.key;
789        ProvidedValue {
790            key,
791            apply: Box::new(move |composer: &Composer| {
792                let runtime = composer.runtime_handle();
793                let entry_ref = composer
794                    .remember(|| Rc::new(LocalStateEntry::new(value.clone(), runtime.clone())));
795                entry_ref.update(|entry| entry.set(value.clone()));
796                entry_ref.with(|entry| entry.clone() as Rc<dyn Any>) // FUTURE(no_std): expose erased handle without Rc boxing.
797            }),
798        }
799    }
800
801    pub fn current(&self) -> T {
802        with_current_composer(|composer| composer.read_composition_local(self))
803    }
804
805    pub fn default_value(&self) -> T {
806        (self.default)()
807    }
808}
809
810#[allow(non_snake_case)]
811pub fn compositionLocalOf<T: Clone + 'static>(
812    default: impl Fn() -> T + 'static,
813) -> CompositionLocal<T> {
814    CompositionLocal {
815        key: next_local_key(),
816        default: Rc::new(default), // FUTURE(no_std): allocate default provider in arena storage.
817    }
818}
819
820/// A `StaticCompositionLocal` is a CompositionLocal that is optimized for values that are
821/// unlikely to change. Unlike `CompositionLocal`, reads of a `StaticCompositionLocal` are not
822/// tracked by the recomposition system, which means:
823/// - Reading `.current()` does NOT establish a subscription
824/// - Changing the provided value does NOT automatically invalidate readers
825/// - This makes it more efficient for truly static values
826///
827/// This matches the API of Jetpack Compose's `staticCompositionLocalOf` but with simplified
828/// semantics. Use this for values that are guaranteed to never change during the lifetime of
829/// the CompositionLocalProvider scope (e.g., application-wide constants, configuration)
830#[derive(Clone)]
831pub struct StaticCompositionLocal<T: Clone + 'static> {
832    key: LocalKey,
833    default: Rc<dyn Fn() -> T>, // FUTURE(no_std): store default provider in arena-managed cell.
834}
835
836impl<T: Clone + 'static> PartialEq for StaticCompositionLocal<T> {
837    fn eq(&self, other: &Self) -> bool {
838        self.key == other.key
839    }
840}
841
842impl<T: Clone + 'static> Eq for StaticCompositionLocal<T> {}
843
844impl<T: Clone + 'static> StaticCompositionLocal<T> {
845    pub fn provides(&self, value: T) -> ProvidedValue {
846        let key = self.key;
847        ProvidedValue {
848            key,
849            apply: Box::new(move |composer: &Composer| {
850                // For static locals, we don't use MutableState - just store the value directly
851                // This means reads won't be tracked, and changes will cause full subtree recomposition
852                let entry_ref = composer.remember(|| Rc::new(StaticLocalEntry::new(value.clone())));
853                entry_ref.update(|entry| entry.set(value.clone()));
854                entry_ref.with(|entry| entry.clone() as Rc<dyn Any>) // FUTURE(no_std): expose erased handle without Rc boxing.
855            }),
856        }
857    }
858
859    pub fn current(&self) -> T {
860        with_current_composer(|composer| composer.read_static_composition_local(self))
861    }
862
863    pub fn default_value(&self) -> T {
864        (self.default)()
865    }
866}
867
868#[allow(non_snake_case)]
869pub fn staticCompositionLocalOf<T: Clone + 'static>(
870    default: impl Fn() -> T + 'static,
871) -> StaticCompositionLocal<T> {
872    StaticCompositionLocal {
873        key: next_local_key(),
874        default: Rc::new(default), // FUTURE(no_std): allocate default provider in arena storage.
875    }
876}
877
878#[derive(Default)]
879struct DisposableEffectState {
880    key: Option<Key>,
881    cleanup: Option<Box<dyn FnOnce()>>,
882}
883
884impl DisposableEffectState {
885    fn should_run(&self, key: Key) -> bool {
886        match self.key {
887            Some(current) => current != key,
888            None => true,
889        }
890    }
891
892    fn set_key(&mut self, key: Key) {
893        self.key = Some(key);
894    }
895
896    fn set_cleanup(&mut self, cleanup: Option<Box<dyn FnOnce()>>) {
897        self.cleanup = cleanup;
898    }
899
900    fn run_cleanup(&mut self) {
901        if let Some(cleanup) = self.cleanup.take() {
902            cleanup();
903        }
904    }
905}
906
907impl Drop for DisposableEffectState {
908    fn drop(&mut self) {
909        self.run_cleanup();
910    }
911}
912
913#[derive(Clone, Copy, Debug, Default)]
914pub struct DisposableEffectScope;
915
916#[derive(Default)]
917pub struct DisposableEffectResult {
918    cleanup: Option<Box<dyn FnOnce()>>,
919}
920
921impl DisposableEffectScope {
922    pub fn on_dispose(&self, cleanup: impl FnOnce() + 'static) -> DisposableEffectResult {
923        DisposableEffectResult::new(cleanup)
924    }
925}
926
927impl DisposableEffectResult {
928    pub fn new(cleanup: impl FnOnce() + 'static) -> Self {
929        Self {
930            cleanup: Some(Box::new(cleanup)),
931        }
932    }
933
934    fn into_cleanup(self) -> Option<Box<dyn FnOnce()>> {
935        self.cleanup
936    }
937}
938
939#[allow(non_snake_case)]
940pub fn SideEffect(effect: impl FnOnce() + 'static) {
941    with_current_composer(|composer| composer.register_side_effect(effect));
942}
943
944pub fn __disposable_effect_impl<K, F>(group_key: Key, keys: K, effect: F)
945where
946    K: Hash,
947    F: FnOnce(DisposableEffectScope) -> DisposableEffectResult + 'static,
948{
949    // Create a group using the caller's location to ensure each DisposableEffect
950    // gets its own slot table entry, even in conditional branches
951    with_current_composer(|composer| {
952        composer.with_group(group_key, |composer| {
953            let key_hash = hash_key(&keys);
954            let state = composer.remember(DisposableEffectState::default);
955            if state.with(|state| state.should_run(key_hash)) {
956                state.update(|state| {
957                    state.run_cleanup();
958                    state.set_key(key_hash);
959                });
960                let state_for_effect = state.clone();
961                let mut effect_opt = Some(effect);
962                composer.register_side_effect(move || {
963                    if let Some(effect) = effect_opt.take() {
964                        let result = effect(DisposableEffectScope);
965                        state_for_effect.update(|state| state.set_cleanup(result.into_cleanup()));
966                    }
967                });
968            }
969        });
970    });
971}
972
973#[macro_export]
974macro_rules! DisposableEffect {
975    ($keys:expr, $effect:expr) => {
976        $crate::__disposable_effect_impl(
977            $crate::location_key(file!(), line!(), column!()),
978            $keys,
979            $effect,
980        )
981    };
982}
983
984#[macro_export]
985macro_rules! clone_captures {
986    ($($alias:ident $(= $value:expr)?),+ $(,)?; $body:expr) => {{
987        $(let $alias = $crate::clone_captures!(@clone $alias $(= $value)?);)+
988        $body
989    }};
990    (@clone $alias:ident = $value:expr) => {
991        ($value).clone()
992    };
993    (@clone $alias:ident) => {
994        $alias.clone()
995    };
996}
997
998pub fn with_node_mut<N: Node + 'static, R>(
999    id: NodeId,
1000    f: impl FnOnce(&mut N) -> R,
1001) -> Result<R, NodeError> {
1002    with_current_composer(|composer| composer.with_node_mut(id, f))
1003}
1004
1005pub fn push_parent(id: NodeId) {
1006    with_current_composer(|composer| composer.push_parent(id));
1007}
1008
1009pub fn pop_parent() {
1010    with_current_composer(|composer| composer.pop_parent());
1011}
1012
1013// ═══════════════════════════════════════════════════════════════════════════
1014// Public SlotStorage trait and newtypes
1015// ═══════════════════════════════════════════════════════════════════════════
1016
1017mod slot_storage;
1018pub use slot_storage::{GroupId, SlotStorage, StartGroup, ValueSlotId};
1019
1020pub mod chunked_slot_storage;
1021pub mod hierarchical_slot_storage;
1022pub mod slot_backend;
1023pub mod split_slot_storage;
1024pub use slot_backend::{make_backend, SlotBackend, SlotBackendKind};
1025
1026// ═══════════════════════════════════════════════════════════════════════════
1027// SlotTable: gap-buffer-based implementation
1028// ═══════════════════════════════════════════════════════════════════════════
1029
1030pub mod slot_table;
1031pub use slot_table::SlotTable;
1032
1033pub trait Node: Any {
1034    fn mount(&mut self) {}
1035    fn update(&mut self) {}
1036    fn unmount(&mut self) {}
1037    fn insert_child(&mut self, _child: NodeId) {}
1038    fn remove_child(&mut self, _child: NodeId) {}
1039    fn move_child(&mut self, _from: usize, _to: usize) {}
1040    fn update_children(&mut self, _children: &[NodeId]) {}
1041    fn children(&self) -> Vec<NodeId> {
1042        Vec::new()
1043    }
1044    /// Called after the node is created to record its own ID.
1045    /// Useful for nodes that need to store their ID for later operations.
1046    fn set_node_id(&mut self, _id: NodeId) {}
1047    /// Called when this node is attached to a parent.
1048    /// Nodes with parent tracking should set their parent reference here.
1049    fn on_attached_to_parent(&mut self, _parent: NodeId) {}
1050    /// Called when this node is removed from its parent.
1051    /// Nodes with parent tracking should clear their parent reference here.
1052    fn on_removed_from_parent(&mut self) {}
1053    /// Get this node's parent ID (for nodes that track parents).
1054    /// Returns None if node has no parent or doesn't track parents.
1055    fn parent(&self) -> Option<NodeId> {
1056        None
1057    }
1058    /// Mark this node as needing layout (for nodes with dirty flags).
1059    /// Called during bubbling to propagate dirtiness up the tree.
1060    fn mark_needs_layout(&self) {}
1061    /// Check if this node needs layout (for nodes with dirty flags).
1062    fn needs_layout(&self) -> bool {
1063        false
1064    }
1065    /// Mark this node as needing measure (size may have changed).
1066    /// Called during bubbling when children are added/removed.
1067    fn mark_needs_measure(&self) {}
1068    /// Check if this node needs measure (for nodes with dirty flags).
1069    fn needs_measure(&self) -> bool {
1070        false
1071    }
1072    /// Mark this node as needing semantics recomputation.
1073    fn mark_needs_semantics(&self) {}
1074    /// Check if this node needs semantics recomputation.
1075    fn needs_semantics(&self) -> bool {
1076        false
1077    }
1078    /// Set parent reference for dirty flag bubbling ONLY.
1079    /// This is a minimal version of on_attached_to_parent that doesn't trigger
1080    /// registry updates or other side effects. Used during measurement when we
1081    /// need to establish parent connections for bubble_measure_dirty without
1082    /// causing the full attachment lifecycle.
1083    ///
1084    /// Default: delegates to on_attached_to_parent for backward compatibility.
1085    fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1086        self.on_attached_to_parent(parent);
1087    }
1088}
1089
1090/// Unified API for bubbling layout dirty flags from a node to the root (Applier context).
1091///
1092/// This is the canonical function for dirty bubbling during the apply phase (structural changes).
1093/// Call this after mutations like insert/remove/move that happen during apply.
1094///
1095/// # Behavior
1096/// 1. Marks the starting node as needing layout
1097/// 2. Walks up the parent chain, marking each ancestor
1098/// 3. Stops when it reaches a node that's already dirty (O(1) optimization)
1099/// 4. Stops at the root (node with no parent)
1100///
1101/// # Performance
1102/// This function is O(height) in the worst case, but typically O(1) due to early exit
1103/// when encountering an already-dirty ancestor.
1104///
1105/// # Usage
1106/// - Call from composer mutations (insert/remove/move) during apply phase
1107/// - Call from applier-level operations that modify the tree structure
1108pub fn bubble_layout_dirty(applier: &mut dyn Applier, node_id: NodeId) {
1109    bubble_layout_dirty_applier(applier, node_id);
1110}
1111
1112/// Unified API for bubbling measure dirty flags from a node to the root (Applier context).
1113///
1114/// Call this when a node's size may have changed (children added/removed, modifier changed).
1115/// This ensures that measure_layout will increment the cache epoch and re-measure the subtree.
1116///
1117/// # Behavior
1118/// 1. Marks the starting node as needing measure
1119/// 2. Walks up the parent chain, marking each ancestor
1120/// 3. Stops when it reaches a node that's already dirty (O(1) optimization)
1121/// 4. Stops at the root (node with no parent)
1122pub fn bubble_measure_dirty(applier: &mut dyn Applier, node_id: NodeId) {
1123    bubble_measure_dirty_applier(applier, node_id);
1124}
1125
1126/// Unified API for bubbling semantics dirty flags from a node to the root (Applier context).
1127///
1128/// This mirrors [`bubble_layout_dirty`] but toggles semantics-specific dirty
1129/// flags instead of layout ones, allowing semantics updates to propagate during
1130/// the apply phase without forcing layout work.
1131pub fn bubble_semantics_dirty(applier: &mut dyn Applier, node_id: NodeId) {
1132    bubble_semantics_dirty_applier(applier, node_id);
1133}
1134
1135/// Schedules semantics bubbling for a node using the active composer if present.
1136///
1137/// This defers the work to the apply phase where we can safely mutate the
1138/// applier tree without re-entrantly borrowing the composer during composition.
1139pub fn queue_semantics_invalidation(node_id: NodeId) {
1140    let _ = composer_context::try_with_composer(|composer| {
1141        composer.enqueue_semantics_invalidation(node_id);
1142    });
1143}
1144
1145/// Unified API for bubbling layout dirty flags from a node to the root (Composer context).
1146///
1147/// This is the canonical function for dirty bubbling during composition (property changes).
1148/// Call this after property changes that happen during composition via with_node_mut.
1149///
1150/// # Behavior
1151/// 1. Marks the starting node as needing layout
1152/// 2. Walks up the parent chain, marking each ancestor
1153/// 3. Stops when it reaches a node that's already dirty (O(1) optimization)
1154/// 4. Stops at the root (node with no parent)
1155///
1156/// # Performance
1157/// This function is O(height) in the worst case, but typically O(1) due to early exit
1158/// when encountering an already-dirty ancestor.
1159///
1160/// # Type Requirements
1161/// The node type N must implement Node (which includes mark_needs_layout, parent, etc.).
1162/// Typically this will be LayoutNode or similar layout-aware node types.
1163///
1164/// # Usage
1165/// - Call from property setters during composition (e.g., set_modifier, set_measure_policy)
1166/// - Call from widget composition when layout-affecting state changes
1167pub fn bubble_layout_dirty_in_composer<N: Node + 'static>(node_id: NodeId) {
1168    bubble_layout_dirty_composer::<N>(node_id);
1169}
1170
1171/// Unified API for bubbling semantics dirty flags from a node to the root (Composer context).
1172///
1173/// This mirrors [`bubble_layout_dirty_in_composer`] but routes through the semantics
1174/// dirty flag instead of the layout one. Modifier nodes can request semantics
1175/// invalidations without triggering measure/layout work, and the runtime can
1176/// query the root to determine whether the semantics tree needs rebuilding.
1177pub fn bubble_semantics_dirty_in_composer<N: Node + 'static>(node_id: NodeId) {
1178    bubble_semantics_dirty_composer::<N>(node_id);
1179}
1180
1181/// Internal implementation for applier-based bubbling.
1182fn bubble_layout_dirty_applier(applier: &mut dyn Applier, mut node_id: NodeId) {
1183    // First, mark the starting node dirty (critical!)
1184    // This ensures root gets marked even if it has no parent
1185    if let Ok(node) = applier.get_mut(node_id) {
1186        node.mark_needs_layout();
1187    }
1188
1189    // Then bubble up to ancestors
1190    loop {
1191        // Get parent of current node
1192        let parent_id = match applier.get_mut(node_id) {
1193            Ok(node) => node.parent(),
1194            Err(_) => None,
1195        };
1196
1197        match parent_id {
1198            Some(pid) => {
1199                // Mark parent as needing layout
1200                if let Ok(parent) = applier.get_mut(pid) {
1201                    if !parent.needs_layout() {
1202                        parent.mark_needs_layout();
1203                        node_id = pid; // Continue bubbling
1204                    } else {
1205                        break; // Already dirty, stop
1206                    }
1207                } else {
1208                    break;
1209                }
1210            }
1211            None => break, // No parent, stop
1212        }
1213    }
1214}
1215
1216/// Internal implementation for applier-based bubbling of measure dirtiness.
1217fn bubble_measure_dirty_applier(applier: &mut dyn Applier, mut node_id: NodeId) {
1218    // First, mark the starting node as needing measure
1219    if let Ok(node) = applier.get_mut(node_id) {
1220        node.mark_needs_measure();
1221    }
1222
1223    // Then bubble up to ancestors
1224    loop {
1225        // Get parent of current node
1226        let parent_id = match applier.get_mut(node_id) {
1227            Ok(node) => node.parent(),
1228            Err(_) => None,
1229        };
1230
1231        match parent_id {
1232            Some(pid) => {
1233                // Mark parent as needing measure
1234                if let Ok(parent) = applier.get_mut(pid) {
1235                    if !parent.needs_measure() {
1236                        parent.mark_needs_measure();
1237                        node_id = pid; // Continue bubbling
1238                    } else {
1239                        break; // Already dirty, stop
1240                    }
1241                } else {
1242                    break;
1243                }
1244            }
1245            None => {
1246                break; // No parent, stop
1247            }
1248        }
1249    }
1250}
1251
1252/// Internal implementation for applier-based bubbling of semantics dirtiness.
1253fn bubble_semantics_dirty_applier(applier: &mut dyn Applier, mut node_id: NodeId) {
1254    if let Ok(node) = applier.get_mut(node_id) {
1255        node.mark_needs_semantics();
1256    }
1257
1258    loop {
1259        let parent_id = match applier.get_mut(node_id) {
1260            Ok(node) => node.parent(),
1261            Err(_) => None,
1262        };
1263
1264        match parent_id {
1265            Some(pid) => {
1266                if let Ok(parent) = applier.get_mut(pid) {
1267                    if !parent.needs_semantics() {
1268                        parent.mark_needs_semantics();
1269                        node_id = pid;
1270                    } else {
1271                        break;
1272                    }
1273                } else {
1274                    break;
1275                }
1276            }
1277            None => break,
1278        }
1279    }
1280}
1281
1282/// Internal implementation for composer-based bubbling.
1283/// This uses with_node_mut and works during composition with a concrete node type.
1284/// The node type N must implement Node (which includes mark_needs_layout, parent, etc.).
1285fn bubble_layout_dirty_composer<N: Node + 'static>(mut node_id: NodeId) {
1286    // Mark the starting node dirty
1287    let _ = with_node_mut(node_id, |node: &mut N| {
1288        node.mark_needs_layout();
1289    });
1290
1291    // Then bubble up to ancestors
1292    while let Ok(Some(pid)) = with_node_mut(node_id, |node: &mut N| node.parent()) {
1293        let parent_id = pid;
1294
1295        // Mark parent as needing layout
1296        let should_continue = with_node_mut(parent_id, |node: &mut N| {
1297            if !node.needs_layout() {
1298                node.mark_needs_layout();
1299                true // Continue bubbling
1300            } else {
1301                false // Already dirty, stop (O(1) optimization)
1302            }
1303        })
1304        .unwrap_or(false);
1305
1306        if should_continue {
1307            node_id = parent_id;
1308        } else {
1309            break;
1310        }
1311    }
1312}
1313
1314/// Internal implementation for composer-based bubbling of semantics dirtiness.
1315fn bubble_semantics_dirty_composer<N: Node + 'static>(mut node_id: NodeId) {
1316    // Mark the starting node semantics-dirty.
1317    let _ = with_node_mut(node_id, |node: &mut N| {
1318        node.mark_needs_semantics();
1319    });
1320
1321    while let Ok(Some(pid)) = with_node_mut(node_id, |node: &mut N| node.parent()) {
1322        let parent_id = pid;
1323
1324        let should_continue = with_node_mut(parent_id, |node: &mut N| {
1325            if !node.needs_semantics() {
1326                node.mark_needs_semantics();
1327                true
1328            } else {
1329                false
1330            }
1331        })
1332        .unwrap_or(false);
1333
1334        if should_continue {
1335            node_id = parent_id;
1336        } else {
1337            break;
1338        }
1339    }
1340}
1341
1342impl dyn Node {
1343    pub fn as_any_mut(&mut self) -> &mut dyn Any {
1344        self
1345    }
1346}
1347
1348pub trait Applier: Any {
1349    fn create(&mut self, node: Box<dyn Node>) -> NodeId;
1350    fn get_mut(&mut self, id: NodeId) -> Result<&mut dyn Node, NodeError>;
1351    fn remove(&mut self, id: NodeId) -> Result<(), NodeError>;
1352
1353    /// Inserts a node with a pre-assigned ID.
1354    ///
1355    /// This is used for virtual nodes whose IDs are allocated separately
1356    /// (e.g., via allocate_virtual_node_id()). Unlike `create()` which assigns
1357    /// a new ID, this method uses the provided ID.
1358    ///
1359    /// Returns Ok(()) if successful, or an error if the ID is already in use.
1360    fn insert_with_id(&mut self, id: NodeId, node: Box<dyn Node>) -> Result<(), NodeError>;
1361
1362    fn as_any(&self) -> &dyn Any
1363    where
1364        Self: Sized,
1365    {
1366        self
1367    }
1368
1369    fn as_any_mut(&mut self) -> &mut dyn Any
1370    where
1371        Self: Sized,
1372    {
1373        self
1374    }
1375}
1376
1377type TypedNodeUpdate = fn(&mut dyn Node, NodeId) -> Result<(), NodeError>;
1378type CommandCallback = Box<dyn FnOnce(&mut dyn Applier) -> Result<(), NodeError> + 'static>;
1379
1380#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1381pub(crate) struct DirtyBubble {
1382    layout: bool,
1383    measure: bool,
1384    semantics: bool,
1385}
1386
1387impl DirtyBubble {
1388    pub(crate) const LAYOUT_AND_MEASURE: Self = Self {
1389        layout: true,
1390        measure: true,
1391        semantics: false,
1392    };
1393
1394    pub(crate) const SEMANTICS: Self = Self {
1395        layout: false,
1396        measure: false,
1397        semantics: true,
1398    };
1399
1400    fn apply(self, applier: &mut dyn Applier, node_id: NodeId) {
1401        if self.layout {
1402            bubble_layout_dirty(applier, node_id);
1403        }
1404        if self.measure {
1405            bubble_measure_dirty(applier, node_id);
1406        }
1407        if self.semantics {
1408            bubble_semantics_dirty(applier, node_id);
1409        }
1410    }
1411}
1412
1413pub(crate) enum Command {
1414    BubbleDirty {
1415        node_id: NodeId,
1416        bubble: DirtyBubble,
1417    },
1418    UpdateTypedNode {
1419        id: NodeId,
1420        updater: TypedNodeUpdate,
1421    },
1422    RemoveNode {
1423        id: NodeId,
1424    },
1425    MountNode {
1426        id: NodeId,
1427    },
1428    AttachChild {
1429        parent_id: NodeId,
1430        child_id: NodeId,
1431        bubble: DirtyBubble,
1432    },
1433    InsertChild {
1434        parent_id: NodeId,
1435        child_id: NodeId,
1436        appended_index: usize,
1437        insert_index: usize,
1438        bubble: DirtyBubble,
1439    },
1440    MoveChild {
1441        parent_id: NodeId,
1442        from_index: usize,
1443        to_index: usize,
1444        bubble: DirtyBubble,
1445    },
1446    RemoveChild {
1447        parent_id: NodeId,
1448        child_id: NodeId,
1449    },
1450    ReconcileChildren {
1451        parent_id: NodeId,
1452        expected_children: Vec<NodeId>,
1453        needs_dirty_check: bool,
1454    },
1455    Callback(CommandCallback),
1456}
1457
1458impl Command {
1459    pub(crate) fn update_node<N: Node + 'static>(id: NodeId) -> Self {
1460        Self::UpdateTypedNode {
1461            id,
1462            updater: update_typed_node::<N>,
1463        }
1464    }
1465
1466    pub(crate) fn callback(
1467        callback: impl FnOnce(&mut dyn Applier) -> Result<(), NodeError> + 'static,
1468    ) -> Self {
1469        Self::Callback(Box::new(callback))
1470    }
1471
1472    pub(crate) fn apply(self, applier: &mut dyn Applier) -> Result<(), NodeError> {
1473        match self {
1474            Self::BubbleDirty { node_id, bubble } => {
1475                bubble.apply(applier, node_id);
1476                Ok(())
1477            }
1478            Self::UpdateTypedNode { id, updater } => {
1479                let node = match applier.get_mut(id) {
1480                    Ok(node) => node,
1481                    Err(NodeError::Missing { .. }) => return Ok(()),
1482                    Err(err) => return Err(err),
1483                };
1484                updater(node, id)
1485            }
1486            Self::RemoveNode { id } => {
1487                if let Ok(node) = applier.get_mut(id) {
1488                    node.unmount();
1489                }
1490                match applier.remove(id) {
1491                    Ok(()) | Err(NodeError::Missing { .. }) => Ok(()),
1492                    Err(err) => Err(err),
1493                }
1494            }
1495            Self::MountNode { id } => {
1496                let node = match applier.get_mut(id) {
1497                    Ok(node) => node,
1498                    Err(NodeError::Missing { .. }) => return Ok(()),
1499                    Err(err) => return Err(err),
1500                };
1501                node.set_node_id(id);
1502                node.mount();
1503                Ok(())
1504            }
1505            Self::AttachChild {
1506                parent_id,
1507                child_id,
1508                bubble,
1509            } => {
1510                insert_child_with_reparenting(applier, parent_id, child_id);
1511                bubble.apply(applier, parent_id);
1512                Ok(())
1513            }
1514            Self::InsertChild {
1515                parent_id,
1516                child_id,
1517                appended_index,
1518                insert_index,
1519                bubble,
1520            } => {
1521                insert_child_with_reparenting(applier, parent_id, child_id);
1522                bubble.apply(applier, parent_id);
1523                if insert_index != appended_index {
1524                    if let Ok(parent_node) = applier.get_mut(parent_id) {
1525                        parent_node.move_child(appended_index, insert_index);
1526                    }
1527                }
1528                Ok(())
1529            }
1530            Self::MoveChild {
1531                parent_id,
1532                from_index,
1533                to_index,
1534                bubble,
1535            } => {
1536                if let Ok(parent_node) = applier.get_mut(parent_id) {
1537                    parent_node.move_child(from_index, to_index);
1538                }
1539                bubble.apply(applier, parent_id);
1540                Ok(())
1541            }
1542            Self::RemoveChild {
1543                parent_id,
1544                child_id,
1545            } => apply_remove_child(applier, parent_id, child_id),
1546            Self::ReconcileChildren {
1547                parent_id,
1548                expected_children,
1549                needs_dirty_check,
1550            } => reconcile_children(applier, parent_id, &expected_children, needs_dirty_check),
1551            Self::Callback(callback) => callback(applier),
1552        }
1553    }
1554}
1555
1556fn update_typed_node<N: Node + 'static>(node: &mut dyn Node, id: NodeId) -> Result<(), NodeError> {
1557    let typed = node
1558        .as_any_mut()
1559        .downcast_mut::<N>()
1560        .ok_or(NodeError::TypeMismatch {
1561            id,
1562            expected: std::any::type_name::<N>(),
1563        })?;
1564    typed.update();
1565    Ok(())
1566}
1567
1568fn insert_child_with_reparenting(applier: &mut dyn Applier, parent_id: NodeId, child_id: NodeId) {
1569    let old_parent = applier
1570        .get_mut(child_id)
1571        .ok()
1572        .and_then(|node| node.parent());
1573    if let Some(old_parent_id) = old_parent {
1574        if old_parent_id != parent_id {
1575            if let Ok(old_parent_node) = applier.get_mut(old_parent_id) {
1576                old_parent_node.remove_child(child_id);
1577            }
1578            if let Ok(child_node) = applier.get_mut(child_id) {
1579                child_node.on_removed_from_parent();
1580            }
1581            bubble_layout_dirty(applier, old_parent_id);
1582            bubble_measure_dirty(applier, old_parent_id);
1583        }
1584    }
1585
1586    if let Ok(parent_node) = applier.get_mut(parent_id) {
1587        parent_node.insert_child(child_id);
1588    }
1589    if let Ok(child_node) = applier.get_mut(child_id) {
1590        child_node.on_attached_to_parent(parent_id);
1591    }
1592}
1593
1594fn apply_remove_child(
1595    applier: &mut dyn Applier,
1596    parent_id: NodeId,
1597    child_id: NodeId,
1598) -> Result<(), NodeError> {
1599    if let Ok(parent_node) = applier.get_mut(parent_id) {
1600        parent_node.remove_child(child_id);
1601    }
1602    bubble_layout_dirty(applier, parent_id);
1603    bubble_measure_dirty(applier, parent_id);
1604
1605    let should_remove = if let Ok(node) = applier.get_mut(child_id) {
1606        match node.parent() {
1607            Some(existing_parent_id) if existing_parent_id == parent_id => {
1608                node.on_removed_from_parent();
1609                node.unmount();
1610                true
1611            }
1612            None => {
1613                node.unmount();
1614                true
1615            }
1616            Some(_) => false,
1617        }
1618    } else {
1619        true
1620    };
1621
1622    if should_remove {
1623        let _ = applier.remove(child_id);
1624    }
1625    Ok(())
1626}
1627
1628fn reconcile_children(
1629    applier: &mut dyn Applier,
1630    parent_id: NodeId,
1631    expected_children: &[NodeId],
1632    needs_dirty_check: bool,
1633) -> Result<(), NodeError> {
1634    let mut repaired = false;
1635    for &child_id in expected_children {
1636        let needs_attach = if let Ok(node) = applier.get_mut(child_id) {
1637            node.parent() != Some(parent_id)
1638        } else {
1639            false
1640        };
1641
1642        if needs_attach {
1643            insert_child_with_reparenting(applier, parent_id, child_id);
1644            repaired = true;
1645        }
1646    }
1647
1648    let is_dirty = if needs_dirty_check {
1649        if let Ok(node) = applier.get_mut(parent_id) {
1650            node.needs_layout()
1651        } else {
1652            false
1653        }
1654    } else {
1655        false
1656    };
1657
1658    if repaired {
1659        bubble_layout_dirty(applier, parent_id);
1660        bubble_measure_dirty(applier, parent_id);
1661    } else if is_dirty {
1662        bubble_layout_dirty(applier, parent_id);
1663    }
1664
1665    Ok(())
1666}
1667
1668#[derive(Default)]
1669pub struct MemoryApplier {
1670    nodes: Vec<Option<Box<dyn Node>>>, // FUTURE(no_std): migrate to arena-backed node storage.
1671    /// Storage for high-ID nodes (like virtual nodes with IDs starting at 0xFFFFFFFF00000000)
1672    /// that can't be stored in the Vec without causing capacity overflow.
1673    high_id_nodes: std::collections::HashMap<NodeId, Box<dyn Node>>,
1674    layout_runtime: Option<RuntimeHandle>,
1675    slots: SlotBackend,
1676}
1677
1678impl MemoryApplier {
1679    pub fn new() -> Self {
1680        Self {
1681            nodes: Vec::new(),
1682            high_id_nodes: std::collections::HashMap::new(),
1683            layout_runtime: None,
1684            slots: SlotBackend::default(),
1685        }
1686    }
1687
1688    pub fn slots(&mut self) -> &mut SlotBackend {
1689        &mut self.slots
1690    }
1691
1692    pub fn with_node<N: Node + 'static, R>(
1693        &mut self,
1694        id: NodeId,
1695        f: impl FnOnce(&mut N) -> R,
1696    ) -> Result<R, NodeError> {
1697        let slot = self
1698            .nodes
1699            .get_mut(id)
1700            .ok_or(NodeError::Missing { id })?
1701            .as_deref_mut()
1702            .ok_or(NodeError::Missing { id })?;
1703        let typed = slot
1704            .as_any_mut()
1705            .downcast_mut::<N>()
1706            .ok_or(NodeError::TypeMismatch {
1707                id,
1708                expected: std::any::type_name::<N>(),
1709            })?;
1710        Ok(f(typed))
1711    }
1712
1713    pub fn len(&self) -> usize {
1714        self.nodes.iter().filter(|n| n.is_some()).count()
1715    }
1716
1717    pub fn is_empty(&self) -> bool {
1718        self.len() == 0
1719    }
1720
1721    pub fn set_runtime_handle(&mut self, handle: RuntimeHandle) {
1722        self.layout_runtime = Some(handle);
1723    }
1724
1725    pub fn clear_runtime_handle(&mut self) {
1726        self.layout_runtime = None;
1727    }
1728
1729    pub fn runtime_handle(&self) -> Option<RuntimeHandle> {
1730        self.layout_runtime.clone()
1731    }
1732
1733    pub fn dump_tree(&self, root: Option<NodeId>) -> String {
1734        let mut output = String::new();
1735        if let Some(root_id) = root {
1736            self.dump_node(&mut output, root_id, 0);
1737        } else {
1738            output.push_str("(no root)\n");
1739        }
1740        output
1741    }
1742
1743    fn dump_node(&self, output: &mut String, id: NodeId, depth: usize) {
1744        let indent = "  ".repeat(depth);
1745        if let Some(Some(node)) = self.nodes.get(id) {
1746            let type_name = std::any::type_name_of_val(&**node);
1747            output.push_str(&format!("{}[{}] {}\n", indent, id, type_name));
1748
1749            let children = node.children();
1750            for child_id in children {
1751                self.dump_node(output, child_id, depth + 1);
1752            }
1753        } else {
1754            output.push_str(&format!("{}[{}] (missing)\n", indent, id));
1755        }
1756    }
1757}
1758
1759impl Applier for MemoryApplier {
1760    fn create(&mut self, node: Box<dyn Node>) -> NodeId {
1761        let id = self.nodes.len();
1762        self.nodes.push(Some(node));
1763        id
1764    }
1765
1766    fn get_mut(&mut self, id: NodeId) -> Result<&mut dyn Node, NodeError> {
1767        // Try Vec first (common case for normal nodes), then HashMap for virtual nodes.
1768        if id < self.nodes.len() {
1769            let slot = self.nodes[id]
1770                .as_deref_mut()
1771                .ok_or(NodeError::Missing { id })?;
1772            return Ok(slot);
1773        }
1774        self.high_id_nodes
1775            .get_mut(&id)
1776            .map(|n| n.as_mut())
1777            .ok_or(NodeError::Missing { id })
1778    }
1779
1780    fn remove(&mut self, id: NodeId) -> Result<(), NodeError> {
1781        // Collect children before removing the node.
1782        let (children, is_high_id) = if id < self.nodes.len() {
1783            let slot = self.nodes.get(id).ok_or(NodeError::Missing { id })?;
1784            let node = slot.as_ref().ok_or(NodeError::Missing { id })?;
1785            (node.children(), false)
1786        } else if let Some(node) = self.high_id_nodes.get(&id) {
1787            (node.children(), true)
1788        } else {
1789            return Err(NodeError::Missing { id });
1790        };
1791
1792        // Recursively remove children owned by this node.
1793        for child_id in children {
1794            let is_owned = self
1795                .get_mut(child_id)
1796                .map(|child| child.parent() == Some(id))
1797                .unwrap_or(false);
1798            if is_owned {
1799                let _ = self.remove(child_id);
1800            }
1801        }
1802
1803        if is_high_id {
1804            self.high_id_nodes.remove(&id);
1805        } else {
1806            self.nodes[id].take();
1807        }
1808        Ok(())
1809    }
1810
1811    fn insert_with_id(&mut self, id: NodeId, node: Box<dyn Node>) -> Result<(), NodeError> {
1812        // Use HashMap for high IDs (virtual nodes) to avoid Vec capacity overflow
1813        // Virtual node IDs start at a very high value that can't fit in a Vec
1814        const HIGH_ID_THRESHOLD: NodeId = 1_000_000_000; // 1 billion
1815
1816        if id >= HIGH_ID_THRESHOLD {
1817            if self.high_id_nodes.contains_key(&id) {
1818                return Err(NodeError::AlreadyExists { id });
1819            }
1820            self.high_id_nodes.insert(id, node);
1821            Ok(())
1822        } else {
1823            // Normal Vec-based insertion for low IDs
1824            if id >= self.nodes.len() {
1825                self.nodes.resize_with(id + 1, || None);
1826            }
1827
1828            if self.nodes[id].is_some() {
1829                return Err(NodeError::AlreadyExists { id });
1830            }
1831
1832            self.nodes[id] = Some(node);
1833            Ok(())
1834        }
1835    }
1836}
1837
1838pub trait ApplierHost {
1839    fn borrow_dyn(&self) -> RefMut<'_, dyn Applier>;
1840}
1841
1842pub struct ConcreteApplierHost<A: Applier + 'static> {
1843    inner: RefCell<A>,
1844}
1845
1846impl<A: Applier + 'static> ConcreteApplierHost<A> {
1847    pub fn new(applier: A) -> Self {
1848        Self {
1849            inner: RefCell::new(applier),
1850        }
1851    }
1852
1853    pub fn borrow_typed(&self) -> RefMut<'_, A> {
1854        self.inner.borrow_mut()
1855    }
1856
1857    pub fn try_borrow_typed(&self) -> Result<RefMut<'_, A>, std::cell::BorrowMutError> {
1858        self.inner.try_borrow_mut()
1859    }
1860
1861    pub fn into_inner(self) -> A {
1862        self.inner.into_inner()
1863    }
1864}
1865
1866impl<A: Applier + 'static> ApplierHost for ConcreteApplierHost<A> {
1867    fn borrow_dyn(&self) -> RefMut<'_, dyn Applier> {
1868        RefMut::map(self.inner.borrow_mut(), |applier| {
1869            applier as &mut dyn Applier
1870        })
1871    }
1872}
1873
1874pub struct ApplierGuard<'a, A: Applier + 'static> {
1875    inner: RefMut<'a, A>,
1876}
1877
1878impl<'a, A: Applier + 'static> ApplierGuard<'a, A> {
1879    fn new(inner: RefMut<'a, A>) -> Self {
1880        Self { inner }
1881    }
1882}
1883
1884impl<'a, A: Applier + 'static> Deref for ApplierGuard<'a, A> {
1885    type Target = A;
1886
1887    fn deref(&self) -> &Self::Target {
1888        &self.inner
1889    }
1890}
1891
1892impl<'a, A: Applier + 'static> DerefMut for ApplierGuard<'a, A> {
1893    fn deref_mut(&mut self) -> &mut Self::Target {
1894        &mut self.inner
1895    }
1896}
1897
1898pub struct SlotsHost {
1899    inner: RefCell<SlotBackend>,
1900}
1901
1902impl SlotsHost {
1903    pub fn new(storage: SlotBackend) -> Self {
1904        Self {
1905            inner: RefCell::new(storage),
1906        }
1907    }
1908
1909    pub fn borrow(&self) -> Ref<'_, SlotBackend> {
1910        self.inner.borrow()
1911    }
1912
1913    pub fn borrow_mut(&self) -> RefMut<'_, SlotBackend> {
1914        self.inner.borrow_mut()
1915    }
1916
1917    pub fn take(&self) -> SlotBackend {
1918        std::mem::take(&mut *self.inner.borrow_mut())
1919    }
1920}
1921
1922pub(crate) struct ComposerCore {
1923    slots: Rc<SlotsHost>,
1924    slots_override: RefCell<Vec<Rc<SlotsHost>>>,
1925    applier: Rc<dyn ApplierHost>,
1926    runtime: RuntimeHandle,
1927    observer: SnapshotStateObserver,
1928    parent_stack: RefCell<Vec<ParentFrame>>,
1929    subcompose_stack: RefCell<Vec<SubcomposeFrame>>,
1930    root: Cell<Option<NodeId>>,
1931    commands: RefCell<Vec<Command>>,
1932    scope_stack: RefCell<Vec<RecomposeScope>>,
1933    local_stack: RefCell<LocalStackSnapshot>,
1934    side_effects: RefCell<Vec<Box<dyn FnOnce()>>>,
1935    pending_scope_options: RefCell<Option<RecomposeOptions>>,
1936    phase: Cell<Phase>,
1937    last_node_reused: Cell<Option<bool>>,
1938    recranpose_parent_hint: Cell<Option<NodeId>>,
1939    _not_send: PhantomData<*const ()>,
1940}
1941
1942impl ComposerCore {
1943    pub fn new(
1944        slots: Rc<SlotsHost>,
1945        applier: Rc<dyn ApplierHost>,
1946        runtime: RuntimeHandle,
1947        observer: SnapshotStateObserver,
1948        root: Option<NodeId>,
1949    ) -> Self {
1950        // Initialize parent_stack with root if provided.
1951        // This enables subcomposed nodes to be properly attached as children of the root
1952        // during composition via normal insert_child commands from pop_parent.
1953        // IMPORTANT: When using this, do NOT use set_active_children - let the composer
1954        // manage children naturally to avoid conflicts.
1955        let parent_stack = if let Some(root_id) = root {
1956            vec![ParentFrame {
1957                id: root_id,
1958                remembered: Owned::new(ParentChildren::default()),
1959                previous: Vec::new(),
1960                new_children: Vec::new(),
1961            }]
1962        } else {
1963            Vec::new()
1964        };
1965
1966        Self {
1967            slots,
1968            slots_override: RefCell::new(Vec::new()),
1969            applier,
1970            runtime,
1971            observer,
1972            parent_stack: RefCell::new(parent_stack),
1973            subcompose_stack: RefCell::new(Vec::new()),
1974            root: Cell::new(root),
1975            commands: RefCell::new(Vec::new()),
1976            scope_stack: RefCell::new(Vec::new()),
1977            local_stack: RefCell::new(Rc::new(Vec::new())),
1978            side_effects: RefCell::new(Vec::new()),
1979            pending_scope_options: RefCell::new(None),
1980            phase: Cell::new(Phase::Compose),
1981            last_node_reused: Cell::new(None),
1982            recranpose_parent_hint: Cell::new(None),
1983            _not_send: PhantomData,
1984        }
1985    }
1986}
1987
1988#[derive(Clone)]
1989pub struct Composer {
1990    core: Rc<ComposerCore>,
1991}
1992
1993impl Composer {
1994    pub fn new(
1995        slots: Rc<SlotsHost>,
1996        applier: Rc<dyn ApplierHost>,
1997        runtime: RuntimeHandle,
1998        observer: SnapshotStateObserver,
1999        root: Option<NodeId>,
2000    ) -> Self {
2001        let core = Rc::new(ComposerCore::new(slots, applier, runtime, observer, root));
2002        Self { core }
2003    }
2004
2005    pub(crate) fn from_core(core: Rc<ComposerCore>) -> Self {
2006        Self { core }
2007    }
2008
2009    pub(crate) fn clone_core(&self) -> Rc<ComposerCore> {
2010        Rc::clone(&self.core)
2011    }
2012
2013    fn observer(&self) -> SnapshotStateObserver {
2014        self.core.observer.clone()
2015    }
2016
2017    fn observe_scope<R>(&self, scope: &RecomposeScope, block: impl FnOnce() -> R) -> R {
2018        let observer = self.observer();
2019        let scope_clone = scope.clone();
2020        observer.observe_reads(scope_clone, move |scope_ref| scope_ref.invalidate(), block)
2021    }
2022
2023    fn active_slots_host(&self) -> Rc<SlotsHost> {
2024        self.core
2025            .slots_override
2026            .borrow()
2027            .last()
2028            .cloned()
2029            .unwrap_or_else(|| Rc::clone(&self.core.slots))
2030    }
2031
2032    fn with_slots<R>(&self, f: impl FnOnce(&SlotBackend) -> R) -> R {
2033        let host = self.active_slots_host();
2034        let slots = host.borrow();
2035        f(&slots)
2036    }
2037
2038    fn with_slots_mut<R>(&self, f: impl FnOnce(&mut SlotBackend) -> R) -> R {
2039        let host = self.active_slots_host();
2040        let mut slots = host.borrow_mut();
2041        f(&mut slots)
2042    }
2043
2044    fn with_slot_override<R>(&self, slots: Rc<SlotsHost>, f: impl FnOnce(&Composer) -> R) -> R {
2045        self.core.slots_override.borrow_mut().push(slots);
2046        struct Guard {
2047            core: Rc<ComposerCore>,
2048        }
2049        impl Drop for Guard {
2050            fn drop(&mut self) {
2051                self.core.slots_override.borrow_mut().pop();
2052            }
2053        }
2054        let guard = Guard {
2055            core: self.clone_core(),
2056        };
2057        let result = f(self);
2058        drop(guard);
2059        result
2060    }
2061
2062    fn parent_stack(&self) -> RefMut<'_, Vec<ParentFrame>> {
2063        self.core.parent_stack.borrow_mut()
2064    }
2065
2066    fn subcompose_stack(&self) -> RefMut<'_, Vec<SubcomposeFrame>> {
2067        self.core.subcompose_stack.borrow_mut()
2068    }
2069
2070    fn commands_mut(&self) -> RefMut<'_, Vec<Command>> {
2071        self.core.commands.borrow_mut()
2072    }
2073
2074    pub(crate) fn enqueue_semantics_invalidation(&self, id: NodeId) {
2075        self.commands_mut().push(Command::BubbleDirty {
2076            node_id: id,
2077            bubble: DirtyBubble::SEMANTICS,
2078        });
2079    }
2080
2081    fn scope_stack(&self) -> RefMut<'_, Vec<RecomposeScope>> {
2082        self.core.scope_stack.borrow_mut()
2083    }
2084
2085    fn local_stack(&self) -> RefMut<'_, LocalStackSnapshot> {
2086        self.core.local_stack.borrow_mut()
2087    }
2088
2089    fn current_local_stack(&self) -> LocalStackSnapshot {
2090        self.core.local_stack.borrow().clone()
2091    }
2092
2093    fn side_effects_mut(&self) -> RefMut<'_, Vec<Box<dyn FnOnce()>>> {
2094        self.core.side_effects.borrow_mut()
2095    }
2096
2097    fn pending_scope_options(&self) -> RefMut<'_, Option<RecomposeOptions>> {
2098        self.core.pending_scope_options.borrow_mut()
2099    }
2100
2101    fn borrow_applier(&self) -> RefMut<'_, dyn Applier> {
2102        self.core.applier.borrow_dyn()
2103    }
2104
2105    /// Registers a virtual node in the Applier.
2106    ///
2107    /// This is used by SubcomposeLayoutNode to register virtual container nodes
2108    /// so that subsequent insert_child commands can find them and attach children.
2109    /// Without this, virtual nodes would only exist in SubcomposeLayoutNodeInner.virtual_nodes
2110    /// and applier.get_mut(virtual_node_id) would fail, breaking child attachment.
2111    pub fn register_virtual_node(
2112        &self,
2113        node_id: NodeId,
2114        node: Box<dyn Node>,
2115    ) -> Result<(), NodeError> {
2116        let mut applier = self.borrow_applier();
2117        applier.insert_with_id(node_id, node)
2118    }
2119
2120    /// Checks if a node has no parent (is a root node).
2121    /// Used by SubcomposeMeasureScope to filter subcompose results.
2122    pub fn node_has_no_parent(&self, node_id: NodeId) -> bool {
2123        let mut applier = self.borrow_applier();
2124        match applier.get_mut(node_id) {
2125            Ok(node) => node.parent().is_none(),
2126            Err(_) => true, // If we can't find the node, treat it as root (conservative)
2127        }
2128    }
2129
2130    /// Gets the children of a node from the Applier.
2131    ///
2132    /// This is used by SubcomposeLayoutNode to get children of virtual nodes
2133    /// directly from the Applier, where insert_child commands have been applied.
2134    pub fn get_node_children(&self, node_id: NodeId) -> Vec<NodeId> {
2135        let mut applier = self.borrow_applier();
2136        match applier.get_mut(node_id) {
2137            Ok(node) => node.children(),
2138            Err(_) => Vec::new(),
2139        }
2140    }
2141
2142    /// Clears all children of a node in the Applier.
2143    ///
2144    /// This is used by SubcomposeLayoutNode when reusing a virtual node for
2145    /// different content. Without clearing, old children remain attached,
2146    /// causing duplicate/interleaved items in lazy lists after scrolling.
2147    pub fn clear_node_children(&self, node_id: NodeId) {
2148        let mut applier = self.borrow_applier();
2149        if let Ok(node) = applier.get_mut(node_id) {
2150            // Use update_children with empty slice to clear all children
2151            node.update_children(&[]);
2152        }
2153    }
2154
2155    pub fn install<R>(&self, f: impl FnOnce(&Composer) -> R) -> R {
2156        let _composer_guard = composer_context::enter(self);
2157        runtime::push_active_runtime(&self.core.runtime);
2158        struct Guard;
2159        impl Drop for Guard {
2160            fn drop(&mut self) {
2161                runtime::pop_active_runtime();
2162            }
2163        }
2164        let guard = Guard;
2165        let result = f(self);
2166        drop(guard);
2167        result
2168    }
2169
2170    pub fn with_group<R>(&self, key: Key, f: impl FnOnce(&Composer) -> R) -> R {
2171        let (group, scope_ref, restored_from_gap) = self.with_slots_mut(|slots| {
2172            let StartGroup {
2173                group,
2174                restored_from_gap,
2175            } = slots.begin_group(key);
2176            let scope_ref = slots
2177                .remember(|| RecomposeScope::new(self.runtime_handle()))
2178                .with(|scope| scope.clone());
2179            (group, scope_ref, restored_from_gap)
2180        });
2181
2182        if restored_from_gap {
2183            scope_ref.force_recompose();
2184        }
2185
2186        if let Some(options) = self.pending_scope_options().take() {
2187            if options.force_recompose {
2188                scope_ref.force_recompose();
2189            } else if options.force_reuse {
2190                scope_ref.force_reuse();
2191            }
2192        }
2193
2194        self.with_slots_mut(|slots| {
2195            SlotStorage::set_group_scope(slots, group, scope_ref.id());
2196        });
2197
2198        let slots_host = self.active_slots_host();
2199        scope_ref.set_slots_host(Rc::downgrade(&slots_host));
2200
2201        {
2202            let mut stack = self.scope_stack();
2203            stack.push(scope_ref.clone());
2204        }
2205
2206        {
2207            let mut stack = self.subcompose_stack();
2208            if let Some(frame) = stack.last_mut() {
2209                frame.scopes.push(scope_ref.clone());
2210            }
2211        }
2212
2213        scope_ref.snapshot_locals(self.current_local_stack());
2214        {
2215            let parent_hint = self.parent_stack().last().map(|frame| frame.id);
2216            scope_ref.set_parent_hint(parent_hint);
2217        }
2218
2219        let result = self.observe_scope(&scope_ref, || f(self));
2220
2221        let trimmed = self.with_slots_mut(|slots| slots.finalize_current_group());
2222        if trimmed {
2223            scope_ref.force_recompose();
2224        }
2225
2226        {
2227            let mut stack = self.scope_stack();
2228            stack.pop();
2229        }
2230        scope_ref.mark_recomposed();
2231        self.with_slots_mut(|slots| slots.end_group());
2232        result
2233    }
2234
2235    pub fn cranpose_with_reuse<R>(
2236        &self,
2237        key: Key,
2238        options: RecomposeOptions,
2239        f: impl FnOnce(&Composer) -> R,
2240    ) -> R {
2241        self.pending_scope_options().replace(options);
2242        self.with_group(key, f)
2243    }
2244
2245    pub fn with_key<K: Hash, R>(&self, key: &K, f: impl FnOnce(&Composer) -> R) -> R {
2246        let hashed = hash_key(key);
2247        self.with_group(hashed, f)
2248    }
2249
2250    pub fn remember<T: 'static>(&self, init: impl FnOnce() -> T) -> Owned<T> {
2251        self.with_slots_mut(|slots| slots.remember(init))
2252    }
2253
2254    pub fn use_value_slot<T: 'static>(&self, init: impl FnOnce() -> T) -> usize {
2255        self.with_slots_mut(|slots| slots.alloc_value_slot(init).index())
2256    }
2257
2258    pub fn with_slot_value<T: 'static, R>(&self, idx: usize, f: impl FnOnce(&T) -> R) -> R {
2259        self.with_slots(|slots| {
2260            let value = SlotStorage::read_value(slots, ValueSlotId::new(idx));
2261            f(value)
2262        })
2263    }
2264
2265    pub fn with_slot_value_mut<T: 'static, R>(&self, idx: usize, f: impl FnOnce(&mut T) -> R) -> R {
2266        self.with_slots_mut(|slots| {
2267            let value = SlotStorage::read_value_mut(slots, ValueSlotId::new(idx));
2268            f(value)
2269        })
2270    }
2271
2272    pub fn write_slot_value<T: 'static>(&self, idx: usize, value: T) {
2273        self.with_slots_mut(|slots| slots.write_value(ValueSlotId::new(idx), value));
2274    }
2275
2276    pub fn mutable_state_of<T: Clone + 'static>(&self, initial: T) -> MutableState<T> {
2277        MutableState::with_runtime(initial, self.runtime_handle())
2278    }
2279
2280    pub fn mutable_state_list_of<T, I>(&self, values: I) -> SnapshotStateList<T>
2281    where
2282        T: Clone + 'static,
2283        I: IntoIterator<Item = T>,
2284    {
2285        SnapshotStateList::with_runtime(values, self.runtime_handle())
2286    }
2287
2288    pub fn mutable_state_map_of<K, V, I>(&self, pairs: I) -> SnapshotStateMap<K, V>
2289    where
2290        K: Clone + Eq + Hash + 'static,
2291        V: Clone + 'static,
2292        I: IntoIterator<Item = (K, V)>,
2293    {
2294        SnapshotStateMap::with_runtime(pairs, self.runtime_handle())
2295    }
2296
2297    pub fn read_composition_local<T: Clone + 'static>(&self, local: &CompositionLocal<T>) -> T {
2298        let stack = self.core.local_stack.borrow();
2299        for context in stack.iter().rev() {
2300            if let Some(entry) = context.values.get(&local.key) {
2301                let typed = entry
2302                    .clone()
2303                    .downcast::<LocalStateEntry<T>>()
2304                    .expect("composition local type mismatch");
2305                return typed.value();
2306            }
2307        }
2308        local.default_value()
2309    }
2310
2311    pub fn read_static_composition_local<T: Clone + 'static>(
2312        &self,
2313        local: &StaticCompositionLocal<T>,
2314    ) -> T {
2315        let stack = self.core.local_stack.borrow();
2316        for context in stack.iter().rev() {
2317            if let Some(entry) = context.values.get(&local.key) {
2318                let typed = entry
2319                    .clone()
2320                    .downcast::<StaticLocalEntry<T>>()
2321                    .expect("static composition local type mismatch");
2322                return typed.value();
2323            }
2324        }
2325        local.default_value()
2326    }
2327
2328    pub fn current_recranpose_scope(&self) -> Option<RecomposeScope> {
2329        self.core.scope_stack.borrow().last().cloned()
2330    }
2331
2332    pub fn phase(&self) -> Phase {
2333        self.core.phase.get()
2334    }
2335
2336    pub(crate) fn set_phase(&self, phase: Phase) {
2337        self.core.phase.set(phase);
2338    }
2339
2340    pub fn enter_phase(&self, phase: Phase) {
2341        self.set_phase(phase);
2342    }
2343
2344    pub(crate) fn subcompose<R>(
2345        &self,
2346        state: &mut SubcomposeState,
2347        slot_id: SlotId,
2348        content: impl FnOnce(&Composer) -> R,
2349    ) -> (R, Vec<NodeId>) {
2350        match self.phase() {
2351            Phase::Measure | Phase::Layout => {}
2352            current => panic!(
2353                "subcompose() may only be called during measure or layout; current phase: {:?}",
2354                current
2355            ),
2356        }
2357
2358        self.subcompose_stack().push(SubcomposeFrame::default());
2359        struct StackGuard {
2360            core: Rc<ComposerCore>,
2361            leaked: bool,
2362        }
2363        impl Drop for StackGuard {
2364            fn drop(&mut self) {
2365                if !self.leaked {
2366                    self.core.subcompose_stack.borrow_mut().pop();
2367                }
2368            }
2369        }
2370        let mut guard = StackGuard {
2371            core: self.clone_core(),
2372            leaked: false,
2373        };
2374
2375        let slot_host = state.get_or_create_slots(slot_id);
2376        {
2377            let mut slots = slot_host.borrow_mut();
2378            slots.reset();
2379        }
2380        let result = self.with_slot_override(slot_host.clone(), |composer| {
2381            // Use with_group to create/reuse a group for this slot_id within the slot table.
2382            composer.with_group(slot_id.raw(), |composer| content(composer))
2383        });
2384        {
2385            let mut slots = slot_host.borrow_mut();
2386            slots.finalize_current_group();
2387            slots.flush();
2388        }
2389
2390        let frame = {
2391            let mut stack = guard.core.subcompose_stack.borrow_mut();
2392            let frame = stack.pop().expect("subcompose stack underflow");
2393            guard.leaked = true;
2394            frame
2395        };
2396        let nodes = frame.nodes;
2397        let scopes = frame.scopes;
2398        state.register_active(slot_id, &nodes, &scopes);
2399        (result, nodes)
2400    }
2401
2402    pub fn subcompose_measurement<R>(
2403        &self,
2404        state: &mut SubcomposeState,
2405        slot_id: SlotId,
2406        content: impl FnOnce(&Composer) -> R,
2407    ) -> (R, Vec<NodeId>) {
2408        let (result, nodes) = self.subcompose(state, slot_id, content);
2409
2410        // Filter to include only root nodes (those without a parent).
2411        // While record_node attempts to track only roots, checking the final
2412        // parent status ensures we only return true roots to the layout system.
2413        let roots = nodes
2414            .into_iter()
2415            .filter(|&id| self.node_has_no_parent(id))
2416            .collect();
2417
2418        (result, roots)
2419    }
2420
2421    pub fn subcompose_in<R>(
2422        &self,
2423        slots: &Rc<SlotsHost>,
2424        root: Option<NodeId>,
2425        f: impl FnOnce(&Composer) -> R,
2426    ) -> Result<R, NodeError> {
2427        let runtime_handle = self.runtime_handle();
2428        slots.borrow_mut().reset();
2429        let phase = self.phase();
2430        let locals = self.current_local_stack();
2431        let core = Rc::new(ComposerCore::new(
2432            Rc::clone(slots),
2433            Rc::clone(&self.core.applier),
2434            runtime_handle.clone(),
2435            self.observer(),
2436            root,
2437        ));
2438        core.phase.set(phase);
2439        *core.local_stack.borrow_mut() = locals;
2440        let composer = Composer::from_core(core);
2441        let (result, mut commands, side_effects) = composer.install(|composer| {
2442            let output = f(composer);
2443            let commands = composer.take_commands();
2444            let side_effects = composer.take_side_effects();
2445            (output, commands, side_effects)
2446        });
2447
2448        {
2449            let mut applier = self.borrow_applier();
2450            for command in commands.drain(..) {
2451                command.apply(&mut *applier)?;
2452            }
2453            for update in runtime_handle.take_updates() {
2454                update.apply(&mut *applier)?;
2455            }
2456        }
2457        runtime_handle.drain_ui();
2458        for effect in side_effects {
2459            effect();
2460        }
2461        runtime_handle.drain_ui();
2462        {
2463            let mut slots_mut = slots.borrow_mut();
2464            slots_mut.finalize_current_group();
2465            slots_mut.flush();
2466        }
2467        Ok(result)
2468    }
2469
2470    /// Subcomposes content using an isolated SlotsHost without resetting it.
2471    /// Unlike `subcompose_in`, this preserves existing slot state across calls,
2472    /// allowing efficient reuse during measurement passes. This is critical for
2473    /// lazy lists where items need stable slot positions.
2474    pub fn subcompose_slot<R>(
2475        &self,
2476        slots: &Rc<SlotsHost>,
2477        root: Option<NodeId>,
2478        f: impl FnOnce(&Composer) -> R,
2479    ) -> Result<R, NodeError> {
2480        let runtime_handle = self.runtime_handle();
2481        // Reset cursor to 0 but preserve slot data for reuse (like JC's setContentWithReuse)
2482        // This allows remembered values to be found and reused
2483        slots.borrow_mut().reset();
2484        let phase = self.phase();
2485        let locals = self.current_local_stack();
2486        let core = Rc::new(ComposerCore::new(
2487            Rc::clone(slots),
2488            Rc::clone(&self.core.applier),
2489            runtime_handle.clone(),
2490            self.observer(),
2491            root, // Root node for parent chain - enables dirty flag bubbling
2492        ));
2493        core.phase.set(phase);
2494        *core.local_stack.borrow_mut() = locals;
2495        let composer = Composer::from_core(core);
2496        let (result, mut commands, side_effects) = composer.install(|composer| {
2497            let output = f(composer);
2498            // CRITICAL FIX: Pop the root parent frame to generate insert_child commands.
2499            // Without this, the root frame's new_children list is populated but never
2500            // processed, so children are never attached to the virtual node.
2501            if root.is_some() {
2502                composer.pop_parent();
2503            }
2504            let commands = composer.take_commands();
2505            let side_effects = composer.take_side_effects();
2506            (output, commands, side_effects)
2507        });
2508
2509        {
2510            let mut applier = self.borrow_applier();
2511            for command in commands.drain(..) {
2512                command.apply(&mut *applier)?;
2513            }
2514            for update in runtime_handle.take_updates() {
2515                update.apply(&mut *applier)?;
2516            }
2517        }
2518        runtime_handle.drain_ui();
2519        for effect in side_effects {
2520            effect();
2521        }
2522        runtime_handle.drain_ui();
2523        // DON'T finalize or flush - for subcompose reuse, we need to keep all groups
2524        // in place so they can be found via O(1) HashMap lookup on the next measurement
2525        // pass. Calling finalize_current_group would convert valid lazy list item
2526        // groups to gaps if the cursor didn't reach them.
2527        Ok(result)
2528    }
2529
2530    pub fn skip_current_group(&self) {
2531        let nodes = self.with_slots(|slots| slots.nodes_in_current_group());
2532        self.with_slots_mut(|slots| slots.skip_current_group());
2533        // Get the current parent from the stack (if any)
2534        let current_parent = {
2535            let stack = self.parent_stack();
2536            stack.last().map(|frame| frame.id)
2537        };
2538
2539        // Only attach nodes whose parent matches the current parent in the stack.
2540        // This ensures we only attach direct children of the current parent,
2541        // not nested nodes that belong to other nodes within the skipped group.
2542        let mut applier = self.borrow_applier();
2543        for id in nodes {
2544            if let Ok(node) = applier.get_mut(id) {
2545                let node_parent = node.parent();
2546                if node_parent.is_none() || node_parent == current_parent {
2547                    drop(applier);
2548                    self.attach_to_parent(id);
2549                    applier = self.borrow_applier();
2550                }
2551            }
2552        }
2553    }
2554
2555    pub fn runtime_handle(&self) -> RuntimeHandle {
2556        self.core.runtime.clone()
2557    }
2558
2559    pub fn set_recranpose_callback<F>(&self, callback: F)
2560    where
2561        F: FnMut(&Composer) + 'static,
2562    {
2563        if let Some(scope) = self.current_recranpose_scope() {
2564            let observer = self.observer();
2565            let scope_weak = scope.downgrade();
2566            let mut callback = callback;
2567            scope.set_recompose(Box::new(move |composer: &Composer| {
2568                if let Some(inner) = scope_weak.upgrade() {
2569                    let scope_instance = RecomposeScope { inner };
2570                    observer.observe_reads(
2571                        scope_instance.clone(),
2572                        move |scope_ref| scope_ref.invalidate(),
2573                        || {
2574                            callback(composer);
2575                        },
2576                    );
2577                }
2578            }));
2579        }
2580    }
2581
2582    pub fn with_composition_locals<R>(
2583        &self,
2584        provided: Vec<ProvidedValue>,
2585        f: impl FnOnce(&Composer) -> R,
2586    ) -> R {
2587        if provided.is_empty() {
2588            return f(self);
2589        }
2590        let mut context = LocalContext::default();
2591        for value in provided {
2592            let (key, entry) = value.into_entry(self);
2593            context.values.insert(key, entry);
2594        }
2595        {
2596            let mut stack = self.local_stack();
2597            Rc::make_mut(&mut *stack).push(context);
2598        }
2599        let result = f(self);
2600        {
2601            let mut stack = self.local_stack();
2602            Rc::make_mut(&mut *stack).pop();
2603        }
2604        result
2605    }
2606
2607    fn recranpose_group(&self, scope: &RecomposeScope) {
2608        // CRITICAL FIX: Check if scope is still invalid before recomposing.
2609        // When parent and child scopes are both invalidated, the child may be
2610        // visited (and marked recomposed) during parent's recomposition.
2611        // Without this check, we'd recompose the child again with wrong parent_stack,
2612        // causing nodes to get attached to root instead of their actual parent.
2613        if !scope.is_invalid() {
2614            scope.mark_recomposed();
2615            return;
2616        }
2617        let started = self.with_slots_mut(|slots| slots.begin_recranpose_at_scope(scope.id()));
2618        if started.is_some() {
2619            let previous_hint = self
2620                .core
2621                .recranpose_parent_hint
2622                .replace(scope.parent_hint());
2623            struct HintGuard {
2624                core: Rc<ComposerCore>,
2625                previous: Option<NodeId>,
2626            }
2627            impl Drop for HintGuard {
2628                fn drop(&mut self) {
2629                    self.core.recranpose_parent_hint.set(self.previous);
2630                }
2631            }
2632            let _hint_guard = HintGuard {
2633                core: self.clone_core(),
2634                previous: previous_hint,
2635            };
2636            {
2637                let mut stack = self.scope_stack();
2638                stack.push(scope.clone());
2639            }
2640            let saved_locals = self.current_local_stack();
2641            {
2642                let mut locals = self.local_stack();
2643                *locals = scope.local_stack();
2644            }
2645            self.observe_scope(scope, || {
2646                scope.run_recompose(self);
2647            });
2648            {
2649                let mut locals = self.local_stack();
2650                *locals = saved_locals;
2651            }
2652            {
2653                let mut stack = self.scope_stack();
2654                stack.pop();
2655            }
2656            self.with_slots_mut(SlotStorage::end_recompose);
2657            scope.mark_recomposed();
2658        } else {
2659            scope.mark_recomposed();
2660        }
2661    }
2662
2663    pub fn use_state<T: Clone + 'static>(&self, init: impl FnOnce() -> T) -> MutableState<T> {
2664        let runtime = self.runtime_handle();
2665        let state = self.with_slots_mut(|slots| {
2666            slots.remember(|| OwnedMutableState::with_runtime(init(), runtime.clone()))
2667        });
2668        state.with(|state| state.handle())
2669    }
2670
2671    pub fn emit_node<N: Node + 'static>(&self, init: impl FnOnce() -> N) -> NodeId {
2672        // Peek at the slot without advancing cursor
2673        let (existing_id, type_matches) = {
2674            if let Some(id) = self.with_slots_mut(|slots| slots.peek_node()) {
2675                // Check if the node type matches
2676                let mut applier = self.borrow_applier();
2677                let matches = match applier.get_mut(id) {
2678                    Ok(node) => node.as_any_mut().downcast_ref::<N>().is_some(),
2679                    Err(_) => false,
2680                };
2681                (Some(id), matches)
2682            } else {
2683                (None, false)
2684            }
2685        };
2686
2687        // If we have a matching node, advance cursor and reuse it
2688        if let Some(id) = existing_id {
2689            if type_matches {
2690                // Type matches - reuse this node. The push_parent conditional ensures
2691                // that new parents start with empty previous children, so we don't
2692                // accidentally inherit children from a different parent.
2693                let reuse_allowed = true;
2694
2695                #[cfg(not(target_arch = "wasm32"))]
2696                if compose_debug_enabled() {
2697                    eprintln!("emit_node: candidate #{id} reuse_allowed={reuse_allowed}");
2698                }
2699
2700                if reuse_allowed {
2701                    self.core.last_node_reused.set(Some(true));
2702                    #[cfg(not(target_arch = "wasm32"))]
2703                    if compose_debug_enabled() {
2704                        eprintln!(
2705                            "emit_node: reusing node #{id} as {}",
2706                            std::any::type_name::<N>()
2707                        );
2708                    }
2709                    self.with_slots_mut(|slots| slots.advance_after_node_read());
2710
2711                    self.commands_mut().push(Command::update_node::<N>(id));
2712                    self.attach_to_parent(id);
2713                    return id;
2714                }
2715            }
2716        }
2717
2718        // If there was a mismatched node in this slot, schedule its removal before creating a new one.
2719        if let Some(old_id) = existing_id {
2720            if !type_matches {
2721                #[cfg(not(target_arch = "wasm32"))]
2722                if compose_debug_enabled() {
2723                    eprintln!(
2724                        "emit_node: replacing node #{old_id} with new {}",
2725                        std::any::type_name::<N>()
2726                    );
2727                }
2728                self.commands_mut().push(Command::RemoveNode { id: old_id });
2729            }
2730        }
2731
2732        // Type mismatch or no node: create new node
2733        // record_node() will handle replacing the mismatched slot
2734        let id = {
2735            let mut applier = self.borrow_applier();
2736            applier.create(Box::new(init()))
2737        };
2738        self.core.last_node_reused.set(Some(false));
2739        #[cfg(not(target_arch = "wasm32"))]
2740        if compose_debug_enabled() {
2741            eprintln!(
2742                "emit_node: creating node #{} as {}",
2743                id,
2744                std::any::type_name::<N>()
2745            );
2746        }
2747        {
2748            self.with_slots_mut(|slots| slots.record_node(id));
2749        }
2750        self.commands_mut().push(Command::MountNode { id });
2751        self.attach_to_parent(id);
2752        id
2753    }
2754
2755    fn attach_to_parent(&self, id: NodeId) {
2756        // IMPORTANT: Check parent_stack FIRST.
2757        // During subcomposition, if there's an active parent (e.g., Row),
2758        // child nodes (e.g., Text) should attach to that parent, NOT to the
2759        // subcompose frame. Only ROOT nodes (nodes with no active parent)
2760        // should be added to the subcompose frame.
2761        let mut parent_stack = self.parent_stack();
2762        if let Some(frame) = parent_stack.last_mut() {
2763            let parent_id = frame.id;
2764            if parent_id == id {
2765                return;
2766            }
2767            frame.new_children.push(id);
2768            drop(parent_stack);
2769
2770            // KEY FIX: Set parent link IMMEDIATELY, matching Jetpack Compose's
2771            // LayoutNode.insertAt pattern where _foldedParent is set synchronously.
2772            // This ensures that when bubble_measure_dirty runs (in commands),
2773            // the parent chain is already established.
2774            //
2775            // IMPORTANT: Only set parent if node doesn't have one or if the new parent
2776            // is not the root. This prevents double-recomposition scenarios where a
2777            // child scope (invalidated by CompositionLocalProvider during parent's
2778            // recomposition) gets processed again with parent_stack=[root], which would
2779            // incorrectly reparent nodes to root.
2780            {
2781                let mut applier = self.borrow_applier();
2782                if let Ok(child_node) = applier.get_mut(id) {
2783                    let existing_parent = child_node.parent();
2784                    // Only set parent if:
2785                    // 1. Node has no parent, OR
2786                    // 2. New parent is NOT the root (parent_id != 0 or != self.root)
2787                    // This prevents root from stealing children that belong to intermediate nodes.
2788                    let should_set = match existing_parent {
2789                        None => true,
2790                        Some(existing) => {
2791                            // Don't let root steal children from proper parents
2792                            let root_id = self.core.root.get();
2793                            parent_id != root_id.unwrap_or(0) || existing == root_id.unwrap_or(0)
2794                        }
2795                    };
2796                    if should_set {
2797                        child_node.set_parent_for_bubbling(parent_id);
2798                    }
2799                }
2800            }
2801            return;
2802        }
2803        drop(parent_stack);
2804
2805        // No active parent - check if we're in subcompose
2806        let in_subcompose = !self.subcompose_stack().is_empty();
2807        if in_subcompose {
2808            // During subcompose, only add ROOT nodes (nodes without a parent).
2809            // Child nodes already have their parent-child relationship from composition;
2810            // re-adding them to the subcompose frame would cause duplication.
2811            let has_parent = {
2812                let mut applier = self.borrow_applier();
2813                applier
2814                    .get_mut(id)
2815                    .map(|node| node.parent().is_some())
2816                    .unwrap_or(false)
2817            };
2818
2819            if !has_parent {
2820                let mut subcompose_stack = self.subcompose_stack();
2821                if let Some(frame) = subcompose_stack.last_mut() {
2822                    frame.nodes.push(id);
2823                }
2824            }
2825            return;
2826        }
2827
2828        // During recomposition, preserve the original parent when possible.
2829        if let Some(parent_hint) = self.core.recranpose_parent_hint.get() {
2830            let parent_status = {
2831                let mut applier = self.borrow_applier();
2832                applier
2833                    .get_mut(id)
2834                    .map(|node| node.parent())
2835                    .unwrap_or(None)
2836            };
2837            match parent_status {
2838                Some(existing) if existing == parent_hint => {}
2839                None => {
2840                    self.commands_mut().push(Command::AttachChild {
2841                        parent_id: parent_hint,
2842                        child_id: id,
2843                        bubble: DirtyBubble::LAYOUT_AND_MEASURE,
2844                    });
2845                }
2846                Some(_) => {}
2847            }
2848            return;
2849        }
2850
2851        // Neither parent nor subcompose - check if this node already has a parent.
2852        // During recomposition, reused nodes already have their correct parent from
2853        // initial composition. We should NOT set them as root, as that would corrupt
2854        // the tree structure and cause duplication.
2855        let has_parent = {
2856            let mut applier = self.borrow_applier();
2857            applier
2858                .get_mut(id)
2859                .map(|node| node.parent().is_some())
2860                .unwrap_or(false)
2861        };
2862        if has_parent {
2863            // Node already has a parent, nothing to do
2864            return;
2865        }
2866
2867        // Node has no parent and is not in subcompose - must be root
2868        self.set_root(Some(id));
2869    }
2870
2871    pub fn with_node_mut<N: Node + 'static, R>(
2872        &self,
2873        id: NodeId,
2874        f: impl FnOnce(&mut N) -> R,
2875    ) -> Result<R, NodeError> {
2876        let mut applier = self.borrow_applier();
2877        let node = applier.get_mut(id)?;
2878        let typed = node
2879            .as_any_mut()
2880            .downcast_mut::<N>()
2881            .ok_or(NodeError::TypeMismatch {
2882                id,
2883                expected: std::any::type_name::<N>(),
2884            })?;
2885        Ok(f(typed))
2886    }
2887
2888    pub fn push_parent(&self, id: NodeId) {
2889        let remembered = self.remember(ParentChildren::default);
2890        let reused = self.core.last_node_reused.take().unwrap_or(true);
2891        let in_subcompose = !self.core.subcompose_stack.borrow().is_empty();
2892
2893        // Only carry over previous children when the parent was reused (or in subcompose).
2894        // Otherwise, start fresh to prevent nodes from teleporting between parents.
2895        let previous = if reused || in_subcompose {
2896            remembered.with(|entry| entry.children.clone())
2897        } else {
2898            Vec::new()
2899        };
2900
2901        self.parent_stack().push(ParentFrame {
2902            id,
2903            remembered,
2904            previous,
2905            new_children: Vec::new(),
2906        });
2907    }
2908
2909    pub fn pop_parent(&self) {
2910        let frame_opt = {
2911            let mut stack = self.parent_stack();
2912            stack.pop()
2913        };
2914        if let Some(frame) = frame_opt {
2915            let ParentFrame {
2916                id,
2917                remembered,
2918                previous,
2919                new_children,
2920            } = frame;
2921
2922            #[cfg(not(target_arch = "wasm32"))]
2923            if compose_debug_enabled() {
2924                eprintln!("pop_parent: node #{}", id);
2925                eprintln!("  previous children: {:?}", previous);
2926                eprintln!("  new children: {:?}", new_children);
2927            }
2928            let children_changed = previous != new_children;
2929
2930            if children_changed {
2931                let target = &new_children;
2932                let mut target_positions = HashMap::default();
2933                target_positions.reserve(target.len());
2934                for (index, &child) in target.iter().enumerate() {
2935                    target_positions.insert(child, index);
2936                }
2937
2938                let mut current = previous;
2939
2940                for index in (0..current.len()).rev() {
2941                    let child = current[index];
2942                    if !target_positions.contains_key(&child) {
2943                        current.remove(index);
2944                        self.commands_mut().push(Command::RemoveChild {
2945                            parent_id: id,
2946                            child_id: child,
2947                        });
2948                    }
2949                }
2950
2951                let mut current_positions = build_child_positions(&current);
2952                for (target_index, &child) in target.iter().enumerate() {
2953                    if let Some(current_index) = current_positions.get(&child).copied() {
2954                        if current_index != target_index {
2955                            let from_index = current_index;
2956                            let to_index = move_child_in_diff_state(
2957                                &mut current,
2958                                &mut current_positions,
2959                                from_index,
2960                                target_index,
2961                            );
2962                            self.commands_mut().push(Command::MoveChild {
2963                                parent_id: id,
2964                                from_index,
2965                                to_index,
2966                                bubble: DirtyBubble::LAYOUT_AND_MEASURE,
2967                            });
2968                        }
2969                    } else {
2970                        let insert_index = target_index.min(current.len());
2971                        let appended_index = current.len();
2972                        insert_child_into_diff_state(
2973                            &mut current,
2974                            &mut current_positions,
2975                            insert_index,
2976                            child,
2977                        );
2978                        self.commands_mut().push(Command::InsertChild {
2979                            parent_id: id,
2980                            child_id: child,
2981                            appended_index,
2982                            insert_index,
2983                            bubble: DirtyBubble::LAYOUT_AND_MEASURE,
2984                        });
2985                    }
2986                }
2987            }
2988
2989            let expected_children = new_children.clone();
2990            let needs_dirty_check = !children_changed;
2991            self.commands_mut().push(Command::ReconcileChildren {
2992                parent_id: id,
2993                expected_children,
2994                needs_dirty_check,
2995            });
2996
2997            remembered.update(|entry| entry.children = new_children);
2998        }
2999    }
3000
3001    pub(crate) fn take_commands(&self) -> Vec<Command> {
3002        std::mem::take(&mut *self.commands_mut())
3003    }
3004
3005    /// Applies any pending applier commands and runtime updates.
3006    ///
3007    /// This is useful during measure-time subcomposition to ensure newly created
3008    /// nodes are available for measurement before the full composition is committed.
3009    pub fn apply_pending_commands(&self) -> Result<(), NodeError> {
3010        let mut commands = self.take_commands();
3011        let runtime_handle = self.runtime_handle();
3012        {
3013            let mut applier = self.borrow_applier();
3014            for command in commands.drain(..) {
3015                command.apply(&mut *applier)?;
3016            }
3017            for update in runtime_handle.take_updates() {
3018                update.apply(&mut *applier)?;
3019            }
3020        }
3021        runtime_handle.drain_ui();
3022        Ok(())
3023    }
3024
3025    pub fn register_side_effect(&self, effect: impl FnOnce() + 'static) {
3026        self.side_effects_mut().push(Box::new(effect));
3027    }
3028
3029    pub fn take_side_effects(&self) -> Vec<Box<dyn FnOnce()>> {
3030        std::mem::take(&mut *self.side_effects_mut())
3031    }
3032
3033    pub(crate) fn root(&self) -> Option<NodeId> {
3034        self.core.root.get()
3035    }
3036
3037    pub(crate) fn set_root(&self, node: Option<NodeId>) {
3038        self.core.root.set(node);
3039    }
3040}
3041
3042#[derive(Default, Clone)]
3043struct ParentChildren {
3044    children: Vec<NodeId>,
3045}
3046
3047fn build_child_positions(children: &[NodeId]) -> HashMap<NodeId, usize> {
3048    let mut positions = HashMap::default();
3049    positions.reserve(children.len());
3050    for (index, &child) in children.iter().enumerate() {
3051        positions.insert(child, index);
3052    }
3053    positions
3054}
3055
3056fn refresh_child_positions(
3057    current: &[NodeId],
3058    positions: &mut HashMap<NodeId, usize>,
3059    start: usize,
3060    end: usize,
3061) {
3062    if current.is_empty() || start >= current.len() {
3063        return;
3064    }
3065    let end = end.min(current.len() - 1);
3066    for (offset, &child) in current[start..=end].iter().enumerate() {
3067        positions.insert(child, start + offset);
3068    }
3069}
3070
3071fn insert_child_into_diff_state(
3072    current: &mut Vec<NodeId>,
3073    positions: &mut HashMap<NodeId, usize>,
3074    index: usize,
3075    child: NodeId,
3076) {
3077    let index = index.min(current.len());
3078    current.insert(index, child);
3079    refresh_child_positions(current, positions, index, current.len() - 1);
3080}
3081
3082fn move_child_in_diff_state(
3083    current: &mut Vec<NodeId>,
3084    positions: &mut HashMap<NodeId, usize>,
3085    from_index: usize,
3086    target_index: usize,
3087) -> usize {
3088    let child = current.remove(from_index);
3089    let to_index = target_index.min(current.len());
3090    current.insert(to_index, child);
3091    refresh_child_positions(
3092        current,
3093        positions,
3094        from_index.min(to_index),
3095        from_index.max(to_index),
3096    );
3097    to_index
3098}
3099
3100struct ParentFrame {
3101    id: NodeId,
3102    remembered: Owned<ParentChildren>,
3103    previous: Vec<NodeId>,
3104    new_children: Vec<NodeId>,
3105}
3106
3107#[derive(Default)]
3108struct SubcomposeFrame {
3109    nodes: Vec<NodeId>,
3110    scopes: Vec<RecomposeScope>,
3111}
3112
3113#[derive(Default, Clone)]
3114struct LocalContext {
3115    values: HashMap<LocalKey, Rc<dyn Any>>,
3116}
3117
3118pub(crate) struct MutableStateInner<T: Clone + 'static> {
3119    state: Arc<SnapshotMutableState<T>>,
3120    watchers: RefCell<HashMap<ScopeId, Weak<RecomposeScopeInner>>>, // FUTURE(no_std): move to stack-allocated subscription list.
3121    runtime: RuntimeHandle,
3122}
3123
3124impl<T: Clone + 'static> MutableStateInner<T> {
3125    fn new(value: T, runtime: RuntimeHandle) -> Self {
3126        Self {
3127            state: SnapshotMutableState::new_in_arc(value, Arc::new(NeverEqual)),
3128            watchers: RefCell::new(HashMap::default()),
3129            runtime,
3130        }
3131    }
3132
3133    fn install_snapshot_observer(&self, state_id: StateId) {
3134        let runtime_handle = self.runtime.clone();
3135        self.state.add_apply_observer(Box::new(move || {
3136            let runtime = runtime_handle.clone();
3137            runtime_handle.enqueue_ui_task(Box::new(move || {
3138                runtime.with_state_arena(|arena| {
3139                    let _ = arena.with_typed_opt::<T, _>(state_id, |inner| {
3140                        inner.invalidate_watchers();
3141                    });
3142                });
3143            }));
3144        }));
3145    }
3146
3147    fn with_value<R>(&self, f: impl FnOnce(&T) -> R) -> R {
3148        let value = self.state.get();
3149        f(&value)
3150    }
3151
3152    fn register_scope(&self, scope: &RecomposeScope) -> bool {
3153        let mut watchers = self.watchers.borrow_mut();
3154        match watchers.get(&scope.id()) {
3155            Some(existing) if existing.upgrade().is_some() => false,
3156            _ => {
3157                watchers.insert(scope.id(), scope.downgrade());
3158                true
3159            }
3160        }
3161    }
3162
3163    fn invalidate_watchers(&self) {
3164        let watchers: Vec<RecomposeScope> = {
3165            let mut watchers = self.watchers.borrow_mut();
3166            let mut live = Vec::with_capacity(watchers.len());
3167            watchers.retain(|_, weak| {
3168                if let Some(inner) = weak.upgrade() {
3169                    live.push(RecomposeScope { inner });
3170                    true
3171                } else {
3172                    false
3173                }
3174            });
3175            live
3176        };
3177
3178        if input_debug_enabled() {
3179            let scope_ids: Vec<_> = watchers.iter().map(|scope| scope.id()).collect();
3180            eprintln!(
3181                "[CRANPOSE_INPUT_DEBUG] state {:?} invalidate_watchers count={} scopes={:?}",
3182                self.state.id(),
3183                scope_ids.len(),
3184                scope_ids
3185            );
3186        }
3187
3188        for watcher in watchers {
3189            watcher.invalidate();
3190        }
3191    }
3192}
3193
3194fn register_current_state_scope<T: Clone + 'static>(inner: &MutableStateInner<T>) {
3195    let Some(Some(scope)) =
3196        with_current_composer_opt(|composer| composer.current_recranpose_scope())
3197    else {
3198        return;
3199    };
3200    let inserted = inner.register_scope(&scope);
3201    if inserted && input_debug_enabled() {
3202        let watchers = inner.watchers.borrow();
3203        eprintln!(
3204            "[CRANPOSE_INPUT_DEBUG] state {:?} subscribe scope={} watchers={}",
3205            inner.state.id(),
3206            scope.id(),
3207            watchers.len()
3208        );
3209    }
3210}
3211
3212/// Cheap copyable read-only view of a state cell.
3213pub struct State<T: Clone + 'static> {
3214    id: StateId,
3215    runtime_id: runtime::RuntimeId,
3216    _marker: PhantomData<fn() -> T>,
3217}
3218
3219/// Cheap copyable mutable view of a state cell.
3220///
3221/// Ownership lives elsewhere: a composition slot, an [`OwnedMutableState`], or
3222/// the runtime for states created with [`mutableStateOf`] / [`MutableState::with_runtime`].
3223pub struct MutableState<T: Clone + 'static> {
3224    id: StateId,
3225    runtime_id: runtime::RuntimeId,
3226    _marker: PhantomData<fn() -> T>,
3227}
3228
3229/// Owning state handle for reclaimable state cells.
3230///
3231/// Store this when the state lifetime should be tied to a Rust object rather
3232/// than the runtime. Use [`OwnedMutableState::handle`] to expose a cheap copyable
3233/// [`MutableState`] to call sites.
3234#[derive(Clone)]
3235pub struct OwnedMutableState<T: Clone + 'static> {
3236    state: MutableState<T>,
3237    _lease: Rc<runtime::StateHandleLease>,
3238    _marker: PhantomData<fn() -> T>,
3239}
3240
3241impl<T: Clone + 'static> PartialEq for State<T> {
3242    fn eq(&self, other: &Self) -> bool {
3243        self.state_id() == other.state_id() && self.runtime_id() == other.runtime_id()
3244    }
3245}
3246
3247impl<T: Clone + 'static> Eq for State<T> {}
3248
3249impl<T: Clone + 'static> PartialEq for MutableState<T> {
3250    fn eq(&self, other: &Self) -> bool {
3251        self.state_id() == other.state_id() && self.runtime_id() == other.runtime_id()
3252    }
3253}
3254
3255impl<T: Clone + 'static> Eq for MutableState<T> {}
3256
3257impl<T: Clone + 'static> Copy for State<T> {}
3258
3259impl<T: Clone + 'static> Clone for State<T> {
3260    fn clone(&self) -> Self {
3261        *self
3262    }
3263}
3264
3265impl<T: Clone + 'static> Copy for MutableState<T> {}
3266
3267impl<T: Clone + 'static> Clone for MutableState<T> {
3268    fn clone(&self) -> Self {
3269        *self
3270    }
3271}
3272
3273impl<T: Clone + 'static> State<T> {
3274    fn state_id(&self) -> StateId {
3275        self.id
3276    }
3277
3278    fn runtime_id(&self) -> runtime::RuntimeId {
3279        self.runtime_id
3280    }
3281
3282    fn runtime_handle(&self) -> RuntimeHandle {
3283        runtime::runtime_handle_by_id(self.runtime_id())
3284            .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
3285    }
3286
3287    fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
3288        self.runtime_handle()
3289            .with_state_arena(|arena| arena.with_typed::<T, R>(self.state_id(), f))
3290    }
3291
3292    fn subscribe_current_scope(&self) {
3293        self.with_inner(register_current_state_scope::<T>);
3294    }
3295
3296    pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
3297        self.subscribe_current_scope();
3298        self.with_inner(|inner| inner.with_value(f))
3299    }
3300
3301    pub fn value(&self) -> T {
3302        self.subscribe_current_scope();
3303        self.with_inner(|inner| inner.state.get())
3304    }
3305
3306    pub fn get(&self) -> T {
3307        self.value()
3308    }
3309}
3310
3311impl<T: Clone + 'static> MutableState<T> {
3312    /// Creates a runtime-owned persistent state handle.
3313    pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
3314        runtime.alloc_persistent_state(value)
3315    }
3316
3317    fn from_parts(id: StateId, runtime_id: runtime::RuntimeId) -> Self {
3318        Self {
3319            id,
3320            runtime_id,
3321            _marker: PhantomData,
3322        }
3323    }
3324
3325    pub(crate) fn from_lease(lease: &Rc<runtime::StateHandleLease>) -> Self {
3326        Self::from_parts(lease.id(), lease.runtime().id())
3327    }
3328
3329    fn state_id(&self) -> StateId {
3330        self.id
3331    }
3332
3333    fn runtime_id(&self) -> runtime::RuntimeId {
3334        self.runtime_id
3335    }
3336
3337    fn runtime_handle(&self) -> RuntimeHandle {
3338        runtime::runtime_handle_by_id(self.runtime_id())
3339            .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
3340    }
3341
3342    fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
3343        self.runtime_handle()
3344            .with_state_arena(|arena| arena.with_typed::<T, R>(self.state_id(), f))
3345    }
3346
3347    fn try_with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> Option<R> {
3348        self.runtime_handle()
3349            .with_state_arena(|arena| arena.with_typed_opt::<T, R>(self.state_id(), f))
3350    }
3351
3352    /// Returns `true` if the underlying state cell is still alive (not released).
3353    pub fn is_alive(&self) -> bool {
3354        self.try_with_inner(|_| ()).is_some()
3355    }
3356
3357    /// Like `with`, but returns `None` if the state cell has been released.
3358    ///
3359    /// Use this in frame callbacks, fling animations, and other contexts where
3360    /// the owning composition group may have been disposed (e.g., tab switch).
3361    pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
3362        self.try_with_inner(|inner| inner.with_value(f))
3363    }
3364
3365    /// Like `value`, but returns `None` if the state cell has been released.
3366    pub fn try_value(&self) -> Option<T> {
3367        self.try_with_inner(|inner| inner.state.get())
3368    }
3369
3370    pub fn as_state(&self) -> State<T> {
3371        State {
3372            id: self.id,
3373            runtime_id: self.runtime_id,
3374            _marker: PhantomData,
3375        }
3376    }
3377
3378    /// Upgrades this handle into an owning state value if the cell is still alive.
3379    pub fn try_retain(&self) -> Option<OwnedMutableState<T>> {
3380        let lease = self.runtime_handle().retain_state_lease(self.state_id())?;
3381        Some(OwnedMutableState {
3382            state: *self,
3383            _lease: lease,
3384            _marker: PhantomData,
3385        })
3386    }
3387
3388    /// Upgrades this handle into an owning state value.
3389    pub fn retain(&self) -> OwnedMutableState<T> {
3390        self.try_retain()
3391            .unwrap_or_else(|| panic!("state {:?} is no longer alive", self.state_id()))
3392    }
3393
3394    pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
3395        self.subscribe_current_scope();
3396        self.with_inner(|inner| inner.with_value(f))
3397    }
3398
3399    pub fn update<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
3400        let runtime = self.runtime_handle();
3401        runtime.assert_ui_thread();
3402        runtime.with_state_arena(|arena| {
3403            arena.with_typed::<T, R>(self.state_id(), |inner| {
3404                let mut value = inner.state.get();
3405                let tracker = UpdateScope::new(inner.state.id());
3406                let result = f(&mut value);
3407                let wrote_elsewhere = tracker.finish();
3408                if !wrote_elsewhere {
3409                    inner.state.set(value);
3410                }
3411                inner.invalidate_watchers();
3412                result
3413            })
3414        })
3415    }
3416
3417    /// Sets a new value, silently skipping the write if the state cell was released.
3418    ///
3419    /// State cells can be released between when a `SideEffect` closure captures a
3420    /// `MutableState` handle and when the side effect actually runs (command
3421    /// application disposes composition groups, freeing their state slots, before
3422    /// side effects execute). Using `with_typed_opt` instead of `with_typed`
3423    /// avoids a panic on the stale handle.
3424    pub fn replace(&self, value: T) {
3425        let runtime = self.runtime_handle();
3426        runtime.assert_ui_thread();
3427        runtime.with_state_arena(|arena| {
3428            if arena
3429                .with_typed_opt::<T, ()>(self.state_id(), |inner| {
3430                    inner.state.set(value);
3431                    inner.invalidate_watchers();
3432                })
3433                .is_none()
3434            {
3435                log::debug!(
3436                    "MutableState::replace skipped: state cell released (slot={}, gen={})",
3437                    self.state_id().slot(),
3438                    self.state_id().generation(),
3439                );
3440            }
3441        });
3442    }
3443
3444    pub fn set_value(&self, value: T) {
3445        self.replace(value);
3446    }
3447
3448    pub fn set(&self, value: T) {
3449        self.replace(value);
3450    }
3451
3452    pub fn value(&self) -> T {
3453        self.subscribe_current_scope();
3454        self.with_inner(|inner| inner.state.get())
3455    }
3456
3457    pub fn get(&self) -> T {
3458        self.value()
3459    }
3460
3461    /// Gets the current value WITHOUT subscribing to recomposition.
3462    ///
3463    /// Use this in layout/measure/draw phases to read state without causing
3464    /// the current composition scope to recompose when the state changes.
3465    ///
3466    /// # When to use
3467    /// - In modifier nodes (like ScrollNode) during measure()
3468    /// - In any layout phase code that reads state but shouldn't trigger recomposition
3469    ///
3470    /// # When NOT to use
3471    /// - In composable functions that should update when state changes
3472    /// - When you want reactive UI updates
3473    pub fn get_non_reactive(&self) -> T {
3474        // Skip subscribe_current_scope() - just read the value directly
3475        self.with_inner(|inner| inner.state.get())
3476    }
3477
3478    fn subscribe_current_scope(&self) {
3479        self.with_inner(register_current_state_scope::<T>);
3480    }
3481
3482    #[cfg(test)]
3483    pub(crate) fn watcher_count(&self) -> usize {
3484        self.with_inner(|inner| inner.watchers.borrow().len())
3485    }
3486
3487    #[cfg(test)]
3488    pub(crate) fn state_id_for_test(&self) -> StateId {
3489        self.state_id()
3490    }
3491
3492    #[cfg(test)]
3493    pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
3494        self.as_state().subscribe_scope_for_test(scope);
3495    }
3496}
3497
3498impl<T: Clone + 'static> OwnedMutableState<T> {
3499    /// Creates a reclaimable state owned by this Rust value.
3500    pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
3501        let lease = runtime.alloc_state(value);
3502        Self {
3503            state: MutableState::from_lease(&lease),
3504            _lease: lease,
3505            _marker: PhantomData,
3506        }
3507    }
3508
3509    /// Returns a cheap copyable mutable handle to the owned state.
3510    pub fn handle(&self) -> MutableState<T> {
3511        self.state
3512    }
3513
3514    /// Returns a cheap copyable read-only handle to the owned state.
3515    pub fn as_state(&self) -> State<T> {
3516        self.state.as_state()
3517    }
3518}
3519
3520impl<T: Clone + 'static> Deref for OwnedMutableState<T> {
3521    type Target = MutableState<T>;
3522
3523    fn deref(&self) -> &Self::Target {
3524        &self.state
3525    }
3526}
3527
3528#[cfg(test)]
3529impl<T: Clone + 'static> State<T> {
3530    pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
3531        self.with_inner(|inner| {
3532            inner.register_scope(scope);
3533        });
3534    }
3535}
3536
3537impl<T: fmt::Debug + Clone + 'static> fmt::Debug for MutableState<T> {
3538    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3539        self.with_inner(|inner| {
3540            inner.with_value(|value| {
3541                f.debug_struct("MutableState")
3542                    .field("value", value)
3543                    .finish()
3544            })
3545        })
3546    }
3547}
3548
3549#[derive(Clone)]
3550pub struct SnapshotStateList<T: Clone + 'static> {
3551    state: OwnedMutableState<Vec<T>>,
3552}
3553
3554impl<T: Clone + 'static> SnapshotStateList<T> {
3555    pub fn with_runtime<I>(values: I, runtime: RuntimeHandle) -> Self
3556    where
3557        I: IntoIterator<Item = T>,
3558    {
3559        let initial: Vec<T> = values.into_iter().collect();
3560        Self {
3561            state: OwnedMutableState::with_runtime(initial, runtime),
3562        }
3563    }
3564
3565    pub fn as_state(&self) -> State<Vec<T>> {
3566        self.state.as_state()
3567    }
3568
3569    pub fn as_mutable_state(&self) -> MutableState<Vec<T>> {
3570        self.state.handle()
3571    }
3572
3573    pub fn len(&self) -> usize {
3574        self.state.with(|values| values.len())
3575    }
3576
3577    pub fn is_empty(&self) -> bool {
3578        self.len() == 0
3579    }
3580
3581    pub fn to_vec(&self) -> Vec<T> {
3582        self.state.with(|values| values.clone())
3583    }
3584
3585    pub fn iter(&self) -> Vec<T> {
3586        self.to_vec()
3587    }
3588
3589    pub fn get(&self, index: usize) -> T {
3590        self.state.with(|values| values[index].clone())
3591    }
3592
3593    pub fn get_opt(&self, index: usize) -> Option<T> {
3594        self.state.with(|values| values.get(index).cloned())
3595    }
3596
3597    pub fn first(&self) -> Option<T> {
3598        self.get_opt(0)
3599    }
3600
3601    pub fn last(&self) -> Option<T> {
3602        self.state.with(|values| values.last().cloned())
3603    }
3604
3605    pub fn push(&self, value: T) {
3606        self.state.update(|values| values.push(value));
3607    }
3608
3609    pub fn extend<I>(&self, iter: I)
3610    where
3611        I: IntoIterator<Item = T>,
3612    {
3613        self.state.update(|values| values.extend(iter));
3614    }
3615
3616    pub fn insert(&self, index: usize, value: T) {
3617        self.state.update(|values| values.insert(index, value));
3618    }
3619
3620    pub fn set(&self, index: usize, value: T) -> T {
3621        self.state
3622            .update(|values| std::mem::replace(&mut values[index], value))
3623    }
3624
3625    pub fn remove(&self, index: usize) -> T {
3626        self.state.update(|values| values.remove(index))
3627    }
3628
3629    pub fn pop(&self) -> Option<T> {
3630        self.state.update(|values| values.pop())
3631    }
3632
3633    pub fn clear(&self) {
3634        self.state.replace(Vec::new());
3635    }
3636
3637    pub fn retain<F>(&self, mut predicate: F)
3638    where
3639        F: FnMut(&T) -> bool,
3640    {
3641        self.state
3642            .update(|values| values.retain(|value| predicate(value)));
3643    }
3644
3645    pub fn replace_with<I>(&self, iter: I)
3646    where
3647        I: IntoIterator<Item = T>,
3648    {
3649        self.state.replace(iter.into_iter().collect());
3650    }
3651}
3652
3653impl<T: fmt::Debug + Clone + 'static> fmt::Debug for SnapshotStateList<T> {
3654    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3655        let contents = self.to_vec();
3656        f.debug_struct("SnapshotStateList")
3657            .field("values", &contents)
3658            .finish()
3659    }
3660}
3661
3662#[derive(Clone)]
3663pub struct SnapshotStateMap<K, V>
3664where
3665    K: Clone + Eq + Hash + 'static,
3666    V: Clone + 'static,
3667{
3668    state: OwnedMutableState<HashMap<K, V>>,
3669}
3670
3671impl<K, V> SnapshotStateMap<K, V>
3672where
3673    K: Clone + Eq + Hash + 'static,
3674    V: Clone + 'static,
3675{
3676    pub fn with_runtime<I>(pairs: I, runtime: RuntimeHandle) -> Self
3677    where
3678        I: IntoIterator<Item = (K, V)>,
3679    {
3680        let map: HashMap<K, V> = pairs.into_iter().collect();
3681        Self {
3682            state: OwnedMutableState::with_runtime(map, runtime),
3683        }
3684    }
3685
3686    pub fn as_state(&self) -> State<HashMap<K, V>> {
3687        self.state.as_state()
3688    }
3689
3690    pub fn as_mutable_state(&self) -> MutableState<HashMap<K, V>> {
3691        self.state.handle()
3692    }
3693
3694    pub fn len(&self) -> usize {
3695        self.state.with(|map| map.len())
3696    }
3697
3698    pub fn is_empty(&self) -> bool {
3699        self.state.with(|map| map.is_empty())
3700    }
3701
3702    pub fn contains_key(&self, key: &K) -> bool {
3703        self.state.with(|map| map.contains_key(key))
3704    }
3705
3706    pub fn get(&self, key: &K) -> Option<V> {
3707        self.state.with(|map| map.get(key).cloned())
3708    }
3709
3710    pub fn to_hash_map(&self) -> HashMap<K, V> {
3711        self.state.with(|map| map.clone())
3712    }
3713
3714    pub fn insert(&self, key: K, value: V) -> Option<V> {
3715        self.state.update(|map| map.insert(key, value))
3716    }
3717
3718    pub fn extend<I>(&self, iter: I)
3719    where
3720        I: IntoIterator<Item = (K, V)>,
3721    {
3722        self.state.update(|map| map.extend(iter));
3723        // extend returns (), but update requires returning something: we can just rely on ()
3724    }
3725
3726    pub fn remove(&self, key: &K) -> Option<V> {
3727        self.state.update(|map| map.remove(key))
3728    }
3729
3730    pub fn clear(&self) {
3731        self.state.replace(HashMap::default());
3732    }
3733
3734    pub fn retain<F>(&self, mut predicate: F)
3735    where
3736        F: FnMut(&K, &mut V) -> bool,
3737    {
3738        self.state.update(|map| map.retain(|k, v| predicate(k, v)));
3739    }
3740}
3741
3742impl<K, V> fmt::Debug for SnapshotStateMap<K, V>
3743where
3744    K: Clone + Eq + Hash + fmt::Debug + 'static,
3745    V: Clone + fmt::Debug + 'static,
3746{
3747    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3748        let contents = self.to_hash_map();
3749        f.debug_struct("SnapshotStateMap")
3750            .field("entries", &contents)
3751            .finish()
3752    }
3753}
3754
3755struct DerivedState<T: Clone + 'static> {
3756    compute: Rc<dyn Fn() -> T>, // FUTURE(no_std): store compute closures in arena-managed cell.
3757    state: OwnedMutableState<T>,
3758}
3759
3760impl<T: Clone + 'static> DerivedState<T> {
3761    fn new(runtime: RuntimeHandle, compute: Rc<dyn Fn() -> T>) -> Self {
3762        // FUTURE(no_std): accept arena-managed compute handle.
3763        let initial = compute();
3764        Self {
3765            compute,
3766            state: OwnedMutableState::with_runtime(initial, runtime),
3767        }
3768    }
3769
3770    fn set_compute(&mut self, compute: Rc<dyn Fn() -> T>) {
3771        // FUTURE(no_std): accept arena-managed compute handle.
3772        self.compute = compute;
3773    }
3774
3775    fn recompute(&self) {
3776        let value = (self.compute)();
3777        self.state.set_value(value);
3778    }
3779}
3780
3781impl<T: fmt::Debug + Clone + 'static> fmt::Debug for State<T> {
3782    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3783        self.with_inner(|inner| {
3784            inner.with_value(|value| f.debug_struct("State").field("value", value).finish())
3785        })
3786    }
3787}
3788
3789pub struct ParamState<T> {
3790    value: Option<T>,
3791}
3792
3793impl<T> ParamState<T> {
3794    pub fn update(&mut self, new_value: &T) -> bool
3795    where
3796        T: PartialEq + Clone,
3797    {
3798        match &self.value {
3799            Some(old) if old == new_value => false,
3800            _ => {
3801                self.value = Some(new_value.clone());
3802                true
3803            }
3804        }
3805    }
3806
3807    pub fn value(&self) -> Option<T>
3808    where
3809        T: Clone,
3810    {
3811        self.value.clone()
3812    }
3813}
3814
3815/// ParamSlot holds function/closure parameters by ownership (no PartialEq/Clone required).
3816/// Used by the #[composable] macro to store Fn-like parameters in the slot table.
3817pub struct ParamSlot<T> {
3818    val: RefCell<Option<T>>,
3819}
3820
3821impl<T> Default for ParamSlot<T> {
3822    fn default() -> Self {
3823        Self {
3824            val: RefCell::new(None),
3825        }
3826    }
3827}
3828
3829impl<T> ParamSlot<T> {
3830    pub fn set(&self, v: T) {
3831        *self.val.borrow_mut() = Some(v);
3832    }
3833
3834    /// Takes the value out temporarily (for recomposition callback)
3835    pub fn take(&self) -> T {
3836        self.val
3837            .borrow_mut()
3838            .take()
3839            .expect("ParamSlot take() called before set")
3840    }
3841}
3842
3843/// CallbackHolder keeps the latest callback closure alive across recompositions.
3844/// It stores the callback in an Rc<RefCell<...>> so that the composer can hand out
3845/// lightweight forwarder closures without cloning the underlying callback value.
3846#[derive(Clone)]
3847pub struct CallbackHolder {
3848    rc: Rc<RefCell<Box<dyn FnMut()>>>,
3849}
3850
3851impl CallbackHolder {
3852    /// Create a new holder with a no-op callback so that callers can immediately invoke it.
3853    pub fn new() -> Self {
3854        Self::default()
3855    }
3856
3857    /// Replace the stored callback with a new closure provided by the caller.
3858    pub fn update<F>(&self, f: F)
3859    where
3860        F: FnMut() + 'static,
3861    {
3862        *self.rc.borrow_mut() = Box::new(f);
3863    }
3864
3865    /// Produce a forwarder closure that keeps the holder alive and forwards calls to it.
3866    pub fn clone_rc(&self) -> impl Fn() + 'static {
3867        let rc = self.rc.clone();
3868        move || {
3869            (rc.borrow_mut())();
3870        }
3871    }
3872}
3873
3874impl Default for CallbackHolder {
3875    fn default() -> Self {
3876        Self {
3877            rc: Rc::new(RefCell::new(Box::new(|| {}) as Box<dyn FnMut()>)),
3878        }
3879    }
3880}
3881
3882/// CallbackHolder1 keeps the latest single-argument callback closure alive across recompositions.
3883/// It mirrors [`CallbackHolder`] but supports callbacks that receive one argument.
3884#[derive(Clone)]
3885pub struct CallbackHolder1<A: 'static> {
3886    #[allow(clippy::type_complexity)]
3887    rc: Rc<RefCell<Box<dyn FnMut(A)>>>,
3888}
3889
3890impl<A: 'static> CallbackHolder1<A> {
3891    /// Create a new holder with a no-op callback so callers can invoke it immediately.
3892    pub fn new() -> Self {
3893        Self::default()
3894    }
3895
3896    /// Replace the stored callback with a new closure provided by the caller.
3897    pub fn update<F>(&self, f: F)
3898    where
3899        F: FnMut(A) + 'static,
3900    {
3901        *self.rc.borrow_mut() = Box::new(f);
3902    }
3903
3904    /// Produce a forwarder closure that keeps the holder alive and forwards calls to it.
3905    pub fn clone_rc(&self) -> impl Fn(A) + 'static {
3906        let rc = self.rc.clone();
3907        move |arg| {
3908            (rc.borrow_mut())(arg);
3909        }
3910    }
3911}
3912
3913impl<A: 'static> Default for CallbackHolder1<A> {
3914    fn default() -> Self {
3915        Self {
3916            rc: Rc::new(RefCell::new(Box::new(|_| {}) as Box<dyn FnMut(A)>)),
3917        }
3918    }
3919}
3920
3921pub struct ReturnSlot<T> {
3922    value: Option<T>,
3923}
3924
3925impl<T: Clone> ReturnSlot<T> {
3926    pub fn store(&mut self, value: T) {
3927        self.value = Some(value);
3928    }
3929
3930    pub fn get(&self) -> Option<T> {
3931        self.value.clone()
3932    }
3933}
3934
3935impl<T> Default for ParamState<T> {
3936    fn default() -> Self {
3937        Self { value: None }
3938    }
3939}
3940
3941impl<T> Default for ReturnSlot<T> {
3942    fn default() -> Self {
3943        Self { value: None }
3944    }
3945}
3946
3947pub struct Composition<A: Applier + 'static> {
3948    slots: Rc<SlotsHost>,
3949    applier: Rc<ConcreteApplierHost<A>>,
3950    runtime: Runtime,
3951    observer: SnapshotStateObserver,
3952    root: Option<NodeId>,
3953}
3954
3955impl<A: Applier + 'static> Composition<A> {
3956    pub fn new(applier: A) -> Self {
3957        Self::with_runtime(applier, Runtime::new(Arc::new(DefaultScheduler)))
3958    }
3959
3960    pub fn with_runtime(applier: A, runtime: Runtime) -> Self {
3961        Self::with_backend(applier, runtime, SlotBackendKind::default())
3962    }
3963
3964    pub fn with_backend(applier: A, runtime: Runtime, backend_kind: SlotBackendKind) -> Self {
3965        let storage = make_backend(backend_kind);
3966        let slots = Rc::new(SlotsHost::new(storage));
3967        let applier = Rc::new(ConcreteApplierHost::new(applier));
3968        let observer_handle = runtime.handle();
3969        let observer = SnapshotStateObserver::new(move |callback| {
3970            observer_handle.enqueue_ui_task(callback);
3971        });
3972        observer.start();
3973        Self {
3974            slots,
3975            applier,
3976            runtime,
3977            observer,
3978            root: None,
3979        }
3980    }
3981
3982    fn slots_host(&self) -> Rc<SlotsHost> {
3983        Rc::clone(&self.slots)
3984    }
3985
3986    fn applier_host(&self) -> Rc<dyn ApplierHost> {
3987        self.applier.clone()
3988    }
3989
3990    pub fn render(&mut self, key: Key, mut content: impl FnMut()) -> Result<(), NodeError> {
3991        self.slots.borrow_mut().reset();
3992        let runtime_handle = self.runtime_handle();
3993        runtime_handle.drain_ui();
3994        let composer = Composer::new(
3995            Rc::clone(&self.slots),
3996            self.applier.clone(),
3997            runtime_handle.clone(),
3998            self.observer.clone(),
3999            self.root,
4000        );
4001        self.observer.begin_frame();
4002        let (root, mut commands, side_effects) = composer.install(|composer| {
4003            composer.with_group(key, |_| content());
4004            let root = composer.root();
4005            let commands = composer.take_commands();
4006            let side_effects = composer.take_side_effects();
4007            (root, commands, side_effects)
4008        });
4009
4010        {
4011            let mut applier = self.applier.borrow_dyn();
4012            for command in commands.drain(..) {
4013                command.apply(&mut *applier)?;
4014            }
4015            for update in runtime_handle.take_updates() {
4016                update.apply(&mut *applier)?;
4017            }
4018        }
4019
4020        runtime_handle.drain_ui();
4021        for effect in side_effects {
4022            effect();
4023        }
4024        runtime_handle.drain_ui();
4025        self.root = root;
4026        {
4027            let mut slots = self.slots.borrow_mut();
4028            let _ = slots.finalize_current_group();
4029            slots.flush();
4030        }
4031        let _ = self.process_invalid_scopes()?;
4032        if !self.runtime.has_updates()
4033            && !runtime_handle.has_invalid_scopes()
4034            && !runtime_handle.has_frame_callbacks()
4035            && !runtime_handle.has_pending_ui()
4036        {
4037            self.runtime.set_needs_frame(false);
4038        }
4039        Ok(())
4040    }
4041
4042    /// Returns true if composition needs to process invalid scopes (recompose).
4043    ///
4044    /// This checks both:
4045    /// - `has_updates()`: composition scopes that were invalidated by state changes
4046    /// - `needs_frame()`: animation callbacks that may have pending work
4047    ///
4048    /// Note: For scroll performance, ensure scroll state changes use Cell<T> instead
4049    /// of MutableState<T> to avoid triggering recomposition on every scroll frame.
4050    pub fn should_render(&self) -> bool {
4051        self.runtime.needs_frame() || self.runtime.has_updates()
4052    }
4053
4054    pub fn runtime_handle(&self) -> RuntimeHandle {
4055        self.runtime.handle()
4056    }
4057
4058    pub fn applier_mut(&mut self) -> ApplierGuard<'_, A> {
4059        ApplierGuard::new(self.applier.borrow_typed())
4060    }
4061
4062    pub fn root(&self) -> Option<NodeId> {
4063        self.root
4064    }
4065
4066    pub fn debug_dump_slot_table_groups(&self) -> Vec<(usize, Key, Option<ScopeId>, usize)> {
4067        self.slots.borrow().debug_dump_groups()
4068    }
4069
4070    pub fn debug_dump_all_slots(&self) -> Vec<(usize, String)> {
4071        self.slots.borrow().debug_dump_all_slots()
4072    }
4073
4074    pub fn process_invalid_scopes(&mut self) -> Result<bool, NodeError> {
4075        let runtime_handle = self.runtime_handle();
4076        let mut did_recompose = false;
4077        let mut loop_count = 0;
4078        loop {
4079            loop_count += 1;
4080            if loop_count > 100 {
4081                log::error!("process_invalid_scopes looped too many times! Breaking loop to prevent freeze.");
4082                break;
4083            }
4084            runtime_handle.drain_ui();
4085            let pending = runtime_handle.take_invalidated_scopes();
4086            if pending.is_empty() {
4087                break;
4088            }
4089            if input_debug_enabled() {
4090                let pending_ids: Vec<_> = pending.iter().map(|(id, _)| *id).collect();
4091                eprintln!(
4092                    "[CRANPOSE_INPUT_DEBUG] process_invalid_scopes pending_ids={:?}",
4093                    pending_ids
4094                );
4095            }
4096            let mut scopes = Vec::new();
4097            for (id, weak) in pending {
4098                if let Some(inner) = weak.upgrade() {
4099                    scopes.push(RecomposeScope { inner });
4100                } else {
4101                    runtime_handle.mark_scope_recomposed(id);
4102                }
4103            }
4104            if scopes.is_empty() {
4105                continue;
4106            }
4107            if input_debug_enabled() {
4108                let scope_ids: Vec<_> = scopes.iter().map(|scope| scope.id()).collect();
4109                eprintln!(
4110                    "[CRANPOSE_INPUT_DEBUG] process_invalid_scopes live_scope_ids={:?}",
4111                    scope_ids
4112                );
4113            }
4114            did_recompose = true;
4115            let runtime_clone = runtime_handle.clone();
4116            let root_host = self.slots_host();
4117            let mut scope_groups: Vec<(Rc<SlotsHost>, Vec<RecomposeScope>)> = Vec::new();
4118            let mut scope_group_index: HashMap<usize, usize> = HashMap::default();
4119            for scope in scopes {
4120                let host = scope.slots_host().unwrap_or_else(|| Rc::clone(&root_host));
4121                let host_key = Rc::as_ptr(&host) as usize;
4122                if let Some(index) = scope_group_index.get(&host_key).copied() {
4123                    scope_groups[index].1.push(scope);
4124                } else {
4125                    scope_group_index.insert(host_key, scope_groups.len());
4126                    scope_groups.push((host, vec![scope]));
4127                }
4128            }
4129            let (mut commands, side_effects) = {
4130                let composer = Composer::new(
4131                    Rc::clone(&root_host),
4132                    self.applier_host(),
4133                    runtime_clone,
4134                    self.observer.clone(),
4135                    self.root,
4136                );
4137                self.observer.begin_frame();
4138                composer.install(|composer| {
4139                    for (host, scopes) in scope_groups.into_iter() {
4140                        if Rc::ptr_eq(&host, &root_host) {
4141                            for scope in scopes.iter() {
4142                                composer.recranpose_group(scope);
4143                            }
4144                        } else {
4145                            composer.with_slot_override(host, |composer| {
4146                                for scope in scopes.iter() {
4147                                    composer.recranpose_group(scope);
4148                                }
4149                            });
4150                        }
4151                    }
4152                    let commands = composer.take_commands();
4153                    let side_effects = composer.take_side_effects();
4154                    (commands, side_effects)
4155                })
4156            };
4157            {
4158                let mut applier = self.applier.borrow_dyn();
4159                for command in commands.drain(..) {
4160                    command.apply(&mut *applier)?;
4161                }
4162                for update in runtime_handle.take_updates() {
4163                    update.apply(&mut *applier)?;
4164                }
4165            }
4166            for effect in side_effects {
4167                effect();
4168            }
4169            runtime_handle.drain_ui();
4170        }
4171        if !self.runtime.has_updates()
4172            && !runtime_handle.has_invalid_scopes()
4173            && !runtime_handle.has_frame_callbacks()
4174            && !runtime_handle.has_pending_ui()
4175        {
4176            self.runtime.set_needs_frame(false);
4177        }
4178        Ok(did_recompose)
4179    }
4180
4181    pub fn flush_pending_node_updates(&mut self) -> Result<(), NodeError> {
4182        let updates = self.runtime_handle().take_updates();
4183        let mut applier = self.applier.borrow_dyn();
4184        for update in updates {
4185            update.apply(&mut *applier)?;
4186        }
4187        Ok(())
4188    }
4189}
4190
4191impl<A: Applier + 'static> Drop for Composition<A> {
4192    fn drop(&mut self) {
4193        self.observer.stop();
4194    }
4195}
4196pub fn location_key(file: &str, line: u32, column: u32) -> Key {
4197    let base = file.as_ptr() as u64;
4198    base
4199        .wrapping_mul(0x9E37_79B9_7F4A_7C15) // cheap mix
4200        ^ ((line as u64) << 32)
4201        ^ (column as u64)
4202}
4203
4204fn hash_key<K: Hash>(key: &K) -> Key {
4205    let mut hasher = hash::default::new();
4206    key.hash(&mut hasher);
4207    hasher.finish()
4208}
4209
4210#[cfg(test)]
4211#[path = "tests/lib_tests.rs"]
4212mod tests;
4213
4214#[cfg(test)]
4215#[path = "tests/recursive_decrease_increase_test.rs"]
4216mod recursive_decrease_increase_test;
4217
4218#[cfg(test)]
4219#[path = "tests/slot_backend_tests.rs"]
4220mod slot_backend_tests;
4221
4222pub mod collections;
4223pub mod hash;