Skip to main content

cranpose_core/
lib.rs

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