fix: round-trip fidelity for inline TOC macros, nbsp emphasis, and strikethrough
Surfaced by `verify` on outside-doc-core-v2.xml (real Confluence XML).
- A TOC macro inside a heading round-trips as `# <!-- ac:toc ... -->`, where
goldmark parses the comment as inline RawHTML. renderRawHTML now calls
convertComment (like the block path) so the macro is reconstructed instead of
leaking back out as a literal comment.
- Confluence stores structured-macro attributes in no fixed order (TOC is
macro-id,name,schema-version on one page, name,schema-version,macro-id on
another) and the verify formatter preserves order, so no reconstruction order
matches everything; sortMacroAttrs canonicalizes them on both sides.
- Bold applied to a lone nbsp (`<strong> </strong>`) emitted `** **`: the
writeEmphasis trim cutset now includes U+00A0, and normalizeForVerify drops
whitespace-only emphasis.
- `<s>` and `<del>` (both strikethrough) are canonicalized to `<del>`.
Adds regression tests for each and documents the gotchas in CLAUDE.md.
fix: round-trip fidelity for heading comments, literal tags, and emphasis
Surfaced by `verify` on two real documents that aren't in the justfile set.
- Heading-only HTML comment (a TOC macro marker, `<h1><!-- ac:toc ... --></h1>`)
was dropped by xml2md — a CommentNode is neither text nor element, so it fell
through walkChildren and vanished, collapsing `# <!-- ... -->` to `#`.
- Literal angle-bracket text stored as `<table>` (e.g. `box.backup.start(<table>)`)
round-tripped into a phantom `<table>` element that swallowed the rest of the
document: xml2md now backslash-escapes a tag-like `<` in both the TextNode and
walkChildrenInline paths, and renderText unescapes only `\<` (never a blanket
UnescapePunctuations, which would eat the deliberate `\|` table-pipe escaping).
- `<strong> x </strong>` (whitespace inside the markers) rendered as literal
`** x **`; writeEmphasis now moves the whitespace outside the markers.
- normalizeForVerify canonicalizes values Markdown can't carry — task ids,
<ul>/<ol> bullet styling, and a trailing nbsp before </ac:task-body>.
Adds regression tests for each and documents the new gotchas in CLAUDE.md.
fix: preserve inline spacing in fmt and code-block round-trip
Three related round-trip/formatting bugs, each pinned by a regression test
that fails on the prior code:
- fmt dropped the significant space between two inline elements
(</strong> <code>): a lone-space text node was always treated as
indentation. Keep it mid-line; drop it only at a block boundary.
- fmt's line-wrapper dropped a word's trailing space before a tag
(strings.Fields discards it) and broke inside <code>...</code>,
injecting indent that xml2md collapses into a spurious in-span space.
Rewrote wrapLine around an atom model with a per-atom spaceBefore flag
and keeps an inline code span whole.
- verify.normalizeForVerify's reBareTextAfterHeading used \S for its
"visible char" token; \S matches '<', so the capture swallowed a
following <ac:structured-macro> and wrapped it in <p>...</p>, splitting
a code macro from its body and breaking round-trip for a no-language
fenced code block after a heading. Use [^\s<] instead.
Full RFC-0010 document now verifies as lossless.
fix: round-trip fidelity for tables, page links, and nested lists
Four real-document failures surfaced via `mdcx verify`:
- Pipes inside `<code>` in GFM table cells were treated as column
separators, splitting cells. `writeTableRow` now escapes `|` as `\|`
per the GFM spec; the parser strips the backslash before inline
parsing so the pipe survives inside the resulting code span.
- Two `<code>` elements separated by whitespace text were being merged
because `isPrev/NextSiblingCode` skipped whitespace nodes. Adjacency
is now strict.
- `<ac:link><ri:page …/></ac:link>` was dropped because goldmark cannot
parse `<ac:link>` as raw inline HTML (the colon disqualifies it).
Preserved via a `<span data-page-link …>` placeholder; the inverse is
driven by a new `pageLinkDepth` counter in the renderer.
- Nested `<ol>` collapsed to a flat list and went loose because the
fixed `2*(depth-1)` indent didn't satisfy CommonMark's column rule
for ordered markers and a stray `\n` after each `<li>` made the list
loose. Indent is now a per-level stack (3 for ol, 2 for ul/task) and
list-item terminators use `ensureSingleNewline`.
Regression tests in converter/md2xml_test.go pin each fix; CLAUDE.md
documents the gotchas.
feat: add pull --with-comments sidecar
`mdcx pull --with-comments` fetches inline + page comments via
`/rest/api/content/{id}/child/comment` (paginated, two-level reply
expansion) and writes them to `<output>.comments.json` alongside the
Markdown file. Inline comments correlate to the markdown's
`data-inline-comment="UUID"` spans through their `marker_ref` field.
Comment bodies are stored as raw Confluence storage XML; pushing
comments back is not implemented. Requires `--output`.
Move CLI to cmd/mdcx/, add justfile and CLAUDE.md
Restructure to standard Go project layout: merge package cmd
into package main under cmd/mdcx/. Fix .gitignore to use /mdcx
so it only ignores the binary at repo root. Update install path
in README.md.
false