OvidijusParsiunas / deep-chat

Fully customizable AI chatbot component for your website
https://deepchat.dev
MIT License
1.57k stars 255 forks source link

I have a problem rendering.How to render Markdown when the type is html? #263

Open venlon789 opened 2 months ago

venlon789 commented 2 months ago

Hello! I would like to ask for some advice on a current requirement. The design is as follows: I want the upper part to have a typewriter effect and render in Markdown format. However, when rendering, I used HTML, but the Markdown style did not take effect. Can you help me solve this problem? Here is my relevant code.

          onmessage(message) {
            console.log('onmessage')
            const data = JSON.parse(message.data)
            currentMesImportantInfo.id = data.id
            if (data.status_code === 'SUCCESS') {
              reference = {}
              if (data?.references?.doc?.length > 0) {
                // 引用部分
                const titleText = data.references.doc.map(item => `《${item.title}》`)
                const titleHtml =
                  `
                  <div style="font-size: 14px; background-color: #f2f6fc; padding: 11px 16px; border-radius: 8px; margin-top: 16px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
                    <span style="color: #67696d;">引用:</span>
                    <span style="color: #728cb6;">
                      来源${titleText}
                    </span>
                  </div>
                  `
                signals.onResponse({ html: titleHtml })

                // 文件链接
                // ====盒子内部的html
                const linkContentHtml = data.references.doc.map(item => `
                  <a
                    onmouseover="this.style.color='#409eff'; this.style.textDecoration='underline';"
                    onmouseout="this.style.color='#606266'; this.style.textDecoration='none';"
                    style="font-size: 14px; color: #606266 ; margin-bottom: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: none;" href="${item.documentUrl}"
                    target="_blank"
                  >
                    ${item.content}
                  </a>
                `).join('')
                // ===包裹文件链接的盒子的html
                const linkBoxHtml =
                `
                <div style="background-color: #f2f6fc; padding: 16px; border-radius: 8px; margin-top: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: flex; flex-direction: column;">
                  ${linkContentHtml}
                </div>
                `
                signals.onResponse({ html: linkBoxHtml }) // 添加消息内容
                reference = data
              } else {
                textContent += data.message.content.replace(/\n/g, '<br />')
                signals.onResponse({ html: data.message.content.replace(/\n/g, '<br />') }) // 添加消息内容
              }
            } else {
              signals.onResponse({ error: data.message.content })
            }
          }
          async onclose() {
            await signals.onResponse({
              html: `
              <div style="margin-top: 15px; display: flex; display: flex; flex-direction: row; justify-content: flex-end">
                <svg id="${currentMesImportantInfo.id}up" conversationid="${currentMesImportantInfo.id}" class="icon icon-thumb-up" style="width: 1.5em;height: 1.5em; margin-right: 10px; vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" p-id="2208">
                  <path style="pointer-events: none;" d="M732.522667 840.832l122.197333-384H595.050667l51.349333-134.186667a85.333333 85.333333 0 0 0-29.056-99.2L580.053333 195.989333l-200.661333 260.864H337.066667v384h395.456z m76.586666 40.746667a64 64 0 0 1-60.970666 44.586666H251.733333v-554.666666h85.653334L525.162667 127.36a64 64 0 0 1 88.704-12.501333l54.101333 39.893333a170.666667 170.666667 0 0 1 58.133333 198.357333l-7.04 18.346667h135.658667c57.749333 0 98.816 56.192 81.322667 111.232l-126.933334 398.869333zM102.4 926.165333v-554.666666h85.333333v554.666666h-85.333333z" fill="#4E5969" p-id="2209"></path>
                </svg>
                <svg id="${currentMesImportantInfo.id}down" conversationid="${currentMesImportantInfo.id}" class="icon icon-thumb-down" style="width: 1.5em;height: 1.5em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" p-id="2238">
                  <path style="pointer-events: none;" d="M287.189333 192L165.013333 576h259.669334L373.333333 710.186667a85.333333 85.333333 0 0 0 29.056 99.2l37.290667 27.456L640.298667 576H682.666667V192H287.189333z m-76.586666-40.746667A64 64 0 0 1 271.616 106.666667H768v554.666666h-85.653333l-187.776 244.117334a64 64 0 0 1-88.704 12.48l-54.101334-39.872a170.666667 170.666667 0 0 1-58.133333-198.378667l7.04-18.346667H164.992c-57.749333 0-98.816-56.170667-81.322667-111.210666L210.624 151.253333zM917.333333 106.666667v554.666666h-85.333333V106.666667h85.333333z" fill="#4E5969" p-id="2239"></path>
                </svg>
              </div>
              `
            })
            signals.onClose()
            eventSourceInstance = null
            textContent = ''
          }

image At the same time, I'm not sure why there are so many tags when the format is set to HTML. image

venlon789 commented 2 months ago

This is MarkdowI want to realize that the first half can render Markdown normally, and the second half is my own defined html. Currently, all types use html, but the first half of the Markdown does not take effect.n, but it has not taken effect. Currently, all types use html, image

ZaMeR12 commented 2 months ago

There are 2 links that list some programming language that is accepted in markdown in general.

And the why html is generally not accepted in markdown code block is that markdown can renderer html.

This is a text in h1 html tag in markdown

So i think there will be nothing that can be done to correct your case or at least i don't know how to be able to tell the markdown preview to not render html tags in markdown. Another thing that can explain for this situation, is that generally markdown preview/editor in JS is rendered back as html tags, so if there is html tags in the markdown the rerenderer into html format with just see that as html and not understand that is just a code block possibly.

venlon789 commented 2 months ago

There are 2 links that list some programming language that is accepted in markdown in general.

And the why html is generally not accepted in markdown code block is that markdown can renderer html.

This is a text in h1 html tag in markdown

So i think there will be nothing that can be done to correct your case or at least i don't know how to be able to tell the markdown preview to not render html tags in markdown. Another thing that can explain for this situation, is that generally markdown preview/editor in JS is rendered back as html tags, so if there is html tags in the markdown the rerenderer into html format with just see that as html and not understand that is just a code block possibly.

Thank you for your reply. I understand what you mean. When using Markdown, we can normally render HTML styles. According to the author's documentation, we need to use the text mode. In the past, it could be rendered, but due to security risks, the author has patched the vulnerability. So currently, we can only choose the HTML mode. However, when using HTML, there are some issues, and Markdown cannot be rendered properly.

Take a look at this document image

This could lead to cross-site scripting (XSS) vulnerabilities when users exchange code snippets containing HTML.You can check the previous issues. They discussed this problem before, so I'm confused now. I want to choose the HTML mode for rendering, but the Markdown styles are not working properly. image

Take a look at this issue.

venlon789 commented 2 months ago

I'm so happy! I solved this issue by using marked every time data is received and pairing it with the overwrite: true property to force a re-render. Now, Markdown can be properly rendered in HTML mode.

import { marked } from 'marked'
 ...
          onmessage(message) {
            console.log('onmessage')
            const data = JSON.parse(message.data)
            currentMesImportantInfo.id = data.id
            if (data.status_code === 'SUCCESS') {
              reference = {}
              if (data?.references?.doc?.length > 0) {
                const titleText = data.references.doc.map(item => `《${item.title}》`)
                const titleHtml =
                  `
                  <div style="font-size: 14px; background-color: #f2f6fc; padding: 11px 16px; border-radius: 8px; margin-top: 16px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
                    <span style="color: #67696d;">引用:</span>
                    <span style="color: #728cb6;">
                      来源${titleText}
                    </span>
                  </div>
                  `
                signals.onResponse({ html: titleHtml })
                const linkContentHtml = data.references.doc.map(item => `
                  <a
                    onmouseover="this.style.color='#409eff'; this.style.textDecoration='underline';"
                    onmouseout="this.style.color='#606266'; this.style.textDecoration='none';"
                    style="font-size: 14px; color: #606266 ; margin-bottom: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: none;" href="${item.documentUrl}"
                    target="_blank"
                  >
                    ${item.content}
                  </a>
                `).join('')
                const linkBoxHtml =
                `
                <div style="background-color: #f2f6fc; padding: 16px; border-radius: 8px; margin-top: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: flex; flex-direction: column;">
                  ${linkContentHtml}
                </div>
                `
                signals.onResponse({ html: linkBoxHtml })
                reference = data
              } else {
                textContent += data.message.content.replace(/\n/g, '<br />')
                signals.onResponse({ html: marked(textContent), overwrite: true })  // look at here
              }
            } else {
              signals.onResponse({ error: data.message.content })
            }
          },

Using this method to achieve a typewriter effect sounds great! It's a creative application of re-rendering.