Skip to content

Commit 657d822

Browse files
authored
fix(pkg-py): Handle statically rendered dependencies correctly (#134)
* Introduce failing test (by statically rendering a UI object with dependencies) * fix(pkg-py): lazily process UI dependencies of a ChatMessage() * Get test passing * Update changelog * Fix import lint
1 parent 56cd34f commit 657d822

File tree

4 files changed

+21
-33
lines changed

4 files changed

+21
-33
lines changed

pkg-py/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [UNRELEASED]
9+
10+
### Bug fixes
11+
12+
* Fixed an issue (introduced in v0.2.0) where statically rendered messages with HTML dependencies weren't being handled properly. (#134)
13+
814
## [0.2.3] - 2025-09-10
915

1016
### Bug fixes

pkg-py/src/shinychat/_chat.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,7 @@
2020
)
2121
from weakref import WeakValueDictionary
2222

23-
from htmltools import (
24-
HTML,
25-
HTMLDependency,
26-
Tag,
27-
TagAttrValue,
28-
TagChild,
29-
TagList,
30-
css,
31-
)
23+
from htmltools import HTML, Tag, TagAttrValue, TagChild, TagList, css
3224
from shiny import reactive
3325
from shiny._deprecated import warn_deprecated
3426
from shiny.bookmark import BookmarkState, RestoreState
@@ -1001,9 +993,12 @@ async def _send_append_message(
1001993
if icon is not None:
1002994
msg["icon"] = str(icon)
1003995

996+
# Register deps with the session and get the dictionary format
997+
# for client-side rendering
1004998
deps = message.html_deps
1005999
if deps:
1006-
msg["html_deps"] = deps
1000+
processed = self._session._process_ui(TagList(*deps))
1001+
msg["html_deps"] = processed["deps"]
10071002

10081003
# print(msg)
10091004

@@ -1766,7 +1761,7 @@ def chat_ui(
17661761
message_tags.append(
17671762
Tag(
17681763
"shiny-chat-message",
1769-
*[HTMLDependency(**d) for d in msg.html_deps],
1764+
*msg.html_deps,
17701765
content=msg.content,
17711766
icon=icon_attr,
17721767
data_role=msg.role,

pkg-py/src/shinychat/_chat_types.py

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
from dataclasses import dataclass
44
from typing import Literal, TypedDict
55

6-
from htmltools import HTML, Tag, TagChild, Tagifiable, TagList
7-
from shiny.session import get_current_session
6+
from htmltools import HTML, HTMLDependency, TagChild, TagList
87

98
from ._typing_extensions import NotRequired
109

@@ -26,28 +25,18 @@ def __init__(
2625
):
2726
self.role: Role = role
2827

29-
is_html = isinstance(content, (Tag, TagList, HTML, Tagifiable))
30-
3128
# content _can_ be a TagChild, but it's most likely just a string (of
3229
# markdown), so only process it if it's not a string.
3330
deps = []
3431
if not isinstance(content, str):
35-
session = get_current_session()
36-
if session and not session.is_stub_session():
37-
res = session._process_ui(content)
38-
deps = res["deps"]
39-
else:
40-
res = TagList(content).render()
41-
deps = [d.as_dict() for d in res["dependencies"]]
42-
content = res["html"]
43-
44-
if is_html:
32+
ui = TagList(content).render()
33+
content, deps = ui["html"], ui["dependencies"]
4534
# Code blocks with `{=html}` infostrings are rendered as-is by a
4635
# custom rendering method in markdown-stream.ts
4736
content = f"\n\n````````{{=html}}\n{content}\n````````\n\n"
4837

4938
self.content = content
50-
self.html_deps = deps
39+
self.html_deps: list[HTMLDependency] = deps
5140

5241

5342
# A message once transformed have been applied
@@ -58,7 +47,7 @@ class TransformedMessage:
5847
role: Role
5948
transform_key: Literal["content_client", "content_server"]
6049
pre_transform_key: Literal["content_client", "content_server"]
61-
html_deps: list[dict[str, str]] | None = None
50+
html_deps: list[HTMLDependency] | None = None
6251

6352
@classmethod
6453
def from_chat_message(cls, message: ChatMessage) -> "TransformedMessage":

pkg-py/tests/playwright/chat/shiny_output/app.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import plotly.express as px # pyright: ignore[reportMissingTypeStubs]
44
from shiny import reactive, render
55
from shiny.express import ui
6+
from shinychat.express import Chat
67
from shinywidgets import render_plotly, render_widget
78

89
ui.page_opts(
@@ -18,12 +19,9 @@ def map():
1819
return ipyl.Map(center=(52, 10), zoom=8)
1920

2021

21-
chat = ui.Chat(
22-
id="chat",
23-
messages=[map_ui],
24-
)
22+
chat = Chat(id="chat")
2523

26-
chat.ui()
24+
chat.ui(messages=[map_ui])
2725

2826
with ui.hold() as df_1:
2927

@@ -48,7 +46,7 @@ def df2():
4846

4947
@reactive.effect
5048
async def _():
51-
await chat.append_message_stream(df_2)
49+
await chat.append_message(df_2)
5250

5351

5452
with ui.hold() as plot_ui:

0 commit comments

Comments
 (0)