diff --git a/README.md b/README.md index faecf5343..f3cf69805 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,19 @@ Before deploying your app, you need to verify the domain by adding it to the [Do - Adjust starter prompts, greeting text, [chatkit theme](https://chatkit.studio/playground), and placeholder copy in [`lib/config.ts`](lib/config.ts). - Update the event handlers inside [`components/.tsx`](components/ChatKitPanel.tsx) to integrate with your product analytics or storage. +## Troubleshooting + +### Performance Issues + +ChatKit uses an iframe-based architecture to maintain security isolation for API keys and credentials. While this ensures your sensitive data stays protected, it relies on cross-frame messaging (postMessage) which can be affected by browser extensions. + +**If you experience slow performance or input lag:** + +- **Browser Extensions**: Some extensions (content blockers, privacy tools, developer tools) can interfere with cross-frame communication. Try testing in an incognito window with extensions disabled to rule out interference. +- **Memory Usage**: Monitor browser memory in Task Manager (Shift+Esc in Chrome). Abnormal memory growth may indicate a configuration issue - please [report it](https://github.com/openai/openai-chatkit-starter-app/issues). + +**For production applications**: Consider implementing a server-side integration with OpenAI's API instead of the embedded ChatKit widget. This avoids the iframe architecture entirely and provides better performance and control. See [Advanced Self-Hosting Examples](https://github.com/openai/openai-chatkit-advanced-samples) for guidance. + ## References - [ChatKit JavaScript Library](http://openai.github.io/chatkit-js/) diff --git a/components/ChatKitPanel.tsx b/components/ChatKitPanel.tsx index 067cae2cf..53c33c258 100644 --- a/components/ChatKitPanel.tsx +++ b/components/ChatKitPanel.tsx @@ -1,6 +1,6 @@ "use client"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { ChatKit, useChatKit } from "@openai/chatkit-react"; import { STARTER_PROMPTS, @@ -261,27 +261,8 @@ export function ChatKitPanel({ [isWorkflowConfigured, setErrorState] ); - const chatkit = useChatKit({ - api: { getClientSecret }, - theme: { - colorScheme: theme, - ...getThemeConfig(theme), - }, - startScreen: { - greeting: GREETING, - prompts: STARTER_PROMPTS, - }, - composer: { - placeholder: PLACEHOLDER_INPUT, - attachments: { - // Enable attachments - enabled: true, - }, - }, - threadItemActions: { - feedback: false, - }, - onClientTool: async (invocation: { + const handleClientTool = useCallback( + async (invocation: { name: string; params: Record; }) => { @@ -314,35 +295,72 @@ export function ChatKitPanel({ return { success: false }; }, - onResponseEnd: () => { - onResponseEnd(); - }, - onResponseStart: () => { - setErrorState({ integration: null, retryable: false }); - }, - onThreadChange: () => { - processedFacts.current.clear(); - }, - onError: ({ error }: { error: unknown }) => { - // Note that Chatkit UI handles errors for your users. - // Thus, your app code doesn't need to display errors on UI. - console.error("ChatKit error", error); - }, + [onThemeRequest, onWidgetAction] + ); + + const handleResponseStart = useCallback(() => { + setErrorState({ integration: null, retryable: false }); + }, [setErrorState]); + + const handleThreadChange = useCallback(() => { + processedFacts.current.clear(); + }, []); + + const handleError = useCallback(({ error }: { error: unknown }) => { + console.error("ChatKit error", error); + }, []); + + const themeConfig = useMemo( + () => ({ + colorScheme: theme, + ...getThemeConfig(theme), + }), + [theme] + ); + + const apiConfig = useMemo(() => ({ getClientSecret }), [getClientSecret]); + + const startScreenConfig = useMemo( + () => ({ + greeting: GREETING, + prompts: STARTER_PROMPTS, + }), + [] + ); + + const composerConfig = useMemo( + () => ({ + placeholder: PLACEHOLDER_INPUT, + attachments: { + enabled: true, + }, + }), + [] + ); + + const threadItemActionsConfig = useMemo( + () => ({ + feedback: false, + }), + [] + ); + + const chatkit = useChatKit({ + api: apiConfig, + theme: themeConfig, + startScreen: startScreenConfig, + composer: composerConfig, + threadItemActions: threadItemActionsConfig, + onClientTool: handleClientTool, + onResponseEnd: onResponseEnd, + onResponseStart: handleResponseStart, + onThreadChange: handleThreadChange, + onError: handleError, }); const activeError = errors.session ?? errors.integration; const blockingError = errors.script ?? activeError; - if (isDev) { - console.debug("[ChatKitPanel] render state", { - isInitializingSession, - hasControl: Boolean(chatkit.control), - scriptStatus, - hasError: Boolean(blockingError), - workflowId: WORKFLOW_ID, - }); - } - return (