bos-settings: first-pass --screenshot CLI mode for automated capture
First bread-ecosystem app on the Tauri/webview stack rather than raw
GTK4 — proves the isolated-headless-Sway capture pipeline works against
a plain (non-layer-shell) tao/wry window with no gtk4-rs-specific signal
to hook. No connect_map here: since the window isn't owned by gtk4-rs
directly, this instead waits a fixed settle delay (2s — longer than the
native apps need, since a webview's first paint means a full page load
plus an async Tauri-command round-trip for real content, not just GTK
widget layout — confirmed by an initial 800ms attempt capturing the
sidebar correctly but the About panel's data still blank) on Tauri's own
async runtime, then captures the known-size canvas and exits.
Only one view ("default", the initial landing section) is wired up.
Capturing a specific sidebar section would mean routing the Svelte
frontend to a URL/hash on window creation — real, separate frontend
work, deferred past this first pass.
bread-screenshots is pulled from bread-ecosystem's dev branch, unlike
this repo's existing bread-theme/bread-utils deps which point at main —
it doesn't exist on main yet.
This commit is contained in:
parent
3d36ebc568
commit
fe4a455cef
4 changed files with 136 additions and 2 deletions
24
src/Cargo.lock
generated
24
src/Cargo.lock
generated
|
|
@ -290,8 +290,10 @@ dependencies = [
|
|||
name = "bos-settings"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bread-screenshots",
|
||||
"bread-theme",
|
||||
"bread-utils",
|
||||
"bread-utils 0.3.1 (git+https://github.com/Breadway/bread-ecosystem?branch=main)",
|
||||
"notify",
|
||||
"regex",
|
||||
"serde",
|
||||
|
|
@ -304,6 +306,16 @@ dependencies = [
|
|||
"toml_edit 0.22.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bread-screenshots"
|
||||
version = "0.3.1"
|
||||
source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=dev#c93f4cf4d0ea1d49e639a1b5b4507e5a2497f52d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=dev)",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bread-theme"
|
||||
version = "0.3.1"
|
||||
|
|
@ -314,6 +326,16 @@ dependencies = [
|
|||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bread-utils"
|
||||
version = "0.3.1"
|
||||
source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=dev#c93f4cf4d0ea1d49e639a1b5b4507e5a2497f52d"
|
||||
dependencies = [
|
||||
"dirs 5.0.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bread-utils"
|
||||
version = "0.3.1"
|
||||
|
|
|
|||
|
|
@ -43,4 +43,8 @@ regex = "1"
|
|||
# checkout exists on the runner) since no tag has these functions yet.
|
||||
bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", branch = "main" }
|
||||
bread-utils = { git = "https://github.com/Breadway/bread-ecosystem", branch = "main", features = ["toml"] }
|
||||
# Capture primitives for `--screenshot` mode — see src/screenshot.rs. On
|
||||
# "dev", not "main" like the two deps above: it doesn't exist on main yet.
|
||||
bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", branch = "dev" }
|
||||
anyhow = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,19 @@
|
|||
mod commands;
|
||||
mod screenshot;
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let screenshot_req = screenshot::parse(&args);
|
||||
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.setup(|app| {
|
||||
.setup(move |app| {
|
||||
commands::theme::watch_and_emit(app.handle());
|
||||
if let Some(req) = screenshot_req {
|
||||
screenshot::dispatch(req);
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
|
|
|
|||
101
src/src/screenshot.rs
Normal file
101
src/src/screenshot.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
//! `--screenshot` CLI mode: capture the settings window via
|
||||
//! `bread-screenshots`, then exit — driven by `bread-ecosystem`'s
|
||||
//! `bread-capture` orchestrator, or run standalone for one-off captures.
|
||||
//!
|
||||
//! First pass at a Tauri (webview, not raw GTK4) target: there's no
|
||||
//! `connect_map`/`glib` signal to hook here the way every other bread-
|
||||
//! ecosystem app's screenshot mode does, since the window is owned by
|
||||
//! tao/wry, not gtk4-rs directly. Instead this just waits a fixed
|
||||
//! [`SETTLE_DELAY`] on Tauri's own async runtime after `setup()` runs —
|
||||
//! longer than the native apps' settle delays, since a webview's first
|
||||
//! paint means a full page load (JS bundle parse + Svelte mount), not just
|
||||
//! GTK widget layout. The window itself is a plain, non-layer-shell
|
||||
//! toplevel (per `tauri.conf.json`'s fixed 960x640 size), so — same
|
||||
//! reasoning as breadman/breadhelp — a full known-size canvas capture is
|
||||
//! enough; no geometry to track.
|
||||
//!
|
||||
//! Only one view ("default", the settings window's initial landing
|
||||
//! section) is wired up so far. The frontend is a Svelte SPA with its own
|
||||
//! sidebar routing for each settings section (Appearance, Network,
|
||||
//! Bluetooth, ...) — capturing one of those specifically would mean
|
||||
//! passing a route via a URL/hash on window creation and is real, separate
|
||||
//! frontend work, deferred past this first pass (which exists to prove the
|
||||
//! isolated-headless-Sway pipeline works against a Tauri/webview window at
|
||||
//! all, not to reach full view parity with the native GTK4 apps).
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
const SETTLE_DELAY: Duration = Duration::from_millis(2000);
|
||||
const KNOWN_VIEWS: &[&str] = &["default"];
|
||||
|
||||
pub struct ScreenshotRequest {
|
||||
pub output: PathBuf,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
/// `None` for a normal run. Exits the process with an error for an unknown
|
||||
/// view, or if `--screenshot` was given without `--output` — before any
|
||||
/// Tauri setup happens.
|
||||
pub fn parse(args: &[String]) -> Option<ScreenshotRequest> {
|
||||
let mut view = None;
|
||||
let mut output = None;
|
||||
let mut width = 1920u32;
|
||||
let mut height = 1080u32;
|
||||
let mut it = args.iter().skip(1);
|
||||
while let Some(arg) = it.next() {
|
||||
match arg.as_str() {
|
||||
"--screenshot" => view = it.next().cloned(),
|
||||
"--output" => output = it.next().cloned(),
|
||||
"--width" => {
|
||||
if let Some(v) = it.next().and_then(|s| s.parse().ok()) {
|
||||
width = v;
|
||||
}
|
||||
}
|
||||
"--height" => {
|
||||
if let Some(v) = it.next().and_then(|s| s.parse().ok()) {
|
||||
height = v;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let view = view?;
|
||||
if !KNOWN_VIEWS.contains(&view.as_str()) {
|
||||
eprintln!(
|
||||
"bos-settings: unknown screenshot view '{view}' (known: {})",
|
||||
KNOWN_VIEWS.join(", ")
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
let Some(output) = output else {
|
||||
eprintln!("bos-settings: --screenshot requires --output");
|
||||
std::process::exit(1);
|
||||
};
|
||||
Some(ScreenshotRequest { output: output.into(), width, height })
|
||||
}
|
||||
|
||||
/// Schedule the capture-then-exit sequence. Called once from `setup()`.
|
||||
pub fn dispatch(req: ScreenshotRequest) {
|
||||
tauri::async_runtime::spawn(async move {
|
||||
tokio::time::sleep(SETTLE_DELAY).await;
|
||||
finish(bread_screenshots::capture_region(
|
||||
0,
|
||||
0,
|
||||
req.width as i32,
|
||||
req.height as i32,
|
||||
&req.output,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
fn finish(result: anyhow::Result<()>) {
|
||||
match result {
|
||||
Ok(()) => std::process::exit(0),
|
||||
Err(e) => {
|
||||
eprintln!("bos-settings: screenshot capture failed: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue