Skip to content

fix(read): read inline content controls (checkboxes, text fields) as paragraph text - #64

Open
kklimuk wants to merge 1 commit into
mainfrom
claude/triage-issue-58-inline-content-controls
Open

kklimuk wants to merge 1 commit into
mainfrom
claude/triage-issue-58-inline-content-controls

Conversation

@kklimuk

@kklimuk kklimuk commented Nov 12, 2026

Copy link
Copy Markdown
Owner

🤖 docx-cli triage

What was wrong

The reader skipped every run-level <w:sdt> (inline content control), so the text inside one never appeared. That covers a Word form's checkbox glyph and any Plain Text or Dropdown field value. For the issue's paragraph, docx read printed Approved <!-- p1 -->, and docx find de.docx "☐" printed no matches (exit 0). wc didn't count the text either.

What changed

  • An inline <w:sdt> is now a run-bearing wrapper: it is in RUN_BEARING_WRAPPER_TAGS, and wrapperContentNode(sdt) returns its <w:sdtContent>. The reader (walkInlineContentControl) and every XML offset walker step into the same node, so find, replace, comments add and span edit offsets agree.
  • A checkbox Word clicked under tracking (the ins/del glyph pair) is still one checkboxToggle tcN, and accept/reject keep the glyph and w14:checked in step. Other revisions inside a control now get ordinary tcNs, and accepting or rejecting one changes only the content inside that control.
  • When a span cuts across a control's edge, each piece stays inside its own copy of the control. Each copy keeps the control's properties except <w:id>, so no two controls share an id.
  • A --text rewrite that repeats the control's text keeps that text inside the control, the same way it already keeps hyperlinks (keptWrapper). A checkbox stays a clickable checkbox.
  • Docs: REFERENCE.md (content controls bullet), plus the nested CLAUDE.md files for core, ast, task-list and track-changes.
  • Fixture: transparent-wrappers.docx gains a third paragraph with a checkbox and a Plain Text field.
  • Tests: 7 new Checkbox content controls (w14:checkbox) are invisible in read and cannot be set #58 tests in tests/cli/invariants.test.ts. Two existing tests that asserted the old skip are rewritten for the new behaviour.

Verification

  • Before: read → Approved <!-- docx:p p1 raw -->; find "☐" → no matches.
  • After: read → ☐ Approved <!-- docx:p p1 raw -->; find "☐" → p1:0-1; wc counts the glyph.
  • Also checked by hand: replace inside a field, replace across a field's edge, comments, footnotes, --bold, --text with and without --track. validate is clean after each.
  • bun run check ✅ bun run test:unit ✅ (2107 tests in the worktree; 2137 in the pre-commit hook run)
  • /simplify (single pass, no Agent tool): merged the two copies of the checkbox-toggle registration into one registerCheckboxToggle helper. /code-review xhigh: the forked run reviewed the wrong scope (commit feat(images)!: refuse HEIC/HEIF input and drop the LGPL libheif dependency #56 in the main checkout), so I didn't apply its output. I did a manual adversarial pass on this diff instead and found no new bugs (details below).

Decisions for you

  • A verb to toggle a form checkbox. docx tasks check/uncheck --at pN handles task-list items only. A form checkbox (Yes/No in a table cell, or several boxes in one paragraph) needs an address for each box, either a new id (e.g. cbN) or a span such as pN:S. Until then, replace ☐ ☒ changes the glyph but leaves w14:checked alone. REFERENCE.md says so.
  • Should read add a hint for checkboxes? For example <!-- docx:checkbox pN:0 checked=no -->, which would tell an agent the glyph is a control and not text. I left it out until the verb question above is settled.
  • Bugs that existed before this PR and now also affect content controls (they already affect fldSimple/smartTag/hyperlinks):
    • A tracked whole-paragraph replace (--track, or --markdown, over a paragraph with pending revisions) leaves wrapper text undeleted. This doubles a glyph, e.g. ☒☒ Buy bread.
    • A tracked span that cuts across a wrapper's edge drops the cut inner text with no <w:del>, so it can't be restored by reject. Before this PR, fldSimple already did this (Today is -05-05).
    • A --markdown whole-paragraph edit flattens the control to plain text.
  • Changed behaviour in one case. An untracked whole-paragraph --text edit over a checkbox toggle that is still pending now resolves the toggle and prints the Whole-paragraph edit --text --track silently resolves other authors' pending changes #17 warning. That is how other pending text behaves. Before, the control was carried through unseen.

Fixes #58

Action needed: approve/merge — review the "Decisions for you" checklist (above all, whether to add a checkbox-toggle verb in this PR or a follow-up), then merge.


Generated by Claude Code

…paragraph text

A run-level <w:sdt> was skipped by the reader, so a Word form's checkbox
glyph and any Plain Text / Dropdown field value vanished from read, find
and wc (#58). Inline <w:sdt> is now a run-bearing wrapper: the reader and
every XML offset walker descend its <w:sdtContent>, a split half keeps the
control (sdtPr minus w:id), and the --text diff rebuilds kept text inside
the control. A checkbox toggled under tracking stays one checkboxToggle
revision. The transparent-wrappers fixture gains a checkbox + text field.

Fixes #58

Triage-Routine: true
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LkctEPkr8mq92sCK3ERfG3

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Checkbox content controls (w14:checkbox) are invisible in read and cannot be set

2 participants