You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
baseUrl: "https://example.com", // required because the chatgpt iframe is sandboxed and absolute URL links are required
27
+
baseUrl: "https://example.com", //if not using a vite `base`, this is required because the chatgpt iframe is sandboxed and absolute URL links are required
28
28
}),
29
29
],
30
30
build: {
@@ -38,10 +38,21 @@ export default defineConfig({
38
38
Create React components in your widgets directory:
@@ -52,15 +63,10 @@ You can optionally create a root layout component that will wrap all widgets. If
52
63
// web/chatgpt-widgets/root.tsx
53
64
exportdefaultfunction RootLayout({ children }: { children:React.ReactNode }) {
54
65
return (
55
-
<divclassName="root-layout">
56
-
<header>
57
-
<h1>Common Header</h1>
58
-
</header>
59
-
<main>{children}</main>
60
-
<footer>
61
-
<p>Common Footer</p>
62
-
</footer>
63
-
</div>
66
+
<SomeProvider>
67
+
<header>Common Header</header>
68
+
{children}
69
+
</SomeProvider>
64
70
);
65
71
}
66
72
```
@@ -69,24 +75,29 @@ The root layout component:
69
75
70
76
- Must accept a `children` prop
71
77
- Will automatically wrap every widget component
72
-
- Is not exposed as a widget itself
73
78
- Is optional - if not present, widgets render without a wrapper
74
79
75
80
### 3. Serve Widgets in Your Application
76
81
77
-
#### Development Mode (with Vite Dev Server)
82
+
After setting up the plugin and writing some widgets, you need to expose the widget HTML snippets generated by this plugin as MCP server resources. In development, the HTML snippets will be generated by Vite dynamically, and in production, they'll be built by your Vite build process and read off the disk.
**⚠️ Required:**When serving widgets in sandboxed iframes like ChatGPT's UI, asset links **must** be fully qualified URLs with protocol and domain. The plugin enforces this requirement and will throw an error if an absolute base URL is not configured.
125
+
When serving widgets in sandboxed iframes like ChatGPT's UI, asset links **must** be fully qualified URLs with protocol and domain. The user's browser loads up your widget on an OpenAI controlled domain, so asset loads must refer directly back to your hosting provider. The plugin enforces this requirement and will throw an error if an absolute base URL is not configured.
131
126
132
127
You must configure an absolute base URL in one of these ways:
133
128
134
-
1.**Vite's `base` config is an absolute URL**: If you've already configured Vite with `base: "https://example.com/"`, the plugin will use it automatically.
129
+
1. Vite's `base` config: If you've already configured Vite with `base: "https://example.com/"`, the plugin will use it automatically.
135
130
136
131
```typescript
137
132
// Option 1: In Vite config (affects both dev and build)
2.**Provide `baseUrl` option**: If Vite's `base` is relative (or not set), provide the `baseUrl` option to the plugin or the production build configuration:
139
+
2.`baseUrl` option to this plugin:: If Vite's `base` is not set, or must be relative,, provide the `baseUrl` option to this plugin directly:
151
140
152
141
```typescript
153
142
// Option 1: In Vite config (affects both dev and build)
- The plugin transforms relative asset URLs like `/assets/widget-abc123.js` to `https://example.com/assets/widget-abc123.js`
173
-
- Only the entry `<script>` and `<link>` tags in the HTML are transformed
174
-
- ES module imports within JavaScript files remain relative (correct behavior - the browser resolves them relative to the parent module's URL)
175
-
176
-
**Validation:**
177
-
178
-
- At build time: The plugin validates that either Vite's `base` or the plugin's `baseUrl` option is an absolute URL
179
-
- At runtime: `getWidgets()` and `getWidgetHTML()` validate that an absolute base URL is available from either Vite's config or the provided `baseUrl` option
180
-
- If validation fails, an error is thrown with clear instructions on how to fix it
181
-
182
153
## How It Works
183
154
184
155
The plugin creates virtual modules for each widget component:
@@ -209,20 +180,6 @@ Get the HTML content for a widget.
209
180
**Parameters:**
210
181
211
182
-`widgetsDir` (string): The path to the directory on disk with your widget components
212
-
-`viteHandle` (ViteDevServer | ProductionViteBuild): A reference to a Vite context we can use for getting widget content.
213
-
- In dev: Pass the Vite dev server instance
214
-
- In prod: Pass an object with:
215
-
-`manifestPath` (string): Path to the Vite manifest.json file (e.g., `"dist/.vite/manifest.json"`)
216
-
-`baseUrl` (string, optional): Base URL for assets if Vite's `base` is not absolute
217
-
218
-
## Architecture
219
-
220
-
The plugin and helpers run in different contexts:
221
-
222
-
-**Plugin context**: Runs during Vite build, creates virtual modules and adds them as entrypoints
223
-
-**Application context**: The helper functions run in your app (e.g., MCP server) to serve the widgets
224
-
225
-
They communicate via:
226
-
227
-
-**Dev mode**: Direct access to Vite's dev server plugin container
228
-
-**Production**: Vite's `manifest.json` file maps virtual module IDs to built file paths
183
+
-`viteHandle` (DevelopmentViteBuild | ProductionViteBuild): A reference to a Vite context we can use for getting widget content.
184
+
- In dev: Pass an object like `{ devServer: ViteDevServer }` to give the Vite dev server to use to build HTML
185
+
- In prod: Pass an object like `{ mainfest: "some/path/to/.vite/manifest.json" }` to list all the entrypoints built by the vite build process
0 commit comments