diff --git a/Cargo.toml b/Cargo.toml index d9530ff1..e16e76d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,12 @@ exclude = [ "/test-projects/**", ] +[features] +default = [] + +# Enable this feature to live-reload assets from the web UI. +dev-live-assets = [] + [workspace] members = [ "rojo-test", diff --git a/assets/index.css b/assets/index.css index c0a1e687..8b96697b 100644 --- a/assets/index.css +++ b/assets/index.css @@ -22,11 +22,10 @@ body { justify-content: center; } -.main { +.root { flex: 0 0; display: flex; flex-direction: column; - padding: 1rem; margin: 0 auto; width: 100%; max-width: 50rem; @@ -38,19 +37,25 @@ body { .header { flex: 0 0; display: flex; - justify-content: space-around; + flex-wrap: wrap; + /*justify-content: space-around;*/ align-items: center; - margin-bottom: 1rem; + border-bottom: 1px solid #666; +} + +.main { + padding: 1rem; } .main-logo { flex: 0 0; - width: 20rem; + width: 10rem; + margin: 1rem; } .stats { flex: 0 0 20rem; - padding: 0 1rem; + margin: 1rem; } .stat { @@ -65,12 +70,13 @@ body { .button-list { flex: 0 0; display: flex; - justify-content: space-around; + flex-wrap: wrap; + justify-content: center; } .button { display: inline-block; border: 1px solid #666; padding: 0.3em 1em; - margin: 0 0.2rem; + margin: 1rem; } \ No newline at end of file diff --git a/src/web/assets.rs b/src/web/assets.rs new file mode 100644 index 00000000..2832f768 --- /dev/null +++ b/src/web/assets.rs @@ -0,0 +1,37 @@ +use std::{fs, path::Path}; + +static LOGO: &[u8] = include_bytes!("../../assets/logo-512.png"); +static ICON: &[u8] = include_bytes!("../../assets/icon-32.png"); +static HOME_CSS: &str = include_str!("../../assets/index.css"); + +macro_rules! declare_asset { + ($name: ident, $path: expr) => { + pub fn $name() -> &'static str { + if cfg!(feature = "dev-live-assets") { + let file_path = Path::new(file!()); + let asset_path = file_path.parent().unwrap().join($path); + + println!("Reloading {}", asset_path.display()); + + let content = fs::read_to_string(asset_path) + .expect("Couldn't read dev live asset") + .into_boxed_str(); + + Box::leak(content) + } else { + static CONTENT: &str = include_str!($path); + CONTENT + } + } + }; +} + +declare_asset!(css, "../../assets/index.css"); + +pub fn logo() -> &'static [u8] { + LOGO +} + +pub fn icon() -> &'static [u8] { + ICON +} diff --git a/src/web/mod.rs b/src/web/mod.rs index ab8242d2..319083fa 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -1,4 +1,5 @@ mod api; +mod assets; pub mod interface; mod ui; mod util; diff --git a/src/web/ui.rs b/src/web/ui.rs index e01d7944..fca27849 100644 --- a/src/web/ui.rs +++ b/src/web/ui.rs @@ -10,15 +10,12 @@ use crate::{ imfs::ImfsFetcher, serve_session::ServeSession, web::{ + assets, interface::{ErrorResponse, SERVER_VERSION}, util::json, }, }; -static LOGO: &[u8] = include_bytes!("../../assets/logo-512.png"); -static ICON: &[u8] = include_bytes!("../../assets/icon-32.png"); -static HOME_CSS: &str = include_str!("../../assets/index.css"); - pub struct UiService { serve_session: Arc>, } @@ -56,46 +53,24 @@ impl UiService { fn handle_logo(&self) -> Response { Response::builder() .header(header::CONTENT_TYPE, "image/png") - .body(Body::from(LOGO)) + .body(Body::from(assets::logo())) .unwrap() } fn handle_icon(&self) -> Response { Response::builder() .header(header::CONTENT_TYPE, "image/png") - .body(Body::from(ICON)) + .body(Body::from(assets::icon())) .unwrap() } fn handle_home(&self) -> Response { - let project_name = self.serve_session.project_name().unwrap_or(""); - let uptime = { - let elapsed = self.serve_session.start_time().elapsed(); - - // Round off all of our sub-second precision to make timestamps - // nicer. - let just_nanos = Duration::from_nanos(elapsed.subsec_nanos() as u64); - let elapsed = elapsed - just_nanos; - - humantime::format_duration(elapsed).to_string() - }; - - let page = Self::page(html! { -
-
- -
- { Self::stat_item("Server Version", SERVER_VERSION) } - { Self::stat_item("Project", project_name) } - { Self::stat_item("Server Uptime", uptime) } -
-
-
- { Self::button("Rojo Documentation", "https://rojo.space/docs") } - { Self::button("View in-memory filesystem state", "/show-imfs") } - { Self::button("View instance tree state", "/show-instances") } -
-
+ let page = self.normal_page(html! { +
+ { Self::button("Rojo Documentation", "https://rojo.space/docs") } + { Self::button("View in-memory filesystem state", "/show-imfs") } + { Self::button("View instance tree state", "/show-instances") } +
}); Response::builder() @@ -133,14 +108,45 @@ impl UiService { } } + fn normal_page<'a>(&'a self, body: HtmlContent<'a>) -> HtmlContent<'a> { + let project_name = self.serve_session.project_name().unwrap_or(""); + let uptime = { + let elapsed = self.serve_session.start_time().elapsed(); + + // Round off all of our sub-second precision to make timestamps + // nicer. + let just_nanos = Duration::from_nanos(elapsed.subsec_nanos() as u64); + let elapsed = elapsed - just_nanos; + + humantime::format_duration(elapsed).to_string() + }; + + Self::page(html! { +
+
+ +
+ { Self::stat_item("Server Version", SERVER_VERSION) } + { Self::stat_item("Project", project_name) } + { Self::stat_item("Server Uptime", uptime) } +
+
+
+ { body } +
+
+ }) + } + fn page(body: HtmlContent<'_>) -> HtmlContent<'_> { html! { "Rojo Live Server" +