-use mastodonochrome::types::*;
-use mastodonochrome::html::{render};
-use mastodonochrome::html;
-use std::collections::HashMap;
use std::io::Write;
+use unicode_width::UnicodeWidthStr;
+
+use mastodonochrome::client::Client;
+use mastodonochrome::coloured_string::ColouredStringSlice;
+use mastodonochrome::text::{parse_html, Paragraph, TextFragment};
use crossterm::{
event::{self, Event, KeyCode, KeyEventKind},
ExecutableCommand,
};
use ratatui::{
- prelude::{CrosstermBackend, Terminal},
+ prelude::{Buffer, CrosstermBackend, Terminal},
style::{Style, Color, Modifier},
};
use std::io::stdout;
}
*/
+fn ratatui_style_from_colour(colour: char) -> Style {
+ match colour {
+ // default
+ ' ' => Style::default(),
+
+ // message separator line, other than the date
+ 'S' => Style::default().fg(Color::White).bg(Color::Blue)
+ .add_modifier(Modifier::REVERSED | Modifier::BOLD),
+
+ // date on a message separator line
+ 'D' => Style::default().fg(Color::White).bg(Color::Blue)
+ .add_modifier(Modifier::REVERSED),
+
+ // username in a From line
+ 'F' => Style::default().fg(Color::Green)
+ .add_modifier(Modifier::BOLD),
+
+ // username in other headers like Via
+ 'f' => Style::default().fg(Color::Green),
+
+ // <code> tags
+ 'c' => Style::default().fg(Color::Yellow),
+
+ // #hashtags
+ '#' => Style::default().fg(Color::Cyan),
+
+ // @mentions of a user
+ '@' => Style::default().fg(Color::Green),
+
+ // <em> tags
+ '_' => Style::default().add_modifier(Modifier::UNDERLINED),
+
+ // <strong> tags
+ 's' => Style::default().add_modifier(Modifier::BOLD),
+
+ // URL
+ 'u' => Style::default().fg(Color::Blue)
+ .add_modifier(Modifier::BOLD | Modifier::UNDERLINED),
+
+ // media URL
+ 'M' => Style::default().fg(Color::Magenta)
+ .add_modifier(Modifier::BOLD),
+
+ // media description
+ 'm' => Style::default().fg(Color::Magenta),
+
+ // Mastodonochrome logo in file headers
+ 'J' => Style::default().fg(Color::Blue).bg(Color::White)
+ .add_modifier(Modifier::REVERSED | Modifier::BOLD),
+
+ // ~~~~~ underline in file headers
+ '~' => Style::default().fg(Color::Blue),
+
+ // actual header text in file headers
+ 'H' => Style::default().fg(Color::Cyan),
+
+ // keypress / keypath names in file headers
+ 'K' => Style::default().fg(Color::Cyan)
+ .add_modifier(Modifier::BOLD),
+
+ // keypresses in file status lines
+ 'k' => Style::default().add_modifier(Modifier::BOLD),
+
+ // separator line between editor header and content
+ '-' => Style::default().fg(Color::Cyan).bg(Color::Black)
+ .add_modifier(Modifier::REVERSED),
+
+ // something really boring, like 'none' in place of data
+ '0' => Style::default().fg(Color::Blue),
+
+ // red nastinesses like blocking/muting in Examine User
+ 'r' => Style::default().fg(Color::Red),
+
+ // # reverse-video > indicating a truncated too-long line
+ '>' => Style::default().add_modifier(Modifier::REVERSED),
+
+ // # error report, or by default any unrecognised colour character
+ '!' | _ => Style::default().fg(Color::Red).bg(Color::Yellow).
+ add_modifier(Modifier::REVERSED | Modifier::BOLD)
+ }
+}
+
+fn ratatui_set_string(buf: &mut Buffer, y: usize, x: usize,
+ text: &ColouredStringSlice<'_>) {
+ let mut x = x;
+ if let Ok(y) = y.try_into() {
+ for (frag, colour) in text.frags() {
+ if let Ok(x) = x.try_into() {
+ buf.set_string(x, y, frag, ratatui_style_from_colour(colour));
+ } else {
+ break;
+ }
+ x += UnicodeWidthStr::width(frag);
+ }
+ }
+}
+
#[allow(unused)]
-fn tui() -> std::io::Result<()> {
+fn tui(paras: Vec<Paragraph>) -> std::io::Result<()> {
stdout().execute(EnterAlternateScreen)?;
enable_raw_mode()?;
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
loop {
terminal.draw(|frame| {
- dbg!(std::time::SystemTime::now());
- let _area = frame.size();
- let buf = frame.buffer_mut();
- buf.reset();
- buf.set_string(2, 2, "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod temp🔭or incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", Style::default().fg(Color::Red).bg(Color::Yellow).add_modifier(Modifier::REVERSED | Modifier::BOLD));
- })?;
+ let area = frame.size();
+ let buf = frame.buffer_mut();
+ buf.reset();
+ let mut y = 0;
+ for para in ¶s {
+ for line in para.render(area.width as usize) {
+ ratatui_set_string(buf, y, 0, &line.slice());
+ y += 1;
+ }
+ }
+ })?;
match receiver.recv() {
Err(_) => break,
Ok(())
}
-struct TestReceiver {}
-
-impl html::Receiver for TestReceiver {
- fn start_tag(&mut self, tag: &str, attrs: &HashMap<String, String>) {
- dbg!("start", tag, attrs);
- }
- fn end_tag(&mut self, tag: &str, attrs: &HashMap<String, String>) {
- dbg!("end", tag, attrs);
+fn main() {
+ let mut client = Client::new().unwrap();
+ if let Some(st) = client.status_by_id("111602135142646031") {
+ let paras = parse_html(&st.content);
+ tui(paras).unwrap();
}
- fn text(&mut self, text: &str) {
- dbg!("text", text);
- }
-}
-
-fn main() -> std::io::Result<()> {
- let client = reqwest::blocking::Client::new();
- let body = client.get(
- "https://hachyderm.io/api/v1/statuses/111602135142646031")
- .send().unwrap().text().unwrap();
- let st: Status = serde_json::from_str(&body).unwrap();
- let mut t = TestReceiver {};
- render(&st.content, &mut t);
- Ok(())
}