💄 update
All checks were successful
ci/woodpecker/push/build Pipeline was successful

This commit is contained in:
JMARyA 2025-02-09 01:05:00 +01:00
parent 7aed380e21
commit 77f1c27981
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
4 changed files with 125 additions and 80 deletions

6
Cargo.lock generated
View file

@ -152,7 +152,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]] [[package]]
name = "based" name = "based"
version = "0.1.0" version = "0.1.0"
source = "git+https://git.hydrar.de/jmarya/based?branch=ui#1e0ab7e0dcd42c304146376176d15ff34784bb34" source = "git+https://git.hydrar.de/jmarya/based?branch=ui#cdaabccd9eb701c5f0050def9706fc167137a481"
dependencies = [ dependencies = [
"bcrypt", "bcrypt",
"chrono", "chrono",
@ -257,9 +257,9 @@ checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.12" version = "1.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2" checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda"
dependencies = [ dependencies = [
"shlex", "shlex",
] ]

View file

@ -27,3 +27,7 @@ contact.email = "mail@example.com"
[project.sub.name] [project.sub.name]
name = "Sub Project" name = "Sub Project"
description = "Sub Project" description = "Sub Project"
[project.sub.name.sub.subname]
name = "Sub Sub Project"
description = "Sub Sub Project"

View file

@ -29,3 +29,15 @@ impl Config {
toml::from_str(&content).unwrap() toml::from_str(&content).unwrap()
} }
} }
pub fn get_prj(c: &ProjectConfig, path: &str) -> Option<Project> {
let paths: Vec<_> = path.split("/").filter(|x| !x.is_empty()).collect();
let name = paths.first()?.to_string();
if paths.len() == 1 {
return c.get(&name).cloned();
} else {
let sub_prj = c.get(&name)?.sub.as_ref()?;
get_prj(sub_prj, &paths[1..].join("/"))
}
}

View file

@ -3,12 +3,12 @@ use std::path::Path;
use based::asset::AssetRoutes; use based::asset::AssetRoutes;
use based::page; use based::page;
use based::request::{RequestContext, StringResponse}; use based::request::{RequestContext, StringResponse};
use based::ui::components::{Shell, Tabs}; use based::ui::components::{Breadcrumb, Shell, Tabs};
use based::ui::prelude::*; use based::ui::prelude::*;
use based::ui::primitives::flex::Row; use based::ui::primitives::flex::Row;
use based::ui::primitives::Optional; use based::ui::primitives::Optional;
use config::{Project, ProjectConfig}; use config::{get_prj, Project, ProjectConfig};
use maud::Render; use maud::{PreEscaped, Render};
use rocket::fs::NamedFile; use rocket::fs::NamedFile;
use rocket::request::FromSegments; use rocket::request::FromSegments;
use rocket::{get, launch, routes, State}; use rocket::{get, launch, routes, State};
@ -61,11 +61,10 @@ pub async fn project_page(
) -> Option<StringResponse> { ) -> Option<StringResponse> {
let mut prj = c.get(path.segments.first()?); let mut prj = c.get(path.segments.first()?);
for p in &path.segments[1..] { for p in &path.segments[1..] {
println!(" --> {p}");
prj = prj.as_ref()?.sub.as_ref()?.get(p); prj = prj.as_ref()?.sub.as_ref()?.get(p);
} }
Some(render_project(shell, ctx, prj?, path.to_str()).await) Some(render_project(shell, ctx, prj?, c, path.to_str()).await)
} }
#[get("/static/<file>")] #[get("/static/<file>")]
@ -85,100 +84,130 @@ pub async fn main_page(
let first = keys.first().unwrap(); let first = keys.first().unwrap();
let prj = c.get(*first).unwrap(); let prj = c.get(*first).unwrap();
render_project(shell, ctx, prj, format!("{first}/")).await render_project(shell, ctx, prj, c, format!("{first}/")).await
} else { } else {
// TODO : root project overview // TODO : root project overview
site::gen_site(c, ctx).await site::gen_site(c, ctx).await
} }
} }
pub fn info_tab(prj: &Project, root: &str) -> PreEscaped<String> {
Flex(
Div()
.vanish()
.push(Rounded(
Background(Padding(Code(&prj.description)).all(ScreenValue::_4)).color(Gray::_900),
))
.push(Optional(prj.website.as_deref(), |website| {
Link(website, Text(&format!("Website: {website}")))
}))
.push(Optional(prj.documentation.as_deref(), |docs| {
Link(docs, Text(&format!("Documentation: {docs}")))
}))
.push(Optional(prj.since.as_deref(), |since| {
Text(&format!("Since: {since}"))
}))
.push(Optional(prj.contact.as_ref(), |contact| {
Div()
.vanish()
.push(Text("Contact").large())
.push(Text(&format!(
"eMail: {}",
contact
.email
.as_ref()
.map(|x| x.as_str())
.unwrap_or_default()
)))
}))
.push(Optional(prj.sub.as_ref(), |sub| {
let mut prj_links = Vec::new();
for key in sub.keys() {
prj_links.push(key);
}
Div()
.vanish()
.push(Text("Sub projects").large())
.push_for_each(&prj_links, |link: &_| {
Width(
ScreenValue::fit,
Button(Link(
&format!("/prj/{root}{link}"),
Text(&prj.sub.as_ref().unwrap().get(*link).unwrap().name),
)),
)
})
})),
)
.gap(ScreenValue::_2)
.direction(Direction::Column)
.render()
}
fn up_to(keyword: &str, v: &[&str], separator: &str) -> String {
let index = v.iter().position(|s| *s == keyword).unwrap_or(v.len());
v[..index + 1].join(separator)
}
#[allow(non_snake_case)]
pub fn ProjectIcon(prj: &Project) -> PreEscaped<String> {
Row(vec![
Optional(prj.icon.as_ref(), |icon| Image(icon).alt("Project Icon")),
Text(&prj.name)._2xl().render(),
])
.gap(ScreenValue::_4)
.full_center()
.render()
}
pub async fn render_project( pub async fn render_project(
shell: &State<Shell>, shell: &State<Shell>,
ctx: RequestContext, ctx: RequestContext,
prj: &Project, prj: &Project,
root: String, c: &State<ProjectConfig>,
mut root: String,
) -> StringResponse { ) -> StringResponse {
let title = format!("{} - Umbrella ☂️", prj.name); let title = format!("{} - Umbrella ☂️", prj.name);
if !root.is_empty() && !root.ends_with('/') {
root.push_str("/");
}
let root_paths: Vec<_> = root.split("/").filter(|x| !x.is_empty()).collect();
let bc: Vec<_> = root_paths
.iter()
.map(|x| {
let prj_path = up_to(x, &root_paths, "/");
(
ProjectIcon(&get_prj(c, &prj_path).unwrap()).0,
format!("/prj/{}", prj_path),
)
})
.collect();
println!("{bc:?}");
page!( page!(
shell, shell,
ctx, ctx,
title, title,
Div() Div()
.vanish() .vanish()
.push( .push(Width(
Margin( ScreenValue::fit,
Row(vec![ Margin(Breadcrumb(bc))
Optional(prj.icon.as_ref(), |icon| Image(icon).alt("Project Icon")), .top(ScreenValue::_6)
Text(&prj.name)._2xl().render() .bottom(ScreenValue::_2)
]) .x(ScreenValue::auto)
.gap(ScreenValue::_4) ))
.full_center()
)
.top(ScreenValue::_6)
.bottom(ScreenValue::_2)
)
.push( .push(
Padding( Padding(
Tabs() Tabs()
.add_tab( .add_tab("info", Text("📜 Info").medium(), info_tab(prj, &root))
"info",
Text("📜 Info").medium(),
Flex(
Div()
.vanish()
.push(Text(&prj.description))
.push(Optional(prj.website.as_deref(), |website| Link(
website,
Text(&format!("Website: {website}"))
)))
.push(Optional(prj.documentation.as_deref(), |docs| Link(
docs,
Text(&format!("Documentation: {docs}"))
)))
.push(Optional(prj.since.as_deref(), |since| Text(&format!(
"Since: {since}"
))))
.push(Optional(prj.contact.as_ref(), |contact| {
Div().vanish().push(Text("Contact").large()).push(Text(
&format!(
"eMail: {}",
contact
.email
.as_ref()
.map(|x| x.as_str())
.unwrap_or_default()
),
))
}))
.push(Optional(prj.sub.as_ref(), |sub| {
let mut prj_links = Vec::new();
for key in sub.keys() {
prj_links.push(key);
}
Div()
.vanish()
.push(Text("Sub projects").large())
.push_for_each(&prj_links, |link: &_| {
Link(
&format!("/prj/{root}{link}"),
Text(
&prj.sub
.as_ref()
.unwrap()
.get(*link)
.unwrap()
.name,
),
)
})
}))
)
.gap(ScreenValue::_2)
.direction(Direction::Column)
)
.add_tab( .add_tab(
"invest", "invest",
Text("💵 Invest").medium(), Text("💵 Invest").medium(),