From f2fc69307b220070fac377da59cedd4bc74c9c25 Mon Sep 17 00:00:00 2001 From: aiongg Date: Tue, 13 Oct 2026 01:19:14 +0000 Subject: [PATCH] Integration (#667): a character style made at a bare caret captures the typing format --- crates/engine/src/cmd/style.rs | 6 +++++- crates/engine/src/cmd/text.rs | 7 +++++++ crates/engine/tests/character_style_from_selection.rs | 9 +++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/cmd/style.rs b/crates/engine/src/cmd/style.rs index 39977d39..fa5988f9 100644 --- a/crates/engine/src/cmd/style.rs +++ b/crates/engine/src/cmd/style.rs @@ -794,7 +794,11 @@ fn create_char(s: &mut Session, p: &Value) -> Result { let story = st.doc.text_story(target.story, target.cell).ok_or(designcraft_doc::DocError::NoStory(target.story))?; // Match paragraph fromSelection's first-character/caret policy, but keep paragraph // attributes inherited instead of freezing the fully resolved character properties. - let format = if target.range.is_empty() { story.char_format_at(target.range.start) } else { story.format_after(target.range.start) }; + let mut format = if target.range.is_empty() { story.char_format_at(target.range.start) } else { story.format_after(target.range.start) }.clone(); + // A bare caret: what typing there would get, as `type.selectionAttrs` reports. + if target.range.is_empty() { + super::text::with_typing_format(s, &mut format); + } let mut captured = st.doc.styles.char_style_attrs(&format.style); captured.merge(&format.over); // Replay the explicit fields so null clears a captured value rather than looking absent. diff --git a/crates/engine/src/cmd/text.rs b/crates/engine/src/cmd/text.rs index 7c87550f..cc17fe32 100644 --- a/crates/engine/src/cmd/text.rs +++ b/crates/engine/src/cmd/text.rs @@ -1593,6 +1593,13 @@ fn mixed_para_attrs(doc: &designcraft_doc::Document, targets: &[Target]) -> Vec< mixed.into_iter().collect() } +/// `cf` with the typing format chosen at the active document's caret, if any. +pub(crate) fn with_typing_format(s: &Session, cf: &mut designcraft_doc::CharFormat) { + if let Some(f) = typing_format(s) { + apply_typing_format(cf, f); + } +} + fn apply_typing_format(cf: &mut designcraft_doc::CharFormat, f: &TypingFormat) { cf.over.merge(&f.attrs); for k in &f.cleared { diff --git a/crates/engine/tests/character_style_from_selection.rs b/crates/engine/tests/character_style_from_selection.rs index 0c9d8083..b5fa166b 100644 --- a/crates/engine/tests/character_style_from_selection.rs +++ b/crates/engine/tests/character_style_from_selection.rs @@ -272,3 +272,12 @@ fn character_capture_explicit_null_restores_live_paragraph_inheritance() { assert_eq!(effective["chars"]["underlineWeight"], 3.0); } } + +#[test] +fn character_capture_at_a_bare_caret_takes_the_typing_format() { + let (mut s, sid) = story("alpha beta"); + select(&mut s, sid, 5, 5); + s.execute("type.char", &json!({"attrs": {"fontStyle": "Bold"}})).unwrap(); + s.execute("style.character.create", &json!({"name": "Typed", "fromSelection": true})).unwrap(); + assert_eq!(chars(&s, "Typed").font_style.as_deref(), Some("Bold")); +}