From aaf25b75b7fc067187a83cc6250751de77a7f13a Mon Sep 17 00:00:00 2001 From: DrMelone <27028174+Classic298@users.noreply.github.com> Date: Sat, 14 Feb 2026 19:32:10 +0100 Subject: [PATCH] Rich UI Docs --- .../plugin/development/events.mdx | 67 ++++- .../plugin/development/rich-ui.mdx | 281 ++++++++++++++++-- 2 files changed, 328 insertions(+), 20 deletions(-) diff --git a/docs/features/extensibility/plugin/development/events.mdx b/docs/features/extensibility/plugin/development/events.mdx index 2fa22a6ff..14de486c4 100644 --- a/docs/features/extensibility/plugin/development/events.mdx +++ b/docs/features/extensibility/plugin/development/events.mdx @@ -379,14 +379,14 @@ await __event_emitter__( ### `execute` (**requires** `__event_call__`) -**Run code dynamically on the user's side:** +**Run JavaScript directly in the user's browser:** ```python result = await __event_call__( { "type": "execute", "data": { - "code": "print(40 + 2);", + "code": "return document.title;", } } ) @@ -396,12 +396,73 @@ await __event_emitter__( "type": "notification", "data": { "type": "info", - "content": f"Code executed, result: {result}" + "content": f"Page title: {result}" } } ) ``` +#### How It Works + +The `execute` event runs JavaScript **directly in the main page context** using `new Function()`. This means: + +- It runs with **full access** to the page's DOM, cookies, localStorage, and session +- It is **not sandboxed** — there are no iframe restrictions +- It can manipulate the Open WebUI interface directly (show/hide elements, read form data, trigger downloads) +- The code runs as an async function, so you can use `await` and `return` a value back to the backend + +#### Example: Display a Custom Form + +```python +result = await __event_call__( + { + "type": "execute", + "data": { + "code": """ + return new Promise((resolve) => { + const overlay = document.createElement('div'); + overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:9999'; + overlay.innerHTML = ` +
+

Enter Details

+ + + +
+ `; + document.body.appendChild(overlay); + document.getElementById('exec-submit').onclick = () => { + const name = document.getElementById('exec-name').value; + const email = document.getElementById('exec-email').value; + overlay.remove(); + resolve({ name, email }); + }; + }); + """ + } + } +) +# result will be {"name": "...", "email": "..."} +``` + +#### Execute vs Rich UI Embeds + +The `execute` event and [Rich UI Embeds](/features/extensibility/plugin/development/rich-ui) are complementary ways to create interactive experiences: + +| | `execute` Event | Rich UI Embed | +|---|---|---| +| **Runs in** | Main page context (no sandbox) | Sandboxed iframe | +| **Persistence** | Ephemeral — gone on reload/navigate | Persistent — saved in chat history | +| **Page access** | Full (DOM, cookies, localStorage) | Isolated from parent by default | +| **Forms** | Always works (no sandbox) | Requires `allowForms` setting enabled | +| **Best for** | Transient interactions, side effects, downloads, DOM manipulation | Persistent visual content, dashboards, charts | + +Use `execute` for transient interactions (confirmations, custom dialogs, triggering downloads, reading page state) and Rich UI embeds for persistent visual content you want to stay in the conversation. + +:::warning +Because `execute` runs unsandboxed JavaScript in the user's browser session, it has full access to the Open WebUI page context. Only use this in trusted functions — never execute untrusted or user-provided code through this event. +::: + --- ## 🏗️ When & Where to Use Events diff --git a/docs/features/extensibility/plugin/development/rich-ui.mdx b/docs/features/extensibility/plugin/development/rich-ui.mdx index 719d6775e..f7168410e 100644 --- a/docs/features/extensibility/plugin/development/rich-ui.mdx +++ b/docs/features/extensibility/plugin/development/rich-ui.mdx @@ -68,32 +68,279 @@ async def action(self, body, __event_emitter__=None): return (html, {"Content-Disposition": "inline", "Content-Type": "text/html"}) ``` -## Advanced Features +## Iframe Height and Auto-Sizing -The embedded iframes support auto-resizing and include configurable security settings. The system automatically handles: +Rich UI embeds are rendered inside a sandboxed iframe. The iframe needs to know how tall its content is in order to display without scrollbars. There are two mechanisms for this: -- **Auto-resizing**: Embedded content automatically adjusts height based on its content -- **Cross-origin communication**: Safe message passing between the iframe and parent window -- **Security sandbox**: Configurable security restrictions for embedded content +### postMessage Height Reporting (Recommended) -## Security Considerations +When `allowSameOrigin` is **off** (the default), the parent page cannot read the iframe's content height directly. Your HTML must report its own height by posting a message to the parent window: -When embedding external content, several security options can be configured through the UI settings: +```html + +``` + +Add this script to the end of your `` in every Rich UI embed. Without it, the iframe will stay at a small default height and your content will be cut off with a scrollbar. + +### Same-Origin Auto-Resize + +When `allowSameOrigin` is **on** (via the user setting `iframeSandboxAllowSameOrigin`), the parent page can directly measure the iframe's content height and resize it automatically — no script needed in your HTML. However, this comes with security trade-offs (see below). + +## Sandbox and Security + +Embedded iframes run inside a [sandbox](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/iframe#sandbox). The following sandbox flags are always enabled by default: + +- `allow-scripts` — JavaScript execution +- `allow-popups` — Popups (e.g. window.open) +- `allow-downloads` — File downloads + +Two additional flags can be toggled by the user in **Settings → Interface**: + +| Setting | Default | Description | +|---|---|---| +| Allow Iframe Same-Origin Access | ❌ Off | Allows the iframe to access parent page context | +| Allow Iframe Form Submissions | ❌ Off | Allows form submissions within embedded content | + +### allowSameOrigin + +This is the most important flag to be aware of. It is **off by default** for security reasons. + +**When off (default):** +- The iframe is fully isolated from the parent page +- It **cannot** read cookies, localStorage, or DOM of the parent +- The parent **cannot** read the iframe's content height (so you must use the postMessage pattern above) +- This is the safest option and recommended for most use cases + +**When on:** +- The iframe can interact with the parent page's context +- Auto-resizing works without any script in your HTML +- Chart.js and Alpine.js dependencies are automatically injected if detected +- ⚠️ **Use with caution** — only enable this when you trust the embedded content -- `iframeSandboxAllowForms`: Allow form submissions within embedded content -- `iframeSandboxAllowSameOrigin`: Allow same-origin requests (use with caution) +Users can toggle this setting in **Settings → Interface → Iframe Same-Origin Access**. + +## Rendering Position + +- **Tool embeds** inside a tool call result render **inline** at the tool call indicator (the "View Result from..." line) +- **Action embeds** and message-level embeds render **below** the message text content + +## Advanced Communication + +The iframe and parent window can communicate beyond just height reporting. The following patterns are available: + +### Payload Requests + +The iframe can request a data payload from the parent. This is useful for passing dynamic data into the embed after it loads: + +```html + +``` + +The parent responds with `{ type: 'payload', requestId: ..., payload: ... }` containing the configured payload data. + +### Tool Args Injection (Tools Only) + +When a **Tool** returns a Rich UI embed, the tool call arguments (the parameters the model passed to the tool) are automatically injected into the iframe's `window.args`. This allows your embedded HTML to access the tool's input: + +```html + +``` + +:::note +This only works for Tool embeds rendered via the tool call display. Action embeds do not have `window.args` since they are triggered by the user, not the model. +::: + +### Auto-Injected Libraries + +When `allowSameOrigin` is enabled, the iframe component auto-detects usage of certain libraries in your HTML and injects them automatically — no CDN ` +``` + +## Rich UI Embeds vs Execute Event + +Rich UI embeds and the [`execute` event](/features/extensibility/plugin/development/events#execute-requires-__event_call__) are complementary ways to create interactive experiences. Choose based on your needs: + +| | Rich UI Embed | `execute` Event | +|---|---|---| +| **Runs in** | Sandboxed iframe | Main page context (no sandbox) | +| **Persistence** | Persistent — saved in chat history | Ephemeral — gone on reload/navigate | +| **Page access** | Isolated from parent by default | Full (DOM, cookies, localStorage) | +| **Forms** | Requires `allowForms` setting enabled | Always works (no sandbox) | +| **Best for** | Persistent visual content, dashboards, charts | Transient interactions, side effects, downloads, DOM manipulation | + +Use Rich UI embeds for persistent visual content you want to stay in the conversation. Use `execute` for transient interactions like custom dialogs, triggering downloads, or reading page state. ## Use Cases Rich UI embedding is perfect for: -- **Interactive dashboards**: Real-time data visualization and controls -- **Form interfaces**: Complex input forms with validation and dynamic behavior -- **Charts and graphs**: Interactive plotting with libraries like Plotly, D3.js, or Chart.js -- **Media players**: Video, audio, or interactive media content -- **Custom widgets**: Specialized UI components for specific tool functionality -- **External integrations**: Embedding content from external services or APIs -- **Human-triggered visualizations**: Actions that display results when a user clicks a button, e.g. generating a report or triggering a download +- **Interactive dashboards** — Real-time data visualization and controls +- **Charts and graphs** — Interactive plotting with libraries like Plotly, D3.js, or Chart.js +- **Form interfaces** — Complex input forms with validation and dynamic behavior +- **Media players** — Video, audio, or interactive media content +- **Download triggers** — Especially useful for iOS PWA where native download links are blocked +- **Custom widgets** — Specialized UI components for specific tool functionality +- **External integrations** — Embedding content from external services or APIs +- **Human-triggered visualizations** — Actions that display results when a user clicks a button, e.g. generating a report or triggering a download + +## Full Sample Action + +
+Complete working Sample Action with Rich UI embed + +This Action returns a styled card with stats, including the recommended height-reporting script: + +```python +""" +title: Rich UI Demo Action +author: open-webui +version: 0.1.0 +description: Demonstrates Rich UI embedding from an Action function. +""" + +from pydantic import BaseModel, Field + + +class Action: + class Valves(BaseModel): + pass + + def __init__(self): + self.valves = self.Valves() + + async def action(self, body: dict, __user__=None, __event_emitter__=None) -> None: + from fastapi.responses import HTMLResponse + + html = """ + + + + + + +
+

Rich UI Embed Demo

+

This embed renders below the message text.

+ Action Embed +
+
+
42
+
Answers
+
+
+
99%
+
Accuracy
+
+
+
0ms
+
Latency
+
+
+
+ + + + """ + + return HTMLResponse(content=html, headers={"Content-Disposition": "inline"}) +``` + +
## External Tool Example @@ -123,7 +370,7 @@ async def create_dashboard(): ) ``` -The embedded content automatically inherits responsive design and integrates seamlessly with the chat interface, providing a native-feeling experience for users interacting with your tools. +The embedded content automatically inherits responsive design and integrates seamlessly with the chat interface, providing a native-feeling experience for users interacting with your tools. ## CORS and Direct Tools