My personal-knowledge-system, with deeply integrated task tracking and long term goal planning capabilities.
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: basic switcher overlay on bottom

+135 -19
+1 -1
.config/config.kdl
··· 3 3 4 4 keymap { 5 5 6 - Home { 6 + Zk { 7 7 <Ctrl-c> Quit // Another way to quit 8 8 <Ctrl-z> Suspend // Suspend the application 9 9 up MoveUp
+1 -1
src/tui/app.rs
··· 42 42 )] 43 43 pub enum Region { 44 44 #[default] 45 - Home, 46 45 Zk, 46 + Todo, 47 47 } 48 48 49 49 impl App {
+10
src/tui/components/todo/mod.rs
··· 18 18 layouts: Layouts, 19 19 } 20 20 21 + impl Todo { 22 + pub fn new(kh: KastenHandle) -> Self { 23 + Self { 24 + kh, 25 + layouts: Layouts::default(), 26 + signal_tx: None, 27 + } 28 + } 29 + } 30 + 21 31 struct Layouts { 22 32 main: Layout, 23 33 }
+50 -13
src/tui/components/viewport/mod.rs
··· 1 1 use async_trait::async_trait; 2 2 use color_eyre::eyre::Result; 3 + use crossterm::event::KeyEvent; 3 4 use ratatui::{ 4 5 Frame, 5 6 layout::{Constraint, Layout, Rect}, ··· 9 10 use tokio::sync::mpsc::UnboundedSender; 10 11 11 12 use crate::{ 12 - tui::{Signal, components::Component}, 13 + tui::{ 14 + Signal, 15 + app::Region, 16 + components::{Component, Todo, Zk}, 17 + }, 13 18 types::KastenHandle, 14 19 }; 15 20 16 - pub struct Viewport { 21 + pub struct Viewport<'text> { 17 22 signal_tx: Option<UnboundedSender<Signal>>, 18 23 kh: KastenHandle, 19 24 layouts: Layouts, 25 + switcher: Switcher<'text>, 26 + active_region: Region, 27 + zk: Zk<'text>, 28 + todo: Todo, 20 29 } 21 30 22 - impl Viewport { 31 + mod switcher; 32 + use switcher::Switcher; 33 + 34 + impl Viewport<'_> { 23 35 pub async fn new(kh: KastenHandle) -> Result<Self> { 24 - let kt = kh.read().await; 25 - drop(kt); 36 + let mut switcher = Switcher::default(); 37 + switcher.select_region(Region::default()); 38 + 26 39 Ok(Self { 27 40 signal_tx: None, 28 - kh, 29 41 layouts: Layouts::default(), 42 + switcher, 43 + zk: Zk::new(kh.clone()).await?, 44 + todo: Todo::new(kh.clone()), 45 + active_region: Region::default(), 46 + kh, 30 47 }) 31 48 } 32 49 } ··· 44 61 } 45 62 46 63 #[async_trait] 47 - impl Component for Viewport { 64 + impl Component for Viewport<'_> { 65 + fn register_signal_handler(&mut self, tx: UnboundedSender<Signal>) -> Result<()> { 66 + self.signal_tx = Some(tx.clone()); 67 + self.zk.register_signal_handler(tx.clone())?; 68 + self.todo.register_signal_handler(tx)?; 69 + Ok(()) 70 + } 71 + 48 72 async fn update(&mut self, signal: Signal) -> color_eyre::Result<Option<Signal>> { 49 - Ok(None) 73 + match self.active_region { 74 + Region::Zk => self.zk.update(signal).await, 75 + Region::Todo => self.todo.update(signal).await, 76 + } 77 + } 78 + 79 + async fn handle_key_event(&mut self, key: KeyEvent) -> color_eyre::Result<Option<Signal>> { 80 + match self.active_region { 81 + Region::Zk => self.zk.handle_key_event(key).await, 82 + Region::Todo => self.todo.handle_key_event(key).await, 83 + } 50 84 } 51 85 52 86 fn draw(&mut self, frame: &mut Frame, area: Rect) -> color_eyre::Result<()> { ··· 55 89 (rects[0], rects[1]) 56 90 }; 57 91 58 - // frame.render_widget(Block::new().fg(Color::Red), main_layout); 59 - // frame.render_widget(Block::new().fg(Color::Green), switcher_layout); 60 - // 61 - frame.render_widget(Block::new().bg(Color::Green), main_layout); 62 - frame.render_widget(Block::new().bg(Color::Yellow), switcher_layout); 92 + match self.active_region { 93 + Region::Zk => self.zk.draw(frame, main_layout), 94 + Region::Todo => self.todo.draw(frame, main_layout), 95 + }?; 96 + 97 + // frame.render_widget(Block::new().bg(Color::Green), main_layout); 98 + // frame.render_widget(self.switcher.clone(), switcher_layout); 99 + frame.render_widget(self.switcher.clone(), area); 63 100 Ok(()) 64 101 } 65 102 }
+68
src/tui/components/viewport/switcher.rs
··· 1 + use ratatui::{ 2 + layout::{Constraint, Layout}, 3 + style::{Color, Style}, 4 + text::{Line, Span}, 5 + widgets::Widget, 6 + }; 7 + use strum::IntoEnumIterator; 8 + 9 + use crate::tui::app::Region; 10 + 11 + #[derive(Debug, Clone)] 12 + pub struct Switcher<'text> { 13 + line: Line<'text>, 14 + layouts: Layouts, 15 + } 16 + 17 + impl Switcher<'_> { 18 + pub fn select_region(&mut self, region: Region) { 19 + self.line = Region::iter() 20 + .map(|r| { 21 + Span::from(format!(" {r} ")).style({ 22 + if r == region { 23 + Style::new().black().on_blue() 24 + } else { 25 + Style::new().black().on_gray() 26 + } 27 + }) 28 + }) 29 + .collect::<Line>(); 30 + } 31 + } 32 + 33 + #[derive(Debug, Clone)] 34 + struct Layouts { 35 + overlay: Layout, 36 + } 37 + 38 + impl Default for Layouts { 39 + fn default() -> Self { 40 + Self { 41 + overlay: Layout::vertical(vec![Constraint::Fill(99), Constraint::Min(1)]), 42 + } 43 + } 44 + } 45 + 46 + impl Default for Switcher<'_> { 47 + fn default() -> Self { 48 + let line = Region::iter() 49 + .map(|r| Span::from(format!(" {r} ")).style(Style::default().bg(Color::DarkGray))) 50 + .collect::<Line>(); 51 + 52 + Self { 53 + line, 54 + layouts: Layouts::default(), 55 + } 56 + } 57 + } 58 + 59 + impl Widget for Switcher<'_> { 60 + fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) 61 + where 62 + Self: Sized, 63 + { 64 + let rect = self.layouts.overlay.split(area)[1]; 65 + 66 + self.line.render(rect, buf); 67 + } 68 + }
+1 -1
src/tui/components/zk/preview.rs
··· 18 18 where 19 19 Self: Sized, 20 20 { 21 - self.content.style(Style::new()).render(area, buf); 21 + self.content.render(area, buf); 22 22 } 23 23 }
+4 -3
src/tui/keymap.rs
··· 1 - use std::{ collections::HashMap, 1 + use std::{ 2 + collections::HashMap, 2 3 ops::{Deref, DerefMut}, 3 4 }; 4 5 ··· 178 179 fn test_quit_in_home_region() { 179 180 let keymap_str = " 180 181 keymap { 181 - Home { 182 + Todo { 182 183 q Quit 183 184 <Ctrl-C> Quit 184 185 } ··· 192 193 let keymap: KeyMap = kdl.try_into().expect("Must be a valid keymap"); 193 194 194 195 let map = keymap 195 - .get(&Region::Home) 196 + .get(&Region::Todo) 196 197 .expect("Home region must exist in keymap"); 197 198 198 199 let signal = map