From f90854520404b4c62b878dc55f1f9a4fb01d9393 Mon Sep 17 00:00:00 2001 From: Vikram Bhaskaran Date: Mon, 12 Oct 2026 23:41:02 +1100 Subject: [PATCH] Paste the picture when the clipboard text beside it is blank. Hosts put whitespace in the text flavour ahead of the bitmap, and that empty text was taken as the paste. --- apps/vectorcraft-web/src/web.rs | 3 ++- crates/ui-egui/src/sysclip.rs | 4 +++- crates/ui-egui/src/tests_sysclip.rs | 5 ++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/vectorcraft-web/src/web.rs b/apps/vectorcraft-web/src/web.rs index 6e539a1b3..500281876 100644 --- a/apps/vectorcraft-web/src/web.rs +++ b/apps/vectorcraft-web/src/web.rs @@ -205,7 +205,8 @@ fn track_paste(pasted: &Rc>) { let Some(data) = e.clipboard_data() else { return }; let Some(files) = data.files().filter(|f| f.length() > 0) else { return }; // Text is egui's, but for the address a browser's Copy Image puts next to the picture. - if data.get_data("text").is_ok_and(|t| !t.is_empty() && !is_address(&t)) { + // Blank text is not a caption. A picture pasted with it is still the picture. + if data.get_data("text").is_ok_and(|t| !t.trim().is_empty() && !is_address(&t)) { return; } e.prevent_default(); diff --git a/crates/ui-egui/src/sysclip.rs b/crates/ui-egui/src/sysclip.rs index 27a8735d1..a2eb8a918 100644 --- a/crates/ui-egui/src/sysclip.rs +++ b/crates/ui-egui/src/sysclip.rs @@ -92,7 +92,9 @@ impl VectorcraftApp { let Some(f) = cb.read(&PASTE_ORDER) else { return Ok(()) }; // Text that only names a picture (its address, or its image markup) goes with a // browser's copied picture: the picture. - let f = if f.mime == TEXT && names_a_picture(&String::from_utf8_lossy(&f.data)) { cb.read(&[BITMAP]).unwrap_or(f) } else { f }; + let text = String::from_utf8_lossy(&f.data); + // Blank text is what some hosts put beside a copied picture. It is not the paste. + let f = if f.mime == TEXT && (text.trim().is_empty() || names_a_picture(&text)) { cb.read(&[BITMAP]).unwrap_or(f) } else { f }; import_command(&f, center) } // Text only: SVG markup is art, other text is left alone. diff --git a/crates/ui-egui/src/tests_sysclip.rs b/crates/ui-egui/src/tests_sysclip.rs index fe053e6bb..8ffc8a889 100644 --- a/crates/ui-egui/src/tests_sysclip.rs +++ b/crates/ui-egui/src/tests_sysclip.rs @@ -159,7 +159,10 @@ fn text_from_another_app_pastes_as_point_text_and_svg_text_as_art() { run(&mut app, "edit.paste", json!({})); assert_eq!(pasted(&app).len(), 2); assert!(pasted(&app).iter().all(|n| matches!(n.kind, NodeKind::Path { .. }))); - // Text that can't be pasted pastes nothing (not the stale internal clipboard). + // Blank text beside a picture is the picture. Text alone that can't be pasted adds nothing. + copy_elsewhere(&board, vec![(TEXT, b" \r\n".to_vec()), (PNG, blue_png())]); + run(&mut app, "edit.paste", json!({})); + assert!(matches!(pasted(&app)[0].kind, NodeKind::Image(_)), "blank text yields the picture"); let before = count(&app); copy_elsewhere(&board, vec![(TEXT, b" \r\n".to_vec())]); assert!(app.run("edit.paste", json!({})).is_err());