Deploy web Studio / deploy (push) Successful in 3m46s
Measured with the new tools/bench_web.py on the web Studio's house demo, CPU slowed 6x:
- Idle: running CSS animations repainted the whole drawing every frame (99% of the main
thread in a window). Animations now step on about ten times a second by default
("light", 5% idle); the new toolbar button switches light / smooth / off and the browser
remembers it; [build] animation sets the default for built pages; view.animation().
Pan and zoom gestures pause them. Status LEDs pause off-screen and with the rest now.
- Pan 28 -> 10 ms, zoom 69 -> 11 ms per step: gestures move the drawing as one compositor
layer (transform) and set the viewBox once they end; --iso-k is set on the tags group
only and only when it changes; the stage box is read once per frame.
- Hover 22 -> 15 ms per move: the tooltip is placed once per frame on its own layer and
measured only when its text changes; the Studio's x/y readout updates ten times a second
in a contained box; hover work in the Studio runs once per frame.
- Long object lists skip layout of rows out of sight (content-visibility).
- WASM built with opt-level 3: engine render 11.7 -> 9.4 ms for 10% more download.
Checks: Studio 102/102 (animation modes, pan as a layer, contained readout), web 87/87.
91 lines
3.4 KiB
Rust
91 lines
3.4 KiB
Rust
//! Export: a self-contained HTML page with every view prerendered, or one
|
|
//! view as a standalone SVG file.
|
|
|
|
use crate::assets;
|
|
use crate::Project;
|
|
use isokit_core::fmt::esc;
|
|
use isokit_script::ScriptError;
|
|
use serde_json::{json, Value};
|
|
|
|
/// JSON safe to put inside `<script type="application/json">`.
|
|
fn script_json(v: &Value) -> String {
|
|
v.to_string().replace("</", "<\\/")
|
|
}
|
|
|
|
/// The static page: all levels in all four rotations, no server needed.
|
|
pub fn static_page(p: &Project) -> Result<String, ScriptError> {
|
|
let meta = p.meta();
|
|
let mut views = serde_json::Map::new();
|
|
for l in &meta.levels {
|
|
for rot in 0..4u8 {
|
|
let v = p.render(&l.id, rot, None, false)?;
|
|
views.insert(
|
|
format!("{}/{rot}", l.id),
|
|
json!({ "html": v.html, "info": v.info, "bbox": v.bbox, "focus": v.focus, "north": v.north, "boxes": v.boxes }),
|
|
);
|
|
}
|
|
}
|
|
let data = json!({
|
|
"meta": {
|
|
"title": meta.title, "note": meta.note, "levels": meta.levels,
|
|
"defaultLevel": meta.default_level, "defaultRotation": meta.default_rotation
|
|
},
|
|
"views": views,
|
|
"cfg": p.cfg(None),
|
|
"animation": p.manifest.build.animation,
|
|
});
|
|
let css = format!(
|
|
"{}\n{}",
|
|
assets::get("viewer/isokit-view.css").unwrap_or_default(),
|
|
p.theme.css()
|
|
);
|
|
let page = assets::get("viewer/page.html").unwrap_or_default();
|
|
Ok(page
|
|
.replace("{{title}}", &esc(&meta.title))
|
|
.replace("{{date}}", &today())
|
|
.replace("/*ISOKIT_CSS*/", &css)
|
|
.replace("/*ISOKIT_DATA*/", &script_json(&data))
|
|
.replace(
|
|
"/*ISOKIT_JS*/",
|
|
&assets::get("viewer/isokit-view.js").unwrap_or_default(),
|
|
))
|
|
}
|
|
|
|
/// One view as a standalone SVG (dark or light scheme, styles inlined).
|
|
pub fn svg(p: &Project, level: &str, rot: u8, light: bool) -> Result<String, ScriptError> {
|
|
let v = p.render(level, rot, None, false)?;
|
|
let css = format!(
|
|
"{}\n{}",
|
|
assets::get("viewer/isokit-view.css").unwrap_or_default(),
|
|
p.theme.css_scoped("svg.iso-svg", Some(light))
|
|
);
|
|
let b = v.bbox;
|
|
let bg = if light { "#e8f4ff" } else { "#0b1428" };
|
|
Ok(format!(
|
|
"<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"iso-svg\" viewBox=\"{} {} {} {}\" width=\"{}\" height=\"{}\">\
|
|
<style>{}</style><rect x=\"{}\" y=\"{}\" width=\"{}\" height=\"{}\" fill=\"{bg}\"/>{}</svg>\n",
|
|
b[0], b[1], b[2], b[3], (b[2] * 2.0).round(), (b[3] * 2.0).round(), css, b[0], b[1], b[2], b[3], v.html
|
|
))
|
|
}
|
|
|
|
/// Today's date (UTC) as YYYY-MM-DD, without a date crate.
|
|
pub fn today() -> String {
|
|
// web_time: std's clock traps in the browser (wasm32-unknown-unknown)
|
|
let secs = web_time::SystemTime::now()
|
|
.duration_since(web_time::UNIX_EPOCH)
|
|
.map(|d| d.as_secs())
|
|
.unwrap_or(0);
|
|
let days = (secs / 86_400) as i64;
|
|
// civil-from-days (H. Hinnant)
|
|
let z = days + 719_468;
|
|
let era = z.div_euclid(146_097);
|
|
let doe = z - era * 146_097;
|
|
let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
|
|
let y = yoe + era * 400;
|
|
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
|
let mp = (5 * doy + 2) / 153;
|
|
let d = doy - (153 * mp + 2) / 5 + 1;
|
|
let m = if mp < 10 { mp + 3 } else { mp - 9 };
|
|
format!("{:04}-{:02}-{:02}", if m <= 2 { y + 1 } else { y }, m, d)
|
|
}
|