Skip to main content

cranpose_core/
lib.rs

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