feat: add initial tui

This commit is contained in:
itsscb 2025-07-24 15:29:17 +02:00
parent cc393ce2c0
commit 035fb15a9d
4 changed files with 145 additions and 12 deletions

View File

@ -5,6 +5,9 @@ edition = "2024"
[dependencies]
chrono = { version = "0.4.41", features = ["serde"] }
color-eyre = "0.6.5"
crossterm = "0.29.0"
ratatui = { version = "0.29.0", features = ["all-widgets"] }
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.141"
uuid = { version = "1.17.0", features = ["serde", "v4"] }

View File

@ -1,5 +1,17 @@
use std::{fs, io::Result, path::PathBuf};
use std::{fs, path::PathBuf};
use color_eyre::Result;
use ratatui::prelude::*;
use ratatui::{
DefaultTerminal, Frame,
crossterm::event::{self, Event, KeyCode},
style::Color,
symbols::border,
text::Line,
widgets::{Block, HighlightSpacing, List, ListItem, ListState, StatefulWidget, Widget},
};
use serde::{Deserialize, Serialize};
use crate::log::Item;
@ -12,18 +24,59 @@ pub static APP_NAME: &str = "lw";
pub struct App {
logs: Vec<Item>,
config: PathBuf,
#[serde(skip)]
state: ListState,
}
impl App {
pub fn new() -> Self {
Self::default()
}
fn draw(&mut self, frame: &mut Frame) {
self.render(frame.area(), frame.buffer_mut());
}
pub fn run(&mut self, mut terminal: DefaultTerminal) -> Result<()> {
loop {
// terminal.draw(render)?;
terminal.draw(|frame| self.draw(frame))?;
if let Ok(event) = event::read()
&& let Event::Key(key_event) = event
&& key_event.kind == event::KeyEventKind::Press
{
match key_event.code {
KeyCode::Char('q') | KeyCode::Esc => break Ok(()),
_ => {}
}
}
// match event::read() {
// Ok(event) => match event {
// Event::Key(key_event) => match key_event.kind {
// event::KeyEventKind::Press => {
// if key_event.code == KeyCode::Char('q') {
// break Ok(());
// }
// }
// _ => {}
// },
// _ => {}
// },
// _ => {}
// }
}
}
pub fn add(&mut self, item: Item) -> Result<()> {
self.logs.push(item);
self.save()
}
pub fn update<T: AsRef<str>>(&mut self, id: T, content: T) -> Result<()> {
if let Some(item) = self.logs.iter_mut().find(|i| i.id() == id.as_ref()) {
item.update(content.as_ref().to_owned());
}
self.save()
}
pub fn remove<T: AsRef<str>>(&mut self, id: T) -> Result<()> {
self.logs.retain(|i| i.id() != id.as_ref());
self.save()
@ -61,6 +114,81 @@ impl Default for App {
Self {
logs: vec![],
config: dir,
..Default::default()
}
}
}
impl Widget for &mut App {
fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer)
where
Self: Sized,
{
let title = Line::from(" Log Your Work ".bold());
let instructions = Line::from(vec![
Span::raw(" Add "),
Span::styled(
"<O>",
Style::default()
.fg(Color::DarkGray)
.add_modifier(Modifier::BOLD),
),
Span::raw(" Select "),
Span::styled(
"<Space>",
Style::default()
.fg(Color::DarkGray)
.add_modifier(Modifier::BOLD),
),
Span::raw(" Down "),
Span::styled(
"<J>",
Style::default()
.fg(Color::DarkGray)
.add_modifier(Modifier::BOLD),
),
Span::raw(" Up "),
Span::styled(
"<K>",
Style::default()
.fg(Color::DarkGray)
.add_modifier(Modifier::BOLD),
),
Span::raw(" Quit "),
Span::styled(
"<Q> | <ESC>",
Style::default()
.fg(Color::DarkGray)
.add_modifier(Modifier::BOLD),
),
]);
let block = Block::bordered()
.title(title.centered())
.title_bottom(instructions.centered())
.border_set(border::THICK);
let items: Vec<ListItem> = self
.logs
.iter()
.enumerate()
.map(|(i, item)| {
ListItem::new(Line::styled(format!("{}", item.content()), Color::White)).bg(
if i % 2 == 0 {
Color::Black
} else {
Color::DarkGray
},
)
})
.collect();
let list = List::new(items)
.block(block)
.highlight_symbol(">")
.highlight_spacing(HighlightSpacing::Always);
StatefulWidget::render(list, area, buf, &mut self.state);
}
}

View File

@ -15,6 +15,10 @@ impl Item {
Self::default()
}
pub fn content(&self) -> String {
self.content.clone()
}
pub fn update(&mut self, content: String) {
self.content = content;
self.modified = Local::now();

View File

@ -1,15 +1,13 @@
use std::error::Error;
use color_eyre::Result;
use lw::{App, log::Item};
fn main() -> Result<(), Box<dyn Error>> {
fn main() -> Result<()> {
color_eyre::install()?;
let terminal = ratatui::init();
let mut app = App::default();
let item = Item::from("hello_world");
let id = item.id();
app.add(item)?;
println!("{app:?}");
app.remove(id)?;
app.save()?;
println!("{app:?}");
Ok(())
let result = app.run(terminal);
ratatui::restore();
result
}