diff --git a/apps/vectorcraft-web/src/web.rs b/apps/vectorcraft-web/src/web.rs index 6e539a1b..50028187 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 27a8735d..a2eb8a91 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 fe053e6b..8ffc8a88 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());