Render markdown in Vue chat TextContent#730
Conversation
|
I would be happy to see this feature merged asap 🙏 |
vishxrad
left a comment
There was a problem hiding this comment.
Thanks for fixing #728. I am requesting changes for two issues before merge: model-produced text should stay within a Markdown-only rendering surface, and the component should consistently support either inline Markdown or correctly structured and styled block Markdown. The dependency and lockfile changes look good, and both CI jobs pass.
|
|
||
| const html = computed(() => { | ||
| const raw = marked.parse(props.text ?? "", { async: false }); | ||
| return DOMPurify.sanitize(raw); |
There was a problem hiding this comment.
[P2] Please disable or escape raw HTML before sanitizing. marked.parse() preserves embedded HTML, while the default DOMPurify policy still permits forms, inputs, external form actions and images, and fixed-position inline styles; a model response can therefore render UI outside the component library controls. HTML-looking plain text such as <script> also loses content instead of being displayed. Prefer treating Marked HTML tokens as text, then keep DOMPurify with a Markdown-specific tag and attribute allowlist.
|
|
||
| <template> | ||
| <p class="text-sm leading-relaxed text-zinc-700 dark:text-zinc-300">{{ props.text ?? "" }}</p> | ||
| <p class="text-sm leading-relaxed text-zinc-700 dark:text-zinc-300" v-html="html"></p> |
There was a problem hiding this comment.
[P2] marked.parse() emits block elements; even plain text becomes
...
, so injecting the result into an outercreates invalid nested or block markup. Tailwind preflight also removes list markers, heading and link styling, and default spacing, leaving most block Markdown visually unformatted. If this component only needs inline formatting, use marked.parseInline(); if it should support full Markdown, use a
|
@shogun444 maybe https://github.com/Simon-He95/markstream-vue would be more suitable for this case |
|
@AlexQuidditch Thanks for the suggestion! markstream-vue looks great for streaming use cases. For now I've addressed the @vishxrad feedback with a minimal fix (HTML escaping, proper block structure, scoped markdown styles). If there's interest in a fuller streaming markdown solution for the Vue chat example, that could be a follow-up. Sorry for the late PR. |
vishxrad
left a comment
There was a problem hiding this comment.
Requesting changes on head 49818825.
-
[P1]
TextContent.vue:11— allTextContentoutput currently throws. With lockedmarked@17.0.5, the per-callrendereroption replaces the complete renderer; this partial{ html() {} }object is not merged with the defaults. Evenmarked.parse("hello", options)calls the missingparagraph()method and throwsTypeError: this.renderer.paragraph is not a function. In the running Vue example, submittingWhat is Vue?renders headings and buttons but no body copy, and the DOM contains zero.markdown-contentnodes. Use a completeRendererinstance withhtmloverridden, or configure a dedicatedMarkedinstance through.use({ renderer: ... }). -
[P2]
TextContent.vue:29— dark-mode body text regresses. The new wrapper removestext-sm leading-relaxed text-zinc-700 dark:text-zinc-300. NeitherCardnorStacksupplies a body-text color, while the page usesdark:bg-zinc-950; after fixing the parser, ordinary Markdown will inherit black and be unreadable in dark mode. Restore the base typography and light/dark text colors. -
[P2]
TextContent.vue:20— Markdown headings have no presentation.h1–h6are allowed through sanitization, but there are no heading rules. Tailwind preflight resets heading size and weight toinherit, so headings render like body text. Add scoped heading typography and spacing.
Please add regression coverage for plain text, bold/inline-code Markdown, and escaped raw HTML. markstream-vue looks worth evaluating separately for streaming Markdown UX, but adopting it needs streaming-state wiring, an explicit escaped-HTML policy, CSS ordering, and bundle/visual QA; it is not required for this focused fix.
c12273c to
e305766
Compare
|
@shogun444 Thanks — the runtime, security, and styling issues from my review are fixed on One small follow-up remains before I can approve: the new Could you please:
I verified that all four tests pass once the dependencies are installed and Nuxt is prepared. After that, I’m good to approve. |
e305766 to
e0c2b23
Compare
Parse TextContent text using marked, sanitize with DOMPurify, render via v-html. Uses a complete Renderer instance with html() overridden to escape raw HTML. Scoped markdown styles added for headings, lists, code, blockquotes, tables, etc. Includes regression tests for plain text, bold, inline code, and raw HTML escaping. Closes thesysdev#728
e0c2b23 to
b74201a
Compare
Parse TextContent text using marked, sanitize with DOMPurify, render via v-html. Uses a complete Renderer instance with html() overridden to escape raw HTML. Scoped markdown styles added for headings, lists, code, blockquotes, tables, etc. Includes regression tests for plain text, bold, inline code, and raw HTML escaping. Closes thesysdev#728
b74201a to
f3b4c53
Compare
…b.com/shogun444/openui into fix/vue-chat-textcontent-markdown-728
44717c1 to
1c8dfbd
Compare
|
Adding multiple providers in the vue-chat could be a follow up similar to the openui-chat so user's can bring in their own API key and play with it. |

Fixes #728
What
The Vue chat example claims that
TextContentsupports markdown, but the component rendered text using plain Vue interpolation, causing markdown syntax (e.g.**NVDA**) to appear literally instead of being formatted.This PR updates
TextContentto render markdown as the prompt already describes.Changes
TextContentusingmarkedDOMPurifyv-htmlmarkedanddompurifyas direct dependencies for the Vue chat exampleWhy
The issue proposed two possible fixes:
This PR chooses the first approach so the implementation matches the documented behavior instead of reducing functionality.
The change is intentionally scoped to
examples/vue-chatonly. No changes were made to@openuidev/vue-langsince the runtime is functioning correctly; the issue was limited to the example's rendering implementation.Test Plan
DOMPurify)Checklist