fix(pkg-r): make viz bookmark restore survive serialization#255
Merged
Conversation
The querychat_visualize tool embeds live bslib/htmltools tags (notably a bslib::input_code_editor() in the footer) in the ContentToolResult extra$display. shinychat bookmarks chat turns via jsonlite::serializeJSON() and restores them with unserializeJSON(), which rebuilds embedded closures from deparsed text and drops their environment. The render hooks on those tags then cannot reach bslib internals, so restoring a bookmark that contains a visualization fails with "could not find function tag_require_client_side". Add freeze_tags() to resolve render hooks to inert HTML plus html_dependency objects up front -- while the hooks can still reach those internals -- and apply it to the visualize display html and footer. The frozen output carries the code-editor dependencies through the round-trip, so the component still hydrates on restore. See posit-dev/shinychat#261.
…rame A URL bookmark round-trips querychat_viz_widgets through jsonlite, which simplifies the JSON array of objects to a data.frame. restore_viz_widgets() then iterated its columns (atomic vectors) and failed with "$ operator is invalid for atomic vectors", so the transcript chart never re-rendered. Add restore_record_list() to rebuild the list-of-lists shape row by row and wrap the querychat_viz_widgets restore with it.
cpsievert
approved these changes
Jun 22, 2026
The visualize tool embeds a base64 PNG preview in the tool result for LLM feedback. Under URL bookmarking this can push the bookmarked state past URL length limits and corrupt it on restore. Emit a one-time, best-effort warning recommending server-side bookmarking; detection and the warning are wrapped so they never interrupt chart creation.
The visualize tool embeds a base64 PNG preview in the tool result for LLM feedback. Under URL bookmarking this can push the bookmarked state past URL length limits and corrupt it on restore. Emit a best-effort warning recommending server-side bookmarking; detection and the warning are wrapped so they never interrupt chart creation.
# Conflicts: # pkg-r/R/querychat_module.R
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Relates to posit-dev/shinychat#261
Problem
With bookmarking enabled, restoring a session whose chat history contains a
querychat_visualizetool call breaks on reload. Two distinct failures stack up:could not find function "tag_require_client_side"— the visualize tool embeds live bslib/htmltools tags (notably abslib::input_code_editor()in the footer) in theContentToolResultextra$display. shinychat bookmarks chat turns withjsonlite::serializeJSON()and restores them withunserializeJSON(), which rebuilds embedded closures from deparsed text and drops their environment. The render hooks on those tags can then no longer reach bslib internals, so re-rendering the restored tool card throws. (Root-caused in detail in Bookmark restore drops render-hook environments: tool-result displays with live bslib/htmltools tags fail to re-render shinychat#261.)$ operator is invalid for atomic vectors— a URL bookmark round-tripsquerychat_viz_widgetsthrough jsonlite, which simplifies the JSON array of objects to a data.frame.restore_viz_widgets()then iterated its columns (atomic vectors), so the transcript chart never re-rendered.Changes
freeze_tags()(new,utils-html.R) resolves a tag's render hooks to inert HTML +html_dependencyobjects up front — while the hooks can still reach those internals — so the display survives the bookmark serialize/deserialize round-trip. Applied to the visualize tool'sdisplay$htmlanddisplay$footer. The frozen footer carries theprism-code-editor/bslib-code-editordependencies through the round-trip, so the editor still hydrates on restore (no explicit dependency injection needed).restore_record_list()(new,querychat_module.R) rebuilds the list-of-lists shape from the URL-decoded data.frame row by row (dropping absent NA fields); thequerychat_viz_widgetsrestore is wrapped with it.Verification
Reproduced and confirmed fixed with this app:
Submit a prompt that produces a chart (e.g. "plot how many cars have 6 or 8 cylinders"), wait for it to render, then reload the page to restore the bookmark. Before: both errors above (the chat transcript fails to re-render). After: no errors, and the transcript chart and code editor both re-render on restore.
Notes