Keep Source Formatting uses the slide's colour map

Review follow-up on #1658 (CodeRabbit): `source_theme` stored the master's
scheme, so a slide or layout that overrides the colour map had its colours
fixed through the wrong map. Detaching made this reach a placeholder's
inherited colours: a title drawn white under an override pasted black.
Store the scheme the slide draws with. Paste as Picture reads the same
value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Stephan Schott-Verdugo
2026-11-04 22:02:22 +01:00
co-authored by Claude Opus 5.5
parent d6cc51e07a
commit 30ccc38e90
2 changed files with 47 additions and 1 deletions
+5 -1
View File
@@ -127,7 +127,11 @@ fn copy_inner(s: &mut Session, p: &Value) -> Result<String> {
let mids = crate::media_refs::shapes_media(&shapes);
let media = st.doc.media.iter().filter(|m| mids.contains(&m.id)).cloned().collect();
let plain = shapes.iter().filter_map(|x| x.text.as_ref().map(|t| t.text())).collect::<Vec<_>>().join("\n");
let source_theme = st.current_slide().and_then(|sl| st.doc.master_for(sl)).map(|(m, _)| (m.scheme(), m.theme.fonts.clone()));
// The scheme the slide draws with: its or its layout's colour map, else the master's.
let source_theme = st
.current_slide()
.and_then(|sl| deckcraft_model::resolve::Ctx::for_slide(&st.doc, sl))
.map(|ctx| (ctx.scheme.clone(), ctx.master.theme.fonts.clone()));
s.clipboard = crate::Clipboard { shapes, placeholders, media, plain: plain.clone(), source_theme, ..Default::default() };
Ok(plain)
}
+42
View File
@@ -294,6 +294,48 @@ fn a_placeholder_duplicated_by_dragging_keeps_its_look() {
}
}
/// Keep Source Formatting fixes a copy's theme colours as the source drew them: through the colour
/// map of its slide or layout, not the master's. A title's inherited `tx1` stays the light colour
/// an override gave it.
#[test]
fn keep_source_formatting_uses_the_slides_colour_map() {
use deckcraft_model::resolve;
let first_run_colour = |s: &Session, id: ShapeId| {
let st = s.doc().unwrap();
let ctx = resolve::Ctx::for_slide(&st.doc, st.current_slide().unwrap()).unwrap();
let sh = st.shape(id).unwrap();
let p = &sh.text.as_ref().unwrap().paragraphs[0];
resolve::text_color(&ctx, &resolve::run(&ctx, sh, p, &p.runs[0].props))
};
let swapped = || Some([("bg1", "dk1"), ("tx1", "lt1"), ("bg2", "dk2"), ("tx2", "lt2")].map(|(a, b)| (a.to_string(), b.to_string())).to_vec());
for on_layout in [false, true] {
let mut s = session();
s.execute("slide.new", &json!({})).unwrap();
let title = s.doc().unwrap().current_slide().unwrap().shapes.iter().find(|x| x.ph.is_some()).unwrap().id;
s.execute("text.set", &json!({"id": title.0, "text": "Title"})).unwrap();
let plain = first_run_colour(&s, title);
let st = s.doc_mut().unwrap();
let at = st.selection.slide;
let doc = std::sync::Arc::make_mut(&mut st.doc);
if on_layout {
let layout = doc.slides[at].layout;
let m = std::sync::Arc::make_mut(&mut doc.masters[0]);
m.layouts.iter_mut().find(|l| l.id == layout).unwrap().color_map = swapped();
} else {
std::sync::Arc::make_mut(&mut doc.slides[at]).color_map = swapped();
}
let want = first_run_colour(&s, title);
assert_ne!(want, plain, "the override changes how the title draws");
s.execute("selection.select", &json!({"id": title.0})).unwrap();
s.execute("edit.copy", &json!({})).unwrap();
s.execute("edit.pasteSpecial", &json!({"as": "keepSource"})).unwrap();
let copy = s.doc().unwrap().selection.shapes[0];
assert_ne!(copy, title);
assert_eq!(first_run_colour(&s, copy), want, "override on the layout: {on_layout}");
}
}
/// A slide's placeholder pasted in Slide Master view stays a placeholder that inherits from where
/// it lands; only a paste on a slide turns it into a regular shape with the look it had.
#[test]