Skip to content

Latest commit

 

History

History
445 lines (325 loc) · 11.4 KB

File metadata and controls

445 lines (325 loc) · 11.4 KB

API Reference

React hooks and devtools for OpenAI Apps SDK

Complete API documentation for react-openai-apps-sdk.

Components

<OpenAIDevTools />

Floating toolbar for debugging OpenAI Apps SDK widgets.

Props:

Prop Type Default Description
initialIsOpen boolean false Show toolbar initially open
buttonPosition 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' 'bottom-right' Toggle button position
enableMock boolean true Enable mock when window.openai doesn't exist
mockConfig Partial<OpenAiGlobals> {} Initial mock configuration
showToolbar boolean true Show toolbar controls
showHotkeys boolean true Show keyboard shortcuts hint
styleNonce string - CSP nonce for inline styles

Example:

<OpenAIDevTools
  initialIsOpen={true}
  buttonPosition="top-right"
  mockConfig={{
    theme: 'dark',
    displayMode: 'fullscreen',
  }}
/>

<OpenAIDevToolsPanel />

Embedded mode - renders toolbar inline.

Props:

Prop Type Default Description
style React.CSSProperties - Custom styles
onClose () => void - Callback when panel closes
showToolbar boolean true Show toolbar controls

Example:

<OpenAIDevToolsPanel
  style={{ height: '400px' }}
  onClose={() => setShowPanel(false)}
/>

<SafeArea />

Container that automatically respects safe area insets from mobile devices, system UI (notches, rounded corners), and ChatGPT's chat input bar.

WHY IT EXISTS: Mobile devices have notches, rounded corners, and system UI that obscure content. ChatGPT's chat input bar takes vertical space. This component automatically adjusts your widget's dimensions to fit within the safe, visible area.

DESIGN DECISIONS:

  • Top inset uses padding (pushes content down from notch, maintains background)
  • Bottom inset reduces height (prevents content from being hidden by chat input)
  • Left/right insets use padding (safe area from rounded corners)
  • Fullscreen: percentage-based height (responsive)
  • Inline: fixed pixel height (embedded in chat)

Props:

Prop Type Default Description
children ReactNode - Child components to render within safe area
className string '' Additional CSS classes (Tailwind, CSS modules, etc.)
style CSSProperties {} Inline styles (merged with safe area styles)
fallbackHeight number 600 Height in pixels for inline mode
applyInsets object All sides Which insets to apply: { top?, bottom?, left?, right? }

Examples:

// Basic usage - handles all safe area concerns automatically
<SafeArea>
  <YourWidget />
</SafeArea>

// Custom fallback height for inline mode
<SafeArea fallbackHeight={800}>
  <TallerWidget />
</SafeArea>

// Apply only top and bottom insets (full-width widget)
<SafeArea applyInsets={{ top: true, bottom: true, left: false, right: false }}>
  <FullWidthWidget />
</SafeArea>

// Combine with className for styling
<SafeArea className="bg-gray-50 flex flex-col" fallbackHeight={600}>
  <Header />
  <Content />
  <Footer />
</SafeArea>

WHEN TO USE:

  • Root container for your widget
  • When content should avoid being obscured by device notches or system UI
  • When you need consistent padding on mobile devices

WHEN NOT TO USE:

  • For nested containers (only use at root level)
  • If you're manually managing device-specific layouts
  • For desktop-only widgets that don't need mobile consideration

Hooks

useOpenAI()

Returns the current window.openai object.

Returns: OpenAI | undefined

Example:

const openai = useOpenAI();

await openai?.callTool('my_tool', { arg: 'value' });
await openai?.sendFollowUpMessage({ prompt: 'Follow up...' });

useOpenAIActions()

Safe wrappers for OpenAI API actions with built-in error handling and null checks. Eliminates the need for manual window.openai availability checks and try-catch blocks.

Returns:

{
  sendFollowUpMessage: (options: SendFollowUpMessageOptions) => Promise<boolean>;
  callTool: <T>(options: CallToolOptions<T>) => Promise<{ success: boolean; data?: T }>;
  requestDisplayMode: (options: RequestDisplayModeOptions) => Promise<boolean>;
  openExternal: (options: OpenExternalOptions) => boolean;
  isAvailable: boolean;
}

Example:

const { sendFollowUpMessage, callTool, requestDisplayMode, openExternal, isAvailable } = useOpenAIActions();

// Send a follow-up message with error handling
const handleShare = async () => {
  const success = await sendFollowUpMessage({
    prompt: 'Here is my exported file: [Download](https://example.com/file.png)',
    fallbackMessage: 'ChatGPT API not available. Try copying the URL instead.',
    onSuccess: () => console.log('Shared successfully!'),
    onError: (error) => console.error('Failed to share:', error),
  });
};

// Call a tool with typed result
const handlePreview = async (postId: string) => {
  const { success, data } = await callTool<PostData>({
    name: 'social_media.preview_post',
    args: { postId },
    onSuccess: (result) => console.log('Preview loaded:', result.structuredContent),
    onError: (error) => alert(`Failed to load preview: ${error.message}`),
  });

  if (success && data) {
    setPreviewData(data);
  }
};

// Request display mode change
const toggleFullscreen = async () => {
  await requestDisplayMode({
    mode: isFullscreen ? 'inline' : 'fullscreen',
    onSuccess: (newMode) => setIsFullscreen(newMode === 'fullscreen'),
  });
};

// Open external link
const openDocs = () => {
  openExternal({
    href: 'https://docs.example.com',
    onError: (error) => alert(`Failed to open link: ${error.message}`),
  });
};

Benefits:

  • ✅ No more null checks - Handles window.openai availability automatically
  • ✅ Built-in error handling - Consistent error handling across the app
  • ✅ Type-safe - Full TypeScript support with generics
  • ✅ Cleaner code - Reduces boilerplate by 50-70%

Options Types:

interface SendFollowUpMessageOptions {
  prompt: string;
  onSuccess?: () => void;
  onError?: (error: Error) => void;
  fallbackMessage?: string;
}

interface CallToolOptions<T = unknown> {
  name: string;
  args: Record<string, unknown>;
  onSuccess?: (result: { structuredContent?: T; result?: string }) => void;
  onError?: (error: Error) => void;
  fallbackMessage?: string;
}

interface RequestDisplayModeOptions {
  mode: 'inline' | 'fullscreen' | 'pip';
  onSuccess?: (mode: 'inline' | 'fullscreen' | 'pip') => void;
  onError?: (error: Error) => void;
  fallbackMessage?: string;
}

interface OpenExternalOptions {
  href: string;
  onError?: (error: Error) => void;
  fallbackMessage?: string;
}

useOpenAIGlobal(key)

Returns a specific global from window.openai.

Parameters:

  • key: Key of the global to access

Returns: OpenAiGlobals[K] | null

Example:

const theme = useOpenAIGlobal('theme');
const displayMode = useOpenAIGlobal('displayMode');
const maxHeight = useOpenAIGlobal('maxHeight');
const toolOutput = useOpenAIGlobal('toolOutput');
const widgetState = useOpenAIGlobal('widgetState');

Based on OpenAI Apps SDK official examples

useDisplayMode()

Convenience hook for accessing display mode. Equivalent to useOpenAIGlobal('displayMode').

Returns: 'inline' | 'fullscreen' | 'pip' | null

Example:

const displayMode = useDisplayMode();
const isFullscreen = displayMode === 'fullscreen';

useMaxHeight()

Convenience hook for accessing max height. Equivalent to useOpenAIGlobal('maxHeight').

Returns: number | null

Example:

const maxHeight = useMaxHeight();
return <div style={{ height: maxHeight || 600 }}>Content</div>;

useWidgetProps(defaultState?)

Get tool output data passed to the widget. Equivalent to useOpenAIGlobal('toolOutput') with fallback support.

Parameters:

  • defaultState (optional): Default state if toolOutput is null

Returns: T (typed based on default state)

Example:

const props = useWidgetProps({ platform: 'instagram', text: '' });
// props.platform, props.text are now type-safe

useWidgetState(defaultState?)

Persistent widget state shared with the model. Returns state and setter function.

Parameters:

  • defaultState (optional): Default state if widgetState is null

Returns: readonly [T | null, (state: SetStateAction<T | null>) => void]

Example:

const [favorites, setFavorites] = useWidgetState<string[]>([]);

// Update state (persisted and shared with model)
setFavorites(prev => [...prev, newItem]);

Advanced Functions

createMockOpenAI(config?)

Manually create a mock window.openai object.

Parameters:

  • config (optional): Initial configuration

Returns: OpenAI

Example:

window.openai = createMockOpenAI({
  theme: 'dark',
  displayMode: 'fullscreen',
});

updateMockState(key, value)

Update a specific value in the mock.

Parameters:

  • key: The key to update
  • value: The new value

Example:

updateMockState('theme', 'light');
updateMockState('maxHeight', 800);

dispatchOpenAIEvent(type, detail)

Dispatch a custom OpenAI event.

Parameters:

  • type: Event type
  • detail: Event details

Example:

dispatchOpenAIEvent('set_globals', {
  theme: 'dark',
  displayMode: 'fullscreen',
});

Types

OpenAI

Complete type definition for window.openai.

interface OpenAI extends OpenAiGlobals, OpenAIAPI {
  __devMock?: boolean;
}

OpenAiGlobals

Global state available from ChatGPT.

interface OpenAiGlobals {
  theme: 'light' | 'dark';
  displayMode: 'inline' | 'fullscreen' | 'pip';
  maxHeight: number;
  locale: string;
  userAgent: UserAgent;
  safeArea: SafeArea;
  toolInput: unknown | null;
  toolOutput: unknown | null;
  toolResponseMetadata: unknown | null;
  widgetState: unknown | null;
  setWidgetState: (state: unknown) => Promise<void>;
}

OpenAIAPI

API methods available on window.openai.

interface OpenAIAPI {
  callTool: (name: string, args: Record<string, unknown>) => Promise<any>;
  sendFollowUpMessage: (args: { prompt: string }) => Promise<void>;
  openExternal: (payload: { href: string }) => void;
  requestDisplayMode: (args: { mode: DisplayMode }) => Promise<{ mode: DisplayMode }>;
}

Keyboard Shortcuts

When DevTools are active:

  • E - Toggle display mode (inline ↔ fullscreen)
  • T - Toggle theme (light ↔ dark)

Production Behavior

In production (NODE_ENV=production):

  • DevTools automatically removed (tree-shaken)
  • Hooks still work (access real window.openai)
  • Zero bundle size impact

Production Debugging

Enable DevTools in production ChatGPT builds for debugging:

# Build with DevTools enabled (read-only inspector)
VITE_ENABLE_OPENAI_DEVTOOLS=true pnpm build

Behavior with flag enabled:

  • DevTools render in production ChatGPT
  • Read-only inspector for real window.openai state
  • No mock creation (safe for real environment)
  • Inspect toolOutput, widgetState, and all globals
  • Useful for debugging production issues

Note: This only works with ESM builds (Vite, webpack 5+). CJS builds always tree-shake DevTools.