feat: add linebreak in details and improve code quality

This commit is contained in:
itsscb 2025-07-26 19:59:02 +02:00
parent 05f885f38d
commit 6b08123ca6
2 changed files with 62 additions and 46 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "lw" name = "lw"
version = "0.1.0" version = "0.2.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]

View File

@ -47,13 +47,6 @@ impl Default for App {
Self::new(config.to_owned()).expect("could not create app struct with default config") Self::new(config.to_owned()).expect("could not create app struct with default config")
} }
} }
fn popup_area(area: Rect, percent_x: u16, percent_y: u16) -> Rect {
let vertical = Layout::vertical([Constraint::Percentage(percent_y)]).flex(Flex::Center);
let horizontal = Layout::horizontal([Constraint::Percentage(percent_x)]).flex(Flex::Center);
let [area] = vertical.areas(area);
let [area] = horizontal.areas(area);
area
}
impl App { impl App {
pub fn new(config: PathBuf) -> Result<Self> { pub fn new(config: PathBuf) -> Result<Self> {
if config.exists() if config.exists()
@ -61,7 +54,7 @@ impl App {
{ {
let mut app: Self = serde_json::from_str(&v)?; let mut app: Self = serde_json::from_str(&v)?;
app.logs.sort_by(|a, b| b.created().cmp(&a.created())); app.logs.sort_by_key(|l| std::cmp::Reverse(l.created()));
return Ok(app); return Ok(app);
} }
Err(eyre!("failed to read config")) Err(eyre!("failed to read config"))
@ -132,7 +125,7 @@ impl App {
Paragraph::new( Paragraph::new(
Text::from(format!( Text::from(format!(
"modified at {}", "modified at {}",
item.modified().format("%Y-%m-%d %H:%M:%S").to_string() item.modified().format("%Y-%m-%d %H:%M:%S")
)) ))
.style(Style::default().fg(COLOR_SECONDARY).bold()) .style(Style::default().fg(COLOR_SECONDARY).bold())
.right_aligned(), .right_aligned(),
@ -145,7 +138,7 @@ impl App {
Paragraph::new( Paragraph::new(
Text::from(format!( Text::from(format!(
"created at {}", "created at {}",
item.created().format("%Y-%m-%d %H:%M:%S").to_string() item.created().format("%Y-%m-%d %H:%M:%S")
)) ))
.style(Style::default().fg(COLOR_SECONDARY).bold()), .style(Style::default().fg(COLOR_SECONDARY).bold()),
), ),
@ -168,12 +161,25 @@ impl App {
}) })
.collect(); .collect();
frame.render_widget(Paragraph::new(v).block(block), outer[0]); frame.render_widget(
Paragraph::new(v)
.block(block)
.wrap(ratatui::widgets::Wrap { trim: false }),
outer[0],
);
} }
} }
pub fn handle_edit_keys(&mut self, key_event: KeyEvent, item: Item) -> Result<()> { pub fn handle_edit_keys(&mut self, key_event: KeyEvent, item: Item) -> Result<()> {
match key_event.code { match key_event.code {
KeyCode::Backspace => {
if let Some(item) = handle_backspace(item.clone(), key_event) {
self.edit = Some(item);
}
}
KeyCode::Esc => {
self.edit = None;
}
KeyCode::Char('o') | KeyCode::Enter KeyCode::Char('o') | KeyCode::Enter
if key_event.modifiers.contains(KeyModifiers::CONTROL) => if key_event.modifiers.contains(KeyModifiers::CONTROL) =>
{ {
@ -183,11 +189,11 @@ impl App {
.replace("\t", "") .replace("\t", "")
.is_empty() .is_empty()
{ {
if self.logs.iter().find(|l| l.id() == item.id()).is_some() { if self.logs.iter().any(|l| l.id() == item.id()) {
self.update(item.id(), item.content()); self.update(item.id(), item.content());
} else { } else {
self.logs.push(item.clone()); self.logs.push(item.clone());
self.logs.sort_by(|a, b| b.created().cmp(&a.created())); self.logs.sort_by_key(|l| std::cmp::Reverse(l.created()));
} }
self.edit = None; self.edit = None;
self.save()?; self.save()?;
@ -201,7 +207,11 @@ impl App {
self.edit = Some(tmp); self.edit = Some(tmp);
} }
KeyCode::Char(key) => { KeyCode::Char(key) => {
if key == 'c' && key_event.modifiers.contains(KeyModifiers::CONTROL) { if key == 'h' && key_event.modifiers.contains(KeyModifiers::CONTROL) {
if let Some(item) = handle_backspace(item.clone(), key_event) {
self.edit = Some(item);
}
} else if key == 'c' && key_event.modifiers.contains(KeyModifiers::CONTROL) {
self.edit = None; self.edit = None;
} else { } else {
let mut tmp = item.clone(); let mut tmp = item.clone();
@ -211,22 +221,6 @@ impl App {
self.edit = Some(tmp); self.edit = Some(tmp);
} }
} }
KeyCode::Backspace => {
let mut tmp = item.clone();
let mut s: String = tmp.content();
if s.len() > 0 {
tmp.update(if key_event.modifiers.contains(KeyModifiers::CONTROL) {
ctrl_backspace_remaining(s)
} else {
s.truncate(s.len() - 1);
s
});
self.edit = Some(tmp);
}
}
KeyCode::Esc => {
self.edit = None;
}
_ => {} _ => {}
} }
Ok(()) Ok(())
@ -300,8 +294,8 @@ impl App {
&& let Event::Key(key_event) = event && let Event::Key(key_event) = event
&& key_event.kind == event::KeyEventKind::Press && key_event.kind == event::KeyEventKind::Press
{ {
if self.edit.is_some() { if let Some(ref item) = self.edit {
let item = self.edit.clone().unwrap(); let item = item.clone();
self.handle_edit_keys(key_event, item)?; self.handle_edit_keys(key_event, item)?;
continue; continue;
} }
@ -312,14 +306,14 @@ impl App {
pub fn add(&mut self, item: Item) { pub fn add(&mut self, item: Item) {
self.logs.push(item); self.logs.push(item);
self.logs.sort_by(|a, b| b.created().cmp(&a.created())); self.logs.sort_by_key(|l| std::cmp::Reverse(l.created()));
} }
pub fn update<T: AsRef<str>>(&mut self, id: T, content: T) { pub fn update<T: AsRef<str>>(&mut self, id: T, content: T) {
if let Some(item) = self.logs.iter_mut().find(|i| i.id() == id.as_ref()) { if let Some(item) = self.logs.iter_mut().find(|i| i.id() == id.as_ref()) {
item.update(content.as_ref().to_owned()); item.update(content.as_ref().to_owned());
} }
self.logs.sort_by(|a, b| b.created().cmp(&a.created())); self.logs.sort_by_key(|l| std::cmp::Reverse(l.created()));
} }
pub fn remove<T: AsRef<str>>(&mut self, id: T) { pub fn remove<T: AsRef<str>>(&mut self, id: T) {
@ -469,21 +463,43 @@ fn ctrl_backspace_remaining<T: AsRef<str>>(s: T) -> String {
} }
} }
match pos_after_trailing_ws { if pos_after_trailing_ws.is_some() {
None => 0, let mut o = 0;
Some(_) => { for (idx, ch) in chars {
let mut o = 0; if ch.is_whitespace() {
for (idx, ch) in chars { o = idx + ch.len_utf8();
if ch.is_whitespace() { o = o.saturating_sub(1);
o = idx + ch.len_utf8(); break;
o = o.saturating_sub(1);
break;
}
} }
o
} }
o
} else {
0
} }
}; };
String::from(&s[..cut_pos]) String::from(&s[..cut_pos])
} }
fn handle_backspace(item: Item, key_event: KeyEvent) -> Option<Item> {
let mut tmp = item;
let mut s: String = tmp.content();
if !s.is_empty() {
tmp.update(if key_event.modifiers.contains(KeyModifiers::CONTROL) {
ctrl_backspace_remaining(s)
} else {
s.truncate(s.len() - 1);
s
});
return Some(tmp);
}
None
}
fn popup_area(area: Rect, percent_x: u16, percent_y: u16) -> Rect {
let vertical = Layout::vertical([Constraint::Percentage(percent_y)]).flex(Flex::Center);
let horizontal = Layout::horizontal([Constraint::Percentage(percent_x)]).flex(Flex::Center);
let [area] = vertical.areas(area);
let [area] = horizontal.areas(area);
area
}