majc: render gemini pages

This commit is contained in:
Cadey Ratio 2020-07-25 14:05:23 -04:00
parent 479fb697ee
commit d96a7a85bf
3 changed files with 90 additions and 25 deletions

View File

@ -1,22 +1,24 @@
# majc
```
__
_____ _____ |__| ____
/ \ \__ \ | |_/ ___\
| Y Y \ / __ \_ | |\ \___
|__|_| /(____ //\__| | \___ >
\/ \/ \______| \/
```
A curses client for Gemini!
=> gemini://gemini.circumlunar.space/ Gemini homepage
## Homepage
The main homepage for majc is on tulpa.dev:
=> https://tulpa.dev/cadey/maj
## Important Keys
<esc>: opens the menubar
c: closes the active window
o: prompts to open a URL

View File

@ -1,7 +1,9 @@
use cursive::{
event::Key,
menu::MenuTree,
theme::{BaseColor, Color, Effect, Style},
traits::*,
utils::markup::StyledString,
views::{Dialog, EditView, Panel, ResizedView, TextView},
Cursive,
};
@ -51,7 +53,7 @@ fn help(siv: &mut Cursive) {
let content = include_str!("./help.gmi");
siv.add_layer(
Dialog::around(Panel::new(TextView::new(content).scrollable()))
Dialog::around(Panel::new(TextView::new(render_gemini(content)).scrollable()))
.title("Help")
.dismiss_button("Ok"),
);
@ -94,23 +96,69 @@ fn open(siv: &mut Cursive, url: &str) {
}
fn show(siv: &mut Cursive, url: &str, resp: Response) {
if resp.status != StatusCode::Success {
siv.add_layer(Dialog::info(format!("{:?}: {}", resp.status, resp.meta)));
return;
}
use StatusCode::*;
match str::from_utf8(&resp.body) {
Ok(content) => {
siv.add_fullscreen_layer(ResizedView::with_full_screen(
Dialog::around(TextView::new(content).scrollable())
.title(format!("{}: {}", url, resp.meta)),
));
match resp.status {
Success => {
match str::from_utf8(&resp.body) {
Ok(content) => {
siv.add_fullscreen_layer(ResizedView::with_full_screen(
Dialog::around(TextView::new(render_gemini(content)).scrollable())
.title(format!("{}: {}", url, resp.meta)),
));
}
Err(why) => {
siv.add_layer(Dialog::info(format!(
"UTF/8 decoding error for {}: {:?}",
url, why
)));
}
}
}
Err(why) => {
siv.add_layer(Dialog::info(format!(
"UTF/8 decoding error for {}: {:?}",
url, why
)));
TemporaryRedirect => {
open(siv, resp.meta.as_str());
}
PermanentRedirect => {
open(siv, resp.meta.as_str());
}
_ => {
siv.add_layer(Dialog::info(format!("{:?}: {}", resp.status, resp.meta)));
return;
}
}
}
fn render_gemini(body: &str) -> StyledString {
let doc = maj::gemini::parse(body);
let mut styled = StyledString::new();
use maj::gemini::Node::*;
for node in doc {
match node {
Text(line) => styled.append(StyledString::plain(line)),
Link { to, name } => match name {
None => styled.append(StyledString::styled(
to,
Style::from(Effect::Underline),
)),
Some(name) => styled.append(StyledString::styled(
format!("{}: {}", to, name),
Style::from(Effect::Underline),
)),
},
Preformatted(data) => styled.append(StyledString::plain(data)),
Heading { level: _, body } => {
styled.append(StyledString::styled(body, Style::from(Effect::Bold)))
}
ListItem(item) => styled.append(StyledString::plain(format!("* {}", item))),
Quote(quote) => styled.append(StyledString::plain(format!("> {}", quote))),
}
styled.append(StyledString::plain("\n"));
}
styled
}

View File

@ -93,7 +93,10 @@ pub fn parse(doc: &str) -> Vec<Node> {
collect_preformatted = !collect_preformatted;
if !collect_preformatted {
result.push(Node::Preformatted(
String::from_utf8(preformatted_buffer).unwrap(),
String::from_utf8(preformatted_buffer)
.unwrap()
.trim_end()
.to_string(),
));
preformatted_buffer = vec![];
}
@ -145,8 +148,14 @@ pub fn parse(doc: &str) -> Vec<Node> {
let sp = line[2..].split_ascii_whitespace().collect::<Vec<&str>>();
match sp.len() {
1 => result.push(Node::Link { to: sp[0].trim().to_string(), name: None }),
_ => result.push(Node::Link { to: sp[0].trim().to_string(), name: Some(sp[1..].join(" ").trim().to_string()) }),
1 => result.push(Node::Link {
to: sp[0].trim().to_string(),
name: None,
}),
_ => result.push(Node::Link {
to: sp[0].trim().to_string(),
name: Some(sp[1..].join(" ").trim().to_string()),
}),
}
continue;
@ -194,7 +203,7 @@ mod tests {
\n\
Test\n";
let expected: Vec<Node> = vec![
Node::Preformatted("hi there\n".to_string()),
Node::Preformatted("hi there".to_string()),
Node::Text(String::new()),
Node::Text("Test".to_string()),
];
@ -227,8 +236,14 @@ mod tests {
let _ = pretty_env_logger::try_init();
let msg = "=>/\n=> / Go home";
let expected: Vec<Node> = vec![
Node::Link{to: "/".to_string(), name: None},
Node::Link{to: "/".to_string(), name: Some("Go home".to_string()) },
Node::Link {
to: "/".to_string(),
name: None,
},
Node::Link {
to: "/".to_string(),
name: Some("Go home".to_string()),
},
];
assert_eq!(expected, parse(msg));
}