Development tools for building and testing ChatGPT Apps using ChatGPT Apps SDK
A powerful, zero-configuration development environment for building ChatGPT apps. Simulate the ChatGPT production environment locally with interactive controls for testing different states, themes, and device types.
- ๐ญ Mock ChatGPT Environment - Simulates
window.openaiAPI exactly like production - ๐จ Theme Switching - Test light and dark themes instantly
- ๐ฑ Device Simulation - Desktop (768px), Tablet (576px), Mobile (380px) viewports aligned with OpenAI Apps SDK
- ๐ State Testing - Loading, data, and error states with customizable delays
- ๐ Debug Overlays - Visual boundary indicators for development
- ๐ Zero Configuration - Works out of the box with sensible defaults
- ๐ฆ Production Ready - Clean separation between dev tools and widget code
- ๐ ChatGPT Apps SDK Compatible - Works with any ChatGPT Apps SDK implementation
- React 18.0.0 or higher
- Node.js 18.0.0 or higher
- Modern browser with ES2020+ support
- Chrome 80+, Firefox 75+, Safari 13.1+, Edge 80+
- @ainativekit/ui 1.0.0 or higher (optional, but recommended for enhanced theming)
- DevTools includes basic theme support out of the box
- For full design system integration, install
@ainativekit/uiand useAppsSDKUIProvider - See usage examples below for both standalone and integrated approaches
npm install --save-dev @ainativekit/devtoolsor with yarn:
yarn add -D @ainativekit/devtoolsDevTools works out of the box with built-in theme support:
import { DevContainer } from '@ainativekit/devtools';
import App from './App';
// Only use DevContainer in development
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
import.meta.env.DEV ? (
<DevContainer>
<App />
</DevContainer>
) : (
<App />
)
);For full design system integration and advanced theming:
import { DevContainer } from '@ainativekit/devtools';
import { ThemeProvider } from '@ainativekit/ui';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<ThemeProvider>
{import.meta.env.DEV ? (
<DevContainer>
<App />
</DevContainer>
) : (
<App />
)}
</ThemeProvider>
);<DevContainer
dataLoader={async () => {
// Return your widget-specific mock data
const response = await fetch('/api/mock-data');
return response.json();
}}
loadingDelay={3000} // Test loading states
theme="dark" // Start with dark theme
>
<YourWidget />
</DevContainer>DevContainer automatically detects when you have multiple widgets and shows a selector. Each widget can have its own data loader:
import { DevContainer } from '@ainativekit/devtools';
import { AppsSDKUIProvider } from '@ainativekit/ui';
import CarouselWidget from './widgets/CarouselWidget';
import MapWidget from './widgets/MapWidget';
import ListWidget from './widgets/ListWidget';
import AlbumWidget from './widgets/AlbumWidget';
function App() {
return (
<AppsSDKUIProvider linkComponent="a">
<DevContainer
widgets={[
{
id: 'carousel',
name: 'Pizza Carousel',
component: CarouselWidget,
dataLoader: () => carouselData,
emptyDataLoader: () => emptyCarouselData
},
{
id: 'map',
name: 'Pizza Map',
component: MapWidget,
dataLoader: () => mapData
},
{
id: 'list',
name: 'Pizza List',
component: ListWidget,
dataLoader: () => listData,
emptyDataLoader: () => emptyListData
},
{
id: 'album',
name: 'Photo Albums',
component: AlbumWidget,
dataLoader: () => albumData
}
]}
loadingDelay={1500}
theme="light"
autoLoad={true}
defaultWidget="carousel"
/>
</AppsSDKUIProvider>
);
}Features:
- Single dev server for all widgets
- Widget-specific data loaders (v1.0.0+)
- Automatic widget selector (only shows when multiple widgets)
- URL support (
?widget=map) for deep linking - Persistent widget selection
- Auto-reload data when switching widgets
| Prop | Type | Default | Description |
|---|---|---|---|
children |
React.ReactNode |
- | Single widget component |
dataLoader |
() => Promise<any> | any |
- | Data loader function |
emptyDataLoader |
() => Promise<any> | any |
- | Empty state data loader |
| Prop | Type | Default | Description |
|---|---|---|---|
widgets |
Widget[] |
- | Array of widget configurations |
dataLoaders |
Record<string, Function> |
{} |
Map of global data loader functions |
emptyDataLoaders |
Record<string, Function> |
{} |
Map of global empty data loader functions |
defaultDataLoader |
string |
- | Key for default data loader |
defaultWidget |
string |
- | ID of default widget to show |
| Property | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Unique identifier for the widget |
name |
string |
Yes | Display name for the widget selector |
component |
React.ComponentType |
Yes | The widget component |
dataLoader |
() => Promise<any> | any |
No | Widget-specific data loader (single, hides dropdown) |
emptyDataLoader |
() => Promise<any> | any |
No | Widget-specific empty state data loader |
dataLoaders |
Record<string, Function> |
No | Multiple named data loaders (v1.2.0+, shows dropdown) |
emptyDataLoaders |
Record<string, Function> |
No | Matching empty state loaders for dataLoaders |
defaultDataLoader |
string |
No | Default data loader key for the widget |
| Prop | Type | Default | Description |
|---|---|---|---|
loadingDelay |
number |
2000 |
Delay (ms) before loading data |
theme |
'light' | 'dark' |
'light' |
Initial theme |
autoLoad |
boolean |
true |
Auto-load data on mount |
createMockData<T>(fullData: T, config?: MockDataConfig<T>): MockData<T>Creates type-safe mock data with automatic empty state generation.
Config Options:
emptyData: Explicit empty state dataemptyTransform: Function to derive empty state from full data- If neither provided, generates empty object automatically
The DevContainer automatically mocks the window.openai API with these methods:
window.openai = {
callTool: async (name, args) => { /* mocked */ },
sendFollowUpMessage: async ({ prompt }) => { /* mocked */ },
openExternal: ({ href }) => { /* mocked */ },
setWidgetState: (state) => { /* mocked */ },
// Plus all OpenAiGlobals properties
theme: 'light' | 'dark',
toolOutput: any,
toolResponseMetadata: any, // Server _meta field (v1.2.0+)
locale: string,
maxHeight: number,
userAgent: { device: { type }, capabilities: { hover, touch } },
// DevTools-specific (v1.1.0+)
widgetState: 'loading' | 'data' | 'empty' | 'error' // Current dev tool state
}- State Controls: Switch between Loading, Instant Data, Delayed Data, Empty, and Error states
- Theme Toggle: Switch between light and dark themes
- Device Simulation: Test desktop, tablet, and mobile viewports
- Debug Border: Toggle visual boundary indicators
- Collapsible UI: Hide/show dev tools with a single click
<DevContainer
loadingDelay={5000} // 5 second delay
dataLoader={async () => {
// Simulate slow API
await new Promise(resolve => setTimeout(resolve, 2000));
return { data: 'loaded' };
}}
>
<App />
</DevContainer><DevContainer
emptyDataLoader={() => {
// Return widget-specific empty state
return {
type: 'search-results',
properties: [],
searchInfo: { totalResults: 0, location: 'Sydney, NSW' }
};
}}
dataLoader={async () => {
// Regular data when not in empty state
return await fetchMockData();
}}
>
<SearchWidget />
</DevContainer><DevContainer
dataLoader={() => {
// Return error data
return { error: 'Something went wrong' };
}}
>
<App />
</DevContainer>// mockData.ts
export const mockSearchResults = {
type: 'search-results',
items: [
{ id: 1, title: 'Result 1' },
{ id: 2, title: 'Result 2' }
]
};
// App.tsx
import { mockSearchResults } from './mockData';
<DevContainer
dataLoader={() => mockSearchResults}
>
<SearchWidget />
</DevContainer>The DevContainer follows these principles:
- Zero Widget Contamination - Your widget code contains no dev-specific logic
- Production Parity - Uses the same APIs as production ChatGPT Apps
- External Debugging - All debug overlays are applied from outside the widget
- Type Safety - Full TypeScript support with AINativeKit types
- Clean Separation - Dev tools are never included in production builds
npm run buildnpm run devnpm run type-checkContributions are welcome! Please feel free to submit a Pull Request.
MIT ยฉ Jake Lin
Built with โค๏ธ for the ChatGPT app developer community. This tool helps developers build and test ChatGPT Apps using the ChatGPT Apps SDK, making development faster and more enjoyable.