fix(preview): pin SVG to intrinsic size for all diagram types
The previous fix only disabled useMaxWidth for flowcharts, so sequence (and other) diagrams still rendered responsively and fit shrank them to a tiny speck. Normalise every rendered SVG to its viewBox pixel size (stripping width:100% / max-width) in the render path, so fit/zoom is exact regardless of diagram type. Drops the now-redundant per-type flowchart flag.
This commit is contained in:
@@ -15,10 +15,37 @@ function configure(theme: string) {
|
|||||||
theme: theme as any,
|
theme: theme as any,
|
||||||
securityLevel: 'loose',
|
securityLevel: 'loose',
|
||||||
fontFamily: 'inherit',
|
fontFamily: 'inherit',
|
||||||
// Render at the diagram's intrinsic pixel size (not responsive 100% width)
|
flowchart: { htmlLabels: true },
|
||||||
// so the preview's own zoom/fit maths stay exact — otherwise a large
|
});
|
||||||
// flowchart is scaled down by the SVG *and* again by fit, ending up tiny.
|
}
|
||||||
flowchart: { useMaxWidth: false, htmlLabels: true },
|
|
||||||
|
/**
|
||||||
|
* Force an SVG to its intrinsic pixel size (from the viewBox), stripping the
|
||||||
|
* responsive `width:100%` / `max-width` that Mermaid emits by default.
|
||||||
|
*
|
||||||
|
* Mermaid's `useMaxWidth` makes diagrams shrink to their container, but the
|
||||||
|
* preview applies its own zoom/fit transform — so a responsive SVG ends up
|
||||||
|
* scaled down twice and renders tiny. Pinning the laid-out size to the viewBox
|
||||||
|
* keeps fit/zoom exact for *every* diagram type (sequence, class, …), which
|
||||||
|
* per-type `useMaxWidth` flags can't guarantee uniformly.
|
||||||
|
*/
|
||||||
|
function pinSize(svg: string): string {
|
||||||
|
const vb = svg.match(/viewBox="([\d.\s-]+)"/);
|
||||||
|
if (!vb) return svg;
|
||||||
|
const p = vb[1].trim().split(/\s+/).map(Number);
|
||||||
|
if (p.length !== 4 || p.some(Number.isNaN)) return svg;
|
||||||
|
const [, , w, h] = p;
|
||||||
|
return svg.replace(/<svg\b[^>]*>/, (tag) => {
|
||||||
|
let t = tag.replace(/\s(?:width|height)="[^"]*"/g, '');
|
||||||
|
t = t.replace(/\sstyle="([^"]*)"/, (_m, s: string) => {
|
||||||
|
const cleaned = s
|
||||||
|
.split(';')
|
||||||
|
.map((d) => d.trim())
|
||||||
|
.filter((d) => d && !/^(?:max-width|width)\s*:/i.test(d))
|
||||||
|
.join('; ');
|
||||||
|
return cleaned ? ` style="${cleaned}"` : '';
|
||||||
|
});
|
||||||
|
return t.replace(/<svg\b/, `<svg width="${w}" height="${h}"`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +60,7 @@ export async function renderMermaid(code: string, theme: string): Promise<Render
|
|||||||
const id = `mmx-render-${++renderSeq}`;
|
const id = `mmx-render-${++renderSeq}`;
|
||||||
try {
|
try {
|
||||||
const { svg } = await mermaid.render(id, code);
|
const { svg } = await mermaid.render(id, code);
|
||||||
return { svg };
|
return { svg: pinSize(svg) };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { error: e instanceof Error ? e.message : String(e) };
|
return { error: e instanceof Error ? e.message : String(e) };
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user