Initial
This commit is contained in:
commit
f90b347880
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/target
|
||||
/static/css
|
||||
Cargo.lock
|
17
Cargo.toml
Normal file
17
Cargo.toml
Normal 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
21
LICENSE
Normal 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
25
README.md
Normal 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
13
scripts/download_styles.sh
Executable 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
1
src/components.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod navigation;
|
63
src/components/navigation.rs
Normal file
63
src/components/navigation.rs
Normal 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
26
src/config.rs
Normal 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
40
src/main.rs
Normal 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
74
src/markdown.rs
Normal 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, "<");
|
||||
out = binding.to_string();
|
||||
|
||||
let binding = RegexBuilder::new(">")
|
||||
.build()
|
||||
.unwrap()
|
||||
.replace_all(&out, ">");
|
||||
out = binding.to_string();
|
||||
|
||||
// unescape arrow alignment
|
||||
let binding = RegexBuilder::new("->>")
|
||||
.build()
|
||||
.unwrap()
|
||||
.replace_all(&out, "->>");
|
||||
out = binding.to_string();
|
||||
|
||||
let binding = RegexBuilder::new("<<-")
|
||||
.build()
|
||||
.unwrap()
|
||||
.replace_all(&out, "<<-");
|
||||
out = binding.to_string();
|
||||
|
||||
let binding = RegexBuilder::new("->")
|
||||
.build()
|
||||
.unwrap()
|
||||
.replace_all(&out, "->");
|
||||
out = binding.to_string();
|
||||
|
||||
let binding = RegexBuilder::new("<-")
|
||||
.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!("<{}>", element))
|
||||
.build()
|
||||
.unwrap()
|
||||
.replace_all(&out, &format!("<{}>", element));
|
||||
|
||||
out = binding.to_string();
|
||||
|
||||
let binding = RegexBuilder::new(&format!("</{}>", 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
1
src/pages.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod home;
|
40
src/pages/home.rs
Normal file
40
src/pages/home.rs
Normal 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
16
src/utility.rs
Normal 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
21
static/style.css
Normal 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;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue