mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-29 13:16:50 -06:00
* 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>
114 lines
3.9 KiB
Rust
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());
|
|
}
|