Files
gridcraft/crates/ui-egui/examples/snapshot.rs
T
Brandon ThomasandClaude Opus 5.5 39082d13c8 About: contributor and model credits compiled in (grab bag + table, self-submitted names) (#2)
* About: contributors compiled in from contributors.json (grab bag + table, sortable, name toggle)

Adds an About GridCraft dialog (tabs About · Contributors · Models) following the craftrules
standard (standards/contributors.md): contributors/contributors.json is generated by
craftrules/scripts/contributors.py and baked into the binary by crates/ui-egui/build.rs as
static tables. Each contributor line: @username, opt-in display/real name, PRs, commits,
lines +/−/Δ, binary assets +/−, first/last commit. Sortable by every column; alphabetical
ignores case and the leading @.

Reachable from the macOS app menu (About GridCraft, previously a web link), Help ▸
Contributors…, and Automate ▸ About GridCraft in the ribbon (all platforms and the web).
Dialog hyperlinks open through the host's open_url service. The snapshot example gains
--dialog 'name={json}' for offscreen renders of dialogs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Contributors: self-submitted names in contributors/people.toml; AGENTS.md credit-yourself section

build.rs reads contributors/people.toml next to the generated contributors.json and shows a name only
for entries the generator verified as written by that person (craftrules standards/contributors.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Contributors: regenerate (verified people.toml entries)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 21:16:34 -04:00

114 lines
3.9 KiB
Rust

//! Renders the whole GridCraft window offscreen (wgpu, no window) to a PNG.
//!
//! `cargo run --release -p gridcraft-ui-egui --example snapshot -- [--sample sales] [--in file.xlsx]
//! [--size 1440x900] [--scale 2] [--cmd 'id={json}']... [--tab Insert] [--dark]
//! [--dialog 'name={json}'] out.png`
use gridcraft_engine::Session;
use gridcraft_ui_egui::SheetApp;
use serde_json::json;
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
let mut sample = None;
let mut input = None;
let mut size = (1440.0f32, 900.0f32);
let mut scale = 2.0f32;
let mut cmds: Vec<String> = vec![];
let mut tab = None;
let mut dark = false;
let mut pane: Option<String> = None;
let mut chart: Option<u32> = None;
let mut dialog: Option<String> = None;
let mut out = "snapshot.png".to_string();
let mut i = 0;
while i < args.len() {
match args[i].as_str() {
"--sample" => {
i += 1;
sample = args.get(i).cloned();
}
"--in" => {
i += 1;
input = args.get(i).cloned();
}
"--size" => {
i += 1;
if let Some((w, h)) = args.get(i).and_then(|s| s.split_once('x')) {
size = (w.parse().unwrap_or(1440.0), h.parse().unwrap_or(900.0));
}
}
"--scale" => {
i += 1;
scale = args.get(i).and_then(|s| s.parse().ok()).unwrap_or(2.0);
}
"--cmd" => {
i += 1;
if let Some(c) = args.get(i) {
cmds.push(c.clone());
}
}
"--tab" => {
i += 1;
tab = args.get(i).cloned();
}
"--dark" => dark = true,
"--pane" => {
i += 1;
pane = args.get(i).cloned();
}
"--chart" => {
i += 1;
chart = args.get(i).and_then(|s| s.parse().ok());
}
"--dialog" => {
i += 1;
dialog = args.get(i).cloned();
}
other => out = other.to_string(),
}
i += 1;
}
let mut session = Session::new();
if let Some(p) = &input {
session.execute("file.open", json!({"path": p})).expect("open");
} else if let Some(s) = &sample {
session.execute("file.new", json!({"sample": s})).expect("sample");
} else {
session.new_workbook();
}
for c in &cmds {
let (id, p) = c.split_once('=').map(|(a, b)| (a.to_string(), serde_json::from_str(b).unwrap_or(json!({})))).unwrap_or((c.clone(), json!({})));
if let Err(e) = session.execute(&id, p) {
eprintln!("{id}: {e}");
}
}
let mut harness =
egui_kittest::Harness::builder().with_size(egui::vec2(size.0, size.1)).with_pixels_per_point(scale).with_max_steps(12).wgpu().build_ui_state(
move |ui, app: &mut SheetApp| {
let ctx = ui.ctx().clone();
app.logic(&ctx);
app.ui(ui);
},
{
let mut app = SheetApp::new(session, Default::default());
app.ui.dark = dark;
if let Some(t) = tab {
app.ui.ribbon_tab = t;
}
app.grid.pane = pane;
app.selected_chart = chart;
if let Some(d) = &dialog {
let (name, p) =
d.split_once('=').map(|(a, b)| (a, serde_json::from_str(b).unwrap_or(json!({})))).unwrap_or((d.as_str(), json!({})));
app.open_dialog(name, p);
}
app
},
);
harness.run_steps(8);
let img = harness.render().expect("render");
img.save(&out).expect("save png");
eprintln!("wrote {out} ({}x{})", img.width(), img.height());
}