majc: render gemini pages
This commit is contained in:
parent
479fb697ee
commit
d96a7a85bf
|
@ -1,22 +1,24 @@
|
||||||
|
# majc
|
||||||
|
|
||||||
|
```
|
||||||
__
|
__
|
||||||
_____ _____ |__| ____
|
_____ _____ |__| ____
|
||||||
/ \ \__ \ | |_/ ___\
|
/ \ \__ \ | |_/ ___\
|
||||||
| Y Y \ / __ \_ | |\ \___
|
| Y Y \ / __ \_ | |\ \___
|
||||||
|__|_| /(____ //\__| | \___ >
|
|__|_| /(____ //\__| | \___ >
|
||||||
\/ \/ \______| \/
|
\/ \/ \______| \/
|
||||||
|
```
|
||||||
|
|
||||||
A curses client for Gemini!
|
A curses client for Gemini!
|
||||||
|
|
||||||
=> gemini://gemini.circumlunar.space/ Gemini homepage
|
=> gemini://gemini.circumlunar.space/ Gemini homepage
|
||||||
|
|
||||||
## Homepage
|
## Homepage
|
||||||
|
|
||||||
The main homepage for majc is on tulpa.dev:
|
The main homepage for majc is on tulpa.dev:
|
||||||
|
|
||||||
=> https://tulpa.dev/cadey/maj
|
=> https://tulpa.dev/cadey/maj
|
||||||
|
|
||||||
## Important Keys
|
## Important Keys
|
||||||
|
|
||||||
<esc>: opens the menubar
|
<esc>: opens the menubar
|
||||||
c: closes the active window
|
c: closes the active window
|
||||||
o: prompts to open a URL
|
o: prompts to open a URL
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use cursive::{
|
use cursive::{
|
||||||
event::Key,
|
event::Key,
|
||||||
menu::MenuTree,
|
menu::MenuTree,
|
||||||
|
theme::{BaseColor, Color, Effect, Style},
|
||||||
traits::*,
|
traits::*,
|
||||||
|
utils::markup::StyledString,
|
||||||
views::{Dialog, EditView, Panel, ResizedView, TextView},
|
views::{Dialog, EditView, Panel, ResizedView, TextView},
|
||||||
Cursive,
|
Cursive,
|
||||||
};
|
};
|
||||||
|
@ -51,7 +53,7 @@ fn help(siv: &mut Cursive) {
|
||||||
let content = include_str!("./help.gmi");
|
let content = include_str!("./help.gmi");
|
||||||
|
|
||||||
siv.add_layer(
|
siv.add_layer(
|
||||||
Dialog::around(Panel::new(TextView::new(content).scrollable()))
|
Dialog::around(Panel::new(TextView::new(render_gemini(content)).scrollable()))
|
||||||
.title("Help")
|
.title("Help")
|
||||||
.dismiss_button("Ok"),
|
.dismiss_button("Ok"),
|
||||||
);
|
);
|
||||||
|
@ -94,23 +96,69 @@ fn open(siv: &mut Cursive, url: &str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(siv: &mut Cursive, url: &str, resp: Response) {
|
fn show(siv: &mut Cursive, url: &str, resp: Response) {
|
||||||
if resp.status != StatusCode::Success {
|
use StatusCode::*;
|
||||||
siv.add_layer(Dialog::info(format!("{:?}: {}", resp.status, resp.meta)));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match str::from_utf8(&resp.body) {
|
match resp.status {
|
||||||
Ok(content) => {
|
Success => {
|
||||||
siv.add_fullscreen_layer(ResizedView::with_full_screen(
|
match str::from_utf8(&resp.body) {
|
||||||
Dialog::around(TextView::new(content).scrollable())
|
Ok(content) => {
|
||||||
.title(format!("{}: {}", url, resp.meta)),
|
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!(
|
TemporaryRedirect => {
|
||||||
"UTF/8 decoding error for {}: {:?}",
|
open(siv, resp.meta.as_str());
|
||||||
url, why
|
}
|
||||||
)));
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -93,7 +93,10 @@ pub fn parse(doc: &str) -> Vec<Node> {
|
||||||
collect_preformatted = !collect_preformatted;
|
collect_preformatted = !collect_preformatted;
|
||||||
if !collect_preformatted {
|
if !collect_preformatted {
|
||||||
result.push(Node::Preformatted(
|
result.push(Node::Preformatted(
|
||||||
String::from_utf8(preformatted_buffer).unwrap(),
|
String::from_utf8(preformatted_buffer)
|
||||||
|
.unwrap()
|
||||||
|
.trim_end()
|
||||||
|
.to_string(),
|
||||||
));
|
));
|
||||||
preformatted_buffer = vec![];
|
preformatted_buffer = vec![];
|
||||||
}
|
}
|
||||||
|
@ -145,8 +148,14 @@ pub fn parse(doc: &str) -> Vec<Node> {
|
||||||
let sp = line[2..].split_ascii_whitespace().collect::<Vec<&str>>();
|
let sp = line[2..].split_ascii_whitespace().collect::<Vec<&str>>();
|
||||||
|
|
||||||
match sp.len() {
|
match sp.len() {
|
||||||
1 => result.push(Node::Link { to: sp[0].trim().to_string(), name: None }),
|
1 => result.push(Node::Link {
|
||||||
_ => result.push(Node::Link { to: sp[0].trim().to_string(), name: Some(sp[1..].join(" ").trim().to_string()) }),
|
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;
|
continue;
|
||||||
|
@ -194,7 +203,7 @@ mod tests {
|
||||||
\n\
|
\n\
|
||||||
Test\n";
|
Test\n";
|
||||||
let expected: Vec<Node> = vec![
|
let expected: Vec<Node> = vec![
|
||||||
Node::Preformatted("hi there\n".to_string()),
|
Node::Preformatted("hi there".to_string()),
|
||||||
Node::Text(String::new()),
|
Node::Text(String::new()),
|
||||||
Node::Text("Test".to_string()),
|
Node::Text("Test".to_string()),
|
||||||
];
|
];
|
||||||
|
@ -227,8 +236,14 @@ mod tests {
|
||||||
let _ = pretty_env_logger::try_init();
|
let _ = pretty_env_logger::try_init();
|
||||||
let msg = "=>/\n=> / Go home";
|
let msg = "=>/\n=> / Go home";
|
||||||
let expected: Vec<Node> = vec![
|
let expected: Vec<Node> = vec![
|
||||||
Node::Link{to: "/".to_string(), name: None},
|
Node::Link {
|
||||||
Node::Link{to: "/".to_string(), name: Some("Go home".to_string()) },
|
to: "/".to_string(),
|
||||||
|
name: None,
|
||||||
|
},
|
||||||
|
Node::Link {
|
||||||
|
to: "/".to_string(),
|
||||||
|
name: Some("Go home".to_string()),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
assert_eq!(expected, parse(msg));
|
assert_eq!(expected, parse(msg));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue