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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions