Skip to content

Commit 0973b52

Browse files
authored
fix(Chat, MarkdownStream): prevent escaping of HTML inside message content strings (#1939)
1 parent 24ef875 commit 0973b52

File tree

3 files changed

+21
-12
lines changed

3 files changed

+21
-12
lines changed

shiny/ui/_chat.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
)
1919
from weakref import WeakValueDictionary
2020

21-
from htmltools import HTML, Tag, TagAttrValue, TagChild, TagList, css
21+
from htmltools import HTML, RenderedHTML, Tag, TagAttrValue, TagChild, TagList, css
2222

2323
from .. import _utils, reactive
2424
from .._deprecated import warn_deprecated
@@ -1458,7 +1458,12 @@ def chat_ui(
14581458
if "role" in x:
14591459
role = x["role"]
14601460

1461-
ui = TagList(content).render()
1461+
# `content` is most likely a string, so avoid overhead in that case
1462+
# (it's also important that we *don't escape HTML* here).
1463+
if isinstance(content, str):
1464+
ui: RenderedHTML = {"html": content, "dependencies": []}
1465+
else:
1466+
ui = TagList(content).render()
14621467

14631468
if role == "user":
14641469
tag_name = "shiny-user-message"

shiny/ui/_markdown_stream.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from contextlib import asynccontextmanager
22
from typing import AsyncIterable, Iterable, Literal, Union
33

4-
from htmltools import TagChild, TagList, css
4+
from htmltools import RenderedHTML, TagChild, TagList, css
55

66
from .. import _utils, reactive
77
from .._deprecated import warn_deprecated
@@ -353,7 +353,13 @@ def output_markdown_stream(
353353
height
354354
The height of the UI element.
355355
"""
356-
ui = TagList(content).render()
356+
357+
# `content` is most likely a string, so avoid overhead in that case
358+
# (it's also important that we *don't escape HTML* here).
359+
if isinstance(content, str):
360+
ui: RenderedHTML = {"html": content, "dependencies": []}
361+
else:
362+
ui = TagList(content).render()
357363

358364
return Tag(
359365
"shiny-markdown-stream",

tests/playwright/shiny/components/chat/input-suggestion/app.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
from shiny.express import ui
22

3-
welcome = """
3+
suggestions1 = """
44
<p>Here is the <span id="first" class='suggestion'>1st input suggestion</span>.
5-
And here is a <span id="second" data-suggestion='The actual suggestion'>2nd suggestion</span>.
5+
And here is a <span id="second" class='suggestion' data-suggestion='The actual suggestion'>2nd suggestion</span>.
66
Finally, a <img id="third" data-suggestion='A 3rd, image-based, suggestion' src='shiny-hex.svg' height="50px" alt='Shiny logo'> image suggestion.</p>
7+
"""
78

9+
suggestion2 = """
810
<p>On the other hand, <span id="fourth" class="suggestion submit">this suggestion will auto-submit</span>.
911
And <span id="fifth" data-suggestion="another suggestion" data-suggestion-submit="true">this suggestion will also auto-submit</span>.</p>
1012
"""
1113

12-
chat = ui.Chat(
13-
"chat",
14-
messages=[welcome],
15-
)
16-
14+
chat = ui.Chat("chat", messages=[suggestion2])
1715

18-
chat.ui()
16+
chat.ui(messages=[suggestions1])
1917

2018

2119
@chat.on_user_submit

0 commit comments

Comments
 (0)