re-implement cli, other tuning

This commit is contained in:
Penelope Gwen 2026-02-23 00:57:01 -08:00
parent 65ab7b0821
commit be34fc7737
6 changed files with 260 additions and 78 deletions

42
Cargo.lock generated
View file

@ -379,6 +379,17 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chacha20"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
dependencies = [
"cfg-if",
"cpufeatures 0.3.0",
"rand_core 0.10.0",
]
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.43" version = "0.4.43"
@ -516,6 +527,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.5.0" version = "1.5.0"
@ -979,6 +999,7 @@ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"r-efi", "r-efi",
"rand_core 0.10.0",
"wasip2", "wasip2",
"wasip3", "wasip3",
] ]
@ -2124,6 +2145,17 @@ dependencies = [
"rand_core 0.9.5", "rand_core 0.9.5",
] ]
[[package]]
name = "rand"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
dependencies = [
"chacha20",
"getrandom 0.4.1",
"rand_core 0.10.0",
]
[[package]] [[package]]
name = "rand_chacha" name = "rand_chacha"
version = "0.9.0" version = "0.9.0"
@ -2149,6 +2181,12 @@ dependencies = [
"getrandom 0.3.4", "getrandom 0.3.4",
] ]
[[package]]
name = "rand_core"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
[[package]] [[package]]
name = "rav1e" name = "rav1e"
version = "0.8.1" version = "0.8.1"
@ -2512,7 +2550,9 @@ dependencies = [
"mdriver", "mdriver",
"mq-markdown", "mq-markdown",
"mq-view", "mq-view",
"rand 0.10.0",
"serde", "serde",
"strip-ansi-escapes",
"termimad", "termimad",
"text-template", "text-template",
"tokio", "tokio",
@ -2528,7 +2568,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures 0.2.17",
"digest", "digest",
] ]

View file

@ -19,3 +19,5 @@ http = "1.4.0"
anyhow = "1.0.101" anyhow = "1.0.101"
toml-frontmatter = "0.1.0" toml-frontmatter = "0.1.0"
serde = "1.0.228" serde = "1.0.228"
rand = "0.10.0"
strip-ansi-escapes = "0.2.1"

View file

@ -0,0 +1,139 @@
use crate::markdowner::MarkdownModule;
use lipgloss::{Border, utils::width};
use mq_markdown::{ListStyle, RenderOptions, UrlSurroundStyle};
use mq_view::{RenderConfig, render_markdown_with_config};
use termimad::minimad::lines;
use warp::Reply;
fn box_content(content: &String, width: i32) -> String {
let lg_border = Border::new("", "", "", "", "", "", "", "", "", "", "", "", "");
let border_style = lipgloss::Style::new()
.border(lg_border)
.width(width)
.padding(0, 2, 0, 2)
.align_horizontal(lipgloss::position::LEFT);
border_style.render(content.as_str())
}
fn markdown_to_cli(content: &String) -> String {
let mut mq: mq_markdown::Markdown = content.parse().expect("could not parse markdown content!");
let render_opts = RenderOptions {
list_style: ListStyle::Dash,
link_url_style: UrlSurroundStyle::None,
link_title_style: mq_markdown::TitleSurroundStyle::Single,
};
mq.set_options(render_opts);
let render_conf = RenderConfig {
header_full_width_highlight: false,
};
let _styled_mq = mq_view::render_markdown_to_string(&mq).unwrap();
let mut writer: Vec<u8> = Vec::new();
render_markdown_with_config(&mq, &mut writer, &render_conf).unwrap();
String::from_utf8(writer).unwrap()
}
fn render_content(content: &String, width: i32) -> String {
let md = markdown_to_cli(content);
box_content(&md, width)
}
fn get_cli_header(page_contents: &Vec<MarkdownModule>, width: i32) -> String {
let header = page_contents
.iter()
.find(|m| m.path.file_name().unwrap_or_default().eq("header.md"));
match header {
Some(h) => render_content(&h.content, width),
None => "".to_string(),
}
}
fn get_cli_content(page_contents: &Vec<MarkdownModule>, width: i32) -> Vec<String> {
let content = page_contents
.iter()
.filter(|m| m.path.file_name().unwrap_or_default().ne("header.md"));
content
.map(|m| render_content(&m.content, (width / 2) - 4))
.collect()
}
fn column_layout(mut left_column: String, mut right_column: String) -> String {
let left_pad = left_column
.lines()
.map(|l| {
String::from_utf8(strip_ansi_escapes::strip(&l))
.expect("could not strip ansi from text")
.chars()
.count()
})
.max()
.unwrap_or(0);
while right_column
.lines()
.count()
.lt(&left_column.lines().count())
{
right_column = format!("{}\n", right_column);
}
while left_column
.lines()
.count()
.lt(&right_column.lines().count())
{
left_column = format!("{}\n", left_column);
}
println!(
"page: {}\nsidebar: {}",
right_column.lines().count(),
left_column.lines().count()
);
let cli_columns: Vec<String> = left_column
.lines()
.zip(right_column.lines())
.map(|(left, right)| format!("{:width$} {}", left, right, width = left_pad))
.collect();
cli_columns.join("\n")
}
pub fn curl_response(
page_contents: Vec<MarkdownModule>,
sidebar_contents: Vec<MarkdownModule>,
width: Option<i32>,
) -> Box<dyn Reply> {
let w = width.unwrap_or(100);
let shell_header = if width.is_none() {
"curl -s beta.pogmom.me/?width=$(tput cols);exit 0\n".to_string()
} else {
"".to_string()
};
let shell_footer = if width.is_none() {
box_content(
&"Did you know‽\nIf you (dangerously) pipe this output to your shell, it autosizes! more interactivity is planned in the future".to_string(),
w - 4)
} else {
"".to_string()
};
let terminal_header = get_cli_header(&page_contents, w - 4);
let terminal_sidebar = get_cli_content(&sidebar_contents, w).join("\n");
let terminal_page = get_cli_content(&page_contents, w).join("\n");
let terminal_body = if w.gt(&110) {
column_layout(terminal_sidebar, terminal_page)
} else {
format!("{}\n{}\n", terminal_sidebar, terminal_page)
};
let cli_contents = format!(
"{}{}\n{}\n{}\n",
shell_header, terminal_header, terminal_body, shell_footer
);
Box::new(warp::reply::with_status(
cli_contents,
warp::http::StatusCode::OK,
))
}

View file

@ -1,13 +1,13 @@
use std::path::PathBuf; use std::path::PathBuf;
#[derive(serde::Deserialize, Debug)] #[derive(serde::Deserialize, Debug, Clone)]
pub struct FrontMatter { pub struct FrontMatter {
pub title: String, pub title: String,
pub date_created: Option<String>, pub date_created: Option<String>,
pub date_updated: Option<String>, pub date_updated: Option<String>,
pub index: Option<usize>, pub index: Option<usize>,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct MarkdownModule { pub struct MarkdownModule {
pub path: PathBuf, pub path: PathBuf,
pub content: String, pub content: String,

View file

@ -1,3 +1,60 @@
pub fn sidebar_content() -> Vec<String> { use crate::{MarkdownModule, markdowner};
vec!["test".to_string()] use rand::seq::IteratorRandom;
use std::{fs::ReadDir, path::PathBuf};
fn random_image_module(module: &MarkdownModule, directory: &str) -> MarkdownModule {
let image = random_image(directory);
let template_content = module.content.clone();
let template = text_template::Template::from(template_content.as_str());
let mut values = std::collections::HashMap::new();
values.insert(
"file_path",
image
.strip_prefix("./serve/")
.unwrap()
.to_str()
.unwrap_or_default(),
);
values.insert(
"file_name",
image
.file_stem()
.unwrap_or_default()
.to_str()
.unwrap_or_default(),
);
let module = MarkdownModule {
path: module.path.clone(),
content: template.fill_in(&values).to_string(),
metadata: module.metadata.clone(),
};
module.clone()
}
pub fn sidebar_content(target_path: &PathBuf) -> Vec<MarkdownModule> {
let mut sidebar_modules = markdowner::get_markdown_modules(target_path);
let sidebar_modules: Vec<MarkdownModule> = sidebar_modules
.iter()
.map(|f| match f.metadata.title.as_str() {
"rats" => random_image_module(f, "rats"),
"buttons" => random_image_module(f, "buttons"),
_ => MarkdownModule {
path: f.path.clone(),
content: f.content.clone(),
metadata: f.metadata.clone(),
},
})
.collect();
sidebar_modules
}
fn random_image(directory: &str) -> PathBuf {
let rat_image = std::fs::read_dir(PathBuf::from("./serve/assets/img/random/").join(directory))
.expect("where the fuck are your rat pictures?")
.map(|f| f.expect("umm what is this?").path())
.choose(&mut rand::rng())
.expect("where is my rat");
println!("{:#?}", rat_image);
rat_image
} }

View file

@ -1,13 +1,12 @@
#![warn(unused_extern_crates)] #![warn(unused_extern_crates)]
#![allow(clippy::style)] #![allow(clippy::style)]
use std::{path::PathBuf, str::FromStr}; use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use http::uri::Authority;
use lipgloss::Border;
use mq_markdown::{ColorTheme, ListStyle, RenderOptions, UrlSurroundStyle};
use mq_view::{RenderConfig, render_markdown_with_config};
use warp::{Filter, filters::path::FullPath}; use warp::{Filter, filters::path::FullPath};
#[path = "lib/curl.rs"]
mod curl;
#[path = "lib/html.rs"] #[path = "lib/html.rs"]
mod html; mod html;
#[path = "lib/markdowner.rs"] #[path = "lib/markdowner.rs"]
@ -15,10 +14,13 @@ mod markdowner;
#[path = "lib/sidebar.rs"] #[path = "lib/sidebar.rs"]
mod sidebar; mod sidebar;
use crate::{ use crate::{
html::html_response, curl::curl_response, html::html_response, markdowner::MarkdownModule, sidebar::sidebar_content,
markdowner::{FrontMatter, MarkdownModule},
}; };
use sidebar::sidebar_content;
#[derive(Serialize, Deserialize)]
struct WebQuery {
width: Option<i32>,
}
fn router(request_path: PathBuf) -> PathBuf { fn router(request_path: PathBuf) -> PathBuf {
std::env::current_dir() std::env::current_dir()
@ -27,8 +29,7 @@ fn router(request_path: PathBuf) -> PathBuf {
.join(request_path) .join(request_path)
} }
fn renderer(path: FullPath, user_agent: String) -> Box<dyn warp::Reply> { fn renderer(path: FullPath, user_agent: String, query: WebQuery) -> Box<dyn warp::Reply> {
//let time = chrono::Local::now().to_rfc2822();
println!("{:?} requested by {}", path, user_agent); println!("{:?} requested by {}", path, user_agent);
let request_path: PathBuf = path.as_str().strip_prefix("/").unwrap_or_default().into(); let request_path: PathBuf = path.as_str().strip_prefix("/").unwrap_or_default().into();
let target_path = router(request_path); let target_path = router(request_path);
@ -45,10 +46,11 @@ fn renderer(path: FullPath, user_agent: String) -> Box<dyn warp::Reply> {
let page_contents = markdowner::get_markdown_modules(&target_path); let page_contents = markdowner::get_markdown_modules(&target_path);
let sidebar_dir = PathBuf::from("assets/sidebar/"); let sidebar_dir = PathBuf::from("assets/sidebar/");
let sidebar_contents = markdowner::get_markdown_modules(&router(sidebar_dir));
let sidebar_contents = sidebar_content(&router(sidebar_dir));
let response = if user_agent.starts_with("curl/") { let response = if user_agent.starts_with("curl/") {
todo!("reimplement curl return") curl_response(page_contents, sidebar_contents, query.width)
} else { } else {
html_response( html_response(
page_contents, page_contents,
@ -62,63 +64,6 @@ fn renderer(path: FullPath, user_agent: String) -> Box<dyn warp::Reply> {
) )
}; };
response response
/*
if user_agent.starts_with("curl/") {
println!("displaying curl formatting");
let page_markdowns: Vec<String> = page_contents
.iter()
.map(|x| {
let source = std::fs::read_to_string(x).unwrap();
let mut mq: mq_markdown::Markdown = source.parse().unwrap();
let render_opts = RenderOptions {
list_style: ListStyle::Dash,
link_url_style: UrlSurroundStyle::None,
link_title_style: mq_markdown::TitleSurroundStyle::Single,
};
mq.set_options(render_opts);
let theme = ColorTheme::parse_colors("heading=34:code=31");
mq.to_colored_string_with_theme(&theme);
let render_conf = RenderConfig {
header_full_width_highlight: false,
};
let _styled_mq = mq_view::render_markdown_to_string(&mq).unwrap();
let mut writer: Vec<u8> = Vec::new();
render_markdown_with_config(&mq, &mut writer, &render_conf).unwrap();
let render_string = String::from_utf8(writer).unwrap();
println!("{}", render_string);
let _md = mdriver::StreamingParser::new().feed(
termimad::text(std::fs::read_to_string(x).unwrap_or_default().as_str())
.to_string()
.as_str(),
);
let border_style = lipgloss::Style::new()
.border(Border::new(
"", "", "", "", "", "", "", "", "", "", "", "", "",
))
.width(80)
.padding(0, 2, 0, 2)
.align_horizontal(lipgloss::position::LEFT);
border_style.render(render_string.as_str())
})
.collect();
for c in &page_markdowns {
println!("{}\n", c);
}
let page_md_collected = format!("{}\n", page_markdowns.join("\n"));
Box::new(warp::reply::with_status(
page_md_collected,
warp::http::StatusCode::OK,
))
}*/
} }
#[tokio::main] #[tokio::main]
@ -127,14 +72,13 @@ async fn main() {
let assets = warp::path("assets").and(warp::fs::dir("./serve/assets/")); let assets = warp::path("assets").and(warp::fs::dir("./serve/assets/"));
let favicon = warp::path("favicon.ico").and(warp::fs::file("./serve/favicon.ico")); let favicon = warp::path("favicon.ico").and(warp::fs::file("./serve/favicon.ico"));
// let no_meta = warp::
let markdowns = warp::any() //path::end() let markdowns = warp::any() //path::end()
.and(warp::path::full()) .and(warp::path::full())
.and(warp::header("user-agent")) .and(warp::header("user-agent"))
.map(|path: FullPath, agent: String| renderer(path, agent)); .and(warp::query::<WebQuery>())
.map(|path: FullPath, agent: String, query: WebQuery| renderer(path, agent, query));
let routes = favicon.or(assets).or(markdowns); let routes = favicon.or(assets).or(markdowns);
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await; warp::serve(routes).run(([0, 0, 0, 0], 3030)).await;
} }