1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Reactive/signals-based API for Blaeck.
//!
//! This is the **recommended way** to build interactive terminal UIs with Blaeck.
//! It provides a React-like hooks system with automatic re-rendering when state changes.
//!
//! # Quick Start
//!
//! ```ignore
//! use blaeck::reactive::*;
//! use blaeck::prelude::*;
//!
//! fn counter(cx: Scope) -> Element {
//! // Create reactive state
//! let count = use_state(cx.clone(), || 0);
//!
//! // Clone signal for use in input handler
//! let count_handler = count.clone();
//! use_input(cx, move |key| {
//! if key.is_char(' ') {
//! count_handler.set(count_handler.get() + 1);
//! }
//! });
//!
//! // UI automatically re-renders when count changes
//! element! {
//! Box(border_style: BorderStyle::Round, padding: 1.0) {
//! Text(content: format!("Count: {}", count.get()))
//! }
//! }
//! }
//!
//! fn main() -> std::io::Result<()> {
//! ReactiveApp::run(counter)
//! }
//! ```
//!
//! # Why Reactive?
//!
//! The reactive API provides several advantages over manual state management:
//!
//! - **Automatic re-rendering**: Call `signal.set()` and the UI updates
//! - **Declarative**: State and UI are connected, not manually synchronized
//! - **Familiar**: Similar to React, Solid.js, and other modern UI frameworks
//! - **Testable**: Multiple ReactiveApp instances can coexist (no global state)
//!
//! # Available Hooks
//!
//! | Hook | Purpose |
//! |------|---------|
//! | [`use_state`] | Create reactive state that triggers re-render on change |
//! | [`use_input`] | Register keyboard input handler (runs once, persists across renders) |
//! | [`use_timeline`] | Create a declarative animation timeline with playback controls |
//!
//! Future hooks (v0.3.0+): `use_effect`, `use_memo`, `use_const`
//!
//! # Rules of Hooks
//!
//! Like React, hooks must follow certain rules:
//!
//! 1. **Call hooks unconditionally** - Don't put hooks inside `if` or `match`
//! 2. **Same order every render** - Hooks are identified by call order
//! 3. **Only in components** - Hooks only work inside component functions
//!
//! ```ignore
//! // WRONG - conditional hook
//! fn bad(cx: Scope) -> Element {
//! if some_condition {
//! let state = use_state(cx, || 0); // Will panic on re-render!
//! }
//! // ...
//! }
//!
//! // CORRECT - unconditional hooks
//! fn good(cx: Scope) -> Element {
//! let state = use_state(cx.clone(), || 0); // Always called
//! if some_condition {
//! // Use the state here
//! }
//! // ...
//! }
//! ```
//!
//! # Clone Pattern for Closures
//!
//! Since `Scope` and `Signal` don't implement `Copy`, you need to clone them
//! before using in closures:
//!
//! ```ignore
//! fn my_component(cx: Scope) -> Element {
//! let count = use_state(cx.clone(), || 0); // Clone cx for use_state
//! let name = use_state(cx.clone(), || String::new()); // Clone again
//!
//! // Clone signals for the input handler
//! let count_handler = count.clone();
//! use_input(cx, move |key| { // cx moved here (last use)
//! count_handler.set(count_handler.get() + 1);
//! });
//!
//! // Original signals still usable for rendering
//! element! {
//! Text(content: format!("{}: {}", name.get(), count.get()))
//! }
//! }
//! ```
//!
//! # Design Principles
//!
//! - **No thread-local state**: Runtime is explicit via [`RuntimeHandle`]
//! - **Hooks use cursor model**: Like React, hooks must be called in consistent order
//! - **`'static` closures**: Event handlers must own their data (use `use_state`)
//! - **Full rerender**: Any state change triggers complete re-render (no diffing in v0.2.0)
//!
//! # Important: Closure Lifetime Requirements
//!
//! Event handlers passed to `use_input` must be `'static`. This means you cannot
//! capture references to stack variables:
//!
//! ```ignore
//! // WON'T COMPILE - items is borrowed from stack
//! let items = vec!["A", "B"];
//! use_input(cx, move |key| { items.len(); });
//!
//! // CORRECT - use use_state for reactive data
//! let items = use_state(cx.clone(), || vec!["A", "B"]);
//! let items_handler = items.clone();
//! use_input(cx, move |key| { items_handler.get().len(); });
//! ```
pub use ;
pub use ;
pub use ;
pub use ;
pub use Scope;
pub use Signal;