1use component::ComponentData;
3use crossterm::{
4 event::{self, DisableMouseCapture, KeyCode, KeyEventKind},
5 terminal::{self, EnterAlternateScreen, LeaveAlternateScreen},
6 ExecutableCommand,
7};
8use kano::Diff;
9use node::{new_node_id, Node, NodeKind, NodeRef};
10use ratatui::prelude::{CrosstermBackend, Terminal};
11use std::{
12 cell::RefCell,
13 io::{self, stdout},
14 panic,
15 rc::Rc,
16};
17
18pub mod component;
19pub mod node;
20
21pub use ratatui;
22
23pub struct Tui;
24
25impl kano::platform::Platform for Tui {
26 type Cursor = TuiCursor;
27
28 fn log(_s: &str) {}
29
30 fn run_app<V: kano::View<Self>, F: (FnOnce() -> V) + 'static>(func: F) -> anyhow::Result<()> {
31 stdout().execute(EnterAlternateScreen)?;
32 terminal::enable_raw_mode()?;
33
34 let panic_hook = panic::take_hook();
35 panic::set_hook(Box::new(move |panic| {
36 reset_terminal().expect("failed to reset the terminal");
37 panic_hook(panic);
38 }));
39
40 let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
41 terminal.clear()?;
42
43 let (mut cursor, empty_root) = TuiCursor::new_root();
44 let state = kano::view::Func(func, ()).init(&mut cursor);
45 std::mem::forget(state);
46
47 let root_node = empty_root.first_child().unwrap();
48
49 loop {
50 terminal.draw(|frame| {
51 let area = frame.size();
52 root_node.clone().render(frame, area);
53 })?;
54
55 if event::poll(std::time::Duration::from_millis(16))? {
56 if let event::Event::Key(key) = event::read()? {
57 if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
58 break;
59 }
60 }
61 }
62 }
63
64 stdout().execute(LeaveAlternateScreen)?;
65 reset_terminal()?;
66 Ok(())
67 }
68
69 fn spawn_task(_task: impl std::future::Future<Output = ()> + 'static) {
70 todo!();
71 }
72}
73
74fn reset_terminal() -> anyhow::Result<()> {
75 terminal::disable_raw_mode()?;
76 crossterm::execute!(io::stderr(), LeaveAlternateScreen, DisableMouseCapture)?;
77 Ok(())
78}
79
80#[derive(Clone, Debug)]
81pub struct TuiCursor {
82 location: Location,
83 mode: Mode,
84}
85
86#[derive(Clone, Debug)]
87enum Location {
88 Detached,
89 Node(NodeRef),
90 EndOfChildren(NodeRef),
91}
92
93#[derive(Clone, Debug)]
94enum Mode {
95 Append,
96 Diff,
97}
98
99impl TuiCursor {
100 fn new_detached() -> Self {
101 Self {
102 location: Location::Detached,
103 mode: Mode::Append,
104 }
105 }
106
107 fn new_root() -> (Self, NodeRef) {
108 let root = NodeRef(Rc::new(RefCell::new(Node {
109 id: new_node_id(),
110 kind: NodeKind::Empty,
111 parent: None,
112 first_child: None,
113 next_sibling: None,
114 })));
115 (
116 Self {
117 location: Location::EndOfChildren(root.clone()),
118 mode: Mode::Append,
119 },
120 root,
121 )
122 }
123
124 fn set_component(&mut self, component: Rc<ComponentData>) {
125 self.set_node(NodeKind::Component(component));
126 }
127
128 fn set_node(&mut self, kind: NodeKind) {
129 match (&self.mode, &self.location) {
130 (Mode::Append, Location::Detached) => {
131 let node = Rc::new(RefCell::new(Node {
132 id: new_node_id(),
133 kind,
134 parent: None,
135 first_child: None,
136 next_sibling: None,
137 }));
138
139 self.location = Location::Node(NodeRef(node));
140 }
141 (Mode::Append, Location::Node(node)) => {
142 node.append_sibling(kind);
143 self.location = Location::Node(node.next_sibling().unwrap());
144 }
145 (Mode::Append, Location::EndOfChildren(parent)) => {
146 let node = Rc::new(RefCell::new(Node {
147 id: new_node_id(),
148 kind,
149 parent: Some(Rc::downgrade(&parent.0)),
150 first_child: None,
151 next_sibling: None,
152 }));
153
154 if let Some(mut child) = parent.first_child() {
155 while let Some(next) = child.next_sibling() {
157 child = next;
158 }
159
160 child.0.borrow_mut().next_sibling = Some(NodeRef(node.clone()));
161 } else {
162 parent.0.borrow_mut().first_child = Some(NodeRef(node.clone()));
163 }
164
165 self.location = Location::Node(NodeRef(node));
166 }
167 other => todo!("{other:?}"),
168 }
169 }
170
171 fn current_node(&self) -> NodeRef {
172 match &self.location {
173 Location::Node(node) => node.clone(),
174 _ => panic!(),
175 }
176 }
177}
178
179impl kano::platform::Cursor for TuiCursor {
180 type TextHandle = NodeRef;
181 type EventHandle = ();
182
183 fn from_text_handle(handle: &NodeRef) -> Self {
184 Self {
185 location: Location::Node(handle.clone()),
186 mode: Mode::Append,
187 }
188 }
189
190 fn empty(&mut self) {
191 self.set_node(NodeKind::Empty);
192 }
193
194 fn text(&mut self, text: &str) -> Self::TextHandle {
195 self.set_node(NodeKind::Text(text.into()));
196 self.current_node()
197 }
198
199 fn update_text(&mut self, new_text: &str) {
200 match &mut self.location {
201 Location::Node(node) => {
202 let mut borrow = node.0.borrow_mut();
203 match &mut borrow.kind {
204 NodeKind::Text(text) => {
205 *text = new_text.into();
206 }
207 _ => {}
208 }
209 }
210 _ => panic!(),
211 }
212 }
213
214 fn on_event(&mut self, _event: kano::On) -> () {}
215
216 fn enter_children(&mut self) {
217 match &self.location {
218 Location::Node(node) => {
219 self.location = match node.first_child() {
220 Some(first_child) => Location::Node(first_child),
221 None => Location::EndOfChildren(node.clone()),
222 }
223 }
224 other => panic!("{other:?}"),
225 }
226 }
227
228 fn exit_children(&mut self) {
229 match &self.location {
230 Location::Node(node) => {
231 self.location = Location::Node(node.parent().unwrap());
232 }
233 Location::EndOfChildren(parent) => {
234 self.location = Location::Node(parent.clone());
235 }
236 _ => panic!(),
237 }
238 }
239
240 fn next_sibling(&mut self) {
241 match &self.location {
242 Location::Node(node) => {
243 self.location = match node.next_sibling() {
244 Some(next) => Location::Node(next),
245 None => match node.parent() {
246 Some(parent) => Location::EndOfChildren(parent),
247 None => Location::Node(node.clone()),
248 },
249 }
250 }
251 Location::EndOfChildren(_) => {}
252 _ => panic!(),
253 }
254 }
255
256 fn remove(&mut self) {
257 match &self.location {
258 Location::Node(node) => {
259 let id = node.id();
260
261 let mut prev_sibling: Option<NodeRef> = None;
262
263 if let Some(mut child) = node.parent().and_then(|parent| parent.first_child()) {
264 loop {
265 if child.id() == id {
266 if let Some(prev_sibling) = prev_sibling {
267 prev_sibling.0.borrow_mut().next_sibling = child.next_sibling();
268 } else {
269 node.parent().unwrap().0.borrow_mut().first_child =
270 child.next_sibling();
271 }
272 return;
273 } else if let Some(next_sibling) = child.next_sibling() {
274 prev_sibling = Some(child);
275 child = next_sibling;
276 } else {
277 return;
278 }
279 }
280 }
281 }
282 Location::EndOfChildren(_) => {}
283 _ => panic!(),
284 }
285 }
286
287 fn enter_diff(&mut self) {
288 self.mode = Mode::Diff;
289 }
290
291 fn exit_diff(&mut self) {
292 self.mode = Mode::Append;
293 }
294
295 fn replace(&mut self, func: impl FnOnce(&mut Self)) {
296 let mut replacement_cursor = Self::new_detached();
297 func(&mut replacement_cursor);
298
299 let Location::Node(node) = replacement_cursor.location else {
300 panic!();
301 };
302
303 let kind = node.0.borrow().kind.clone();
304
305 match &self.location {
306 Location::Node(node) => {
307 node.0.borrow_mut().kind = kind;
308 }
309 _ => panic!(),
310 }
311 }
312}