Thanks @DuBearte.
This commit is contained in:
1 parent
bcacb7066d
commit
9194f4be09
5 files changed
+84
-5
No files matched your search
@@ -831,6 +831,59 @@ fn bridged_screenshots_with_grid_or_axes_are_model_renders() {
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// #1677: a hidden window gives no picture. With no `source`, a model render comes back and says
|
||||
/// it is one (`fallback`); asked for the window, the tool says why there is none, at once.
|
||||
#[test]
|
||||
fn a_hidden_window_screenshot_says_so() {
|
||||
struct Hidden {
|
||||
calls: Vec<String>,
|
||||
}
|
||||
impl crate::backend::Backend for Hidden {
|
||||
fn call(&mut self, method: &str, params: Value) -> Result<Value, String> {
|
||||
self.calls.push(method.to_string());
|
||||
match method {
|
||||
"ui.screenshot" => Err("no frame was presented (window hidden?); use ui.render".into()),
|
||||
"ui.render" => {
|
||||
let path = params["path"].as_str().ok_or("missing `path`")?;
|
||||
std::fs::write(path, b"\x89PNG").map_err(|e| e.to_string())?;
|
||||
Ok(json!({"path": path, "width": 64, "height": 48}))
|
||||
}
|
||||
other => Err(format!("unknown method `{other}`")),
|
||||
}
|
||||
}
|
||||
fn has_ui(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn describe(&self) -> String {
|
||||
"app".into()
|
||||
}
|
||||
}
|
||||
let dir = std::env::temp_dir().join(format!("solidcraft-mcp-hidden-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let path = dir.join("shot.png").to_string_lossy().to_string();
|
||||
let shoot = |extra: Value| {
|
||||
let mut args = json!({"path": path, "width": 64, "height": 48});
|
||||
args.as_object_mut().unwrap().extend(extra.as_object().unwrap().clone());
|
||||
let mut app = Hidden { calls: Vec::new() };
|
||||
let r = crate::tools::call_tool(&mut app, "screenshot", &args);
|
||||
(app.calls, r)
|
||||
};
|
||||
// The default: the window first, then a model render that says why.
|
||||
let (calls, r) = shoot(json!({}));
|
||||
assert!(r["isError"] != json!(true), "{r}");
|
||||
assert_eq!(calls, ["ui.screenshot", "ui.render"]);
|
||||
let info: Value = serde_json::from_str(r["content"][1]["text"].as_str().unwrap()).unwrap();
|
||||
assert_eq!(info["source"], "model", "{info}");
|
||||
assert!(info["fallback"].as_str().is_some_and(|f| f.contains("window hidden")), "{info}");
|
||||
// Asked for the window: an error naming the cause and the way out, and no render.
|
||||
let (calls, r) = shoot(json!({"source": "window"}));
|
||||
assert_eq!(r["isError"], json!(true), "{r}");
|
||||
let text = r["content"][0]["text"].as_str().unwrap();
|
||||
assert!(text.contains("window hidden") && text.contains("source \"model\""), "{text}");
|
||||
assert_eq!(calls, ["ui.screenshot"]);
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// #530: in a design with many bodies every feature result lists all of them, so a batch reply
|
||||
/// is mostly the same body list once per step. A compact batch reply names only what each step
|
||||
/// added or changed.
|
||||
|
||||
+15
-2
@@ -639,11 +639,21 @@ fn capture(b: &mut dyn Backend, a: &Map<String, Value>) -> Result<(Vec<u8>, Valu
|
||||
}
|
||||
b.call("ui.render", params)
|
||||
};
|
||||
let asked_window = str_arg(a, "source") == Some("window");
|
||||
let mut fallback = None;
|
||||
let (info, source) = if window {
|
||||
match b.call("ui.screenshot", json!({"path": path})) {
|
||||
Ok(v) => (v, "window"),
|
||||
// No presented frame (minimised or hidden window): fall back to a model render.
|
||||
Err(_) => (render(b)?, "model"),
|
||||
// Asked for the window: say why there is none, so the caller can show it or ask for
|
||||
// a model render (#1677).
|
||||
Err(e) if asked_window => {
|
||||
return Err(format!("screenshot: the window gave no picture ({e}); show the window, or use source \"model\""));
|
||||
}
|
||||
// No presented frame (minimised or hidden window): a model render, and the reply says so.
|
||||
Err(e) => {
|
||||
fallback = Some(e);
|
||||
(render(b)?, "model")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(render(b)?, "model")
|
||||
@@ -659,6 +669,9 @@ fn capture(b: &mut dyn Backend, a: &Map<String, Value>) -> Result<(Vec<u8>, Valu
|
||||
};
|
||||
let bytes = png.len();
|
||||
let mut reply = json!({"source": source, "path": keep, "width": w, "height": h, "bytes": bytes});
|
||||
if let Some(why) = fallback {
|
||||
reply["fallback"] = json!(format!("the window gave no picture ({why}); this is a model render"));
|
||||
}
|
||||
// What drew a model render (gpu, gpu-software or cpu), and the warning a CPU render carries.
|
||||
for k in ["renderer", "warning"] {
|
||||
if let Some(v) = info.get(k).filter(|v| !v.is_null()) {
|
||||
|
||||
Reference in new issue
Block a user