Fixed images/videos not rendering properly in the chatbox, added source and...
What this does (issue #3586518)
Constrains images and HTML5 videos so they render inside the chatbot viewport instead of overflowing it (forcing a horizontal scrollbar / breaking page layout).
Root cause
The DeepChat component renders message content inside a shadow DOM, so the module's CSS files can't reach in-message <img>/<video>. There was also no max-width constraint on media, so anything wider than the chat column overflowed. Additionally, <video> was being stripped entirely because it wasn't in the chatbot's allowed-tags list.
Changes
modules/ai_chatbot/src/Controller/DeepChatApi.php
- Added
videoandsourceto$allowedTagsso HTML5 video survivesXss::filter(). - New
tagMedia()helper addsclass="chat-media-image"to<img>andclass="chat-media-embed"to<video>, applied afterXss::filter()(necessary becauseXss::filter()keeps theclassattribute but strips inlinestyle). Called from both the normal response (createResponse()) and the streamed (createSseMessage()) paths.
modules/ai_chatbot/js/deepchat-init.js
- Registered
htmlClassUtilitiesrules for those two classes (max-width: 100%; height: auto; display: block). This is the same shadow-DOM-aware styling mechanism the module already uses for buttons/loading indicators — the only reliable way to style in-message content.
Note on "embedded video"
This uses an HTML5 <video> tag rather than an <iframe> embed. The core HostnameFilter (ai/src/Service/HostnameFilter.php) unconditionally removes iframe/script/embed/object/style from all AI output as a security measure, regardless of the allowed-hosts list. Supporting iframe embeds would require loosening that deliberate control, so <video> (which is gated normally by the allowed-hosts allowlist) was chosen instead.
How to test
Manual
- Apply the patch/MR and rebuild caches (
drush cr) and clear the browser cache (so the updateddeepchat-init.jsloads). - Enable AI Chatbot and place a DeepChat block, pointing it at any AI Assistant.
- Go to
/admin/config/ai/settings→ Advanced settings → Allowed hosts and add the host(s) you'll test with, e.g.:placehold.co(image)- the host of a sample
.mp4(video)
- Get the assistant to return media — e.g. set its instructions to always reply with:
- a large markdown image:
 - an HTML5 video:
<video controls src="https://<allowed-host>/sample.mp4"></video>
- a large markdown image:
- Open the chatbot and send a message.
Expected (with MR): both the image and the video scale down to fit the chat width — no horizontal scrollbar, and the video is visible/playable. Before the MR change: the image overflows the viewport (horizontal scroll) and the video is stripped entirely.
Automated
Two tests are included:
- Unit —
modules/ai_chatbot/tests/src/Unit/Controller/DeepChatApiTest.php: coverstagMedia()(correct classes added to<img>/<video>, non-media untouched) and the$allowedTagschange (<video>/<source>allowed;<iframe>/<script>still stripped). - Kernel —
modules/ai_chatbot/tests/src/Kernel/Controller/DeepChatApiRenderTest.php: drives the realcreateResponse()pipeline (CommonMark → liveXss::filter()→tagMedia()) and asserts a markdown image and a<video>end up tagged and sanitized in the JSON response, while dangerous tags are removed.
Run: vendor/bin/phpunit -c web/core modules/contrib/ai/modules/ai_chatbot/tests/src/Unit/Controller/DeepChatApiTest.php vendor/bin/phpunit -c web/core modules/contrib/ai/modules/ai_chatbot/tests/src/Kernel/Controller/DeepChatApiRenderTest.php phpcs (Drupal/DrupalPractice), phpstan (level 1), and cspell all pass on the changed PHP files.
Possible follow-up
The legacy form-stream chat UI (modules/ai_chatbot/src/Form/ChatForm.php) has its own duplicate hardcoded $allowedTags and would need the same video/source + tagging treatment if media rendering is wanted there too.
Checklist
- I have linked the related issue in the MR title or description
- I have performed a self-review of my own code
- I have added or updated tests, or explained in the description why this change is not covered by tests
- I have updated documentation for any new or changed functionality
- I have written testing instructions and verified them locally
- I have noted any required post-merge steps (config imports, cache rebuilds, manual changes)
- This MR contains no breaking API or hook changes, or they are explicitly documented in the description
AI Compliance
Note
Check the one that best describes your usage, or leave all unchecked if AI was not significantly used.
- AI Assisted Code
Mainly written by a human; AI used for autocomplete or partial generation under full human supervision. - AI Generated Code
Mainly generated by AI, reviewed and approved by a human before this MR was created. - Vibe Coded
Generated by AI and only functionally reviewed before this MR was created.
Closes #3586518