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