This commit is contained in:
hkau 2024-01-12 23:20:50 -05:00
commit f90b347880
14 changed files with 361 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/target
/static/css
Cargo.lock

17
Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[package]
name = "bundlrs"
authors = ["hkau"]
license = "MIT"
version = "0.1.0"
edition = "2021"
rust-version = "1.75"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
actix-files = "0.6.5"
actix-web = "4.4.1"
regex = "1.10.2"
yew = { version = "0.21.0", features = ["ssr"] }

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 hkau
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

25
README.md Normal file
View file

@ -0,0 +1,25 @@
# bundlrs
*Bundlrs* is a rewrite of [Bundles](https://codeberg.org/SentryTwo/bundles) in Rust without some of the extra features.
Bundlrs has **not** reached acceptable parity with Bundles *yet*.
## Install
Install styles:
```bash
chmod +x scripts/download_styles.sh && ./scripts/download_styles.sh
```
Build:
```bash
cargo build
```
Run:
```bash
cargo run
```

13
scripts/download_styles.sh Executable file
View file

@ -0,0 +1,13 @@
wget https://codeberg.org/api/packages/hkau/npm/fusion/-/1.0.11/fusion-1.0.11.tgz -O fusion.tgz
mv ./fusion.tgz ./static/fusion.tgz
cd static
tar -xzf fusion.tgz
mv package/src/css ./css
sed -i -e 's/\/utility.css/\/static\/css\/utility.css/' ./css/fusion.css
rm -r package
rm ./fusion.tgz
cd ../

1
src/components.rs Normal file
View file

@ -0,0 +1 @@
pub mod navigation;

View file

@ -0,0 +1,63 @@
use yew::prelude::*;
#[function_component]
pub fn Footer() -> Html {
let info_req = std::env::var("INFO");
let mut info: String = String::new();
if info_req.is_err() && info.is_empty() {
info = "/pub/info".to_string();
} else {
info = info_req.unwrap();
}
// ...
return html! {
<div class="flex justify-center align-center flex-column">
<hr class="small" style="width:425px; max-width:100%; margin-top:1rem;" />
<ul class="__footernav" style="padding: 0; margin: 0;">
<li><a href="/">{"new"}</a></li>
<li><a href="/s">{"settings"}</a></li>
<li><a href="/search">{"search"}</a></li>
<li><a href={info}>{"info"}</a></li>
</ul>
<p style="font-size: 12px; margin: 0.4rem 0 0 0;">
{"bundles - Markdown Delivery Service"}
</p>
<style>{".__footernav {
display: flex;
gap: 0.25rem;
}
.__footernav li {
list-style-type: \"·\";
padding: 0 0.25rem;
}
.__footernav li:first-child {
margin-left: -0.25rem;
}
.__footernav li:first-child {
list-style-type: none;
}
.__footer_cardbtn {
width: calc(33% - 0.25rem);
height: 10rem !important;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 0.5rem;
border-radius: 0.4rem;
}"}
</style>
// <ThemeButton>
</div>
};
}

26
src/config.rs Normal file
View file

@ -0,0 +1,26 @@
use std::{env, ops::Index};
pub fn collect_arguments() -> Vec<String> {
return env::args().collect::<Vec<String>>();
}
pub fn get_named_argument(args: &Vec<String>, name: &str) -> Option<String> {
for (i, v) in args.iter().enumerate() {
// if name does not match, continue
if v != &format!("--{}", name) {
continue;
};
// return value
let val: &String = args.index(i + 1);
// ...make sure val exists (return None if it doesn't!)
if val.is_empty() {
return Option::None;
}
return Option::Some(String::from(val));
}
return Option::None;
}

40
src/main.rs Normal file
View file

@ -0,0 +1,40 @@
use actix_files as fs;
use actix_web::{App, HttpServer};
mod config;
mod utility;
mod components;
mod pages;
mod markdown;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let args: Vec<String> = config::collect_arguments();
let port_search: Option<String> = config::get_named_argument(&args, "port");
let mut port: u16 = 8080;
if port_search.is_some() {
port = port_search.unwrap().parse::<u16>().unwrap();
}
println!(
"{}",
markdown::parse_markdown("Hello, <friend>! <style>test</style> &!ast;".to_string())
);
// start server
println!("Starting server at: http://localhost:{port}");
HttpServer::new(|| {
App::new()
// static dir
.service(fs::Files::new("/static", "./static").show_files_listing())
// GET root
.service(crate::pages::home::home_request)
})
.bind(("127.0.0.1", port))?
.run()
.await
}

74
src/markdown.rs Normal file
View file

@ -0,0 +1,74 @@
use regex::RegexBuilder;
pub fn parse_markdown(input: String) -> String {
let mut out: String = input;
// escape < and >
let binding = RegexBuilder::new("<")
.build()
.unwrap()
.replace_all(&out, "&lt;");
out = binding.to_string();
let binding = RegexBuilder::new(">")
.build()
.unwrap()
.replace_all(&out, "&gt;");
out = binding.to_string();
// unescape arrow alignment
let binding = RegexBuilder::new("-&gt;&gt;")
.build()
.unwrap()
.replace_all(&out, "->>");
out = binding.to_string();
let binding = RegexBuilder::new("&lt;&lt-")
.build()
.unwrap()
.replace_all(&out, "<<-");
out = binding.to_string();
let binding = RegexBuilder::new("-&gt;")
.build()
.unwrap()
.replace_all(&out, "->");
out = binding.to_string();
let binding = RegexBuilder::new("&lt;-")
.build()
.unwrap()
.replace_all(&out, "<-");
out = binding.to_string();
// allowed elements
let allowed_elements: Vec<&str> = Vec::from([
"hue", "sat", "lit", "theme", "comment", "p", "span", "style",
]);
for element in allowed_elements {
let binding = RegexBuilder::new(&format!("&lt;{}&gt;", element))
.build()
.unwrap()
.replace_all(&out, &format!("<{}>", element));
out = binding.to_string();
let binding = RegexBuilder::new(&format!("&lt;/{}&gt;", element))
.build()
.unwrap()
.replace_all(&out, &format!("</{}>", element));
out = binding.to_string();
}
// HTML escapes
let binding = RegexBuilder::new("(&!)(.*?);")
.build()
.unwrap()
.replace_all(&out, "&$2;");
out = binding.to_string();
// return
return out.to_string();
}

1
src/pages.rs Normal file
View file

@ -0,0 +1 @@
pub mod home;

40
src/pages/home.rs Normal file
View file

@ -0,0 +1,40 @@
use actix_web::{get, HttpResponse, Responder};
use yew::prelude::*;
use yew::ServerRenderer;
use crate::components::navigation::Footer;
use crate::utility::format_html;
#[function_component]
fn Home() -> Html {
return html! {
<div class="flex flex-column g-4" style="height: 100dvh;">
<main style="height: calc(100% - 1rem);">
<div class="tabbar justify-space-between full">
// left
<div class="flex">
<button id="editor-open-tab-text">{"Text"}</button>
<button id="editor-open-tab-preview" class="secondary">
{"Preview"}
</button>
</div>
</div>
<div id="-editor" class="tab-container card secondary round" style="border-top-left-radius: 0px; border-top-right-radius: 0px;">
<div id="editor-tab-text" class="editor-tab -editor active" style="100%;">
{"text input here"}
</div>
</div>
<Footer />
</main>
</div>
};
}
#[get("/")]
pub async fn home_request() -> impl Responder {
let renderer = ServerRenderer::<Home>::new();
return HttpResponse::Ok().body(format_html(renderer.render().await));
}

16
src/utility.rs Normal file
View file

@ -0,0 +1,16 @@
pub fn format_html(input: String) -> String {
return format!(
"<!DOCTYPE html>
<html lang=\"en\">
<head>
<meta charset=\"UTF-8\" />
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />
<title>Document</title>
<link rel=\"stylesheet\" href=\"/static/style.css\" />
</head>
<body>{input}</body>
</html>"
)
.to_string();
}

21
static/style.css Normal file
View file

@ -0,0 +1,21 @@
/* https://codeberg.org/hkau/fusion */
@import url("./css/fusion.css");
.tab-container {
background: var(--background-surface1);
transition: background 0.15s;
padding: 1.5rem !important;
height: 78dvh;
overflow-y: auto;
max-height: 90vh;
margin-bottom: 0.5rem;
max-width: 100vw;
min-height: 15rem;
}
@media screen and (max-width: 900px) {
.tab-container {
max-height: 65vh;
padding: 1rem;
}
}