diff --git a/src/renderer/src/components/Preview/utils.ts b/src/renderer/src/components/Preview/utils.ts index 5b9e23cf96..4fd082ce67 100644 --- a/src/renderer/src/components/Preview/utils.ts +++ b/src/renderer/src/components/Preview/utils.ts @@ -32,8 +32,6 @@ export function renderSvgInShadowHost(svgContent: string, hostElement: HTMLEleme } svg { - max-width: 100%; - max-height: 100%; width: auto; height: auto; } diff --git a/src/renderer/src/pages/home/Markdown/Markdown.tsx b/src/renderer/src/pages/home/Markdown/Markdown.tsx index 98a24b8735..544de3b897 100644 --- a/src/renderer/src/pages/home/Markdown/Markdown.tsx +++ b/src/renderer/src/pages/home/Markdown/Markdown.tsx @@ -30,6 +30,7 @@ import { Pluggable } from 'unified' import CodeBlock from './CodeBlock' import Link from './Link' import rehypeHeadingIds from './plugins/rehypeHeadingIds' +import rehypeScalableSvg from './plugins/rehypeScalableSvg' import remarkDisableConstructs from './plugins/remarkDisableConstructs' import Table from './Table' @@ -113,7 +114,7 @@ const Markdown: FC = ({ block, postProcess }) => { const rehypePlugins = useMemo(() => { const plugins: Pluggable[] = [] if (ALLOWED_ELEMENTS.test(messageContent)) { - plugins.push(rehypeRaw) + plugins.push(rehypeRaw, rehypeScalableSvg) } plugins.push([rehypeHeadingIds, { prefix: `heading-${block.id}` }]) if (mathEngine === 'KaTeX') { diff --git a/src/renderer/src/pages/home/Markdown/plugins/rehypeScalableSvg.ts b/src/renderer/src/pages/home/Markdown/plugins/rehypeScalableSvg.ts new file mode 100644 index 0000000000..8d7959e90a --- /dev/null +++ b/src/renderer/src/pages/home/Markdown/plugins/rehypeScalableSvg.ts @@ -0,0 +1,47 @@ +import type { Element, Root } from 'hast' +import { visit } from 'unist-util-visit' + +/** + * A Rehype plugin that makes SVG elements scalable. + * + * This plugin traverses the HAST (HTML Abstract Syntax Tree) and performs + * the following operations on each `` element: + * + * 1. Ensures a `viewBox` attribute exists. If it's missing but `width` and + * `height` are present, it generates a `viewBox` from them. This is + * crucial for making the SVG scalable. + * + * 2. Removes the `width` and `height` attributes. This allows the SVG's size + * to be controlled by CSS (e.g., `max-width: 100%`), making it responsive + * and preventing it from overflowing its container. + * + * @returns A unified transformer function. + */ +function rehypeScalableSvg() { + return (tree: Root) => { + visit(tree, 'element', (node: Element) => { + if (node.tagName === 'svg') { + const properties = node.properties || {} + const hasViewBox = 'viewBox' in properties + const width = properties.width as string | number | undefined + const height = properties.height as string | number | undefined + + if (!hasViewBox && width && height) { + const numericWidth = parseFloat(String(width)) + const numericHeight = parseFloat(String(height)) + if (!isNaN(numericWidth) && !isNaN(numericHeight)) { + properties.viewBox = `0 0 ${numericWidth} ${numericHeight}` + } + } + + // Remove fixed width and height to allow CSS to control the size + delete properties.width + delete properties.height + + node.properties = properties + } + }) + } +} + +export default rehypeScalableSvg diff --git a/src/renderer/src/utils/image.ts b/src/renderer/src/utils/image.ts index a94df580f6..8198eda730 100644 --- a/src/renderer/src/utils/image.ts +++ b/src/renderer/src/utils/image.ts @@ -291,6 +291,7 @@ export const makeSvgScalable = (element: Element): Element => { const numericHeight = parseFloat(height) if (!isNaN(numericWidth) && !isNaN(numericHeight)) { element.setAttribute('viewBox', `0 0 ${numericWidth} ${numericHeight}`) + element.setAttribute('max-width', `${numericWidth}px`) } }