Add abstraction for files to make iteration on UI easier

This commit is contained in:
Lucien Greathouse
2019-09-24 14:00:48 -07:00
parent 486319407a
commit e8a5e44319
5 changed files with 99 additions and 43 deletions

View File

@@ -12,6 +12,12 @@ exclude = [
"/test-projects/**", "/test-projects/**",
] ]
[features]
default = []
# Enable this feature to live-reload assets from the web UI.
dev-live-assets = []
[workspace] [workspace]
members = [ members = [
"rojo-test", "rojo-test",

View File

@@ -22,11 +22,10 @@ body {
justify-content: center; justify-content: center;
} }
.main { .root {
flex: 0 0; flex: 0 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 1rem;
margin: 0 auto; margin: 0 auto;
width: 100%; width: 100%;
max-width: 50rem; max-width: 50rem;
@@ -38,19 +37,25 @@ body {
.header { .header {
flex: 0 0; flex: 0 0;
display: flex; display: flex;
justify-content: space-around; flex-wrap: wrap;
/*justify-content: space-around;*/
align-items: center; align-items: center;
margin-bottom: 1rem; border-bottom: 1px solid #666;
}
.main {
padding: 1rem;
} }
.main-logo { .main-logo {
flex: 0 0; flex: 0 0;
width: 20rem; width: 10rem;
margin: 1rem;
} }
.stats { .stats {
flex: 0 0 20rem; flex: 0 0 20rem;
padding: 0 1rem; margin: 1rem;
} }
.stat { .stat {
@@ -65,12 +70,13 @@ body {
.button-list { .button-list {
flex: 0 0; flex: 0 0;
display: flex; display: flex;
justify-content: space-around; flex-wrap: wrap;
justify-content: center;
} }
.button { .button {
display: inline-block; display: inline-block;
border: 1px solid #666; border: 1px solid #666;
padding: 0.3em 1em; padding: 0.3em 1em;
margin: 0 0.2rem; margin: 1rem;
} }

37
src/web/assets.rs Normal file
View File

@@ -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
}

View File

@@ -1,4 +1,5 @@
mod api; mod api;
mod assets;
pub mod interface; pub mod interface;
mod ui; mod ui;
mod util; mod util;

View File

@@ -10,15 +10,12 @@ use crate::{
imfs::ImfsFetcher, imfs::ImfsFetcher,
serve_session::ServeSession, serve_session::ServeSession,
web::{ web::{
assets,
interface::{ErrorResponse, SERVER_VERSION}, interface::{ErrorResponse, SERVER_VERSION},
util::json, 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<F> { pub struct UiService<F> {
serve_session: Arc<ServeSession<F>>, serve_session: Arc<ServeSession<F>>,
} }
@@ -56,46 +53,24 @@ impl<F: ImfsFetcher> UiService<F> {
fn handle_logo(&self) -> Response<Body> { fn handle_logo(&self) -> Response<Body> {
Response::builder() Response::builder()
.header(header::CONTENT_TYPE, "image/png") .header(header::CONTENT_TYPE, "image/png")
.body(Body::from(LOGO)) .body(Body::from(assets::logo()))
.unwrap() .unwrap()
} }
fn handle_icon(&self) -> Response<Body> { fn handle_icon(&self) -> Response<Body> {
Response::builder() Response::builder()
.header(header::CONTENT_TYPE, "image/png") .header(header::CONTENT_TYPE, "image/png")
.body(Body::from(ICON)) .body(Body::from(assets::icon()))
.unwrap() .unwrap()
} }
fn handle_home(&self) -> Response<Body> { fn handle_home(&self) -> Response<Body> {
let project_name = self.serve_session.project_name().unwrap_or("<unnamed>"); let page = self.normal_page(html! {
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! {
<main class="main">
<header class="header">
<img class="main-logo" src="/logo.png" />
<div class="stats">
{ Self::stat_item("Server Version", SERVER_VERSION) }
{ Self::stat_item("Project", project_name) }
{ Self::stat_item("Server Uptime", uptime) }
</div>
</header>
<div class="button-list"> <div class="button-list">
{ Self::button("Rojo Documentation", "https://rojo.space/docs") } { Self::button("Rojo Documentation", "https://rojo.space/docs") }
{ Self::button("View in-memory filesystem state", "/show-imfs") } { Self::button("View in-memory filesystem state", "/show-imfs") }
{ Self::button("View instance tree state", "/show-instances") } { Self::button("View instance tree state", "/show-instances") }
</div> </div>
</main>
}); });
Response::builder() Response::builder()
@@ -133,14 +108,45 @@ impl<F: ImfsFetcher> UiService<F> {
} }
} }
fn normal_page<'a>(&'a self, body: HtmlContent<'a>) -> HtmlContent<'a> {
let project_name = self.serve_session.project_name().unwrap_or("<unnamed>");
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! {
<div class="root">
<header class="header">
<img class="main-logo" src="/logo.png" />
<div class="stats">
{ Self::stat_item("Server Version", SERVER_VERSION) }
{ Self::stat_item("Project", project_name) }
{ Self::stat_item("Server Uptime", uptime) }
</div>
</header>
<main class="main">
{ body }
</main>
</div>
})
}
fn page(body: HtmlContent<'_>) -> HtmlContent<'_> { fn page(body: HtmlContent<'_>) -> HtmlContent<'_> {
html! { html! {
<html> <html>
<head> <head>
<title>"Rojo Live Server"</title> <title>"Rojo Live Server"</title>
<link rel="icon" type="image/png" sizes="32x32" href="/icon.png" /> <link rel="icon" type="image/png" sizes="32x32" href="/icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" />
<style> <style>
{ ritz::UnescapedText::new(HOME_CSS) } { ritz::UnescapedText::new(assets::css()) }
</style> </style>
</head> </head>