Widget host simulatorPro
A host on your desk for building ChatGPT Apps and MCP Apps widgets: theme, locale, display mode, max height, screen width, tool input and output editors, widget state, and a log of every call the widget makes.
On this page
Installation
Usage
import { WidgetHostSimulator } from "@/components/ui/chatcn/pro/widget-host-simulator"
<WidgetHostSimulator
title="Tables"
defaultToolOutput={fixture}
onCallTool={(name, args) => api.call(name, args)}
>
<BookingWidget />
</WidgetHostSimulator>Widgets read their host through useWidgetHost() from use-chatcn-pro-host: displayMode, requestDisplayMode, maxHeight, safeArea, theme, locale, dir, toolInput, toolOutput, widgetState and the calls callTool, sendFollowUp, setWidgetState and openExternal. The contract mirrors the Apps SDK window.openai globals, so a widget built against it ports to ChatGPT Apps or MCP Apps by swapping the provider.
The simulated screen renders a short conversation with your widget inside WidgetDisplayModes, so all four modes, the docked composer and the PiP safe area behave as they would in a host. Locales that read right to left flip the screen's direction. Edits to the JSON apply after a short pause; invalid JSON keeps the last good value and names the line and column to fix.
Every call lands in the log with its payload, result or error and how long it took. sendFollowUp also appends the message to the conversation. Return or throw from onCallTool to test success and failure paths.
Examples
Errors, RTL and dark
Props
| Prop | Type | Notes |
|---|---|---|
children | ReactNode | The widget under test |
title, icon | ReactNode | Widget header |
prompt, reply | string | The conversation around the widget |
defaultTheme | "light" | "dark" | Defaults to the page theme |
defaultLocale, locales | string, { value, label }[] | Six locales by default, Arabic among them |
defaultDisplayMode, modes | WidgetDisplayMode | All four modes by default |
defaultMaxHeight | number | Default 480, 200 to 800 |
defaultDevice | "phone" | "tablet" | "desktop" | 390 px, 768 px or the full width |
viewportHeight | number | Default 680 |
defaultToolInput, defaultToolOutput, defaultWidgetState | unknown | Starting data |
onCallTool | (name, args) => unknown | Resolve or throw. Default resolves { ok: true } |
onEvent | (event: WidgetHostEvent) => void | Every call once it settles |
Keyboard
| Key | Action |
|---|---|
Tab in a JSON editor | Indents two spaces. Press Escape first to leave with Tab |
Enter on a log row | Expands its payload and result |
Accessibility
- The call log is a
logregion. Each row says Done, Failed or Pending in words next to its icon. - JSON status lines are polite live regions that name the line and column of an error.
Motion
- The simulator itself does not animate. Mode changes use the display mode transitions.