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 inspector views

+273 -2
+65
src/tui/components/todo/inspector/groupview.rs
··· 1 + use ratatui::{ 2 + layout::{Constraint, Layout}, 3 + widgets::{Paragraph, Widget}, 4 + }; 5 + 6 + use crate::types::Group; 7 + 8 + #[derive(Debug, Clone)] 9 + pub struct GroupView<'text> { 10 + name: Paragraph<'text>, 11 + priority: Paragraph<'text>, 12 + created_at: Paragraph<'text>, 13 + layouts: Layouts, 14 + } 15 + 16 + #[derive(Debug, Clone)] 17 + struct Layouts { 18 + left_content: Layout, 19 + name_priority_created_at: Layout, 20 + } 21 + 22 + impl Default for Layouts { 23 + fn default() -> Self { 24 + Self { 25 + left_content: Layout::horizontal(vec![ 26 + Constraint::Percentage(50), 27 + Constraint::Fill(100), 28 + ]), 29 + name_priority_created_at: Layout::vertical(vec![ 30 + Constraint::Percentage(33), 31 + Constraint::Percentage(33), 32 + Constraint::Percentage(33), 33 + ]), 34 + } 35 + } 36 + } 37 + 38 + impl From<&Group> for GroupView<'_> { 39 + fn from(value: &Group) -> Self { 40 + Self { 41 + name: Paragraph::new(value.name.clone()), 42 + priority: Paragraph::new(value.priority.to_string()), 43 + created_at: Paragraph::new(value.created_at()), 44 + layouts: Layouts::default(), 45 + } 46 + } 47 + } 48 + 49 + impl Widget for GroupView<'_> { 50 + fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) 51 + where 52 + Self: Sized, 53 + { 54 + let (name_rect, priority_rect, created_at, content_rect) = { 55 + let rects = self.layouts.left_content.split(area); 56 + let l_rects = self.layouts.name_priority_created_at.split(rects[0]); 57 + 58 + (l_rects[0], l_rects[1], l_rects[2], rects[1]) 59 + }; 60 + 61 + self.name.render(name_rect, buf); 62 + self.priority.render(priority_rect, buf); 63 + self.created_at.render(created_at, buf); 64 + } 65 + }
+80
src/tui/components/todo/inspector/mod.rs
··· 1 + use ratatui::{ 2 + layout::{Constraint, Direction, Layout}, 3 + style::{Color, Style}, 4 + widgets::{Block, BorderType, Borders, Widget}, 5 + }; 6 + 7 + use crate::types::{TodoNode, TodoNodeKind}; 8 + 9 + mod rootview; 10 + use rootview::RootView; 11 + 12 + mod taskview; 13 + use taskview::TaskView; 14 + 15 + mod groupview; 16 + use groupview::GroupView; 17 + 18 + pub struct Inspector<'text> { 19 + render_data: RenderData<'text>, 20 + margins: Layout, 21 + } 22 + 23 + enum RenderData<'text> { 24 + Root { widget: Box<RootView<'text>> }, 25 + Task { widget: Box<TaskView<'text>> }, 26 + Group { widget: Box<GroupView<'text>> }, 27 + } 28 + 29 + impl From<&TodoNode> for Inspector<'_> { 30 + fn from(value: &TodoNode) -> Self { 31 + let margins = Layout::new(Direction::Horizontal, [Constraint::Percentage(100)]) 32 + .horizontal_margin(3) 33 + .vertical_margin(2); 34 + 35 + match value.kind { 36 + TodoNodeKind::Root => Self { 37 + render_data: RenderData::Root { 38 + widget: Box::new(RootView::default()), 39 + }, 40 + margins, 41 + }, 42 + TodoNodeKind::Group(ref group) => Self { 43 + render_data: RenderData::Group { 44 + widget: Box::new(GroupView::from(&**group)), 45 + }, 46 + margins, 47 + }, 48 + TodoNodeKind::Task(ref task) => Self { 49 + render_data: RenderData::Task { 50 + widget: Box::new(TaskView::from(&**task)), 51 + }, 52 + margins, 53 + }, 54 + } 55 + } 56 + } 57 + 58 + impl Widget for &Inspector<'_> { 59 + fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) 60 + where 61 + Self: Sized, 62 + { 63 + let block = Block::new() 64 + .title("[3]") 65 + .title("Inspector") 66 + .borders(Borders::LEFT | Borders::TOP | Borders::BOTTOM) 67 + .border_style(Style::new().fg(Color::Gray)) 68 + .border_type(BorderType::Rounded); 69 + 70 + block.render(area, buf); 71 + 72 + let area = self.margins.split(area)[0]; 73 + 74 + match &self.render_data { 75 + RenderData::Root { widget } => widget.clone().render(area, buf), 76 + RenderData::Task { widget } => widget.clone().render(area, buf), 77 + RenderData::Group { widget } => widget.clone().render(area, buf), 78 + } 79 + } 80 + }
+23
src/tui/components/todo/inspector/rootview.rs
··· 1 + use ratatui::widgets::{Paragraph, Widget}; 2 + 3 + #[derive(Debug, Clone)] 4 + pub struct RootView<'text> { 5 + help: Paragraph<'text>, 6 + } 7 + 8 + impl Default for RootView<'_> { 9 + fn default() -> Self { 10 + Self { 11 + help: Paragraph::new("Try making a selection!"), 12 + } 13 + } 14 + } 15 + 16 + impl Widget for RootView<'_> { 17 + fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) 18 + where 19 + Self: Sized, 20 + { 21 + self.help.render(area, buf); 22 + } 23 + }
+71
src/tui/components/todo/inspector/taskview.rs
··· 1 + use ratatui::{ 2 + layout::{Constraint, Layout}, 3 + widgets::{Paragraph, Widget}, 4 + }; 5 + 6 + use crate::types::Task; 7 + 8 + #[derive(Debug, Clone)] 9 + pub struct TaskView<'text> { 10 + name: Paragraph<'text>, 11 + priority: Paragraph<'text>, 12 + created_at: Paragraph<'text>, 13 + parent_group: Paragraph<'text>, 14 + 15 + due: Paragraph<'text>, 16 + 17 + layouts: Layouts, 18 + } 19 + 20 + #[derive(Debug, Clone)] 21 + struct Layouts { 22 + left_content: Layout, 23 + name_priority_due: Layout, 24 + } 25 + 26 + impl Default for Layouts { 27 + fn default() -> Self { 28 + Self { 29 + left_content: Layout::horizontal(vec![ 30 + Constraint::Percentage(50), 31 + Constraint::Fill(100), 32 + ]), 33 + name_priority_due: Layout::vertical(vec![ 34 + Constraint::Percentage(33), 35 + Constraint::Percentage(33), 36 + Constraint::Percentage(33), 37 + ]), 38 + } 39 + } 40 + } 41 + 42 + impl From<&Task> for TaskView<'_> { 43 + fn from(value: &Task) -> Self { 44 + Self { 45 + name: Paragraph::new(value.name.clone()), 46 + priority: Paragraph::new(value.priority.to_string()), 47 + created_at: Paragraph::new(value.created_at()), 48 + parent_group: Paragraph::new(value.group.name.clone()), 49 + due: Paragraph::new(value.due().unwrap_or_else(|| "None".to_owned())), 50 + layouts: Layouts::default(), 51 + } 52 + } 53 + } 54 + 55 + impl Widget for TaskView<'_> { 56 + fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) 57 + where 58 + Self: Sized, 59 + { 60 + let (name_rect, priority_rect, due_rect, content_rect) = { 61 + let rects = self.layouts.left_content.split(area); 62 + let l_rects = self.layouts.name_priority_due.split(rects[0]); 63 + 64 + (l_rects[0], l_rects[1], l_rects[2], rects[1]) 65 + }; 66 + 67 + self.name.render(name_rect, buf); 68 + self.priority.render(priority_rect, buf); 69 + self.due.render(due_rect, buf); 70 + } 71 + }
+20 -1
src/tui/components/todo/mod.rs
··· 14 14 15 15 mod explorer; 16 16 use explorer::Explorer; 17 + 17 18 mod tasklist; 18 19 use tasklist::TaskList; 19 20 21 + mod inspector; 22 + use inspector::Inspector; 23 + 20 24 pub struct Todo<'text> { 21 25 #[expect(dead_code)] 22 26 signal_tx: Option<UnboundedSender<Signal>>, ··· 24 28 layouts: Layouts, 25 29 explorer: Option<Explorer<'text>>, 26 30 task_list: Option<TaskList<'text>>, 31 + inspector: Option<Inspector<'text>>, 27 32 } 28 33 29 34 impl Todo<'_> { ··· 34 39 signal_tx: None, 35 40 explorer: None, 36 41 task_list: None, 42 + inspector: None, 37 43 } 38 44 } 39 45 } ··· 90 96 let mut explorer = Explorer::new(tree, &tree.root_id, l_state, splits.explorer.width); 91 97 let mut task_list = TaskList::new(tree, &tree.root_id, l_state, splits.task_list.width); 92 98 99 + let first = tree 100 + .tree 101 + .get( 102 + task_list 103 + .id_list 104 + .first() 105 + .unwrap_or_else(|| tree.tree.root_node_id().expect("Root node must exist")), 106 + ) 107 + .expect("Node id must be valid"); 108 + 109 + let inspector = first.data().into(); 110 + 93 111 // explorer.set_active(); 94 112 explorer.set_inactive(); 95 113 // task_list.set_inactive(); ··· 97 115 98 116 self.explorer = Some(explorer); 99 117 self.task_list = Some(task_list); 118 + self.inspector = Some(inspector); 100 119 101 120 Ok(()) 102 121 } ··· 140 159 splits.task_list, 141 160 &mut task_list.state, 142 161 ); 143 - frame.render_widget(Block::new().bg(Color::Green), splits.inspector); 162 + frame.render_widget(self.inspector.as_ref().unwrap(), splits.inspector); 144 163 Ok(()) 145 164 } 146 165 }
+14 -1
src/types/group.rs
··· 1 1 use dto::{DateTime, GroupModelEx, NanoId}; 2 2 3 - use crate::types::{Priority, Tag, Zettel}; 3 + use crate::types::{Priority, Tag, Zettel, frontmatter}; 4 4 5 5 /// A `Group` which contains tasks! 6 6 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] ··· 21 21 22 22 /// The `Tag` that is related to this `Group` 23 23 pub tag: Tag, 24 + } 25 + 26 + impl Group { 27 + pub fn created_at(&self) -> String { 28 + self.created_at 29 + .format(frontmatter::DATE_FMT_STR) 30 + .to_string() 31 + } 32 + pub fn modified_at(&self) -> String { 33 + self.modified_at 34 + .format(frontmatter::DATE_FMT_STR) 35 + .to_string() 36 + } 24 37 } 25 38 26 39 impl From<GroupModelEx> for Group {