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 video and source to $allowedTags so HTML5 video survives Xss::filter().
  • New tagMedia() helper adds class="chat-media-image" to <img> and class="chat-media-embed" to <video>, applied after Xss::filter() (necessary because Xss::filter() keeps the class attribute but strips inline style). Called from both the normal response (createResponse()) and the streamed (createSseMessage()) paths.

modules/ai_chatbot/js/deepchat-init.js

  • Registered htmlClassUtilities rules 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

  1. Apply the patch/MR and rebuild caches (drush cr) and clear the browser cache (so the updated deepchat-init.js loads).
  2. Enable AI Chatbot and place a DeepChat block, pointing it at any AI Assistant.
  3. 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)
  4. Get the assistant to return media — e.g. set its instructions to always reply with:
    • a large markdown image: ![big test image](https://placehold.co/4000x3000)
    • an HTML5 video: <video controls src="https://<allowed-host>/sample.mp4"></video>
  5. 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:

  • Unitmodules/ai_chatbot/tests/src/Unit/Controller/DeepChatApiTest.php: covers tagMedia() (correct classes added to <img>/<video>, non-media untouched) and the $allowedTags change (<video>/<source> allowed; <iframe>/<script> still stripped).
  • Kernelmodules/ai_chatbot/tests/src/Kernel/Controller/DeepChatApiRenderTest.php: drives the real createResponse() pipeline (CommonMark → live Xss::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

Merge request reports

Loading