Cross-references keep edited text and break at spaces

InDesign keeps text typed over a cross-reference's generated text (until the
cross-reference is updated), with its page number still live. The IDML
importer dropped that text and generated it again from the format, so a
credits list whose format reads "Last, First. ... YYYY. Volume X, issue Y."
showed those placeholders instead of the names and dates typed in.

- CrossRef gains `text` (edited text, `<pageNum />` for the live page) and
  `page` (the page last shown). Import keeps saved text that no longer
  reads as its format produces it; export writes it back with InDesign's
  XRefPageNumber variable. Setting a format drops the edits, as InDesign does.
- Edited text whose destination is gone shows its saved page, not "??".
- Spaces in a cross-reference's text are word spaces the line may break at
  (it was set as one unbreakable piece, so long ones broke mid-word).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CgGsJkVe5GYG9qK6VFdECa
This commit is contained in:
Claude
2026-10-17 20:00:06 +00:00
parent fc36957fe0
commit 4e263ad821
9 changed files with 221 additions and 18 deletions
+5 -2
View File
@@ -94,7 +94,7 @@ fn insert(s: &mut Session, p: &Value) -> Result<Value> {
let r = t2.range();
let r = r.start.min(st.len())..r.end.min(st.len());
st.delete(r.clone());
st.insert_xref(r.start, CrossRef { target, format });
st.insert_xref(r.start, CrossRef { target, format, ..Default::default() });
let pos = r.start + designcraft_doc::XREF_MARK.len_utf8();
sel.text = Some(TextSel { anchor: pos, focus: pos, ..t2 });
Ok(json!({"target": target, "pos": r.start}))
@@ -161,7 +161,10 @@ fn set_format(s: &mut Session, p: &Value) -> Result<Value> {
}
let st = d.story_mut(sid).ok_or(designcraft_doc::DocError::NoStory(sid))?;
let x = st.xrefs.get_mut(k).ok_or_else(|| bad("xref.setFormat", "no such cross-reference"))?;
std::sync::Arc::make_mut(x).format = format;
let x = std::sync::Arc::make_mut(x);
x.format = format;
// A new format generates the text again, replacing any edits (InDesign does the same).
x.text = None;
st.rev = st.rev.wrapping_add(1);
Ok(Value::Null)
})