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    __launched_effect_async_impl, __launched_effect_impl, CancelToken, LaunchedEffectScope,
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 measure dirty flags from a node to the root during composition.
1172///
1173/// This queues a dirty-bubble command on the active composer so measure invalidation
1174/// runs during the apply phase, avoiding re-entrant applier borrows while widgets are
1175/// mutating nodes via `with_node_mut`.
1176pub fn bubble_measure_dirty_in_composer(node_id: NodeId) {
1177    with_current_composer(|composer| {
1178        composer.commands_mut().push(Command::BubbleDirty {
1179            node_id,
1180            bubble: DirtyBubble {
1181                layout: false,
1182                measure: true,
1183                semantics: false,
1184            },
1185        });
1186    });
1187}
1188
1189/// Unified API for bubbling semantics dirty flags from a node to the root (Composer context).
1190///
1191/// This mirrors [`bubble_layout_dirty_in_composer`] but routes through the semantics
1192/// dirty flag instead of the layout one. Modifier nodes can request semantics
1193/// invalidations without triggering measure/layout work, and the runtime can
1194/// query the root to determine whether the semantics tree needs rebuilding.
1195pub fn bubble_semantics_dirty_in_composer<N: Node + 'static>(node_id: NodeId) {
1196    bubble_semantics_dirty_composer::<N>(node_id);
1197}
1198
1199/// Internal implementation for applier-based bubbling.
1200fn bubble_layout_dirty_applier(applier: &mut dyn Applier, mut node_id: NodeId) {
1201    // First, mark the starting node dirty (critical!)
1202    // This ensures root gets marked even if it has no parent
1203    if let Ok(node) = applier.get_mut(node_id) {
1204        node.mark_needs_layout();
1205    }
1206
1207    // Then bubble up to ancestors
1208    loop {
1209        // Get parent of current node
1210        let parent_id = match applier.get_mut(node_id) {
1211            Ok(node) => node.parent(),
1212            Err(_) => None,
1213        };
1214
1215        match parent_id {
1216            Some(pid) => {
1217                // Mark parent as needing layout
1218                if let Ok(parent) = applier.get_mut(pid) {
1219                    if !parent.needs_layout() {
1220                        parent.mark_needs_layout();
1221                        node_id = pid; // Continue bubbling
1222                    } else {
1223                        break; // Already dirty, stop
1224                    }
1225                } else {
1226                    break;
1227                }
1228            }
1229            None => break, // No parent, stop
1230        }
1231    }
1232}
1233
1234/// Internal implementation for applier-based bubbling of measure dirtiness.
1235fn bubble_measure_dirty_applier(applier: &mut dyn Applier, mut node_id: NodeId) {
1236    // First, mark the starting node as needing measure
1237    if let Ok(node) = applier.get_mut(node_id) {
1238        node.mark_needs_measure();
1239    }
1240
1241    // Then bubble up to ancestors
1242    loop {
1243        // Get parent of current node
1244        let parent_id = match applier.get_mut(node_id) {
1245            Ok(node) => node.parent(),
1246            Err(_) => None,
1247        };
1248
1249        match parent_id {
1250            Some(pid) => {
1251                // Mark parent as needing measure
1252                if let Ok(parent) = applier.get_mut(pid) {
1253                    if !parent.needs_measure() {
1254                        parent.mark_needs_measure();
1255                        node_id = pid; // Continue bubbling
1256                    } else {
1257                        break; // Already dirty, stop
1258                    }
1259                } else {
1260                    break;
1261                }
1262            }
1263            None => {
1264                break; // No parent, stop
1265            }
1266        }
1267    }
1268}
1269
1270/// Internal implementation for applier-based bubbling of semantics dirtiness.
1271fn bubble_semantics_dirty_applier(applier: &mut dyn Applier, mut node_id: NodeId) {
1272    if let Ok(node) = applier.get_mut(node_id) {
1273        node.mark_needs_semantics();
1274    }
1275
1276    loop {
1277        let parent_id = match applier.get_mut(node_id) {
1278            Ok(node) => node.parent(),
1279            Err(_) => None,
1280        };
1281
1282        match parent_id {
1283            Some(pid) => {
1284                if let Ok(parent) = applier.get_mut(pid) {
1285                    if !parent.needs_semantics() {
1286                        parent.mark_needs_semantics();
1287                        node_id = pid;
1288                    } else {
1289                        break;
1290                    }
1291                } else {
1292                    break;
1293                }
1294            }
1295            None => break,
1296        }
1297    }
1298}
1299
1300/// Internal implementation for composer-based bubbling.
1301/// This uses with_node_mut and works during composition with a concrete node type.
1302/// The node type N must implement Node (which includes mark_needs_layout, parent, etc.).
1303fn bubble_layout_dirty_composer<N: Node + 'static>(mut node_id: NodeId) {
1304    // Mark the starting node dirty
1305    let _ = with_node_mut(node_id, |node: &mut N| {
1306        node.mark_needs_layout();
1307    });
1308
1309    // Then bubble up to ancestors
1310    while let Ok(Some(pid)) = with_node_mut(node_id, |node: &mut N| node.parent()) {
1311        let parent_id = pid;
1312
1313        // Mark parent as needing layout
1314        let should_continue = with_node_mut(parent_id, |node: &mut N| {
1315            if !node.needs_layout() {
1316                node.mark_needs_layout();
1317                true // Continue bubbling
1318            } else {
1319                false // Already dirty, stop (O(1) optimization)
1320            }
1321        })
1322        .unwrap_or(false);
1323
1324        if should_continue {
1325            node_id = parent_id;
1326        } else {
1327            break;
1328        }
1329    }
1330}
1331
1332/// Internal implementation for composer-based bubbling of semantics dirtiness.
1333fn bubble_semantics_dirty_composer<N: Node + 'static>(mut node_id: NodeId) {
1334    // Mark the starting node semantics-dirty.
1335    let _ = with_node_mut(node_id, |node: &mut N| {
1336        node.mark_needs_semantics();
1337    });
1338
1339    while let Ok(Some(pid)) = with_node_mut(node_id, |node: &mut N| node.parent()) {
1340        let parent_id = pid;
1341
1342        let should_continue = with_node_mut(parent_id, |node: &mut N| {
1343            if !node.needs_semantics() {
1344                node.mark_needs_semantics();
1345                true
1346            } else {
1347                false
1348            }
1349        })
1350        .unwrap_or(false);
1351
1352        if should_continue {
1353            node_id = parent_id;
1354        } else {
1355            break;
1356        }
1357    }
1358}
1359
1360impl dyn Node {
1361    pub fn as_any_mut(&mut self) -> &mut dyn Any {
1362        self
1363    }
1364}
1365
1366pub trait Applier: Any {
1367    fn create(&mut self, node: Box<dyn Node>) -> NodeId;
1368    fn get_mut(&mut self, id: NodeId) -> Result<&mut dyn Node, NodeError>;
1369    fn remove(&mut self, id: NodeId) -> Result<(), NodeError>;
1370
1371    /// Inserts a node with a pre-assigned ID.
1372    ///
1373    /// This is used for virtual nodes whose IDs are allocated separately
1374    /// (e.g., via allocate_virtual_node_id()). Unlike `create()` which assigns
1375    /// a new ID, this method uses the provided ID.
1376    ///
1377    /// Returns Ok(()) if successful, or an error if the ID is already in use.
1378    fn insert_with_id(&mut self, id: NodeId, node: Box<dyn Node>) -> Result<(), NodeError>;
1379
1380    fn as_any(&self) -> &dyn Any
1381    where
1382        Self: Sized,
1383    {
1384        self
1385    }
1386
1387    fn as_any_mut(&mut self) -> &mut dyn Any
1388    where
1389        Self: Sized,
1390    {
1391        self
1392    }
1393}
1394
1395type TypedNodeUpdate = fn(&mut dyn Node, NodeId) -> Result<(), NodeError>;
1396type CommandCallback = Box<dyn FnOnce(&mut dyn Applier) -> Result<(), NodeError> + 'static>;
1397
1398#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1399pub(crate) struct DirtyBubble {
1400    layout: bool,
1401    measure: bool,
1402    semantics: bool,
1403}
1404
1405impl DirtyBubble {
1406    pub(crate) const LAYOUT_AND_MEASURE: Self = Self {
1407        layout: true,
1408        measure: true,
1409        semantics: false,
1410    };
1411
1412    pub(crate) const SEMANTICS: Self = Self {
1413        layout: false,
1414        measure: false,
1415        semantics: true,
1416    };
1417
1418    fn apply(self, applier: &mut dyn Applier, node_id: NodeId) {
1419        if self.layout {
1420            bubble_layout_dirty(applier, node_id);
1421        }
1422        if self.measure {
1423            bubble_measure_dirty(applier, node_id);
1424        }
1425        if self.semantics {
1426            bubble_semantics_dirty(applier, node_id);
1427        }
1428    }
1429}
1430
1431pub(crate) enum Command {
1432    BubbleDirty {
1433        node_id: NodeId,
1434        bubble: DirtyBubble,
1435    },
1436    UpdateTypedNode {
1437        id: NodeId,
1438        updater: TypedNodeUpdate,
1439    },
1440    RemoveNode {
1441        id: NodeId,
1442    },
1443    MountNode {
1444        id: NodeId,
1445    },
1446    AttachChild {
1447        parent_id: NodeId,
1448        child_id: NodeId,
1449        bubble: DirtyBubble,
1450    },
1451    InsertChild {
1452        parent_id: NodeId,
1453        child_id: NodeId,
1454        appended_index: usize,
1455        insert_index: usize,
1456        bubble: DirtyBubble,
1457    },
1458    MoveChild {
1459        parent_id: NodeId,
1460        from_index: usize,
1461        to_index: usize,
1462        bubble: DirtyBubble,
1463    },
1464    RemoveChild {
1465        parent_id: NodeId,
1466        child_id: NodeId,
1467    },
1468    ReconcileChildren {
1469        parent_id: NodeId,
1470        expected_children: Vec<NodeId>,
1471        needs_dirty_check: bool,
1472    },
1473    Callback(CommandCallback),
1474}
1475
1476impl Command {
1477    pub(crate) fn update_node<N: Node + 'static>(id: NodeId) -> Self {
1478        Self::UpdateTypedNode {
1479            id,
1480            updater: update_typed_node::<N>,
1481        }
1482    }
1483
1484    pub(crate) fn callback(
1485        callback: impl FnOnce(&mut dyn Applier) -> Result<(), NodeError> + 'static,
1486    ) -> Self {
1487        Self::Callback(Box::new(callback))
1488    }
1489
1490    pub(crate) fn apply(self, applier: &mut dyn Applier) -> Result<(), NodeError> {
1491        match self {
1492            Self::BubbleDirty { node_id, bubble } => {
1493                bubble.apply(applier, node_id);
1494                Ok(())
1495            }
1496            Self::UpdateTypedNode { id, updater } => {
1497                let node = match applier.get_mut(id) {
1498                    Ok(node) => node,
1499                    Err(NodeError::Missing { .. }) => return Ok(()),
1500                    Err(err) => return Err(err),
1501                };
1502                updater(node, id)
1503            }
1504            Self::RemoveNode { id } => {
1505                if let Ok(node) = applier.get_mut(id) {
1506                    node.unmount();
1507                }
1508                match applier.remove(id) {
1509                    Ok(()) | Err(NodeError::Missing { .. }) => Ok(()),
1510                    Err(err) => Err(err),
1511                }
1512            }
1513            Self::MountNode { id } => {
1514                let node = match applier.get_mut(id) {
1515                    Ok(node) => node,
1516                    Err(NodeError::Missing { .. }) => return Ok(()),
1517                    Err(err) => return Err(err),
1518                };
1519                node.set_node_id(id);
1520                node.mount();
1521                Ok(())
1522            }
1523            Self::AttachChild {
1524                parent_id,
1525                child_id,
1526                bubble,
1527            } => {
1528                insert_child_with_reparenting(applier, parent_id, child_id);
1529                bubble.apply(applier, parent_id);
1530                Ok(())
1531            }
1532            Self::InsertChild {
1533                parent_id,
1534                child_id,
1535                appended_index,
1536                insert_index,
1537                bubble,
1538            } => {
1539                insert_child_with_reparenting(applier, parent_id, child_id);
1540                bubble.apply(applier, parent_id);
1541                if insert_index != appended_index {
1542                    if let Ok(parent_node) = applier.get_mut(parent_id) {
1543                        parent_node.move_child(appended_index, insert_index);
1544                    }
1545                }
1546                Ok(())
1547            }
1548            Self::MoveChild {
1549                parent_id,
1550                from_index,
1551                to_index,
1552                bubble,
1553            } => {
1554                if let Ok(parent_node) = applier.get_mut(parent_id) {
1555                    parent_node.move_child(from_index, to_index);
1556                }
1557                bubble.apply(applier, parent_id);
1558                Ok(())
1559            }
1560            Self::RemoveChild {
1561                parent_id,
1562                child_id,
1563            } => apply_remove_child(applier, parent_id, child_id),
1564            Self::ReconcileChildren {
1565                parent_id,
1566                expected_children,
1567                needs_dirty_check,
1568            } => reconcile_children(applier, parent_id, &expected_children, needs_dirty_check),
1569            Self::Callback(callback) => callback(applier),
1570        }
1571    }
1572}
1573
1574fn update_typed_node<N: Node + 'static>(node: &mut dyn Node, id: NodeId) -> Result<(), NodeError> {
1575    let typed = node
1576        .as_any_mut()
1577        .downcast_mut::<N>()
1578        .ok_or(NodeError::TypeMismatch {
1579            id,
1580            expected: std::any::type_name::<N>(),
1581        })?;
1582    typed.update();
1583    Ok(())
1584}
1585
1586fn insert_child_with_reparenting(applier: &mut dyn Applier, parent_id: NodeId, child_id: NodeId) {
1587    let old_parent = applier
1588        .get_mut(child_id)
1589        .ok()
1590        .and_then(|node| node.parent());
1591    if let Some(old_parent_id) = old_parent {
1592        if old_parent_id != parent_id {
1593            if let Ok(old_parent_node) = applier.get_mut(old_parent_id) {
1594                old_parent_node.remove_child(child_id);
1595            }
1596            if let Ok(child_node) = applier.get_mut(child_id) {
1597                child_node.on_removed_from_parent();
1598            }
1599            bubble_layout_dirty(applier, old_parent_id);
1600            bubble_measure_dirty(applier, old_parent_id);
1601        }
1602    }
1603
1604    if let Ok(parent_node) = applier.get_mut(parent_id) {
1605        parent_node.insert_child(child_id);
1606    }
1607    if let Ok(child_node) = applier.get_mut(child_id) {
1608        child_node.on_attached_to_parent(parent_id);
1609    }
1610}
1611
1612fn apply_remove_child(
1613    applier: &mut dyn Applier,
1614    parent_id: NodeId,
1615    child_id: NodeId,
1616) -> Result<(), NodeError> {
1617    if let Ok(parent_node) = applier.get_mut(parent_id) {
1618        parent_node.remove_child(child_id);
1619    }
1620    bubble_layout_dirty(applier, parent_id);
1621    bubble_measure_dirty(applier, parent_id);
1622
1623    let should_remove = if let Ok(node) = applier.get_mut(child_id) {
1624        match node.parent() {
1625            Some(existing_parent_id) if existing_parent_id == parent_id => {
1626                node.on_removed_from_parent();
1627                node.unmount();
1628                true
1629            }
1630            None => {
1631                node.unmount();
1632                true
1633            }
1634            Some(_) => false,
1635        }
1636    } else {
1637        true
1638    };
1639
1640    if should_remove {
1641        let _ = applier.remove(child_id);
1642    }
1643    Ok(())
1644}
1645
1646fn reconcile_children(
1647    applier: &mut dyn Applier,
1648    parent_id: NodeId,
1649    expected_children: &[NodeId],
1650    needs_dirty_check: bool,
1651) -> Result<(), NodeError> {
1652    let mut repaired = false;
1653    for &child_id in expected_children {
1654        let needs_attach = if let Ok(node) = applier.get_mut(child_id) {
1655            node.parent() != Some(parent_id)
1656        } else {
1657            false
1658        };
1659
1660        if needs_attach {
1661            insert_child_with_reparenting(applier, parent_id, child_id);
1662            repaired = true;
1663        }
1664    }
1665
1666    let is_dirty = if needs_dirty_check {
1667        if let Ok(node) = applier.get_mut(parent_id) {
1668            node.needs_layout()
1669        } else {
1670            false
1671        }
1672    } else {
1673        false
1674    };
1675
1676    if repaired {
1677        bubble_layout_dirty(applier, parent_id);
1678        bubble_measure_dirty(applier, parent_id);
1679    } else if is_dirty {
1680        bubble_layout_dirty(applier, parent_id);
1681    }
1682
1683    Ok(())
1684}
1685
1686#[derive(Default)]
1687pub struct MemoryApplier {
1688    nodes: Vec<Option<Box<dyn Node>>>, // FUTURE(no_std): migrate to arena-backed node storage.
1689    /// Storage for high-ID nodes (like virtual nodes with IDs starting at 0xFFFFFFFF00000000)
1690    /// that can't be stored in the Vec without causing capacity overflow.
1691    high_id_nodes: std::collections::HashMap<NodeId, Box<dyn Node>>,
1692    layout_runtime: Option<RuntimeHandle>,
1693    slots: SlotBackend,
1694}
1695
1696impl MemoryApplier {
1697    pub fn new() -> Self {
1698        Self {
1699            nodes: Vec::new(),
1700            high_id_nodes: std::collections::HashMap::new(),
1701            layout_runtime: None,
1702            slots: SlotBackend::default(),
1703        }
1704    }
1705
1706    pub fn slots(&mut self) -> &mut SlotBackend {
1707        &mut self.slots
1708    }
1709
1710    pub fn with_node<N: Node + 'static, R>(
1711        &mut self,
1712        id: NodeId,
1713        f: impl FnOnce(&mut N) -> R,
1714    ) -> Result<R, NodeError> {
1715        let slot = self
1716            .nodes
1717            .get_mut(id)
1718            .ok_or(NodeError::Missing { id })?
1719            .as_deref_mut()
1720            .ok_or(NodeError::Missing { id })?;
1721        let typed = slot
1722            .as_any_mut()
1723            .downcast_mut::<N>()
1724            .ok_or(NodeError::TypeMismatch {
1725                id,
1726                expected: std::any::type_name::<N>(),
1727            })?;
1728        Ok(f(typed))
1729    }
1730
1731    pub fn len(&self) -> usize {
1732        self.nodes.iter().filter(|n| n.is_some()).count()
1733    }
1734
1735    pub fn is_empty(&self) -> bool {
1736        self.len() == 0
1737    }
1738
1739    pub fn set_runtime_handle(&mut self, handle: RuntimeHandle) {
1740        self.layout_runtime = Some(handle);
1741    }
1742
1743    pub fn clear_runtime_handle(&mut self) {
1744        self.layout_runtime = None;
1745    }
1746
1747    pub fn runtime_handle(&self) -> Option<RuntimeHandle> {
1748        self.layout_runtime.clone()
1749    }
1750
1751    pub fn dump_tree(&self, root: Option<NodeId>) -> String {
1752        let mut output = String::new();
1753        if let Some(root_id) = root {
1754            self.dump_node(&mut output, root_id, 0);
1755        } else {
1756            output.push_str("(no root)\n");
1757        }
1758        output
1759    }
1760
1761    fn dump_node(&self, output: &mut String, id: NodeId, depth: usize) {
1762        let indent = "  ".repeat(depth);
1763        if let Some(Some(node)) = self.nodes.get(id) {
1764            let type_name = std::any::type_name_of_val(&**node);
1765            output.push_str(&format!("{}[{}] {}\n", indent, id, type_name));
1766
1767            let children = node.children();
1768            for child_id in children {
1769                self.dump_node(output, child_id, depth + 1);
1770            }
1771        } else {
1772            output.push_str(&format!("{}[{}] (missing)\n", indent, id));
1773        }
1774    }
1775}
1776
1777impl Applier for MemoryApplier {
1778    fn create(&mut self, node: Box<dyn Node>) -> NodeId {
1779        let id = self.nodes.len();
1780        self.nodes.push(Some(node));
1781        id
1782    }
1783
1784    fn get_mut(&mut self, id: NodeId) -> Result<&mut dyn Node, NodeError> {
1785        // Try Vec first (common case for normal nodes), then HashMap for virtual nodes.
1786        if id < self.nodes.len() {
1787            let slot = self.nodes[id]
1788                .as_deref_mut()
1789                .ok_or(NodeError::Missing { id })?;
1790            return Ok(slot);
1791        }
1792        self.high_id_nodes
1793            .get_mut(&id)
1794            .map(|n| n.as_mut())
1795            .ok_or(NodeError::Missing { id })
1796    }
1797
1798    fn remove(&mut self, id: NodeId) -> Result<(), NodeError> {
1799        // Collect children before removing the node.
1800        let (children, is_high_id) = if id < self.nodes.len() {
1801            let slot = self.nodes.get(id).ok_or(NodeError::Missing { id })?;
1802            let node = slot.as_ref().ok_or(NodeError::Missing { id })?;
1803            (node.children(), false)
1804        } else if let Some(node) = self.high_id_nodes.get(&id) {
1805            (node.children(), true)
1806        } else {
1807            return Err(NodeError::Missing { id });
1808        };
1809
1810        // Recursively remove children owned by this node.
1811        for child_id in children {
1812            let is_owned = self
1813                .get_mut(child_id)
1814                .map(|child| child.parent() == Some(id))
1815                .unwrap_or(false);
1816            if is_owned {
1817                let _ = self.remove(child_id);
1818            }
1819        }
1820
1821        if is_high_id {
1822            self.high_id_nodes.remove(&id);
1823        } else {
1824            self.nodes[id].take();
1825        }
1826        Ok(())
1827    }
1828
1829    fn insert_with_id(&mut self, id: NodeId, node: Box<dyn Node>) -> Result<(), NodeError> {
1830        // Use HashMap for high IDs (virtual nodes) to avoid Vec capacity overflow
1831        // Virtual node IDs start at a very high value that can't fit in a Vec
1832        const HIGH_ID_THRESHOLD: NodeId = 1_000_000_000; // 1 billion
1833
1834        if id >= HIGH_ID_THRESHOLD {
1835            if self.high_id_nodes.contains_key(&id) {
1836                return Err(NodeError::AlreadyExists { id });
1837            }
1838            self.high_id_nodes.insert(id, node);
1839            Ok(())
1840        } else {
1841            // Normal Vec-based insertion for low IDs
1842            if id >= self.nodes.len() {
1843                self.nodes.resize_with(id + 1, || None);
1844            }
1845
1846            if self.nodes[id].is_some() {
1847                return Err(NodeError::AlreadyExists { id });
1848            }
1849
1850            self.nodes[id] = Some(node);
1851            Ok(())
1852        }
1853    }
1854}
1855
1856pub trait ApplierHost {
1857    fn borrow_dyn(&self) -> RefMut<'_, dyn Applier>;
1858}
1859
1860pub struct ConcreteApplierHost<A: Applier + 'static> {
1861    inner: RefCell<A>,
1862}
1863
1864impl<A: Applier + 'static> ConcreteApplierHost<A> {
1865    pub fn new(applier: A) -> Self {
1866        Self {
1867            inner: RefCell::new(applier),
1868        }
1869    }
1870
1871    pub fn borrow_typed(&self) -> RefMut<'_, A> {
1872        self.inner.borrow_mut()
1873    }
1874
1875    pub fn try_borrow_typed(&self) -> Result<RefMut<'_, A>, std::cell::BorrowMutError> {
1876        self.inner.try_borrow_mut()
1877    }
1878
1879    pub fn into_inner(self) -> A {
1880        self.inner.into_inner()
1881    }
1882}
1883
1884impl<A: Applier + 'static> ApplierHost for ConcreteApplierHost<A> {
1885    fn borrow_dyn(&self) -> RefMut<'_, dyn Applier> {
1886        RefMut::map(self.inner.borrow_mut(), |applier| {
1887            applier as &mut dyn Applier
1888        })
1889    }
1890}
1891
1892pub struct ApplierGuard<'a, A: Applier + 'static> {
1893    inner: RefMut<'a, A>,
1894}
1895
1896impl<'a, A: Applier + 'static> ApplierGuard<'a, A> {
1897    fn new(inner: RefMut<'a, A>) -> Self {
1898        Self { inner }
1899    }
1900}
1901
1902impl<'a, A: Applier + 'static> Deref for ApplierGuard<'a, A> {
1903    type Target = A;
1904
1905    fn deref(&self) -> &Self::Target {
1906        &self.inner
1907    }
1908}
1909
1910impl<'a, A: Applier + 'static> DerefMut for ApplierGuard<'a, A> {
1911    fn deref_mut(&mut self) -> &mut Self::Target {
1912        &mut self.inner
1913    }
1914}
1915
1916pub struct SlotsHost {
1917    inner: RefCell<SlotBackend>,
1918}
1919
1920impl SlotsHost {
1921    pub fn new(storage: SlotBackend) -> Self {
1922        Self {
1923            inner: RefCell::new(storage),
1924        }
1925    }
1926
1927    pub fn borrow(&self) -> Ref<'_, SlotBackend> {
1928        self.inner.borrow()
1929    }
1930
1931    pub fn borrow_mut(&self) -> RefMut<'_, SlotBackend> {
1932        self.inner.borrow_mut()
1933    }
1934
1935    pub fn take(&self) -> SlotBackend {
1936        std::mem::take(&mut *self.inner.borrow_mut())
1937    }
1938}
1939
1940pub(crate) struct ComposerCore {
1941    slots: Rc<SlotsHost>,
1942    slots_override: RefCell<Vec<Rc<SlotsHost>>>,
1943    applier: Rc<dyn ApplierHost>,
1944    runtime: RuntimeHandle,
1945    observer: SnapshotStateObserver,
1946    parent_stack: RefCell<Vec<ParentFrame>>,
1947    subcompose_stack: RefCell<Vec<SubcomposeFrame>>,
1948    root: Cell<Option<NodeId>>,
1949    commands: RefCell<Vec<Command>>,
1950    scope_stack: RefCell<Vec<RecomposeScope>>,
1951    local_stack: RefCell<LocalStackSnapshot>,
1952    side_effects: RefCell<Vec<Box<dyn FnOnce()>>>,
1953    pending_scope_options: RefCell<Option<RecomposeOptions>>,
1954    phase: Cell<Phase>,
1955    last_node_reused: Cell<Option<bool>>,
1956    recranpose_parent_hint: Cell<Option<NodeId>>,
1957    _not_send: PhantomData<*const ()>,
1958}
1959
1960impl ComposerCore {
1961    pub fn new(
1962        slots: Rc<SlotsHost>,
1963        applier: Rc<dyn ApplierHost>,
1964        runtime: RuntimeHandle,
1965        observer: SnapshotStateObserver,
1966        root: Option<NodeId>,
1967    ) -> Self {
1968        // Initialize parent_stack with root if provided.
1969        // This enables subcomposed nodes to be properly attached as children of the root
1970        // during composition via normal insert_child commands from pop_parent.
1971        // IMPORTANT: When using this, do NOT use set_active_children - let the composer
1972        // manage children naturally to avoid conflicts.
1973        let parent_stack = if let Some(root_id) = root {
1974            vec![ParentFrame {
1975                id: root_id,
1976                remembered: Owned::new(ParentChildren::default()),
1977                previous: Vec::new(),
1978                new_children: Vec::new(),
1979            }]
1980        } else {
1981            Vec::new()
1982        };
1983
1984        Self {
1985            slots,
1986            slots_override: RefCell::new(Vec::new()),
1987            applier,
1988            runtime,
1989            observer,
1990            parent_stack: RefCell::new(parent_stack),
1991            subcompose_stack: RefCell::new(Vec::new()),
1992            root: Cell::new(root),
1993            commands: RefCell::new(Vec::new()),
1994            scope_stack: RefCell::new(Vec::new()),
1995            local_stack: RefCell::new(Rc::new(Vec::new())),
1996            side_effects: RefCell::new(Vec::new()),
1997            pending_scope_options: RefCell::new(None),
1998            phase: Cell::new(Phase::Compose),
1999            last_node_reused: Cell::new(None),
2000            recranpose_parent_hint: Cell::new(None),
2001            _not_send: PhantomData,
2002        }
2003    }
2004}
2005
2006#[derive(Clone)]
2007pub struct Composer {
2008    core: Rc<ComposerCore>,
2009}
2010
2011impl Composer {
2012    pub fn new(
2013        slots: Rc<SlotsHost>,
2014        applier: Rc<dyn ApplierHost>,
2015        runtime: RuntimeHandle,
2016        observer: SnapshotStateObserver,
2017        root: Option<NodeId>,
2018    ) -> Self {
2019        let core = Rc::new(ComposerCore::new(slots, applier, runtime, observer, root));
2020        Self { core }
2021    }
2022
2023    pub(crate) fn from_core(core: Rc<ComposerCore>) -> Self {
2024        Self { core }
2025    }
2026
2027    pub(crate) fn clone_core(&self) -> Rc<ComposerCore> {
2028        Rc::clone(&self.core)
2029    }
2030
2031    fn observer(&self) -> SnapshotStateObserver {
2032        self.core.observer.clone()
2033    }
2034
2035    fn observe_scope<R>(&self, scope: &RecomposeScope, block: impl FnOnce() -> R) -> R {
2036        let observer = self.observer();
2037        let scope_clone = scope.clone();
2038        observer.observe_reads(scope_clone, move |scope_ref| scope_ref.invalidate(), block)
2039    }
2040
2041    fn active_slots_host(&self) -> Rc<SlotsHost> {
2042        self.core
2043            .slots_override
2044            .borrow()
2045            .last()
2046            .cloned()
2047            .unwrap_or_else(|| Rc::clone(&self.core.slots))
2048    }
2049
2050    fn with_slots<R>(&self, f: impl FnOnce(&SlotBackend) -> R) -> R {
2051        let host = self.active_slots_host();
2052        let slots = host.borrow();
2053        f(&slots)
2054    }
2055
2056    fn with_slots_mut<R>(&self, f: impl FnOnce(&mut SlotBackend) -> R) -> R {
2057        let host = self.active_slots_host();
2058        let mut slots = host.borrow_mut();
2059        f(&mut slots)
2060    }
2061
2062    fn with_slot_override<R>(&self, slots: Rc<SlotsHost>, f: impl FnOnce(&Composer) -> R) -> R {
2063        self.core.slots_override.borrow_mut().push(slots);
2064        struct Guard {
2065            core: Rc<ComposerCore>,
2066        }
2067        impl Drop for Guard {
2068            fn drop(&mut self) {
2069                self.core.slots_override.borrow_mut().pop();
2070            }
2071        }
2072        let guard = Guard {
2073            core: self.clone_core(),
2074        };
2075        let result = f(self);
2076        drop(guard);
2077        result
2078    }
2079
2080    fn parent_stack(&self) -> RefMut<'_, Vec<ParentFrame>> {
2081        self.core.parent_stack.borrow_mut()
2082    }
2083
2084    fn subcompose_stack(&self) -> RefMut<'_, Vec<SubcomposeFrame>> {
2085        self.core.subcompose_stack.borrow_mut()
2086    }
2087
2088    fn commands_mut(&self) -> RefMut<'_, Vec<Command>> {
2089        self.core.commands.borrow_mut()
2090    }
2091
2092    pub(crate) fn enqueue_semantics_invalidation(&self, id: NodeId) {
2093        self.commands_mut().push(Command::BubbleDirty {
2094            node_id: id,
2095            bubble: DirtyBubble::SEMANTICS,
2096        });
2097    }
2098
2099    fn scope_stack(&self) -> RefMut<'_, Vec<RecomposeScope>> {
2100        self.core.scope_stack.borrow_mut()
2101    }
2102
2103    fn local_stack(&self) -> RefMut<'_, LocalStackSnapshot> {
2104        self.core.local_stack.borrow_mut()
2105    }
2106
2107    fn current_local_stack(&self) -> LocalStackSnapshot {
2108        self.core.local_stack.borrow().clone()
2109    }
2110
2111    fn side_effects_mut(&self) -> RefMut<'_, Vec<Box<dyn FnOnce()>>> {
2112        self.core.side_effects.borrow_mut()
2113    }
2114
2115    fn pending_scope_options(&self) -> RefMut<'_, Option<RecomposeOptions>> {
2116        self.core.pending_scope_options.borrow_mut()
2117    }
2118
2119    fn borrow_applier(&self) -> RefMut<'_, dyn Applier> {
2120        self.core.applier.borrow_dyn()
2121    }
2122
2123    /// Registers a virtual node in the Applier.
2124    ///
2125    /// This is used by SubcomposeLayoutNode to register virtual container nodes
2126    /// so that subsequent insert_child commands can find them and attach children.
2127    /// Without this, virtual nodes would only exist in SubcomposeLayoutNodeInner.virtual_nodes
2128    /// and applier.get_mut(virtual_node_id) would fail, breaking child attachment.
2129    pub fn register_virtual_node(
2130        &self,
2131        node_id: NodeId,
2132        node: Box<dyn Node>,
2133    ) -> Result<(), NodeError> {
2134        let mut applier = self.borrow_applier();
2135        applier.insert_with_id(node_id, node)
2136    }
2137
2138    /// Checks if a node has no parent (is a root node).
2139    /// Used by SubcomposeMeasureScope to filter subcompose results.
2140    pub fn node_has_no_parent(&self, node_id: NodeId) -> bool {
2141        let mut applier = self.borrow_applier();
2142        match applier.get_mut(node_id) {
2143            Ok(node) => node.parent().is_none(),
2144            Err(_) => true, // If we can't find the node, treat it as root (conservative)
2145        }
2146    }
2147
2148    /// Gets the children of a node from the Applier.
2149    ///
2150    /// This is used by SubcomposeLayoutNode to get children of virtual nodes
2151    /// directly from the Applier, where insert_child commands have been applied.
2152    pub fn get_node_children(&self, node_id: NodeId) -> Vec<NodeId> {
2153        let mut applier = self.borrow_applier();
2154        match applier.get_mut(node_id) {
2155            Ok(node) => node.children(),
2156            Err(_) => Vec::new(),
2157        }
2158    }
2159
2160    /// Clears all children of a node in the Applier.
2161    ///
2162    /// This is used by SubcomposeLayoutNode when reusing a virtual node for
2163    /// different content. Without clearing, old children remain attached,
2164    /// causing duplicate/interleaved items in lazy lists after scrolling.
2165    pub fn clear_node_children(&self, node_id: NodeId) {
2166        let mut applier = self.borrow_applier();
2167        if let Ok(node) = applier.get_mut(node_id) {
2168            // Use update_children with empty slice to clear all children
2169            node.update_children(&[]);
2170        }
2171    }
2172
2173    pub fn install<R>(&self, f: impl FnOnce(&Composer) -> R) -> R {
2174        let _composer_guard = composer_context::enter(self);
2175        runtime::push_active_runtime(&self.core.runtime);
2176        struct Guard;
2177        impl Drop for Guard {
2178            fn drop(&mut self) {
2179                runtime::pop_active_runtime();
2180            }
2181        }
2182        let guard = Guard;
2183        let result = f(self);
2184        drop(guard);
2185        result
2186    }
2187
2188    pub fn with_group<R>(&self, key: Key, f: impl FnOnce(&Composer) -> R) -> R {
2189        let (group, scope_ref, restored_from_gap) = self.with_slots_mut(|slots| {
2190            let StartGroup {
2191                group,
2192                restored_from_gap,
2193            } = slots.begin_group(key);
2194            let scope_ref = slots
2195                .remember(|| RecomposeScope::new(self.runtime_handle()))
2196                .with(|scope| scope.clone());
2197            (group, scope_ref, restored_from_gap)
2198        });
2199
2200        if restored_from_gap {
2201            scope_ref.force_recompose();
2202        }
2203
2204        if let Some(options) = self.pending_scope_options().take() {
2205            if options.force_recompose {
2206                scope_ref.force_recompose();
2207            } else if options.force_reuse {
2208                scope_ref.force_reuse();
2209            }
2210        }
2211
2212        self.with_slots_mut(|slots| {
2213            SlotStorage::set_group_scope(slots, group, scope_ref.id());
2214        });
2215
2216        let slots_host = self.active_slots_host();
2217        scope_ref.set_slots_host(Rc::downgrade(&slots_host));
2218
2219        {
2220            let mut stack = self.scope_stack();
2221            stack.push(scope_ref.clone());
2222        }
2223
2224        {
2225            let mut stack = self.subcompose_stack();
2226            if let Some(frame) = stack.last_mut() {
2227                frame.scopes.push(scope_ref.clone());
2228            }
2229        }
2230
2231        scope_ref.snapshot_locals(self.current_local_stack());
2232        {
2233            let parent_hint = self.parent_stack().last().map(|frame| frame.id);
2234            scope_ref.set_parent_hint(parent_hint);
2235        }
2236
2237        let result = self.observe_scope(&scope_ref, || f(self));
2238
2239        let trimmed = self.with_slots_mut(|slots| slots.finalize_current_group());
2240        if trimmed {
2241            scope_ref.force_recompose();
2242        }
2243
2244        {
2245            let mut stack = self.scope_stack();
2246            stack.pop();
2247        }
2248        scope_ref.mark_recomposed();
2249        self.with_slots_mut(|slots| slots.end_group());
2250        result
2251    }
2252
2253    pub fn cranpose_with_reuse<R>(
2254        &self,
2255        key: Key,
2256        options: RecomposeOptions,
2257        f: impl FnOnce(&Composer) -> R,
2258    ) -> R {
2259        self.pending_scope_options().replace(options);
2260        self.with_group(key, f)
2261    }
2262
2263    pub fn with_key<K: Hash, R>(&self, key: &K, f: impl FnOnce(&Composer) -> R) -> R {
2264        let hashed = hash_key(key);
2265        self.with_group(hashed, f)
2266    }
2267
2268    pub fn remember<T: 'static>(&self, init: impl FnOnce() -> T) -> Owned<T> {
2269        self.with_slots_mut(|slots| slots.remember(init))
2270    }
2271
2272    pub fn use_value_slot<T: 'static>(&self, init: impl FnOnce() -> T) -> usize {
2273        self.with_slots_mut(|slots| slots.alloc_value_slot(init).index())
2274    }
2275
2276    pub fn with_slot_value<T: 'static, R>(&self, idx: usize, f: impl FnOnce(&T) -> R) -> R {
2277        self.with_slots(|slots| {
2278            let value = SlotStorage::read_value(slots, ValueSlotId::new(idx));
2279            f(value)
2280        })
2281    }
2282
2283    pub fn with_slot_value_mut<T: 'static, R>(&self, idx: usize, f: impl FnOnce(&mut T) -> R) -> R {
2284        self.with_slots_mut(|slots| {
2285            let value = SlotStorage::read_value_mut(slots, ValueSlotId::new(idx));
2286            f(value)
2287        })
2288    }
2289
2290    pub fn write_slot_value<T: 'static>(&self, idx: usize, value: T) {
2291        self.with_slots_mut(|slots| slots.write_value(ValueSlotId::new(idx), value));
2292    }
2293
2294    pub fn mutable_state_of<T: Clone + 'static>(&self, initial: T) -> MutableState<T> {
2295        MutableState::with_runtime(initial, self.runtime_handle())
2296    }
2297
2298    pub fn mutable_state_list_of<T, I>(&self, values: I) -> SnapshotStateList<T>
2299    where
2300        T: Clone + 'static,
2301        I: IntoIterator<Item = T>,
2302    {
2303        SnapshotStateList::with_runtime(values, self.runtime_handle())
2304    }
2305
2306    pub fn mutable_state_map_of<K, V, I>(&self, pairs: I) -> SnapshotStateMap<K, V>
2307    where
2308        K: Clone + Eq + Hash + 'static,
2309        V: Clone + 'static,
2310        I: IntoIterator<Item = (K, V)>,
2311    {
2312        SnapshotStateMap::with_runtime(pairs, self.runtime_handle())
2313    }
2314
2315    pub fn read_composition_local<T: Clone + 'static>(&self, local: &CompositionLocal<T>) -> T {
2316        let stack = self.core.local_stack.borrow();
2317        for context in stack.iter().rev() {
2318            if let Some(entry) = context.values.get(&local.key) {
2319                let typed = entry
2320                    .clone()
2321                    .downcast::<LocalStateEntry<T>>()
2322                    .expect("composition local type mismatch");
2323                return typed.value();
2324            }
2325        }
2326        local.default_value()
2327    }
2328
2329    pub fn read_static_composition_local<T: Clone + 'static>(
2330        &self,
2331        local: &StaticCompositionLocal<T>,
2332    ) -> T {
2333        let stack = self.core.local_stack.borrow();
2334        for context in stack.iter().rev() {
2335            if let Some(entry) = context.values.get(&local.key) {
2336                let typed = entry
2337                    .clone()
2338                    .downcast::<StaticLocalEntry<T>>()
2339                    .expect("static composition local type mismatch");
2340                return typed.value();
2341            }
2342        }
2343        local.default_value()
2344    }
2345
2346    pub fn current_recranpose_scope(&self) -> Option<RecomposeScope> {
2347        self.core.scope_stack.borrow().last().cloned()
2348    }
2349
2350    pub fn phase(&self) -> Phase {
2351        self.core.phase.get()
2352    }
2353
2354    pub(crate) fn set_phase(&self, phase: Phase) {
2355        self.core.phase.set(phase);
2356    }
2357
2358    pub fn enter_phase(&self, phase: Phase) {
2359        self.set_phase(phase);
2360    }
2361
2362    pub(crate) fn subcompose<R>(
2363        &self,
2364        state: &mut SubcomposeState,
2365        slot_id: SlotId,
2366        content: impl FnOnce(&Composer) -> R,
2367    ) -> (R, Vec<NodeId>) {
2368        match self.phase() {
2369            Phase::Measure | Phase::Layout => {}
2370            current => panic!(
2371                "subcompose() may only be called during measure or layout; current phase: {:?}",
2372                current
2373            ),
2374        }
2375
2376        self.subcompose_stack().push(SubcomposeFrame::default());
2377        struct StackGuard {
2378            core: Rc<ComposerCore>,
2379            leaked: bool,
2380        }
2381        impl Drop for StackGuard {
2382            fn drop(&mut self) {
2383                if !self.leaked {
2384                    self.core.subcompose_stack.borrow_mut().pop();
2385                }
2386            }
2387        }
2388        let mut guard = StackGuard {
2389            core: self.clone_core(),
2390            leaked: false,
2391        };
2392
2393        let slot_host = state.get_or_create_slots(slot_id);
2394        {
2395            let mut slots = slot_host.borrow_mut();
2396            slots.reset();
2397        }
2398        let result = self.with_slot_override(slot_host.clone(), |composer| {
2399            // Use with_group to create/reuse a group for this slot_id within the slot table.
2400            composer.with_group(slot_id.raw(), |composer| content(composer))
2401        });
2402        {
2403            let mut slots = slot_host.borrow_mut();
2404            slots.finalize_current_group();
2405            slots.flush();
2406        }
2407
2408        let frame = {
2409            let mut stack = guard.core.subcompose_stack.borrow_mut();
2410            let frame = stack.pop().expect("subcompose stack underflow");
2411            guard.leaked = true;
2412            frame
2413        };
2414        let nodes = frame.nodes;
2415        let scopes = frame.scopes;
2416        state.register_active(slot_id, &nodes, &scopes);
2417        (result, nodes)
2418    }
2419
2420    pub fn subcompose_measurement<R>(
2421        &self,
2422        state: &mut SubcomposeState,
2423        slot_id: SlotId,
2424        content: impl FnOnce(&Composer) -> R,
2425    ) -> (R, Vec<NodeId>) {
2426        let (result, nodes) = self.subcompose(state, slot_id, content);
2427
2428        // Filter to include only root nodes (those without a parent).
2429        // While record_node attempts to track only roots, checking the final
2430        // parent status ensures we only return true roots to the layout system.
2431        let roots = nodes
2432            .into_iter()
2433            .filter(|&id| self.node_has_no_parent(id))
2434            .collect();
2435
2436        (result, roots)
2437    }
2438
2439    pub fn subcompose_in<R>(
2440        &self,
2441        slots: &Rc<SlotsHost>,
2442        root: Option<NodeId>,
2443        f: impl FnOnce(&Composer) -> R,
2444    ) -> Result<R, NodeError> {
2445        let runtime_handle = self.runtime_handle();
2446        slots.borrow_mut().reset();
2447        let phase = self.phase();
2448        let locals = self.current_local_stack();
2449        let core = Rc::new(ComposerCore::new(
2450            Rc::clone(slots),
2451            Rc::clone(&self.core.applier),
2452            runtime_handle.clone(),
2453            self.observer(),
2454            root,
2455        ));
2456        core.phase.set(phase);
2457        *core.local_stack.borrow_mut() = locals;
2458        let composer = Composer::from_core(core);
2459        let (result, mut commands, side_effects) = composer.install(|composer| {
2460            let output = f(composer);
2461            let commands = composer.take_commands();
2462            let side_effects = composer.take_side_effects();
2463            (output, commands, side_effects)
2464        });
2465
2466        {
2467            let mut applier = self.borrow_applier();
2468            for command in commands.drain(..) {
2469                command.apply(&mut *applier)?;
2470            }
2471            for update in runtime_handle.take_updates() {
2472                update.apply(&mut *applier)?;
2473            }
2474        }
2475        runtime_handle.drain_ui();
2476        for effect in side_effects {
2477            effect();
2478        }
2479        runtime_handle.drain_ui();
2480        {
2481            let mut slots_mut = slots.borrow_mut();
2482            slots_mut.finalize_current_group();
2483            slots_mut.flush();
2484        }
2485        Ok(result)
2486    }
2487
2488    /// Subcomposes content using an isolated SlotsHost without resetting it.
2489    /// Unlike `subcompose_in`, this preserves existing slot state across calls,
2490    /// allowing efficient reuse during measurement passes. This is critical for
2491    /// lazy lists where items need stable slot positions.
2492    pub fn subcompose_slot<R>(
2493        &self,
2494        slots: &Rc<SlotsHost>,
2495        root: Option<NodeId>,
2496        f: impl FnOnce(&Composer) -> R,
2497    ) -> Result<(R, Vec<RecomposeScope>), NodeError> {
2498        let runtime_handle = self.runtime_handle();
2499        // Reset cursor to 0 but preserve slot data for reuse (like JC's setContentWithReuse)
2500        // This allows remembered values to be found and reused
2501        slots.borrow_mut().reset();
2502        let phase = self.phase();
2503        let locals = self.current_local_stack();
2504        let core = Rc::new(ComposerCore::new(
2505            Rc::clone(slots),
2506            Rc::clone(&self.core.applier),
2507            runtime_handle.clone(),
2508            self.observer(),
2509            root, // Root node for parent chain - enables dirty flag bubbling
2510        ));
2511        core.phase.set(phase);
2512        *core.local_stack.borrow_mut() = locals;
2513        let composer = Composer::from_core(core);
2514        composer.subcompose_stack().push(SubcomposeFrame::default());
2515        struct StackGuard {
2516            core: Rc<ComposerCore>,
2517            leaked: bool,
2518        }
2519        impl Drop for StackGuard {
2520            fn drop(&mut self) {
2521                if !self.leaked {
2522                    self.core.subcompose_stack.borrow_mut().pop();
2523                }
2524            }
2525        }
2526        let mut guard = StackGuard {
2527            core: composer.clone_core(),
2528            leaked: false,
2529        };
2530        let (result, mut commands, side_effects) = composer.install(|composer| {
2531            let output = f(composer);
2532            // CRITICAL FIX: Pop the root parent frame to generate insert_child commands.
2533            // Without this, the root frame's new_children list is populated but never
2534            // processed, so children are never attached to the virtual node.
2535            if root.is_some() {
2536                composer.pop_parent();
2537            }
2538            let commands = composer.take_commands();
2539            let side_effects = composer.take_side_effects();
2540            (output, commands, side_effects)
2541        });
2542        let frame = {
2543            let mut stack = guard.core.subcompose_stack.borrow_mut();
2544            let frame = stack.pop().expect("subcompose stack underflow");
2545            guard.leaked = true;
2546            frame
2547        };
2548
2549        {
2550            let mut applier = self.borrow_applier();
2551            for command in commands.drain(..) {
2552                command.apply(&mut *applier)?;
2553            }
2554            for update in runtime_handle.take_updates() {
2555                update.apply(&mut *applier)?;
2556            }
2557        }
2558        runtime_handle.drain_ui();
2559        for effect in side_effects {
2560            effect();
2561        }
2562        runtime_handle.drain_ui();
2563        // DON'T finalize or flush - for subcompose reuse, we need to keep all groups
2564        // in place so they can be found via O(1) HashMap lookup on the next measurement
2565        // pass. Calling finalize_current_group would convert valid lazy list item
2566        // groups to gaps if the cursor didn't reach them.
2567        Ok((result, frame.scopes))
2568    }
2569
2570    pub fn skip_current_group(&self) {
2571        let nodes = self.with_slots(|slots| slots.nodes_in_current_group());
2572        self.with_slots_mut(|slots| slots.skip_current_group());
2573        // Get the current parent from the stack (if any)
2574        let current_parent = {
2575            let stack = self.parent_stack();
2576            stack.last().map(|frame| frame.id)
2577        };
2578
2579        // Only attach nodes whose parent matches the current parent in the stack.
2580        // This ensures we only attach direct children of the current parent,
2581        // not nested nodes that belong to other nodes within the skipped group.
2582        let mut applier = self.borrow_applier();
2583        for id in nodes {
2584            if let Ok(node) = applier.get_mut(id) {
2585                let node_parent = node.parent();
2586                if node_parent.is_none() || node_parent == current_parent {
2587                    drop(applier);
2588                    self.attach_to_parent(id);
2589                    applier = self.borrow_applier();
2590                }
2591            }
2592        }
2593    }
2594
2595    pub fn runtime_handle(&self) -> RuntimeHandle {
2596        self.core.runtime.clone()
2597    }
2598
2599    pub fn set_recranpose_callback<F>(&self, callback: F)
2600    where
2601        F: FnMut(&Composer) + 'static,
2602    {
2603        if let Some(scope) = self.current_recranpose_scope() {
2604            let observer = self.observer();
2605            let scope_weak = scope.downgrade();
2606            let mut callback = callback;
2607            scope.set_recompose(Box::new(move |composer: &Composer| {
2608                if let Some(inner) = scope_weak.upgrade() {
2609                    let scope_instance = RecomposeScope { inner };
2610                    observer.observe_reads(
2611                        scope_instance.clone(),
2612                        move |scope_ref| scope_ref.invalidate(),
2613                        || {
2614                            callback(composer);
2615                        },
2616                    );
2617                }
2618            }));
2619        }
2620    }
2621
2622    pub fn with_composition_locals<R>(
2623        &self,
2624        provided: Vec<ProvidedValue>,
2625        f: impl FnOnce(&Composer) -> R,
2626    ) -> R {
2627        if provided.is_empty() {
2628            return f(self);
2629        }
2630        let mut context = LocalContext::default();
2631        for value in provided {
2632            let (key, entry) = value.into_entry(self);
2633            context.values.insert(key, entry);
2634        }
2635        {
2636            let mut stack = self.local_stack();
2637            Rc::make_mut(&mut *stack).push(context);
2638        }
2639        let result = f(self);
2640        {
2641            let mut stack = self.local_stack();
2642            Rc::make_mut(&mut *stack).pop();
2643        }
2644        result
2645    }
2646
2647    fn recranpose_group(&self, scope: &RecomposeScope) {
2648        // CRITICAL FIX: Check if scope is still invalid before recomposing.
2649        // When parent and child scopes are both invalidated, the child may be
2650        // visited (and marked recomposed) during parent's recomposition.
2651        // Without this check, we'd recompose the child again with wrong parent_stack,
2652        // causing nodes to get attached to root instead of their actual parent.
2653        if !scope.is_invalid() {
2654            scope.mark_recomposed();
2655            return;
2656        }
2657        let started = self.with_slots_mut(|slots| slots.begin_recranpose_at_scope(scope.id()));
2658        if started.is_some() {
2659            let previous_hint = self
2660                .core
2661                .recranpose_parent_hint
2662                .replace(scope.parent_hint());
2663            struct HintGuard {
2664                core: Rc<ComposerCore>,
2665                previous: Option<NodeId>,
2666            }
2667            impl Drop for HintGuard {
2668                fn drop(&mut self) {
2669                    self.core.recranpose_parent_hint.set(self.previous);
2670                }
2671            }
2672            let _hint_guard = HintGuard {
2673                core: self.clone_core(),
2674                previous: previous_hint,
2675            };
2676            {
2677                let mut stack = self.scope_stack();
2678                stack.push(scope.clone());
2679            }
2680            let saved_locals = self.current_local_stack();
2681            {
2682                let mut locals = self.local_stack();
2683                *locals = scope.local_stack();
2684            }
2685            self.observe_scope(scope, || {
2686                scope.run_recompose(self);
2687            });
2688            {
2689                let mut locals = self.local_stack();
2690                *locals = saved_locals;
2691            }
2692            {
2693                let mut stack = self.scope_stack();
2694                stack.pop();
2695            }
2696            self.with_slots_mut(SlotStorage::end_recompose);
2697            scope.mark_recomposed();
2698        } else {
2699            scope.mark_recomposed();
2700        }
2701    }
2702
2703    pub fn use_state<T: Clone + 'static>(&self, init: impl FnOnce() -> T) -> MutableState<T> {
2704        let runtime = self.runtime_handle();
2705        let state = self.with_slots_mut(|slots| {
2706            slots.remember(|| OwnedMutableState::with_runtime(init(), runtime.clone()))
2707        });
2708        state.with(|state| state.handle())
2709    }
2710
2711    pub fn emit_node<N: Node + 'static>(&self, init: impl FnOnce() -> N) -> NodeId {
2712        // Peek at the slot without advancing cursor
2713        let (existing_id, type_matches) = {
2714            if let Some(id) = self.with_slots_mut(|slots| slots.peek_node()) {
2715                // Check if the node type matches
2716                let mut applier = self.borrow_applier();
2717                let matches = match applier.get_mut(id) {
2718                    Ok(node) => node.as_any_mut().downcast_ref::<N>().is_some(),
2719                    Err(_) => false,
2720                };
2721                (Some(id), matches)
2722            } else {
2723                (None, false)
2724            }
2725        };
2726
2727        // If we have a matching node, advance cursor and reuse it
2728        if let Some(id) = existing_id {
2729            if type_matches {
2730                // Type matches - reuse this node. The push_parent conditional ensures
2731                // that new parents start with empty previous children, so we don't
2732                // accidentally inherit children from a different parent.
2733                let reuse_allowed = true;
2734
2735                #[cfg(not(target_arch = "wasm32"))]
2736                if compose_debug_enabled() {
2737                    eprintln!("emit_node: candidate #{id} reuse_allowed={reuse_allowed}");
2738                }
2739
2740                if reuse_allowed {
2741                    self.core.last_node_reused.set(Some(true));
2742                    #[cfg(not(target_arch = "wasm32"))]
2743                    if compose_debug_enabled() {
2744                        eprintln!(
2745                            "emit_node: reusing node #{id} as {}",
2746                            std::any::type_name::<N>()
2747                        );
2748                    }
2749                    self.with_slots_mut(|slots| slots.advance_after_node_read());
2750
2751                    self.commands_mut().push(Command::update_node::<N>(id));
2752                    self.attach_to_parent(id);
2753                    return id;
2754                }
2755            }
2756        }
2757
2758        // If there was a mismatched node in this slot, schedule its removal before creating a new one.
2759        if let Some(old_id) = existing_id {
2760            if !type_matches {
2761                #[cfg(not(target_arch = "wasm32"))]
2762                if compose_debug_enabled() {
2763                    eprintln!(
2764                        "emit_node: replacing node #{old_id} with new {}",
2765                        std::any::type_name::<N>()
2766                    );
2767                }
2768                self.commands_mut().push(Command::RemoveNode { id: old_id });
2769            }
2770        }
2771
2772        // Type mismatch or no node: create new node
2773        // record_node() will handle replacing the mismatched slot
2774        let id = {
2775            let mut applier = self.borrow_applier();
2776            applier.create(Box::new(init()))
2777        };
2778        self.core.last_node_reused.set(Some(false));
2779        #[cfg(not(target_arch = "wasm32"))]
2780        if compose_debug_enabled() {
2781            eprintln!(
2782                "emit_node: creating node #{} as {}",
2783                id,
2784                std::any::type_name::<N>()
2785            );
2786        }
2787        {
2788            self.with_slots_mut(|slots| slots.record_node(id));
2789        }
2790        self.commands_mut().push(Command::MountNode { id });
2791        self.attach_to_parent(id);
2792        id
2793    }
2794
2795    fn attach_to_parent(&self, id: NodeId) {
2796        // IMPORTANT: Check parent_stack FIRST.
2797        // During subcomposition, if there's an active parent (e.g., Row),
2798        // child nodes (e.g., Text) should attach to that parent, NOT to the
2799        // subcompose frame. Only ROOT nodes (nodes with no active parent)
2800        // should be added to the subcompose frame.
2801        let mut parent_stack = self.parent_stack();
2802        if let Some(frame) = parent_stack.last_mut() {
2803            let parent_id = frame.id;
2804            if parent_id == id {
2805                return;
2806            }
2807            frame.new_children.push(id);
2808            drop(parent_stack);
2809
2810            // KEY FIX: Set parent link IMMEDIATELY, matching Jetpack Compose's
2811            // LayoutNode.insertAt pattern where _foldedParent is set synchronously.
2812            // This ensures that when bubble_measure_dirty runs (in commands),
2813            // the parent chain is already established.
2814            //
2815            // IMPORTANT: Only set parent if node doesn't have one or if the new parent
2816            // is not the root. This prevents double-recomposition scenarios where a
2817            // child scope (invalidated by CompositionLocalProvider during parent's
2818            // recomposition) gets processed again with parent_stack=[root], which would
2819            // incorrectly reparent nodes to root.
2820            {
2821                let mut applier = self.borrow_applier();
2822                if let Ok(child_node) = applier.get_mut(id) {
2823                    let existing_parent = child_node.parent();
2824                    // Only set parent if:
2825                    // 1. Node has no parent, OR
2826                    // 2. New parent is NOT the root (parent_id != 0 or != self.root)
2827                    // This prevents root from stealing children that belong to intermediate nodes.
2828                    let should_set = match existing_parent {
2829                        None => true,
2830                        Some(existing) => {
2831                            // Don't let root steal children from proper parents
2832                            let root_id = self.core.root.get();
2833                            parent_id != root_id.unwrap_or(0) || existing == root_id.unwrap_or(0)
2834                        }
2835                    };
2836                    if should_set {
2837                        child_node.set_parent_for_bubbling(parent_id);
2838                    }
2839                }
2840            }
2841            return;
2842        }
2843        drop(parent_stack);
2844
2845        // No active parent - check if we're in subcompose
2846        let in_subcompose = !self.subcompose_stack().is_empty();
2847        if in_subcompose {
2848            // During subcompose, only add ROOT nodes (nodes without a parent).
2849            // Child nodes already have their parent-child relationship from composition;
2850            // re-adding them to the subcompose frame would cause duplication.
2851            let has_parent = {
2852                let mut applier = self.borrow_applier();
2853                applier
2854                    .get_mut(id)
2855                    .map(|node| node.parent().is_some())
2856                    .unwrap_or(false)
2857            };
2858
2859            if !has_parent {
2860                let mut subcompose_stack = self.subcompose_stack();
2861                if let Some(frame) = subcompose_stack.last_mut() {
2862                    frame.nodes.push(id);
2863                }
2864            }
2865            return;
2866        }
2867
2868        // During recomposition, preserve the original parent when possible.
2869        if let Some(parent_hint) = self.core.recranpose_parent_hint.get() {
2870            let parent_status = {
2871                let mut applier = self.borrow_applier();
2872                applier
2873                    .get_mut(id)
2874                    .map(|node| node.parent())
2875                    .unwrap_or(None)
2876            };
2877            match parent_status {
2878                Some(existing) if existing == parent_hint => {}
2879                None => {
2880                    self.commands_mut().push(Command::AttachChild {
2881                        parent_id: parent_hint,
2882                        child_id: id,
2883                        bubble: DirtyBubble::LAYOUT_AND_MEASURE,
2884                    });
2885                }
2886                Some(_) => {}
2887            }
2888            return;
2889        }
2890
2891        // Neither parent nor subcompose - check if this node already has a parent.
2892        // During recomposition, reused nodes already have their correct parent from
2893        // initial composition. We should NOT set them as root, as that would corrupt
2894        // the tree structure and cause duplication.
2895        let has_parent = {
2896            let mut applier = self.borrow_applier();
2897            applier
2898                .get_mut(id)
2899                .map(|node| node.parent().is_some())
2900                .unwrap_or(false)
2901        };
2902        if has_parent {
2903            // Node already has a parent, nothing to do
2904            return;
2905        }
2906
2907        // Node has no parent and is not in subcompose - must be root
2908        self.set_root(Some(id));
2909    }
2910
2911    pub fn with_node_mut<N: Node + 'static, R>(
2912        &self,
2913        id: NodeId,
2914        f: impl FnOnce(&mut N) -> R,
2915    ) -> Result<R, NodeError> {
2916        let mut applier = self.borrow_applier();
2917        let node = applier.get_mut(id)?;
2918        let typed = node
2919            .as_any_mut()
2920            .downcast_mut::<N>()
2921            .ok_or(NodeError::TypeMismatch {
2922                id,
2923                expected: std::any::type_name::<N>(),
2924            })?;
2925        Ok(f(typed))
2926    }
2927
2928    pub fn push_parent(&self, id: NodeId) {
2929        let remembered = self.remember(ParentChildren::default);
2930        let reused = self.core.last_node_reused.take().unwrap_or(true);
2931        let in_subcompose = !self.core.subcompose_stack.borrow().is_empty();
2932
2933        // Only carry over previous children when the parent was reused (or in subcompose).
2934        // Otherwise, start fresh to prevent nodes from teleporting between parents.
2935        let previous = if reused || in_subcompose {
2936            remembered.with(|entry| entry.children.clone())
2937        } else {
2938            Vec::new()
2939        };
2940
2941        self.parent_stack().push(ParentFrame {
2942            id,
2943            remembered,
2944            previous,
2945            new_children: Vec::new(),
2946        });
2947    }
2948
2949    pub fn pop_parent(&self) {
2950        let frame_opt = {
2951            let mut stack = self.parent_stack();
2952            stack.pop()
2953        };
2954        if let Some(frame) = frame_opt {
2955            let ParentFrame {
2956                id,
2957                remembered,
2958                previous,
2959                new_children,
2960            } = frame;
2961
2962            #[cfg(not(target_arch = "wasm32"))]
2963            if compose_debug_enabled() {
2964                eprintln!("pop_parent: node #{}", id);
2965                eprintln!("  previous children: {:?}", previous);
2966                eprintln!("  new children: {:?}", new_children);
2967            }
2968            let children_changed = previous != new_children;
2969
2970            if children_changed {
2971                let target = &new_children;
2972                let mut target_positions = HashMap::default();
2973                target_positions.reserve(target.len());
2974                for (index, &child) in target.iter().enumerate() {
2975                    target_positions.insert(child, index);
2976                }
2977
2978                let mut current = previous;
2979
2980                for index in (0..current.len()).rev() {
2981                    let child = current[index];
2982                    if !target_positions.contains_key(&child) {
2983                        current.remove(index);
2984                        self.commands_mut().push(Command::RemoveChild {
2985                            parent_id: id,
2986                            child_id: child,
2987                        });
2988                    }
2989                }
2990
2991                let mut current_positions = build_child_positions(&current);
2992                for (target_index, &child) in target.iter().enumerate() {
2993                    if let Some(current_index) = current_positions.get(&child).copied() {
2994                        if current_index != target_index {
2995                            let from_index = current_index;
2996                            let to_index = move_child_in_diff_state(
2997                                &mut current,
2998                                &mut current_positions,
2999                                from_index,
3000                                target_index,
3001                            );
3002                            self.commands_mut().push(Command::MoveChild {
3003                                parent_id: id,
3004                                from_index,
3005                                to_index,
3006                                bubble: DirtyBubble::LAYOUT_AND_MEASURE,
3007                            });
3008                        }
3009                    } else {
3010                        let insert_index = target_index.min(current.len());
3011                        let appended_index = current.len();
3012                        insert_child_into_diff_state(
3013                            &mut current,
3014                            &mut current_positions,
3015                            insert_index,
3016                            child,
3017                        );
3018                        self.commands_mut().push(Command::InsertChild {
3019                            parent_id: id,
3020                            child_id: child,
3021                            appended_index,
3022                            insert_index,
3023                            bubble: DirtyBubble::LAYOUT_AND_MEASURE,
3024                        });
3025                    }
3026                }
3027            }
3028
3029            let expected_children = new_children.clone();
3030            let needs_dirty_check = !children_changed;
3031            self.commands_mut().push(Command::ReconcileChildren {
3032                parent_id: id,
3033                expected_children,
3034                needs_dirty_check,
3035            });
3036
3037            remembered.update(|entry| entry.children = new_children);
3038        }
3039    }
3040
3041    pub(crate) fn take_commands(&self) -> Vec<Command> {
3042        std::mem::take(&mut *self.commands_mut())
3043    }
3044
3045    /// Applies any pending applier commands and runtime updates.
3046    ///
3047    /// This is useful during measure-time subcomposition to ensure newly created
3048    /// nodes are available for measurement before the full composition is committed.
3049    pub fn apply_pending_commands(&self) -> Result<(), NodeError> {
3050        let mut commands = self.take_commands();
3051        let runtime_handle = self.runtime_handle();
3052        {
3053            let mut applier = self.borrow_applier();
3054            for command in commands.drain(..) {
3055                command.apply(&mut *applier)?;
3056            }
3057            for update in runtime_handle.take_updates() {
3058                update.apply(&mut *applier)?;
3059            }
3060        }
3061        runtime_handle.drain_ui();
3062        Ok(())
3063    }
3064
3065    pub fn register_side_effect(&self, effect: impl FnOnce() + 'static) {
3066        self.side_effects_mut().push(Box::new(effect));
3067    }
3068
3069    pub fn take_side_effects(&self) -> Vec<Box<dyn FnOnce()>> {
3070        std::mem::take(&mut *self.side_effects_mut())
3071    }
3072
3073    pub(crate) fn root(&self) -> Option<NodeId> {
3074        self.core.root.get()
3075    }
3076
3077    pub(crate) fn set_root(&self, node: Option<NodeId>) {
3078        self.core.root.set(node);
3079    }
3080}
3081
3082#[derive(Default, Clone)]
3083struct ParentChildren {
3084    children: Vec<NodeId>,
3085}
3086
3087fn build_child_positions(children: &[NodeId]) -> HashMap<NodeId, usize> {
3088    let mut positions = HashMap::default();
3089    positions.reserve(children.len());
3090    for (index, &child) in children.iter().enumerate() {
3091        positions.insert(child, index);
3092    }
3093    positions
3094}
3095
3096fn refresh_child_positions(
3097    current: &[NodeId],
3098    positions: &mut HashMap<NodeId, usize>,
3099    start: usize,
3100    end: usize,
3101) {
3102    if current.is_empty() || start >= current.len() {
3103        return;
3104    }
3105    let end = end.min(current.len() - 1);
3106    for (offset, &child) in current[start..=end].iter().enumerate() {
3107        positions.insert(child, start + offset);
3108    }
3109}
3110
3111fn insert_child_into_diff_state(
3112    current: &mut Vec<NodeId>,
3113    positions: &mut HashMap<NodeId, usize>,
3114    index: usize,
3115    child: NodeId,
3116) {
3117    let index = index.min(current.len());
3118    current.insert(index, child);
3119    refresh_child_positions(current, positions, index, current.len() - 1);
3120}
3121
3122fn move_child_in_diff_state(
3123    current: &mut Vec<NodeId>,
3124    positions: &mut HashMap<NodeId, usize>,
3125    from_index: usize,
3126    target_index: usize,
3127) -> usize {
3128    let child = current.remove(from_index);
3129    let to_index = target_index.min(current.len());
3130    current.insert(to_index, child);
3131    refresh_child_positions(
3132        current,
3133        positions,
3134        from_index.min(to_index),
3135        from_index.max(to_index),
3136    );
3137    to_index
3138}
3139
3140struct ParentFrame {
3141    id: NodeId,
3142    remembered: Owned<ParentChildren>,
3143    previous: Vec<NodeId>,
3144    new_children: Vec<NodeId>,
3145}
3146
3147#[derive(Default)]
3148struct SubcomposeFrame {
3149    nodes: Vec<NodeId>,
3150    scopes: Vec<RecomposeScope>,
3151}
3152
3153#[derive(Default, Clone)]
3154struct LocalContext {
3155    values: HashMap<LocalKey, Rc<dyn Any>>,
3156}
3157
3158pub(crate) struct MutableStateInner<T: Clone + 'static> {
3159    state: Arc<SnapshotMutableState<T>>,
3160    watchers: RefCell<HashMap<ScopeId, Weak<RecomposeScopeInner>>>, // FUTURE(no_std): move to stack-allocated subscription list.
3161    runtime: RuntimeHandle,
3162}
3163
3164impl<T: Clone + 'static> MutableStateInner<T> {
3165    fn new(value: T, runtime: RuntimeHandle) -> Self {
3166        Self {
3167            state: SnapshotMutableState::new_in_arc(value, Arc::new(NeverEqual)),
3168            watchers: RefCell::new(HashMap::default()),
3169            runtime,
3170        }
3171    }
3172
3173    fn install_snapshot_observer(&self, state_id: StateId) {
3174        let runtime_handle = self.runtime.clone();
3175        self.state.add_apply_observer(Box::new(move || {
3176            let runtime = runtime_handle.clone();
3177            runtime_handle.enqueue_ui_task(Box::new(move || {
3178                runtime.with_state_arena(|arena| {
3179                    let _ = arena.with_typed_opt::<T, _>(state_id, |inner| {
3180                        inner.invalidate_watchers();
3181                    });
3182                });
3183            }));
3184        }));
3185    }
3186
3187    fn with_value<R>(&self, f: impl FnOnce(&T) -> R) -> R {
3188        let value = self.state.get();
3189        f(&value)
3190    }
3191
3192    fn register_scope(&self, scope: &RecomposeScope) -> bool {
3193        let mut watchers = self.watchers.borrow_mut();
3194        match watchers.get(&scope.id()) {
3195            Some(existing) if existing.upgrade().is_some() => false,
3196            _ => {
3197                watchers.insert(scope.id(), scope.downgrade());
3198                true
3199            }
3200        }
3201    }
3202
3203    fn invalidate_watchers(&self) {
3204        let watchers: Vec<RecomposeScope> = {
3205            let mut watchers = self.watchers.borrow_mut();
3206            let mut live = Vec::with_capacity(watchers.len());
3207            watchers.retain(|_, weak| {
3208                if let Some(inner) = weak.upgrade() {
3209                    live.push(RecomposeScope { inner });
3210                    true
3211                } else {
3212                    false
3213                }
3214            });
3215            live
3216        };
3217
3218        if input_debug_enabled() {
3219            let scope_ids: Vec<_> = watchers.iter().map(|scope| scope.id()).collect();
3220            eprintln!(
3221                "[CRANPOSE_INPUT_DEBUG] state {:?} invalidate_watchers count={} scopes={:?}",
3222                self.state.id(),
3223                scope_ids.len(),
3224                scope_ids
3225            );
3226        }
3227
3228        for watcher in watchers {
3229            watcher.invalidate();
3230        }
3231    }
3232}
3233
3234fn register_current_state_scope<T: Clone + 'static>(inner: &MutableStateInner<T>) {
3235    let Some(Some(scope)) =
3236        with_current_composer_opt(|composer| composer.current_recranpose_scope())
3237    else {
3238        return;
3239    };
3240    let inserted = inner.register_scope(&scope);
3241    if inserted && input_debug_enabled() {
3242        let watchers = inner.watchers.borrow();
3243        eprintln!(
3244            "[CRANPOSE_INPUT_DEBUG] state {:?} subscribe scope={} watchers={}",
3245            inner.state.id(),
3246            scope.id(),
3247            watchers.len()
3248        );
3249    }
3250}
3251
3252/// Cheap copyable read-only view of a state cell.
3253pub struct State<T: Clone + 'static> {
3254    id: StateId,
3255    runtime_id: runtime::RuntimeId,
3256    _marker: PhantomData<fn() -> T>,
3257}
3258
3259/// Cheap copyable mutable view of a state cell.
3260///
3261/// Ownership lives elsewhere: a composition slot, an [`OwnedMutableState`], or
3262/// the runtime for states created with [`mutableStateOf`] / [`MutableState::with_runtime`].
3263pub struct MutableState<T: Clone + 'static> {
3264    id: StateId,
3265    runtime_id: runtime::RuntimeId,
3266    _marker: PhantomData<fn() -> T>,
3267}
3268
3269/// Owning state handle for reclaimable state cells.
3270///
3271/// Store this when the state lifetime should be tied to a Rust object rather
3272/// than the runtime. Use [`OwnedMutableState::handle`] to expose a cheap copyable
3273/// [`MutableState`] to call sites.
3274#[derive(Clone)]
3275pub struct OwnedMutableState<T: Clone + 'static> {
3276    state: MutableState<T>,
3277    _lease: Rc<runtime::StateHandleLease>,
3278    _marker: PhantomData<fn() -> T>,
3279}
3280
3281impl<T: Clone + 'static> PartialEq for State<T> {
3282    fn eq(&self, other: &Self) -> bool {
3283        self.state_id() == other.state_id() && self.runtime_id() == other.runtime_id()
3284    }
3285}
3286
3287impl<T: Clone + 'static> Eq for State<T> {}
3288
3289impl<T: Clone + 'static> PartialEq for MutableState<T> {
3290    fn eq(&self, other: &Self) -> bool {
3291        self.state_id() == other.state_id() && self.runtime_id() == other.runtime_id()
3292    }
3293}
3294
3295impl<T: Clone + 'static> Eq for MutableState<T> {}
3296
3297impl<T: Clone + 'static> Copy for State<T> {}
3298
3299impl<T: Clone + 'static> Clone for State<T> {
3300    fn clone(&self) -> Self {
3301        *self
3302    }
3303}
3304
3305impl<T: Clone + 'static> Copy for MutableState<T> {}
3306
3307impl<T: Clone + 'static> Clone for MutableState<T> {
3308    fn clone(&self) -> Self {
3309        *self
3310    }
3311}
3312
3313impl<T: Clone + 'static> State<T> {
3314    fn state_id(&self) -> StateId {
3315        self.id
3316    }
3317
3318    fn runtime_id(&self) -> runtime::RuntimeId {
3319        self.runtime_id
3320    }
3321
3322    fn runtime_handle(&self) -> RuntimeHandle {
3323        runtime::runtime_handle_by_id(self.runtime_id())
3324            .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
3325    }
3326
3327    fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
3328        self.runtime_handle()
3329            .with_state_arena(|arena| arena.with_typed::<T, R>(self.state_id(), f))
3330    }
3331
3332    fn subscribe_current_scope(&self) {
3333        self.with_inner(register_current_state_scope::<T>);
3334    }
3335
3336    pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
3337        self.subscribe_current_scope();
3338        self.with_inner(|inner| inner.with_value(f))
3339    }
3340
3341    pub fn value(&self) -> T {
3342        self.subscribe_current_scope();
3343        self.with_inner(|inner| inner.state.get())
3344    }
3345
3346    pub fn get(&self) -> T {
3347        self.value()
3348    }
3349}
3350
3351impl<T: Clone + 'static> MutableState<T> {
3352    /// Creates a runtime-owned persistent state handle.
3353    pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
3354        runtime.alloc_persistent_state(value)
3355    }
3356
3357    fn from_parts(id: StateId, runtime_id: runtime::RuntimeId) -> Self {
3358        Self {
3359            id,
3360            runtime_id,
3361            _marker: PhantomData,
3362        }
3363    }
3364
3365    pub(crate) fn from_lease(lease: &Rc<runtime::StateHandleLease>) -> Self {
3366        Self::from_parts(lease.id(), lease.runtime().id())
3367    }
3368
3369    fn state_id(&self) -> StateId {
3370        self.id
3371    }
3372
3373    fn runtime_id(&self) -> runtime::RuntimeId {
3374        self.runtime_id
3375    }
3376
3377    fn runtime_handle(&self) -> RuntimeHandle {
3378        runtime::runtime_handle_by_id(self.runtime_id())
3379            .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
3380    }
3381
3382    fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
3383        self.runtime_handle()
3384            .with_state_arena(|arena| arena.with_typed::<T, R>(self.state_id(), f))
3385    }
3386
3387    fn try_with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> Option<R> {
3388        self.runtime_handle()
3389            .with_state_arena(|arena| arena.with_typed_opt::<T, R>(self.state_id(), f))
3390    }
3391
3392    /// Returns `true` if the underlying state cell is still alive (not released).
3393    pub fn is_alive(&self) -> bool {
3394        self.try_with_inner(|_| ()).is_some()
3395    }
3396
3397    /// Like `with`, but returns `None` if the state cell has been released.
3398    ///
3399    /// Use this in frame callbacks, fling animations, and other contexts where
3400    /// the owning composition group may have been disposed (e.g., tab switch).
3401    pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
3402        self.try_with_inner(|inner| inner.with_value(f))
3403    }
3404
3405    /// Like `value`, but returns `None` if the state cell has been released.
3406    pub fn try_value(&self) -> Option<T> {
3407        self.try_with_inner(|inner| inner.state.get())
3408    }
3409
3410    pub fn as_state(&self) -> State<T> {
3411        State {
3412            id: self.id,
3413            runtime_id: self.runtime_id,
3414            _marker: PhantomData,
3415        }
3416    }
3417
3418    /// Upgrades this handle into an owning state value if the cell is still alive.
3419    pub fn try_retain(&self) -> Option<OwnedMutableState<T>> {
3420        let lease = self.runtime_handle().retain_state_lease(self.state_id())?;
3421        Some(OwnedMutableState {
3422            state: *self,
3423            _lease: lease,
3424            _marker: PhantomData,
3425        })
3426    }
3427
3428    /// Upgrades this handle into an owning state value.
3429    pub fn retain(&self) -> OwnedMutableState<T> {
3430        self.try_retain()
3431            .unwrap_or_else(|| panic!("state {:?} is no longer alive", self.state_id()))
3432    }
3433
3434    pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
3435        self.subscribe_current_scope();
3436        self.with_inner(|inner| inner.with_value(f))
3437    }
3438
3439    pub fn update<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
3440        let runtime = self.runtime_handle();
3441        runtime.assert_ui_thread();
3442        runtime.with_state_arena(|arena| {
3443            arena.with_typed::<T, R>(self.state_id(), |inner| {
3444                let mut value = inner.state.get();
3445                let tracker = UpdateScope::new(inner.state.id());
3446                let result = f(&mut value);
3447                let wrote_elsewhere = tracker.finish();
3448                if !wrote_elsewhere {
3449                    inner.state.set(value);
3450                }
3451                inner.invalidate_watchers();
3452                result
3453            })
3454        })
3455    }
3456
3457    /// Sets a new value, silently skipping the write if the state cell was released.
3458    ///
3459    /// State cells can be released between when a `SideEffect` closure captures a
3460    /// `MutableState` handle and when the side effect actually runs (command
3461    /// application disposes composition groups, freeing their state slots, before
3462    /// side effects execute). Using `with_typed_opt` instead of `with_typed`
3463    /// avoids a panic on the stale handle.
3464    pub fn replace(&self, value: T) {
3465        let runtime = self.runtime_handle();
3466        runtime.assert_ui_thread();
3467        runtime.with_state_arena(|arena| {
3468            if arena
3469                .with_typed_opt::<T, ()>(self.state_id(), |inner| {
3470                    inner.state.set(value);
3471                    inner.invalidate_watchers();
3472                })
3473                .is_none()
3474            {
3475                log::debug!(
3476                    "MutableState::replace skipped: state cell released (slot={}, gen={})",
3477                    self.state_id().slot(),
3478                    self.state_id().generation(),
3479                );
3480            }
3481        });
3482    }
3483
3484    pub fn set_value(&self, value: T) {
3485        self.replace(value);
3486    }
3487
3488    pub fn set(&self, value: T) {
3489        self.replace(value);
3490    }
3491
3492    pub fn value(&self) -> T {
3493        self.subscribe_current_scope();
3494        self.with_inner(|inner| inner.state.get())
3495    }
3496
3497    pub fn get(&self) -> T {
3498        self.value()
3499    }
3500
3501    /// Gets the current value WITHOUT subscribing to recomposition.
3502    ///
3503    /// Use this in layout/measure/draw phases to read state without causing
3504    /// the current composition scope to recompose when the state changes.
3505    ///
3506    /// # When to use
3507    /// - In modifier nodes (like ScrollNode) during measure()
3508    /// - In any layout phase code that reads state but shouldn't trigger recomposition
3509    ///
3510    /// # When NOT to use
3511    /// - In composable functions that should update when state changes
3512    /// - When you want reactive UI updates
3513    pub fn get_non_reactive(&self) -> T {
3514        // Skip subscribe_current_scope() - just read the value directly
3515        self.with_inner(|inner| inner.state.get())
3516    }
3517
3518    fn subscribe_current_scope(&self) {
3519        self.with_inner(register_current_state_scope::<T>);
3520    }
3521
3522    #[cfg(test)]
3523    pub(crate) fn watcher_count(&self) -> usize {
3524        self.with_inner(|inner| inner.watchers.borrow().len())
3525    }
3526
3527    #[cfg(test)]
3528    pub(crate) fn state_id_for_test(&self) -> StateId {
3529        self.state_id()
3530    }
3531
3532    #[cfg(test)]
3533    pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
3534        self.as_state().subscribe_scope_for_test(scope);
3535    }
3536}
3537
3538impl<T: Clone + 'static> OwnedMutableState<T> {
3539    /// Creates a reclaimable state owned by this Rust value.
3540    pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
3541        let lease = runtime.alloc_state(value);
3542        Self {
3543            state: MutableState::from_lease(&lease),
3544            _lease: lease,
3545            _marker: PhantomData,
3546        }
3547    }
3548
3549    /// Returns a cheap copyable mutable handle to the owned state.
3550    pub fn handle(&self) -> MutableState<T> {
3551        self.state
3552    }
3553
3554    /// Returns a cheap copyable read-only handle to the owned state.
3555    pub fn as_state(&self) -> State<T> {
3556        self.state.as_state()
3557    }
3558}
3559
3560impl<T: Clone + 'static> Deref for OwnedMutableState<T> {
3561    type Target = MutableState<T>;
3562
3563    fn deref(&self) -> &Self::Target {
3564        &self.state
3565    }
3566}
3567
3568#[cfg(test)]
3569impl<T: Clone + 'static> State<T> {
3570    pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
3571        self.with_inner(|inner| {
3572            inner.register_scope(scope);
3573        });
3574    }
3575}
3576
3577impl<T: fmt::Debug + Clone + 'static> fmt::Debug for MutableState<T> {
3578    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3579        self.with_inner(|inner| {
3580            inner.with_value(|value| {
3581                f.debug_struct("MutableState")
3582                    .field("value", value)
3583                    .finish()
3584            })
3585        })
3586    }
3587}
3588
3589#[derive(Clone)]
3590pub struct SnapshotStateList<T: Clone + 'static> {
3591    state: OwnedMutableState<Vec<T>>,
3592}
3593
3594impl<T: Clone + 'static> SnapshotStateList<T> {
3595    pub fn with_runtime<I>(values: I, runtime: RuntimeHandle) -> Self
3596    where
3597        I: IntoIterator<Item = T>,
3598    {
3599        let initial: Vec<T> = values.into_iter().collect();
3600        Self {
3601            state: OwnedMutableState::with_runtime(initial, runtime),
3602        }
3603    }
3604
3605    pub fn as_state(&self) -> State<Vec<T>> {
3606        self.state.as_state()
3607    }
3608
3609    pub fn as_mutable_state(&self) -> MutableState<Vec<T>> {
3610        self.state.handle()
3611    }
3612
3613    pub fn len(&self) -> usize {
3614        self.state.with(|values| values.len())
3615    }
3616
3617    pub fn is_empty(&self) -> bool {
3618        self.len() == 0
3619    }
3620
3621    pub fn to_vec(&self) -> Vec<T> {
3622        self.state.with(|values| values.clone())
3623    }
3624
3625    pub fn iter(&self) -> Vec<T> {
3626        self.to_vec()
3627    }
3628
3629    pub fn get(&self, index: usize) -> T {
3630        self.state.with(|values| values[index].clone())
3631    }
3632
3633    pub fn get_opt(&self, index: usize) -> Option<T> {
3634        self.state.with(|values| values.get(index).cloned())
3635    }
3636
3637    pub fn first(&self) -> Option<T> {
3638        self.get_opt(0)
3639    }
3640
3641    pub fn last(&self) -> Option<T> {
3642        self.state.with(|values| values.last().cloned())
3643    }
3644
3645    pub fn push(&self, value: T) {
3646        self.state.update(|values| values.push(value));
3647    }
3648
3649    pub fn extend<I>(&self, iter: I)
3650    where
3651        I: IntoIterator<Item = T>,
3652    {
3653        self.state.update(|values| values.extend(iter));
3654    }
3655
3656    pub fn insert(&self, index: usize, value: T) {
3657        self.state.update(|values| values.insert(index, value));
3658    }
3659
3660    pub fn set(&self, index: usize, value: T) -> T {
3661        self.state
3662            .update(|values| std::mem::replace(&mut values[index], value))
3663    }
3664
3665    pub fn remove(&self, index: usize) -> T {
3666        self.state.update(|values| values.remove(index))
3667    }
3668
3669    pub fn pop(&self) -> Option<T> {
3670        self.state.update(|values| values.pop())
3671    }
3672
3673    pub fn clear(&self) {
3674        self.state.replace(Vec::new());
3675    }
3676
3677    pub fn retain<F>(&self, mut predicate: F)
3678    where
3679        F: FnMut(&T) -> bool,
3680    {
3681        self.state
3682            .update(|values| values.retain(|value| predicate(value)));
3683    }
3684
3685    pub fn replace_with<I>(&self, iter: I)
3686    where
3687        I: IntoIterator<Item = T>,
3688    {
3689        self.state.replace(iter.into_iter().collect());
3690    }
3691}
3692
3693impl<T: fmt::Debug + Clone + 'static> fmt::Debug for SnapshotStateList<T> {
3694    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3695        let contents = self.to_vec();
3696        f.debug_struct("SnapshotStateList")
3697            .field("values", &contents)
3698            .finish()
3699    }
3700}
3701
3702#[derive(Clone)]
3703pub struct SnapshotStateMap<K, V>
3704where
3705    K: Clone + Eq + Hash + 'static,
3706    V: Clone + 'static,
3707{
3708    state: OwnedMutableState<HashMap<K, V>>,
3709}
3710
3711impl<K, V> SnapshotStateMap<K, V>
3712where
3713    K: Clone + Eq + Hash + 'static,
3714    V: Clone + 'static,
3715{
3716    pub fn with_runtime<I>(pairs: I, runtime: RuntimeHandle) -> Self
3717    where
3718        I: IntoIterator<Item = (K, V)>,
3719    {
3720        let map: HashMap<K, V> = pairs.into_iter().collect();
3721        Self {
3722            state: OwnedMutableState::with_runtime(map, runtime),
3723        }
3724    }
3725
3726    pub fn as_state(&self) -> State<HashMap<K, V>> {
3727        self.state.as_state()
3728    }
3729
3730    pub fn as_mutable_state(&self) -> MutableState<HashMap<K, V>> {
3731        self.state.handle()
3732    }
3733
3734    pub fn len(&self) -> usize {
3735        self.state.with(|map| map.len())
3736    }
3737
3738    pub fn is_empty(&self) -> bool {
3739        self.state.with(|map| map.is_empty())
3740    }
3741
3742    pub fn contains_key(&self, key: &K) -> bool {
3743        self.state.with(|map| map.contains_key(key))
3744    }
3745
3746    pub fn get(&self, key: &K) -> Option<V> {
3747        self.state.with(|map| map.get(key).cloned())
3748    }
3749
3750    pub fn to_hash_map(&self) -> HashMap<K, V> {
3751        self.state.with(|map| map.clone())
3752    }
3753
3754    pub fn insert(&self, key: K, value: V) -> Option<V> {
3755        self.state.update(|map| map.insert(key, value))
3756    }
3757
3758    pub fn extend<I>(&self, iter: I)
3759    where
3760        I: IntoIterator<Item = (K, V)>,
3761    {
3762        self.state.update(|map| map.extend(iter));
3763        // extend returns (), but update requires returning something: we can just rely on ()
3764    }
3765
3766    pub fn remove(&self, key: &K) -> Option<V> {
3767        self.state.update(|map| map.remove(key))
3768    }
3769
3770    pub fn clear(&self) {
3771        self.state.replace(HashMap::default());
3772    }
3773
3774    pub fn retain<F>(&self, mut predicate: F)
3775    where
3776        F: FnMut(&K, &mut V) -> bool,
3777    {
3778        self.state.update(|map| map.retain(|k, v| predicate(k, v)));
3779    }
3780}
3781
3782impl<K, V> fmt::Debug for SnapshotStateMap<K, V>
3783where
3784    K: Clone + Eq + Hash + fmt::Debug + 'static,
3785    V: Clone + fmt::Debug + 'static,
3786{
3787    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3788        let contents = self.to_hash_map();
3789        f.debug_struct("SnapshotStateMap")
3790            .field("entries", &contents)
3791            .finish()
3792    }
3793}
3794
3795struct DerivedState<T: Clone + 'static> {
3796    compute: Rc<dyn Fn() -> T>, // FUTURE(no_std): store compute closures in arena-managed cell.
3797    state: OwnedMutableState<T>,
3798}
3799
3800impl<T: Clone + 'static> DerivedState<T> {
3801    fn new(runtime: RuntimeHandle, compute: Rc<dyn Fn() -> T>) -> Self {
3802        // FUTURE(no_std): accept arena-managed compute handle.
3803        let initial = compute();
3804        Self {
3805            compute,
3806            state: OwnedMutableState::with_runtime(initial, runtime),
3807        }
3808    }
3809
3810    fn set_compute(&mut self, compute: Rc<dyn Fn() -> T>) {
3811        // FUTURE(no_std): accept arena-managed compute handle.
3812        self.compute = compute;
3813    }
3814
3815    fn recompute(&self) {
3816        let value = (self.compute)();
3817        self.state.set_value(value);
3818    }
3819}
3820
3821impl<T: fmt::Debug + Clone + 'static> fmt::Debug for State<T> {
3822    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3823        self.with_inner(|inner| {
3824            inner.with_value(|value| f.debug_struct("State").field("value", value).finish())
3825        })
3826    }
3827}
3828
3829pub struct ParamState<T> {
3830    value: Option<T>,
3831}
3832
3833impl<T> ParamState<T> {
3834    pub fn update(&mut self, new_value: &T) -> bool
3835    where
3836        T: PartialEq + Clone,
3837    {
3838        match &self.value {
3839            Some(old) if old == new_value => false,
3840            _ => {
3841                self.value = Some(new_value.clone());
3842                true
3843            }
3844        }
3845    }
3846
3847    pub fn value(&self) -> Option<T>
3848    where
3849        T: Clone,
3850    {
3851        self.value.clone()
3852    }
3853}
3854
3855/// ParamSlot holds function/closure parameters by ownership (no PartialEq/Clone required).
3856/// Used by the #[composable] macro to store Fn-like parameters in the slot table.
3857pub struct ParamSlot<T> {
3858    val: RefCell<Option<T>>,
3859}
3860
3861impl<T> Default for ParamSlot<T> {
3862    fn default() -> Self {
3863        Self {
3864            val: RefCell::new(None),
3865        }
3866    }
3867}
3868
3869impl<T> ParamSlot<T> {
3870    pub fn set(&self, v: T) {
3871        *self.val.borrow_mut() = Some(v);
3872    }
3873
3874    /// Takes the value out temporarily (for recomposition callback)
3875    pub fn take(&self) -> T {
3876        self.val
3877            .borrow_mut()
3878            .take()
3879            .expect("ParamSlot take() called before set")
3880    }
3881}
3882
3883/// CallbackHolder keeps the latest callback closure alive across recompositions.
3884/// It stores the callback in an Rc<RefCell<...>> so that the composer can hand out
3885/// lightweight forwarder closures without cloning the underlying callback value.
3886#[derive(Clone)]
3887pub struct CallbackHolder {
3888    rc: Rc<RefCell<Box<dyn FnMut()>>>,
3889}
3890
3891impl CallbackHolder {
3892    /// Create a new holder with a no-op callback so that callers can immediately invoke it.
3893    pub fn new() -> Self {
3894        Self::default()
3895    }
3896
3897    /// Replace the stored callback with a new closure provided by the caller.
3898    pub fn update<F>(&self, f: F)
3899    where
3900        F: FnMut() + 'static,
3901    {
3902        *self.rc.borrow_mut() = Box::new(f);
3903    }
3904
3905    /// Produce a forwarder closure that keeps the holder alive and forwards calls to it.
3906    pub fn clone_rc(&self) -> impl Fn() + 'static {
3907        let rc = self.rc.clone();
3908        move || {
3909            (rc.borrow_mut())();
3910        }
3911    }
3912}
3913
3914impl Default for CallbackHolder {
3915    fn default() -> Self {
3916        Self {
3917            rc: Rc::new(RefCell::new(Box::new(|| {}) as Box<dyn FnMut()>)),
3918        }
3919    }
3920}
3921
3922/// CallbackHolder1 keeps the latest single-argument callback closure alive across recompositions.
3923/// It mirrors [`CallbackHolder`] but supports callbacks that receive one argument.
3924#[derive(Clone)]
3925pub struct CallbackHolder1<A: 'static> {
3926    #[allow(clippy::type_complexity)]
3927    rc: Rc<RefCell<Box<dyn FnMut(A)>>>,
3928}
3929
3930impl<A: 'static> CallbackHolder1<A> {
3931    /// Create a new holder with a no-op callback so callers can invoke it immediately.
3932    pub fn new() -> Self {
3933        Self::default()
3934    }
3935
3936    /// Replace the stored callback with a new closure provided by the caller.
3937    pub fn update<F>(&self, f: F)
3938    where
3939        F: FnMut(A) + 'static,
3940    {
3941        *self.rc.borrow_mut() = Box::new(f);
3942    }
3943
3944    /// Produce a forwarder closure that keeps the holder alive and forwards calls to it.
3945    pub fn clone_rc(&self) -> impl Fn(A) + 'static {
3946        let rc = self.rc.clone();
3947        move |arg| {
3948            (rc.borrow_mut())(arg);
3949        }
3950    }
3951}
3952
3953impl<A: 'static> Default for CallbackHolder1<A> {
3954    fn default() -> Self {
3955        Self {
3956            rc: Rc::new(RefCell::new(Box::new(|_| {}) as Box<dyn FnMut(A)>)),
3957        }
3958    }
3959}
3960
3961pub struct ReturnSlot<T> {
3962    value: Option<T>,
3963}
3964
3965impl<T: Clone> ReturnSlot<T> {
3966    pub fn store(&mut self, value: T) {
3967        self.value = Some(value);
3968    }
3969
3970    pub fn get(&self) -> Option<T> {
3971        self.value.clone()
3972    }
3973}
3974
3975impl<T> Default for ParamState<T> {
3976    fn default() -> Self {
3977        Self { value: None }
3978    }
3979}
3980
3981impl<T> Default for ReturnSlot<T> {
3982    fn default() -> Self {
3983        Self { value: None }
3984    }
3985}
3986
3987pub struct Composition<A: Applier + 'static> {
3988    slots: Rc<SlotsHost>,
3989    applier: Rc<ConcreteApplierHost<A>>,
3990    runtime: Runtime,
3991    observer: SnapshotStateObserver,
3992    root: Option<NodeId>,
3993}
3994
3995impl<A: Applier + 'static> Composition<A> {
3996    pub fn new(applier: A) -> Self {
3997        Self::with_runtime(applier, Runtime::new(Arc::new(DefaultScheduler)))
3998    }
3999
4000    pub fn with_runtime(applier: A, runtime: Runtime) -> Self {
4001        Self::with_backend(applier, runtime, SlotBackendKind::default())
4002    }
4003
4004    pub fn with_backend(applier: A, runtime: Runtime, backend_kind: SlotBackendKind) -> Self {
4005        let storage = make_backend(backend_kind);
4006        let slots = Rc::new(SlotsHost::new(storage));
4007        let applier = Rc::new(ConcreteApplierHost::new(applier));
4008        let observer_handle = runtime.handle();
4009        let observer = SnapshotStateObserver::new(move |callback| {
4010            observer_handle.enqueue_ui_task(callback);
4011        });
4012        observer.start();
4013        Self {
4014            slots,
4015            applier,
4016            runtime,
4017            observer,
4018            root: None,
4019        }
4020    }
4021
4022    fn slots_host(&self) -> Rc<SlotsHost> {
4023        Rc::clone(&self.slots)
4024    }
4025
4026    fn applier_host(&self) -> Rc<dyn ApplierHost> {
4027        self.applier.clone()
4028    }
4029
4030    pub fn render(&mut self, key: Key, mut content: impl FnMut()) -> Result<(), NodeError> {
4031        self.slots.borrow_mut().reset();
4032        let runtime_handle = self.runtime_handle();
4033        runtime_handle.drain_ui();
4034        let composer = Composer::new(
4035            Rc::clone(&self.slots),
4036            self.applier.clone(),
4037            runtime_handle.clone(),
4038            self.observer.clone(),
4039            self.root,
4040        );
4041        self.observer.begin_frame();
4042        let (root, mut commands, side_effects) = composer.install(|composer| {
4043            composer.with_group(key, |_| content());
4044            let root = composer.root();
4045            let commands = composer.take_commands();
4046            let side_effects = composer.take_side_effects();
4047            (root, commands, side_effects)
4048        });
4049
4050        {
4051            let mut applier = self.applier.borrow_dyn();
4052            for command in commands.drain(..) {
4053                command.apply(&mut *applier)?;
4054            }
4055            for update in runtime_handle.take_updates() {
4056                update.apply(&mut *applier)?;
4057            }
4058        }
4059
4060        runtime_handle.drain_ui();
4061        for effect in side_effects {
4062            effect();
4063        }
4064        runtime_handle.drain_ui();
4065        self.root = root;
4066        {
4067            let mut slots = self.slots.borrow_mut();
4068            let _ = slots.finalize_current_group();
4069            slots.flush();
4070        }
4071        let _ = self.process_invalid_scopes()?;
4072        if !self.runtime.has_updates()
4073            && !runtime_handle.has_invalid_scopes()
4074            && !runtime_handle.has_frame_callbacks()
4075            && !runtime_handle.has_pending_ui()
4076        {
4077            self.runtime.set_needs_frame(false);
4078        }
4079        Ok(())
4080    }
4081
4082    /// Returns true if composition needs to process invalid scopes (recompose).
4083    ///
4084    /// This checks both:
4085    /// - `has_updates()`: composition scopes that were invalidated by state changes
4086    /// - `needs_frame()`: animation callbacks that may have pending work
4087    ///
4088    /// Note: For scroll performance, ensure scroll state changes use Cell<T> instead
4089    /// of MutableState<T> to avoid triggering recomposition on every scroll frame.
4090    pub fn should_render(&self) -> bool {
4091        self.runtime.needs_frame() || self.runtime.has_updates()
4092    }
4093
4094    pub fn runtime_handle(&self) -> RuntimeHandle {
4095        self.runtime.handle()
4096    }
4097
4098    pub fn applier_mut(&mut self) -> ApplierGuard<'_, A> {
4099        ApplierGuard::new(self.applier.borrow_typed())
4100    }
4101
4102    pub fn root(&self) -> Option<NodeId> {
4103        self.root
4104    }
4105
4106    pub fn debug_dump_slot_table_groups(&self) -> Vec<(usize, Key, Option<ScopeId>, usize)> {
4107        self.slots.borrow().debug_dump_groups()
4108    }
4109
4110    pub fn debug_dump_all_slots(&self) -> Vec<(usize, String)> {
4111        self.slots.borrow().debug_dump_all_slots()
4112    }
4113
4114    pub fn process_invalid_scopes(&mut self) -> Result<bool, NodeError> {
4115        let runtime_handle = self.runtime_handle();
4116        let mut did_recompose = false;
4117        let mut loop_count = 0;
4118        loop {
4119            loop_count += 1;
4120            if loop_count > 100 {
4121                log::error!("process_invalid_scopes looped too many times! Breaking loop to prevent freeze.");
4122                break;
4123            }
4124            runtime_handle.drain_ui();
4125            let pending = runtime_handle.take_invalidated_scopes();
4126            if pending.is_empty() {
4127                break;
4128            }
4129            if input_debug_enabled() {
4130                let pending_ids: Vec<_> = pending.iter().map(|(id, _)| *id).collect();
4131                eprintln!(
4132                    "[CRANPOSE_INPUT_DEBUG] process_invalid_scopes pending_ids={:?}",
4133                    pending_ids
4134                );
4135            }
4136            let mut scopes = Vec::new();
4137            for (id, weak) in pending {
4138                if let Some(inner) = weak.upgrade() {
4139                    scopes.push(RecomposeScope { inner });
4140                } else {
4141                    runtime_handle.mark_scope_recomposed(id);
4142                }
4143            }
4144            if scopes.is_empty() {
4145                continue;
4146            }
4147            if input_debug_enabled() {
4148                let scope_ids: Vec<_> = scopes.iter().map(|scope| scope.id()).collect();
4149                eprintln!(
4150                    "[CRANPOSE_INPUT_DEBUG] process_invalid_scopes live_scope_ids={:?}",
4151                    scope_ids
4152                );
4153            }
4154            did_recompose = true;
4155            let runtime_clone = runtime_handle.clone();
4156            let root_host = self.slots_host();
4157            let mut scope_groups: Vec<(Rc<SlotsHost>, Vec<RecomposeScope>)> = Vec::new();
4158            let mut scope_group_index: HashMap<usize, usize> = HashMap::default();
4159            for scope in scopes {
4160                let host = scope.slots_host().unwrap_or_else(|| Rc::clone(&root_host));
4161                let host_key = Rc::as_ptr(&host) as usize;
4162                if let Some(index) = scope_group_index.get(&host_key).copied() {
4163                    scope_groups[index].1.push(scope);
4164                } else {
4165                    scope_group_index.insert(host_key, scope_groups.len());
4166                    scope_groups.push((host, vec![scope]));
4167                }
4168            }
4169            let (mut commands, side_effects) = {
4170                let composer = Composer::new(
4171                    Rc::clone(&root_host),
4172                    self.applier_host(),
4173                    runtime_clone,
4174                    self.observer.clone(),
4175                    self.root,
4176                );
4177                self.observer.begin_frame();
4178                composer.install(|composer| {
4179                    for (host, scopes) in scope_groups.into_iter() {
4180                        if Rc::ptr_eq(&host, &root_host) {
4181                            for scope in scopes.iter() {
4182                                composer.recranpose_group(scope);
4183                            }
4184                        } else {
4185                            composer.with_slot_override(host, |composer| {
4186                                for scope in scopes.iter() {
4187                                    composer.recranpose_group(scope);
4188                                }
4189                            });
4190                        }
4191                    }
4192                    let commands = composer.take_commands();
4193                    let side_effects = composer.take_side_effects();
4194                    (commands, side_effects)
4195                })
4196            };
4197            {
4198                let mut applier = self.applier.borrow_dyn();
4199                for command in commands.drain(..) {
4200                    command.apply(&mut *applier)?;
4201                }
4202                for update in runtime_handle.take_updates() {
4203                    update.apply(&mut *applier)?;
4204                }
4205            }
4206            for effect in side_effects {
4207                effect();
4208            }
4209            runtime_handle.drain_ui();
4210        }
4211        if !self.runtime.has_updates()
4212            && !runtime_handle.has_invalid_scopes()
4213            && !runtime_handle.has_frame_callbacks()
4214            && !runtime_handle.has_pending_ui()
4215        {
4216            self.runtime.set_needs_frame(false);
4217        }
4218        Ok(did_recompose)
4219    }
4220
4221    pub fn flush_pending_node_updates(&mut self) -> Result<(), NodeError> {
4222        let updates = self.runtime_handle().take_updates();
4223        let mut applier = self.applier.borrow_dyn();
4224        for update in updates {
4225            update.apply(&mut *applier)?;
4226        }
4227        Ok(())
4228    }
4229}
4230
4231impl<A: Applier + 'static> Drop for Composition<A> {
4232    fn drop(&mut self) {
4233        self.observer.stop();
4234    }
4235}
4236pub fn location_key(file: &str, line: u32, column: u32) -> Key {
4237    let base = file.as_ptr() as u64;
4238    base
4239        .wrapping_mul(0x9E37_79B9_7F4A_7C15) // cheap mix
4240        ^ ((line as u64) << 32)
4241        ^ (column as u64)
4242}
4243
4244fn hash_key<K: Hash>(key: &K) -> Key {
4245    let mut hasher = hash::default::new();
4246    key.hash(&mut hasher);
4247    hasher.finish()
4248}
4249
4250#[cfg(test)]
4251#[path = "tests/lib_tests.rs"]
4252mod tests;
4253
4254#[cfg(test)]
4255#[path = "tests/recursive_decrease_increase_test.rs"]
4256mod recursive_decrease_increase_test;
4257
4258#[cfg(test)]
4259#[path = "tests/slot_backend_tests.rs"]
4260mod slot_backend_tests;
4261
4262pub mod collections;
4263pub mod hash;