fix(muya): render SVG images that state no size of their own (#4991) by Jocs · Pull Request #5462 · marktext/marktext · GitHub
Skip to content

fix(muya): render SVG images that state no size of their own (#4991) - #5462

Merged
Jocs merged 2 commits into
developfrom
fix/svg-viewbox-only-collapses-4991
Sep 20, 2026
Merged

Jocs merged 2 commits into
developfrom
fix/svg-viewbox-only-collapses-4991

Conversation

@Jocs

@Jocs Jocs commented Sep 19, 2026

Copy link
Copy Markdown
Member

Fixes #4991.

The bug

A local SVG that states no width/height, only a viewBox — the shape every
rich/Textual/mermaid-style export produces — renders as nothing in the editor.
Reproduced on 0.20.0-rc.4 with the exact file from the issue: the <img> loads
fine (.mu-image-success, complete === true, naturalWidth 300), yet its
layout box measures 0x0.

Why

Such an image has no intrinsic size, so CSS 2.1 §10.3.2 resolves its used width
against its containing block. .mu-inline-image and .mu-image-container are
both inline-block, i.e. shrink-to-fit: the container's width comes from the
image, the image's from the container, and Chromium breaks the cycle at 0. The
same SVG in ordinary block flow (or on GitHub) lays out at 300x136.

This is not a regression — the legacy engine's .ag-inline-image /
.ag-image-container chain is identical, which is why the issue was filed
against 0.19.1. An SVG that states its own size has always worked.

CSS alone can't fix it: there is no selector for "replaced element with no
intrinsic size", and max-width: none, width: fit-content, display: block
on the container and 100cqw were each measured in the real app — all still 0.

The fix

When a loaded image's measured size is the browser's fallback (the CSS default
object size, 300x150 contained to the image's ratio — what loadImage already
reports), pin that width on the <img>, so the shrink-to-fit container has
something to shrink to. The height stays automatic, so max-width: 100% still
scales the image by its ratio when the editor is narrow.

  • Images with an intrinsic size are untouched.
  • Images whose markdown/HTML attributes state a size are untouched.
  • An image that really is 300px wide is pinned to its own intrinsic width, which
    renders identically.

Applied in both render paths: loadImageAsync's success handler (first load)
and image.ts (every re-render rebuilds the <img> from vnodes).

Verification

Real app (electron-vite build of this branch), the issue's own SVG plus a
matrix of local files:

markdown before after
the issue's screenshot.svg (viewBox only) 0x0 300x136
viewbox-only.svg (viewBox 200x100) 0x0 300x150
percent.svg (width="100%", viewBox) 0x0 300x120
sized.svg (120x60) 120x60 120x60
big-sized.svg (4000x2000) 700x350 700x350
big.png (2000x1000) 700x350 700x350

Sizes survive a re-render, and narrowing the editor still scales every image by
its ratio (max-width: 100% unaffected).

Tests: new e2e spec fails on develop (0) and passes with the fix (300x150);
unit tests cover the helper and both render paths. vitest run 1911 passed,
eslint clean, tsc --noEmit clean, madge --circular clean, and the image
e2e suites (drag, image tools, reference images, inline) all pass.

🤖 Generated with Claude Code

Jocs and others added 2 commits September 19, 2026 23:43
An SVG carrying only a `viewBox` has no intrinsic size, so it resolves its
used width against its containing block (CSS 2.1 §10.3.2). But
`.mu-image-container` is shrink-to-fit: the container's width comes from
the image and the image's from the container, so Chromium settles on 0 and
the image renders as an empty 0x0 box — nothing is shown at all.

Pin the width the browser fell back to (the CSS default object size,
300x150 contained to the image's ratio, which `loadImage` already measures)
whenever neither the image nor the markdown states a size. The height stays
automatic, so `max-width: 100%` keeps scaling such an image by its ratio,
and an image with an intrinsic size is left untouched. The pin is applied on
the first async load and restated by the renderer, which rebuilds the <img>
from vnodes on every re-render.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The collapse is a layout outcome, so no unit test in happy-dom can see it:
only a real browser lays the image out. This spec renders a `viewBox`-only
SVG and asserts the box the browser gives it (300x150), with a sized SVG as
the control. It fails on develop (0x0) and passes with the pin.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Jocs
Jocs force-pushed the fix/svg-viewbox-only-collapses-4991 branch from c35a597 to 647ec50 Compare September 19, 2026 15:43
@Jocs
Jocs merged commit d197b26 into develop Sep 20, 2026
7 checks passed
@Jocs
Jocs deleted the fix/svg-viewbox-only-collapses-4991 branch September 20, 2026 00:06
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.

[Bug] SVG does not show

1 participant