Skip to content

Markdown image refs silently dropped on cfl page edit — would love auto-upload + translation to <ac:image> macros #423

Description

@rianjs

As a user, I wanted to…

…write a Confluence page in markdown that includes screenshots, and have cfl "just work" the way every other markdown tool does — point at a local image file in the markdown source, run cfl page edit, and end up with the screenshot inlined on the page.

What I tried

I was updating an existing page with cfl page edit <page-id> --file content.md. The markdown referenced two screenshots I'd already uploaded to the same page as attachments, using standard markdown image syntax:

Selecting CheckSync opens the pre-enrollment splash:

![Q2 UUX — CheckSync pre-enrollment splash](q2-checksync-uux-pre-enrollment.png)

After enrollment, the same entry opens the dashboard view:

![Q2 UUX — CheckSync post-enrollment Positive Pay Automation view](q2-checksync-uux-post-enrollment.png)

I had already uploaded both PNGs to the page via cfl attachment upload --page <id> --file … immediately before, so the attachment names matched exactly.

What happened

The page updated successfully (no error, exit 0), but the two ![…](…) lines were silently dropped from the rendered page. What survived was only the alt text, promoted to a standalone paragraph:

Selecting CheckSync opens the pre-enrollment splash:

Q2 UUX — CheckSync pre-enrollment splash

After enrollment, the same entry opens the dashboard view:

Q2 UUX — CheckSync post-enrollment Positive Pay Automation view

No warning, no log line, no non-zero exit — the failure mode is a confidently-broken page with caption-shaped paragraphs where the images should be. I only noticed because I went back to look at the rendered page.

What I had to do instead

To get the images to actually appear, I had to:

  1. Re-fetch the page in storage format: cfl page view <id> --raw --content-only > page.xhtml
  2. Hand-patch in the Confluence storage macros where the images should go:
    <ac:image ac:align="center" ac:layout="center">
      <ri:attachment ri:filename="q2-checksync-uux-pre-enrollment.png" />
    </ac:image>
  3. Re-upload with cfl page edit <id> --file page.xhtml --storage

That worked, but it's a workflow I had to discover by inspecting raw XHTML — not something a typical user is going to find on their own.

What I'd expect / would love

In rough order of value:

  1. At minimum, fail loud. If the markdown→ADF converter encounters an image reference it can't resolve into a real image macro, it should error out (or at least warn to stderr) instead of silently dropping the node and keeping the alt text. The current behavior produces a page that looks intentional but is wrong, and there's no signal that anything went sideways.
  2. Bare filename → <ri:attachment>. When a markdown image reference is a bare filename (no scheme, no path) and an attachment by that name already exists on the target page, translate it to <ac:image><ri:attachment ri:filename="…"/></ac:image>. This handles the "I uploaded separately, now I just want to reference it from markdown" case.
  3. Local path → auto-upload + reference. When a markdown image reference is a local file path (relative to the markdown source file), upload it as an attachment to the target page and emit the same <ac:image><ri:attachment/></ac:image> macro. Idempotent on re-edit (skip upload if same filename + hash already attached). This is the "just works" path that matches how every other markdown editor handles local images.
  4. Leave http(s):// URLs alone. External image URLs should pass through as <ac:image><ri:url ri:value="…"/></ac:image> (or whatever the current ADF equivalent is).
  5. Caption support (nice-to-have). Markdown's ![alt](file.png "title") could render as image macro + a following italic caption paragraph, mirroring what I ended up hand-writing.

Tradeoffs worth thinking through:

  • Path resolution: relative to the markdown file (what every other tool does) vs. relative to cwd. File-relative is the safer default.
  • Re-edit idempotency: don't double-upload on every cfl page edit pass; key on filename + hash or filename + size.
  • Sizing: optional, but file.png?width=800 style query-string suffix maps cleanly onto ac:width/ac:height if you want to support it.

Even just shipping (1) would have saved me the round-trip — silent-drop is the actual sharp edge.

Versions

  • cfl (whatever ships at HEAD as of June 2026)
  • Target: Confluence Cloud, page is cloud-editor (ADF) format
  • Editing was via cfl page edit --file content.md (no --legacy, no --no-markdown, no --storage)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions