forked from rojo-rbx/rojo
Improve command line and web interface
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -376,7 +376,7 @@ version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -578,7 +578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1458,6 +1458,7 @@ dependencies = [
|
||||
"env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.12.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jod-thread 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1476,6 +1477,7 @@ dependencies = [
|
||||
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -2194,7 +2196,7 @@ dependencies = [
|
||||
"checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4"
|
||||
"checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d"
|
||||
"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
|
||||
"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
|
||||
"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||
"checksum hyper 0.12.33 (registry+https://github.com/rust-lang/crates.io-index)" = "7cb44cbce9d8ee4fb36e4c0ad7b794ac44ebaad924b9c8291a63215bb44c2c8f"
|
||||
"checksum hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f"
|
||||
"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
|
||||
|
||||
@@ -37,8 +37,8 @@ csv = "1.0"
|
||||
env_logger = "0.6"
|
||||
failure = "0.1.3"
|
||||
futures = "0.1"
|
||||
humantime = "1.3.0"
|
||||
hyper = "0.12"
|
||||
reqwest = "0.9.20"
|
||||
jod-thread = "0.1.0"
|
||||
log = "0.4"
|
||||
maplit = "1.0.1"
|
||||
@@ -48,9 +48,11 @@ rbx_dom_weak = "1.9.0"
|
||||
rbx_reflection = "3.1.388"
|
||||
rbx_xml = "0.11.0"
|
||||
regex = "1.0"
|
||||
reqwest = "0.9.20"
|
||||
ritz = "0.1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
termcolor = "1.0.5"
|
||||
uuid = { version = "0.7", features = ["v4", "serde"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
|
||||
BIN
assets/icon-32.png
Normal file
BIN
assets/icon-32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 975 B |
@@ -1,11 +1,17 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
text-decoration: inherit;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
font-family: sans-serif;
|
||||
text-decoration: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -17,27 +23,54 @@ body {
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 0 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
max-width: 60rem;
|
||||
max-width: 50rem;
|
||||
background-color: #efefef;
|
||||
border: 1px solid #666;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 2rem;
|
||||
.header {
|
||||
flex: 0 0;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.main-logo {
|
||||
flex: 0 0;
|
||||
width: 20rem;
|
||||
}
|
||||
|
||||
.stats {
|
||||
flex: 0 0 20rem;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.stat {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.stat-name {
|
||||
display: inline;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
.button-list {
|
||||
flex: 0 0;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.docs {
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
.button {
|
||||
display: inline-block;
|
||||
border: 1px solid #666;
|
||||
padding: 0.3em 1em;
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
@@ -1,7 +1,13 @@
|
||||
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::{self, Write},
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use failure::Fail;
|
||||
use rbx_dom_weak::RbxInstanceProperties;
|
||||
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
|
||||
|
||||
use crate::{
|
||||
imfs::{Imfs, RealFetcher, WatchMode},
|
||||
@@ -46,7 +52,7 @@ pub fn serve(options: &ServeOptions) -> Result<(), ServeError> {
|
||||
})
|
||||
.unwrap_or(DEFAULT_PORT);
|
||||
|
||||
println!("Rojo server listening on port {}", port);
|
||||
let _ = show_start_message(port);
|
||||
|
||||
let mut tree = RojoTree::new(InstancePropertiesWithMeta {
|
||||
properties: RbxInstanceProperties {
|
||||
@@ -75,20 +81,36 @@ pub fn serve(options: &ServeOptions) -> Result<(), ServeError> {
|
||||
|
||||
server.start(port);
|
||||
|
||||
// let receiver = imfs.change_receiver();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// while let Ok(change) = receiver.recv() {
|
||||
// imfs.commit_change(&change)
|
||||
// .expect("Failed to commit Imfs change");
|
||||
fn show_start_message(port: u16) -> io::Result<()> {
|
||||
let mut writer = BufferWriter::stderr(ColorChoice::Auto);
|
||||
let mut buffer = writer.buffer();
|
||||
|
||||
// use notify::DebouncedEvent;
|
||||
// if let DebouncedEvent::Write(path) = change {
|
||||
// let contents = imfs.get_contents(path)
|
||||
// .expect("Failed to read changed path");
|
||||
writeln!(&mut buffer, "Rojo server listening:")?;
|
||||
|
||||
// println!("{:?}", std::str::from_utf8(contents));
|
||||
// }
|
||||
// }
|
||||
write!(&mut buffer, " Address: ")?;
|
||||
buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
|
||||
writeln!(&mut buffer, "localhost")?;
|
||||
|
||||
buffer.set_color(&ColorSpec::new())?;
|
||||
write!(&mut buffer, " Port: ")?;
|
||||
buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
|
||||
writeln!(&mut buffer, "{}", port)?;
|
||||
|
||||
writeln!(&mut buffer, "")?;
|
||||
|
||||
buffer.set_color(&ColorSpec::new())?;
|
||||
write!(&mut buffer, "Visit ")?;
|
||||
|
||||
buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
|
||||
write!(&mut buffer, "http://localhost:{}/", port)?;
|
||||
|
||||
buffer.set_color(&ColorSpec::new())?;
|
||||
writeln!(&mut buffer, " in your browser for more information.")?;
|
||||
|
||||
writer.print(&buffer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Recursion limit bump is to support Ritz, a JSX-like proc macro used for
|
||||
// Rojo's web UI currently.
|
||||
#![recursion_limit = "128"]
|
||||
#![recursion_limit = "1024"]
|
||||
|
||||
#[macro_use]
|
||||
mod impl_from;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
sync::{Arc, Mutex, MutexGuard},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -20,6 +21,10 @@ use crate::{
|
||||
/// future. `ServeSession` would be roughly the right interface to expose for
|
||||
/// those cases.
|
||||
pub struct ServeSession<F> {
|
||||
/// When the serve session was started. Used only for user-facing
|
||||
/// diagnostics.
|
||||
start_time: Instant,
|
||||
|
||||
/// The root project for the serve session, if there was one defined.
|
||||
///
|
||||
/// This will be defined if a folder with a `default.project.json` file was
|
||||
@@ -66,6 +71,8 @@ pub struct ServeSession<F> {
|
||||
/// that handles ServeSession.
|
||||
impl<F: ImfsFetcher + Send + 'static> ServeSession<F> {
|
||||
pub fn new(imfs: Imfs<F>, tree: RojoTree, root_project: Option<Project>) -> Self {
|
||||
let start_time = Instant::now();
|
||||
|
||||
let session_id = SessionId::new();
|
||||
let message_queue = MessageQueue::new();
|
||||
|
||||
@@ -80,6 +87,7 @@ impl<F: ImfsFetcher + Send + 'static> ServeSession<F> {
|
||||
);
|
||||
|
||||
Self {
|
||||
start_time,
|
||||
session_id,
|
||||
root_project,
|
||||
tree,
|
||||
@@ -107,6 +115,16 @@ impl<F: ImfsFetcher> ServeSession<F> {
|
||||
self.session_id
|
||||
}
|
||||
|
||||
pub fn project_name(&self) -> Option<&str> {
|
||||
self.root_project
|
||||
.as_ref()
|
||||
.map(|project| project.name.as_str())
|
||||
}
|
||||
|
||||
pub fn start_time(&self) -> Instant {
|
||||
self.start_time
|
||||
}
|
||||
|
||||
pub fn serve_place_ids(&self) -> Option<&HashSet<u64>> {
|
||||
self.root_project
|
||||
.as_ref()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Defines the HTTP-based UI. These endpoints generally return HTML and SVG.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use futures::{future, Future};
|
||||
use hyper::{header, service::Service, Body, Method, Request, Response, StatusCode};
|
||||
@@ -15,10 +15,11 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
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> {
|
||||
#[allow(unused)] // TODO: Fill out interface service
|
||||
serve_session: Arc<ServeSession<F>>,
|
||||
}
|
||||
|
||||
@@ -31,6 +32,8 @@ impl<F: ImfsFetcher> Service for UiService<F> {
|
||||
fn call(&mut self, request: Request<Self::ReqBody>) -> Self::Future {
|
||||
let response = match (request.method(), request.uri().path()) {
|
||||
(&Method::GET, "/") => self.handle_home(),
|
||||
(&Method::GET, "/logo.png") => self.handle_logo(),
|
||||
(&Method::GET, "/icon.png") => self.handle_icon(),
|
||||
(&Method::GET, "/visualize/rbx") => self.handle_visualize_rbx(),
|
||||
(&Method::GET, "/visualize/imfs") => self.handle_visualize_imfs(),
|
||||
(_method, path) => {
|
||||
@@ -50,28 +53,74 @@ impl<F: ImfsFetcher> UiService<F> {
|
||||
UiService { serve_session }
|
||||
}
|
||||
|
||||
fn handle_logo(&self) -> Response<Body> {
|
||||
Response::builder()
|
||||
.header(header::CONTENT_TYPE, "image/png")
|
||||
.body(Body::from(LOGO))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn handle_icon(&self) -> Response<Body> {
|
||||
Response::builder()
|
||||
.header(header::CONTENT_TYPE, "image/png")
|
||||
.body(Body::from(ICON))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn handle_home(&self) -> Response<Body> {
|
||||
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()
|
||||
};
|
||||
|
||||
let page = html! {
|
||||
<html>
|
||||
<head>
|
||||
<title>"Rojo"</title>
|
||||
<title>"Rojo Live Server"</title>
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/icon.png" />
|
||||
<style>
|
||||
{ ritz::UnescapedText::new(HOME_CSS) }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="main">
|
||||
<h1 class="title">
|
||||
"Rojo Live Sync is up and running!"
|
||||
</h1>
|
||||
<h2 class="subtitle">
|
||||
"Version " { SERVER_VERSION }
|
||||
</h2>
|
||||
<a class="docs" href="https://rojo.space/docs">
|
||||
"Rojo Documentation"
|
||||
</a>
|
||||
</div>
|
||||
<main class="main">
|
||||
<header class="header">
|
||||
<img class="main-logo" src="/logo.png" />
|
||||
<div class="stats">
|
||||
<span class="stat">
|
||||
<span class="stat-name">"Server Version: "</span>
|
||||
<span class="stat-value">{ SERVER_VERSION }</span>
|
||||
</span>
|
||||
<span class="stat">
|
||||
<span class="stat-name">"Project: "</span>
|
||||
<span class="stat-value">{ project_name }</span>
|
||||
</span>
|
||||
<span class="stat">
|
||||
<span class="stat-name">"Server Uptime: "</span>
|
||||
<span class="stat-value">{ uptime.to_string() }</span>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
<div class="button-list">
|
||||
<a class="button" href="https://rojo.space/docs">
|
||||
"Rojo Documentation"
|
||||
</a>
|
||||
<a class="button" href="/visualize/imfs">
|
||||
"View in-memory filesystem state"
|
||||
</a>
|
||||
<a class="button" href="/visualize/rbx">
|
||||
"View instance tree state"
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user