fix(assistant): render markdown in TextInput output#540
Conversation
Wrap the output field in NcRichText when isOutput is true, falling back to NcRichContenteditable for the input case. Headings, lists, bold, italic, code blocks and links are now rendered. Tables are not rendered due to the markdown-it configuration of NcRichText in @nextcloud/vue, which does not enable the table plugin by default. The copy still functions and keeps the markdown as expected. Signed-off-by: niv <nicolas.varlot@ac-versailles.fr>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Renders markdown content in the assistant's "generate text" output by using NcRichText for output mode while keeping NcRichContenteditable for input mode.
Changes:
- Conditionally render
NcRichTextwhenisOutput && hasValueis true. - Import and register the
NcRichTextcomponent.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Thank you for this PR! The problem with this is that is makes the output uneditable for the user, and we assume that the LLM output is not perfect and has to be adapted. Can you find a way to keep that function, and also keep the "easy copy" button? |
|
Hi @janepie, the easy copy button is still present and can be used. I added a very short video to show it. |
|
Ah thanks for showing me! It's a bit unfortunate that the button and the AI-generation note disappear into the scrollable area, we should keep them visible and make the result scrollable instead. So points to adapt would be
|
Wrap the rendered output in a bounded scrollable container and allow switching to edit mode with a double-click. The contenteditable returns to rendered mode on blur. This preserves the editing capability while keeping the markdown rendering as the default view. Addresses review feedback on nextcloud#540. Signed-off-by: Nicolas Varlot <nicolas.varlot@ac-versailles.fr>
janepie
left a comment
There was a problem hiding this comment.
Looks good!
I'd prefer to have the double-click hint somewhere more visible but I'm not sure yet where to put it, so let's do that in a follow-up.
A few small remarks
| <br v-if="limitLabel"> | ||
| {{ limitLabel ?? '' }} | ||
| </label> | ||
| <div |
There was a problem hiding this comment.
Why wrapping it in a div? Should work with directly using NcRichText
There was a problem hiding this comment.
If the wrapper can be easily removed, I agree with Jana it would be nice to remove it. But if it's way more convenient to have it because styling NcRichText directly is sketchy, then let's keep the wrapper.
| .copy-button, | ||
| .choose-file-button { | ||
| position: absolute !important; | ||
| z-index: 10; |
There was a problem hiding this comment.
I tried removing it and couldn't see any difference. Is it fixing something in a very special case?
julien-nc
left a comment
There was a problem hiding this comment.
Nice change! I like the increase of the max-height.
To make this more discoverable, there could be an "edit" button in the top right corner when it's not editable. Wdyt @janepie and @n-iv ? It would require to slightly increase the min-height so this edit button does not overlap with the copy one.
I spotted a minor bug, when the field is empty, we can only type one character and it leaves the edit mode. A double click is necessary to continue editing. This does not happen when the field is not empty. I'd say it's fine if we don't find an easy fix for that.
| .copy-button, | ||
| .choose-file-button { | ||
| position: absolute !important; | ||
| z-index: 10; |
There was a problem hiding this comment.
I tried removing it and couldn't see any difference. Is it fixing something in a very special case?
| display: block !important; | ||
| box-sizing: border-box !important; | ||
| border: 2px solid var(--color-primary-element) !important; | ||
| border-radius: var(--border-radius) !important; |
There was a problem hiding this comment.
This should be --border-radius-large to be consistent with the NcRichContenteditable border radius.
| border: 2px solid var(--color-primary-element) !important; | ||
| padding-bottom: 38px !important; | ||
| max-height: 35vh !important; | ||
| overflow-y: auto !important; |
There was a problem hiding this comment.
.rich-contenteditable__input already has overflow-y: auto set by the server style. This can be removed.
| <br v-if="limitLabel"> | ||
| {{ limitLabel ?? '' }} | ||
| </label> | ||
| <div |
There was a problem hiding this comment.
If the wrapper can be easily removed, I agree with Jana it would be nice to remove it. But if it's way more convenient to have it because styling NcRichText directly is sketchy, then let's keep the wrapper.
| padding-bottom: 42px !important; | ||
| max-height: 35vh !important; | ||
| overflow-y: auto !important; | ||
| cursor: text; |
There was a problem hiding this comment.
This is not enough.
| cursor: text; | |
| .rendered-output, .rendered-output * { | |
| cursor: text; | |
| } |




Summary
The assistant's "generate text" form currently displays its output through NcRichContenteditable, which renders text as-is. When the underlying LLM returns markdown (heading, lists, tables, bold, links), the user sees raw markdown syntax instead of formatted content.
This PR wraps the output field in NcRichText when isOutput is true, falling back to NcRichContenteditable for the input case. Headings, lists, bold, italic, code blocks and links are now rendered. Tables are not rendered due to the markdown-it configuration of NcRichText in @nextcloud/vue, which does not enable the table plugin by default. The copy still functions and keeps the markdown as expected.
Screenshots
Before

After
