diff --git a/crates/engine/src/code_connect.rs b/crates/engine/src/code_connect.rs index a0f117e4a..08b3af022 100644 --- a/crates/engine/src/code_connect.rs +++ b/crates/engine/src/code_connect.rs @@ -166,7 +166,8 @@ fn call_imports(doc: &Document, id: NodeId, lang: CodeLang, depth: usize, out: & && v.as_bool() == Some(true) && let Some((slot, _)) = slots.iter().find(|(_, name)| *name == p.prop) { - tree(&Ctx::new(doc, *slot), *slot, lang, depth + 1, out); + // The same depth base as the slot's generated content, so its connected children count. + tree(&Ctx { doc, root: *slot, comp: None, base: depth }, *slot, lang, depth + 1, out); } } } diff --git a/crates/engine/src/tools.rs b/crates/engine/src/tools.rs index 927b59988..715d1a4ea 100644 --- a/crates/engine/src/tools.rs +++ b/crates/engine/src/tools.rs @@ -1228,8 +1228,11 @@ impl Session { vec![target] }; self.set_selection(new_sel.clone()); - // Dev Mode only selects: nothing moves. + // Dev Mode only selects (Shift-click still deselects): nothing moves. if !edits { + if deselect.is_some() { + self.set_selection(without_target()); + } return Ok(()); } // Locked layers selected in the layers panel stay put. diff --git a/crates/ui-egui/locales/de.toml b/crates/ui-egui/locales/de.toml index 32c13360b..d429e9a8c 100644 --- a/crates/ui-egui/locales/de.toml +++ b/crates/ui-egui/locales/de.toml @@ -372,7 +372,7 @@ status = "draft — needs native review" "Detach variable" = "Variable lösen" "Details" = "Details" "Dev Mode" = "Entwicklermodus" -"Dev Mode only inspects: switch to Design (Shift+D) to edit" = "Der Entwicklermodus prüft nur: wechsle zu Design (Shift+D), um zu bearbeiten" +"Dev Mode only inspects: switch to Design (Shift+D) to edit" = "Der Entwicklermodus prüft nur: Wechseln Sie zu Design (Shift+D), um zu bearbeiten" "Dev resources" = "Entwicklerressourcen" "Development" = "Entwicklung" "Device" = "Gerät" diff --git a/crates/ui-egui/src/devmode_tail_tests.rs b/crates/ui-egui/src/devmode_tail_tests.rs index 59380ec94..7008e9e52 100644 --- a/crates/ui-egui/src/devmode_tail_tests.rs +++ b/crates/ui-egui/src/devmode_tail_tests.rs @@ -85,3 +85,26 @@ fn read_only_plugins_run_from_the_inspect_tab_show_their_panel() { assert!(u.app.session.execute("plugin.event", &json!({"id": "org.test.inspect", "control": "go"})).is_err()); assert_eq!(layers(&u), before); } + +/// A clipboard holding only text. +struct TextClip(String); + +impl crate::Services for TextClip { + fn clipboard_get_text(&mut self) -> Option { + Some(self.0.clone()) + } +} + +#[test] +fn dev_mode_pastes_only_a_comments_link() { + let mut s = Session::new(); + let c = s.execute("comment.add", &json!({"x": 10, "y": 10, "text": "Check the spacing"})).unwrap()["id"].as_u64().unwrap(); + let layers = |app: &crate::FigApp| app.session.doc().unwrap().node(app.session.page().unwrap()).unwrap().children.len(); + let mut app = crate::FigApp::new(s, Box::new(TextClip("Pasted words".into()))); + app.ui.right_tab = crate::RightTab::Inspect; + crate::menus::paste(&mut app, None, None); + assert_eq!(layers(&app), 0, "text doesn't paste into the design in Dev Mode"); + app.services = Box::new(TextClip(format!("figcraft://comment/{c}"))); + crate::menus::paste(&mut app, None, None); + assert_eq!(app.ui.comments.open, Some(c), "the link opens its thread"); +} diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index 7ff9d3b0b..57c90f10a 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -35,12 +35,13 @@ pub const UI_COMMANDS: &[&str] = &[ /// `event_text` is the text the platform handed over with the paste event (the browser has no /// other way to read it). `at`: the page point to paste at (Paste here); `None` pastes in the /// middle of the view. +/// In Dev Mode only a comment's link pastes (it opens the thread); everything else would edit. pub fn paste(app: &mut FigApp, event_text: Option<&str>, at: Option<(f64, f64)>) { - if dev_mode_refuses(app, "edit.paste") { - return; - } // Only HTML that holds readable design data takes this path; other HTML pastes as before. if let Some(html) = app.services.clipboard_get_html().filter(|h| figcraft_engine::cmd::edit::design_clipboard_buffer(h).is_some()) { + if dev_mode_refuses(app, "edit.paste") { + return; + } let mut params = json!({"html": html}); if let Some((x, y)) = at { params["x"] = json!(x); @@ -61,6 +62,9 @@ pub fn paste(app: &mut FigApp, event_text: Option<&str>, at: Option<(f64, f64)>) let (cx, cy) = at.or_else(|| app.session.ds().ok().map(|d| (d.view.cx, d.view.cy))).unwrap_or((0.0, 0.0)); if !ours { if let Some((png, w, h)) = clipboard_png(app) { + if dev_mode_refuses(app, "edit.paste") { + return; + } use base64::Engine as _; let b64 = base64::engine::general_purpose::STANDARD.encode(png); // Centred in the view; Paste here puts its top-left at the point, as it does layers. @@ -73,6 +77,8 @@ pub fn paste(app: &mut FigApp, event_text: Option<&str>, at: Option<(f64, f64)>) // A comment's link opens its thread. if text.trim().starts_with("figcraft://comment/") { app.run("comment.open", json!({"link": text.trim()})); + } else if dev_mode_refuses(app, "edit.paste") { + // Dev Mode: the note says why nothing was pasted. } else if figcraft_engine::looks_like_svg(&text) { use base64::Engine as _; let b64 = base64::engine::general_purpose::STANDARD.encode(text.as_bytes()); @@ -83,7 +89,9 @@ pub fn paste(app: &mut FigApp, event_text: Option<&str>, at: Option<(f64, f64)>) return; } } - app.run("edit.paste", at.map_or(json!({}), |(x, y)| json!({"x": x, "y": y}))); + if !dev_mode_refuses(app, "edit.paste") { + app.run("edit.paste", at.map_or(json!({}), |(x, y)| json!({"x": x, "y": y}))); + } } /// The system clipboard's image as PNG bytes, with its size. diff --git a/crates/ui-egui/src/toolbar.rs b/crates/ui-egui/src/toolbar.rs index 6c05b2b23..a5ff426b2 100644 --- a/crates/ui-egui/src/toolbar.rs +++ b/crates/ui-egui/src/toolbar.rs @@ -115,7 +115,8 @@ fn main_toolbar(app: &mut FigApp, ui: &mut Ui, canvas: Rect, t: &Tokens) { if resp.clicked() { // Enter (a keyboard click) on the shape or frame tool already chosen places one; the // canvas takes the keyboard. - if in_group && shown.draws_shape() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { + // In Dev Mode the tool goes through `choose`, which returns to the design first. + if in_group && shown.draws_shape() && app.ui.right_tab != crate::RightTab::Inspect && ui.input(|i| i.key_pressed(egui::Key::Enter)) { app.run("tool.place", json!({})); app.ui.nav.go(Region::Canvas); } else {