diff --git a/Cargo.lock b/Cargo.lock index 75e8e6f..f152d67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -202,7 +202,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "based" version = "0.1.0" -source = "git+https://git.hydrar.de/jmarya/based?branch=ui#79f08fd202abcfbc52cbab09be7dccc02f4c7c01" +source = "git+https://git.hydrar.de/jmarya/based?branch=ui#3f411e071fd4d47ff0e2d7e7eeab4f638bd35799" dependencies = [ "bcrypt", "chrono", @@ -403,9 +403,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -1350,9 +1350,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -1376,19 +1376,19 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "is-terminal" -version = "0.4.13" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" dependencies = [ "hermit-abi 0.4.0", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1601,9 +1601,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" dependencies = [ "libc", "log", @@ -1706,9 +1706,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl" -version = "0.10.68" +version = "0.10.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" dependencies = [ "bitflags 2.8.0", "cfg-if", @@ -1732,9 +1732,9 @@ dependencies = [ [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" @@ -2313,9 +2313,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.43" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags 2.8.0", "errno", @@ -2459,9 +2459,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.135" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" +checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" dependencies = [ "itoa", "memchr", @@ -3286,9 +3286,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" [[package]] name = "unicode-normalization" @@ -3354,9 +3354,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4" +checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" dependencies = [ "getrandom", "serde", diff --git a/Cargo.toml b/Cargo.toml index 0c35b39..65a1c78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -based = { git = "https://git.hydrar.de/jmarya/based", features = ["htmx"], branch = "ui" } +based = { git = "https://git.hydrar.de/jmarya/based", branch = "ui" } bytesize = "1.3.0" chrono = "0.4.39" env_logger = "0.11.6" diff --git a/src/main.rs b/src/main.rs index 8bbff53..650095b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,8 @@ +use based::asset::AssetRoutes; use based::auth::User; use based::get_pg; +use based::ui::components::Shell; +use based::ui::prelude::*; use config::Config; use rocket::routes; @@ -19,9 +22,20 @@ async fn launch() -> _ { let _ = User::create("admin".to_string(), "admin", based::auth::UserRole::Admin).await; + let shell = Shell::new( + Nothing(), + Nothing(), + Background(MinHeight( + ScreenValue::screen, + Padding(Text("").color(&Gray::_200)).all(ScreenValue::_10), + )) + .color(Gray::_900), + ) + .use_ui(); + rocket::build() + .mount_assets() .mount("/", routes![ - based::htmx::htmx_script_route, routes::index_page, routes::pkg_route, routes::push::upload_pkg, @@ -36,4 +50,5 @@ async fn launch() -> _ { routes::user::change_password_post ]) .manage(config) + .manage(shell) } diff --git a/src/pkg/package.rs b/src/pkg/package.rs index 16ad15f..3ecc9b5 100644 --- a/src/pkg/package.rs +++ b/src/pkg/package.rs @@ -91,7 +91,7 @@ impl Package { } } - let mut ret: Vec<_> = ret.into_iter().map(|x| (x.0, x.1.join(" "))).collect(); + let mut ret: Vec<_> = ret.into_iter().map(|x| (x.0, x.1.join(";"))).collect(); ret.sort_by(|a, b| a.0.cmp(&b.0)); ret @@ -218,6 +218,15 @@ impl Package { .collect() } + pub fn fonts(&self) -> Vec { + let files = self.file_list(); + files + .into_iter() + .filter(|x| x.starts_with("usr/share/fonts")) + .map(|x| x.trim_start_matches("usr/share/fonts/").to_string()) + .collect() + } + pub fn firmware(&self) -> Vec { let files = self.file_list(); files diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 825be5b..8e7356b 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,8 +1,8 @@ use based::auth::MaybeUser; use based::request::{RawResponse, RequestContext, StringResponse, respond_with}; use based::ui::components::Shell; -use based::ui::{prelude::*, render_page}; -use maud::{PreEscaped, Render, html}; +use based::ui::prelude::*; +use maud::{Render, html}; use pacco::pkg::mirror::MirrorRepository; use rocket::http::{ContentType, Status}; use rocket::{State, get}; @@ -21,6 +21,7 @@ pub async fn index_page( ctx: RequestContext, user: MaybeUser, config: &State, + shell: &State, ) -> StringResponse { let repos: Vec = Repository::list(); @@ -40,8 +41,8 @@ pub async fn index_page( Padding( Shadow::medium( Flex(Hover( - Background(Gray::_600, Nothing())).on( - Background(Gray::_700, + Background(Nothing()).color(Gray::_600)).on( + Background( Link( &format!("/{repo}"), Div().vanish().push(Text(&repo).medium().color(&Gray::_100)) @@ -49,8 +50,8 @@ pub async fn index_page( if config.is_mirrored_repo(&repo) { Some(&()) } else { None }, - |_: &()| Context(Padding(Rounded(Background(Blue::_500, Text("Mirrored").white().medium().sm())).size(Size::Full)).x(ScreenValue::_3).y(ScreenValue::_1)))).use_htmx() - ))).items_center().gap(ScreenValue::_4) + |_: &()| Context(Padding(Rounded(Background(Text("Mirrored").white().medium().sm()).color(Blue::_500)).size(Size::Full)).x(ScreenValue::_3).y(ScreenValue::_1)))).use_htmx() + ).color(Gray::_700))).items_center().gap(ScreenValue::_4) )).all(ScreenValue::_4)) .size(Size::Large))) } @@ -58,7 +59,7 @@ pub async fn index_page( ) ).render(); - render(content, "Repositories", ctx).await + shell.render_page(content, "Repositories", ctx).await } #[get("/pkg///")] @@ -159,28 +160,6 @@ pub async fn pkg_route( ) } -pub async fn render( - content: PreEscaped, - title: &str, - ctx: RequestContext, -) -> StringResponse { - render_page( - content, - title, - ctx, - &Shell::new( - html! { - script src="https://cdn.tailwindcss.com" {}; - script src="/assets/htmx.min.js" {}; - meta name="viewport" content="width=device-width, initial-scale=1.0"; - }, - html! {}, - Some("bg-gray-900 text-gray-200 min-h-screen p-10".to_string()), - ), - ) - .await -} - pub fn is_repo_db(pkg_name: &str) -> bool { pkg_name.ends_with("db.tar.gz") || pkg_name.ends_with("db") diff --git a/src/routes/ui/mod.rs b/src/routes/ui/mod.rs index c12f915..948ce60 100644 --- a/src/routes/ui/mod.rs +++ b/src/routes/ui/mod.rs @@ -1,8 +1,6 @@ use based::ui::prelude::*; use maud::{PreEscaped, Render}; -use super::render; - // TODO : API pub mod pkg; @@ -21,9 +19,9 @@ pub fn arch_card(a: &str, repo_name: &str, current: bool) -> PreEscaped Rounded( Padding(if current { - Background(Blue::_500, link) + Background(link).color(Blue::_500) } else { - Background(Gray::_700, link) + Background(link).color(Gray::_700) }) .x(ScreenValue::_3) .y(ScreenValue::_1), diff --git a/src/routes/ui/pkg.rs b/src/routes/ui/pkg.rs index 4c922f3..5c06b3e 100644 --- a/src/routes/ui/pkg.rs +++ b/src/routes/ui/pkg.rs @@ -1,15 +1,14 @@ use based::request::{RequestContext, StringResponse}; +use based::ui::components::Shell; use based::ui::primitives::flex::Strategy; use based::ui::primitives::space::SpaceBetweenWidget; use based::ui::primitives::text::{Code, TextWidget}; use based::ui::{UIWidget, prelude::*}; use maud::{PreEscaped, Render, html}; -use rocket::get; +use rocket::{State, get}; use pacco::pkg::{Package, Repository, arch::Architecture, find_package_by_name}; -use crate::routes::render; - use super::take_out; #[get("//?")] @@ -18,6 +17,7 @@ pub async fn pkg_ui( pkg_name: &str, ctx: RequestContext, ver: Option<&str>, + shell: &State, ) -> Option { let repo = Repository::new(repo).unwrap(); let mut pkg = repo.get_pkg_by_name(pkg_name)?; @@ -42,6 +42,7 @@ pub async fn pkg_ui( let shared_objects = pkg.shared_objects(); let icons = pkg.icons(); let readmes = pkg.readmes(); + let fonts = pkg.fonts(); let mut pkginfo = pkg.pkginfo(); let pkg_info_header = Flex( @@ -69,15 +70,15 @@ pub async fn pkg_ui( .push_for_each(&arch, |arch: &Architecture| { Rounded( Padding( - Background(Gray::_700, - Span(&arch.to_string()).medium().sm()) + Background( + Span(&arch.to_string()).medium().sm()).color(Gray::_700) ).x(ScreenValue::_3).y(ScreenValue::_1) ).size(Size::Full) }) .push(Margin( Flex( Padding( - Hover(Background(Gray::_300, Nothing())).on(Background(Gray::_200, + Hover(Background(Nothing()).color(Gray::_300)).on(Background( Rounded(Shadow::medium( Link(&format!("/pkg/{}/{}/{}", pkg.repo, pkg.arch.to_string(), pkg.file_name()), Paragraph( @@ -88,7 +89,7 @@ pub async fn pkg_ui( p class="ml-2 text-black" { "Download" }; } ).black().xs()))).size(Size::Large) - )) + ).color(Gray::_200)) ).x(ScreenValue::_2).y(ScreenValue::_2) ).items_center() ).left(ScreenValue::_4) @@ -101,9 +102,8 @@ pub async fn pkg_ui( .vanish() .push(Context(FlexGrow( Strategy::Grow, - Animated(Background( - Gray::_800, - Shadow::large( + Animated( + Background(Shadow::large( Rounded( Margin( Padding( @@ -143,14 +143,14 @@ pub async fn pkg_ui( .all(ScreenValue::_2), ) .size(Size::Large), - ), - )), + )) + .color(Gray::_800), + ), ))) .push( SpaceBetween( - Padding(Background( - Gray::_800, - Shadow::large( + Padding( + Background(Shadow::large( Rounded( Margin( Screen::medium(FlexGrow(Strategy::NoGrow, Nothing())).on( @@ -210,8 +210,9 @@ pub async fn pkg_ui( .all(ScreenValue::_2), ) .size(Size::Large), - ), - )) + )) + .color(Gray::_800), + ) .all(ScreenValue::_4), ) .y(ScreenValue::_2), @@ -257,12 +258,14 @@ pub async fn pkg_ui( ) .push( Padding( - Rounded(Background( - Gray::_700, - Code(&hook.1) - .sm() - .color(&Gray::_100), - )) + Rounded( + Background( + Code(&hook.1) + .sm() + .color(&Gray::_100), + ) + .color(Gray::_700), + ) .size(Size::Large), ) .all(ScreenValue::_4), @@ -293,12 +296,14 @@ pub async fn pkg_ui( ) .push( Padding( - Rounded(Background( - Gray::_700, - Code(&readme.1) - .sm() - .color(&Gray::_100), - )) + Rounded( + Background( + Code(&readme.1) + .sm() + .color(&Gray::_100), + ) + .color(Gray::_700), + ) .size(Size::Large), ) .all(ScreenValue::_4), @@ -336,6 +341,14 @@ pub async fn pkg_ui( .push(ListElements(&shared_objects)), ) }) + .push_if(!fonts.is_empty(), || { + InfoCard( + Div() + .vanish() + .push(CardTitle("Fonts")) + .push(ListElements(&fonts)), + ) + }) .push_if(!firmware.is_empty(), || { InfoCard( Div() @@ -412,13 +425,15 @@ pub async fn pkg_ui( ) .push( Rounded( - Padding(Background( - Gray::_700, - Code(install_script.trim()) - .color(&Gray::_100) - .sm() - .indentation(ScreenValue::_0), - )) + Padding( + Background( + Code(install_script.trim()) + .color(&Gray::_100) + .sm() + .indentation(ScreenValue::_0), + ) + .color(Gray::_700), + ) .all(ScreenValue::_2), ) .size(Size::Large), @@ -430,16 +445,18 @@ pub async fn pkg_ui( }) .render(); - Some(render(content, pkg_name, ctx).await) + Some(shell.render_page(content, pkg_name, ctx).await) } pub fn build_info(key: String, value: String) -> PreEscaped { match key.as_str() { "pkgname" => {} + "pkgbase" => {} "xdata" => {} "arch" => {} "pkbase" => {} "pkgver" => {} + "provides" => {} "pkgdesc" => { return key_value("Description".to_string(), value); } @@ -522,13 +539,13 @@ pub fn CardTitle(title: &str) -> PreEscaped { #[allow(non_snake_case)] pub fn InfoCard(inner: T) -> SpaceBetweenWidget { SpaceBetween( - Padding(Background( - Gray::_800, - Shadow::large( + Padding( + Background(Shadow::large( Rounded(Margin(FlexGrow(Strategy::Grow, inner)).all(ScreenValue::_2)) .size(Size::Large), - ), - )) + )) + .color(Gray::_800), + ) .all(ScreenValue::_4), ) .y(ScreenValue::_2) diff --git a/src/routes/ui/repo.rs b/src/routes/ui/repo.rs index 28630a6..ea7fa5c 100644 --- a/src/routes/ui/repo.rs +++ b/src/routes/ui/repo.rs @@ -1,4 +1,5 @@ use based::request::{RequestContext, StringResponse}; +use based::ui::components::Shell; use based::ui::prelude::*; use maud::{PreEscaped, Render, html}; use rocket::{State, get}; @@ -8,14 +9,13 @@ use pacco::pkg::{Repository, arch::Architecture}; use crate::config::Config; use crate::routes::ui::arch_card; -use super::render; - #[get("/?")] pub async fn repo_ui( repo: &str, ctx: RequestContext, arch: Option<&str>, config: &State, + shell: &State, ) -> StringResponse { let arch = arch.map(|x| Architecture::parse(x).unwrap_or(Architecture::any)); @@ -35,7 +35,6 @@ pub async fn repo_ui( .push(Text(&repo.name)._3xl().bold().color(&Gray::_100)) .push_if(config.is_mirrored_repo(&repo.name), || { Background( - Blue::_500, Rounded(Margin( Padding(Text("Mirrored").sm().medium().white()) .x(ScreenValue::_3) @@ -43,6 +42,7 @@ pub async fn repo_ui( )) .size(Size::Full), ) + .color(Blue::_500) }) .push( Screen::medium(Margin(Nothing()).top(ScreenValue::_0)).on(Margin( @@ -81,8 +81,7 @@ pub async fn repo_ui( Animated( Flex( Padding( - Hover(Background(Gray::_600, Nothing())).on(Background( - Gray::_700, + Hover(Background(Nothing()).color(Gray::_600)).on(Background( Rounded(Shadow::medium( Link( &format!("/{}/{pkg}", repo.name), @@ -103,7 +102,8 @@ pub async fn repo_ui( .use_htmx(), )) .size(Size::Large), - )), + ) + .color(Gray::_700)), ) .all(ScreenValue::_4), ) @@ -115,7 +115,7 @@ pub async fn repo_ui( let content = Div().vanish().push(repo_info).push(package_list).render(); - render(content, &repo.name, ctx).await + shell.render_page(content, &repo.name, ctx).await } #[allow(non_snake_case)] @@ -123,23 +123,25 @@ pub fn FirstLetterCircle(name: &str) -> PreEscaped { Sized( ScreenValue::_10, ScreenValue::_10, - Rounded(Background( - Blue::_500, - Flex( - Text( - &name - .chars() - .next() - .unwrap_or_default() - .to_uppercase() - .to_string(), + Rounded( + Background( + Flex( + Text( + &name + .chars() + .next() + .unwrap_or_default() + .to_uppercase() + .to_string(), + ) + .white() + .semibold(), ) - .white() - .semibold(), + .full_center() + .group(), ) - .full_center() - .group(), - )) + .color(Blue::_500), + ) .size(Size::Full), ) .render() diff --git a/src/routes/user.rs b/src/routes/user.rs index 9a914a1..5e78529 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -1,11 +1,11 @@ use based::{ auth::{Session, Sessions, User, csrf::CSRF}, request::{RequestContext, StringResponse, api::to_uuid, respond_html}, - ui::AttrExtendable, + ui::{AttrExtendable, components::Shell}, }; use maud::{PreEscaped, html}; use rocket::{ - FromForm, + FromForm, State, form::Form, get, http::{Cookie, CookieJar}, @@ -13,11 +13,10 @@ use rocket::{ response::Redirect, }; -use super::render; use based::ui::prelude::*; #[get("/login")] -pub async fn login(ctx: RequestContext) -> StringResponse { +pub async fn login(ctx: RequestContext, shell: &State) -> StringResponse { let content = html! { div class="min-w-screen justify-center flex items-center" { div class="bg-gray-800 shadow-lg rounded-lg p-8 max-w-sm w-full" { @@ -37,7 +36,7 @@ pub async fn login(ctx: RequestContext) -> StringResponse { }; }; - render(content, "Login", ctx).await + shell.render_page(content, "Login", ctx).await } #[derive(FromForm)] @@ -116,7 +115,7 @@ pub async fn new_api_key(user: User, csrf: &str, session_name: &str) -> StringRe } #[get("/account")] -pub async fn account_page(user: User, ctx: RequestContext) -> StringResponse { +pub async fn account_page(user: User, ctx: RequestContext, shell: &State) -> StringResponse { let sessions = user.list_sessions().await; let content = html! { @@ -128,8 +127,8 @@ pub async fn account_page(user: User, ctx: RequestContext) -> StringResponse { (Link("/passwd", Margin( Rounded(Padding( Hover( - Background(Green::_600, Nothing())).on( - Background(Green::_500, Text("Change Password").white()) + Background(Nothing()).color(Green::_600)).on( + Background(Text("Change Password").white()).color(Green::_500) ) ).x(ScreenValue::_6).y(ScreenValue::_2)) ).bottom(ScreenValue::_6)).use_htmx()) @@ -155,7 +154,7 @@ pub async fn account_page(user: User, ctx: RequestContext) -> StringResponse { }; }; - render(content, "Account", ctx).await + shell.render_page(content, "Account", ctx).await } pub async fn build_session_block(ses: &Session, user: &User) -> PreEscaped { @@ -198,7 +197,11 @@ pub async fn change_password_post(form: Form, user: User) -> } #[get("/passwd")] -pub async fn change_password(ctx: RequestContext, user: User) -> StringResponse { +pub async fn change_password( + ctx: RequestContext, + user: User, + shell: &State, +) -> StringResponse { let content = html! { div class="min-w-screen justify-center flex items-center" { div class="bg-gray-800 shadow-lg rounded-lg p-8 max-w-sm w-full" { @@ -223,5 +226,5 @@ pub async fn change_password(ctx: RequestContext, user: User) -> StringResponse }; }; - render(content, "Change Password", ctx).await + shell.render_page(content, "Change Password", ctx).await }