cranpose_core/
lib.rs

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