[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6EN0ACKNFRY0WQNPWS2H7TV":3,"skill-same:01M6EN0ACKNFRY0WQNPWS2H7TV":104},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":51,"commitSha":52,"skillDirectory":53,"forksCount":54,"repositoryHtmlUrl":55,"repositoryHomepageUrl":56,"ownerHtmlUrl":57,"ownerBlogUrl":6,"ownerDescription":58,"updatedAt":59,"install":60,"resources":73},"01M6EN0ACKNFRY0WQNPWS2H7TV","bodhi-sdk-react-integration",null,"Integrate React+Vite web apps with bodhi-js-sdk for local LLM integration. Use when user asks to: \"integrate bodhi\", \"add bodhi sdk\", \"connect to bodhi\", \"setup bodhi provider\", \"bodhi react integration\", \"deploy bodhi to github pages\", or troubleshoot bodhi-js-sdk connection\u002Fauth issues.","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fbodhisearch\u002Fbodhi-sdk-react-integration\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"bodhi sdk react integration","react","integration","sdk","write","add","apps","local","local llm","provider","integrate","vite","edit","read","github pages","Bash","issues","connect","auth","troubleshoot","2026-11-10 10:12:30",false,"---\nname: bodhi-sdk-react-integration\ndescription: >\n  Integrate React+Vite web apps with bodhi-js-sdk for local LLM integration. Use when user asks to:\n  \"integrate bodhi\", \"add bodhi sdk\", \"connect to bodhi\", \"setup bodhi provider\",\n  \"bodhi react integration\", \"deploy bodhi to github pages\", or troubleshoot bodhi-js-sdk connection\u002Fauth issues.\nallowed-tools: Read, Grep, Glob, Edit, Write, Bash(npm:*), Bash(npx:*)\n---\n\n# Bodhi JS SDK React Integration\n\nGuide for integrating React+Vite applications with bodhi-js-sdk to enable local LLM chat capabilities through the Bodhi Browser ecosystem.\n\n## When to Use This Skill\n\n- User wants to integrate a React app with bodhi-js-sdk\n- User needs to add chat\u002FLLM capabilities to their React+Vite app\n- User is deploying a bodhi-integrated app to GitHub Pages\n- User is troubleshooting SDK connection, authentication, or streaming issues\n\n## Quick Integration Checklist\n\n1. **Install**: `npm install @bodhiapp\u002Fbodhi-js-react`\n2. **Register**: Create OAuth client at https:\u002F\u002Fdeveloper.getbodhi.app\n3. **Wrap App**: Add `\u003CBodhiProvider authClientId={...}>` around your app\n4. **Use Hook**: Access `useBodhi()` for client, auth state, and actions\n5. **Build UI**: Create chat interface with streaming support\n\n## Core Concepts\n\n### Package Architecture\n\n- `@bodhiapp\u002Fbodhi-js-react` - Preset package for web apps (auto-creates WebUIClient)\n- `@bodhiapp\u002Fbodhi-js-react-ext` - Preset package for Chrome extensions (auto-creates ExtUIClient)\n- Both include React bindings + OpenAI-compatible API\n\n### Connection Modes\n\n- **Extension mode**: Via Bodhi Browser extension (preferred)\n- **Direct mode**: Direct HTTP to local server (fallback)\n- SDK auto-detects and switches modes automatically\n\n### Authentication\n\n- OAuth 2.0 + PKCE flow\n- Two auth servers:\n  - **Dev**: `https:\u002F\u002Fmain-id.getbodhi.app\u002Frealms\u002Fbodhi` (allows localhost)\n  - **Prod**: `https:\u002F\u002Fid.getbodhi.app\u002Frealms\u002Fbodhi` (requires real domain)\n\n## Basic Integration Steps\n\n### Step 1: Install Package\n\n```bash\nnpm install @bodhiapp\u002Fbodhi-js-react\n```\n\n### Step 2: Wrap App with BodhiProvider\n\n```tsx\n\u002F\u002F App.tsx\nimport { BodhiProvider } from '@bodhiapp\u002Fbodhi-js-react';\nimport Chat from '.\u002FChat';\n\nconst CLIENT_ID = 'your-client-id-from-developer.getbodhi.app';\n\nfunction App() {\n  return (\n    \u003CBodhiProvider authClientId={CLIENT_ID}>\n      \u003Cdiv className=\"app\">\n        \u003Ch1>My Bodhi Chat App\u003C\u002Fh1>\n        \u003CChat \u002F>\n      \u003C\u002Fdiv>\n    \u003C\u002FBodhiProvider>\n  );\n}\n\nexport default App;\n```\n\n### Step 3: Create Chat Component\n\n```tsx\n\u002F\u002F Chat.tsx\nimport { useState, useEffect } from 'react';\nimport { useBodhi } from '@bodhiapp\u002Fbodhi-js-react';\n\nfunction Chat() {\n  const { client, isOverallReady, isAuthenticated, login, showSetup } = useBodhi();\n  const [prompt, setPrompt] = useState('');\n  const [response, setResponse] = useState('');\n  const [loading, setLoading] = useState(false);\n  const [models, setModels] = useState\u003Cstring[]>([]);\n  const [selectedModel, setSelectedModel] = useState('');\n\n  \u002F\u002F Load models on mount\n  useEffect(() => {\n    if (isOverallReady && isAuthenticated) {\n      loadModels();\n    }\n  }, [isOverallReady, isAuthenticated]);\n\n  const loadModels = async () => {\n    const modelList: string[] = [];\n    for await (const model of client.models.list()) {\n      modelList.push(model.id);\n    }\n    setModels(modelList);\n    if (modelList.length > 0) setSelectedModel(modelList[0]);\n  };\n\n  const handleSubmit = async (e: React.FormEvent) => {\n    e.preventDefault();\n    if (!prompt.trim() || !selectedModel) return;\n\n    setLoading(true);\n    setResponse('');\n\n    try {\n      const stream = client.chat.completions.create({\n        model: selectedModel,\n        messages: [{ role: 'user', content: prompt }],\n        stream: true,\n      });\n\n      for await (const chunk of stream) {\n        const content = chunk.choices?.[0]?.delta?.content || '';\n        setResponse(prev => prev + content);\n      }\n    } catch (err) {\n      setResponse(`Error: ${err instanceof Error ? err.message : String(err)}`);\n    } finally {\n      setLoading(false);\n    }\n  };\n\n  if (!isOverallReady) {\n    return \u003Cbutton onClick={showSetup}>Open Setup\u003C\u002Fbutton>;\n  }\n\n  if (!isAuthenticated) {\n    return \u003Cbutton onClick={login}>Login\u003C\u002Fbutton>;\n  }\n\n  return (\n    \u003Cdiv>\n      \u003Cselect value={selectedModel} onChange={e => setSelectedModel(e.target.value)}>\n        {models.map(model => (\n          \u003Coption key={model} value={model}>\n            {model}\n          \u003C\u002Foption>\n        ))}\n      \u003C\u002Fselect>\n      \u003Cform onSubmit={handleSubmit}>\n        \u003Cinput value={prompt} onChange={e => setPrompt(e.target.value)} \u002F>\n        \u003Cbutton type=\"submit\" disabled={loading}>\n          {loading ? 'Generating...' : 'Send'}\n        \u003C\u002Fbutton>\n      \u003C\u002Fform>\n      {response && \u003Cdiv>{response}\u003C\u002Fdiv>}\n    \u003C\u002Fdiv>\n  );\n}\n\nexport default Chat;\n```\n\n## Key APIs and Hooks\n\n### useBodhi() Hook\n\n```tsx\nconst {\n  client, \u002F\u002F SDK client instance (OpenAI-compatible API)\n  isOverallReady, \u002F\u002F Both client AND server ready (most common check)\n  isAuthenticated, \u002F\u002F User has valid OAuth token\n  login, \u002F\u002F Initiate OAuth login flow\n  logout, \u002F\u002F Logout and clear tokens\n  showSetup, \u002F\u002F Open setup wizard modal\n\n  \u002F\u002F Additional properties\n  isReady, \u002F\u002F Client initialized (extension or direct URL)\n  isServerReady, \u002F\u002F Server status is 'ready'\n  isInitializing, \u002F\u002F client.init() in progress\n  isExtension, \u002F\u002F Using extension mode\n  isDirect, \u002F\u002F Using direct HTTP mode\n  canLogin, \u002F\u002F isReady && !isAuthLoading\n  isAuthLoading, \u002F\u002F Auth operation in progress\n} = useBodhi();\n```\n\n### Client Methods (OpenAI-Compatible)\n\n```tsx\n\u002F\u002F List models (AsyncGenerator)\nfor await (const model of client.models.list()) {\n  console.log(model.id);\n}\n\n\u002F\u002F Streaming chat\nconst stream = client.chat.completions.create({\n  model: 'gemma-3n-e4b-it',\n  messages: [{ role: 'user', content: 'Hello!' }],\n  stream: true,\n});\n\nfor await (const chunk of stream) {\n  const content = chunk.choices?.[0]?.delta?.content || '';\n  \u002F\u002F Append to response\n}\n\n\u002F\u002F Non-streaming chat\nconst response = await client.chat.completions.create({\n  model: 'gemma-3n-e4b-it',\n  messages: [{ role: 'user', content: 'Hello!' }],\n  stream: false,\n});\n```\n\n## Advanced Configuration\n\n### Custom Client Config\n\n```tsx\n\u003CBodhiProvider\n  authClientId={CLIENT_ID}\n  clientConfig={{\n    redirectUri: 'https:\u002F\u002Fmyapp.com\u002Fcallback',\n    basePath: '\u002Fapp',\n    logLevel: 'debug',\n  }}\n>\n  \u003CApp \u002F>\n\u003C\u002FBodhiProvider>\n```\n\n### basePath for Sub-paths or GitHub Pages\n\nWhen your app runs on a sub-path (e.g., GitHub Pages at `\u002Frepo-name\u002F`):\n\n```tsx\n\u002F\u002F Vite config\nexport default defineConfig({\n  base: '\u002Frepo-name\u002F',\n});\n\n\u002F\u002F BodhiProvider\n\u003CBodhiProvider authClientId={CLIENT_ID} basePath=\"\u002Frepo-name\" callbackPath=\"\u002Frepo-name\u002Fcallback\">\n  \u003CApp \u002F>\n\u003C\u002FBodhiProvider>;\n```\n\n## Common Patterns\n\n### Conditional Rendering\n\n```tsx\nfunction App() {\n  const { isOverallReady, isAuthenticated, showSetup, login } = useBodhi();\n\n  if (!isOverallReady) {\n    return \u003Cbutton onClick={showSetup}>Setup Required\u003C\u002Fbutton>;\n  }\n\n  if (!isAuthenticated) {\n    return \u003Cbutton onClick={login}>Login Required\u003C\u002Fbutton>;\n  }\n\n  return \u003CChatInterface \u002F>;\n}\n```\n\n### Model Loading with Caching\n\n```tsx\nconst loadModels = async () => {\n  const cached = localStorage.getItem('bodhi_models');\n  if (cached) {\n    const { models: cachedModels, expiry } = JSON.parse(cached);\n    if (Date.now() \u003C expiry) {\n      setModels(cachedModels);\n      return;\n    }\n  }\n\n  const modelList: string[] = [];\n  for await (const model of client.models.list()) {\n    modelList.push(model.id);\n  }\n  setModels(modelList);\n\n  localStorage.setItem(\n    'bodhi_models',\n    JSON.stringify({\n      models: modelList,\n      expiry: Date.now() + 3600000, \u002F\u002F 1 hour\n    })\n  );\n};\n```\n\n### Error Handling\n\n```tsx\ntry {\n  const stream = client.chat.completions.create({ ... });\n  for await (const chunk of stream) {\n    \u002F\u002F Process chunk\n  }\n} catch (err) {\n  if (err instanceof Error) {\n    console.error('Chat error:', err.message);\n    setError(err.message);\n  }\n}\n```\n\n## Detailed Guides\n\nFor comprehensive information on specific topics, see the supporting documentation:\n\n- **[Quick Start Guide](.\u002Fquick-start.md)** - Complete 5-minute integration walkthrough\n- **[OAuth Setup](.\u002Foauth-setup.md)** - Dev vs prod environments, client registration\n- **[GitHub Pages Deployment](.\u002Fgithub-pages.md)** - basePath config, 404 hack, workflows\n- **[Troubleshooting](.\u002Ftroubleshooting.md)** - Common issues and solutions\n- **[Code Examples](.\u002Fcode-examples.md)** - Copy-paste snippets for common patterns\n\n## SDK Documentation Reference\n\nThe bodhi-js-sdk repository contains comprehensive documentation:\n\n- `bodhi-js-sdk\u002Fdocs\u002Fquick-start.md` - Official quick start\n- `bodhi-js-sdk\u002Fdocs\u002Freact-integration.md` - Deep dive into React integration\n- `bodhi-js-sdk\u002Fdocs\u002Fauthentication.md` - OAuth flow details\n- `bodhi-js-sdk\u002Fdocs\u002Fstreaming.md` - Streaming patterns\n- `bodhi-js-sdk\u002Fdocs\u002Fapi-reference.md` - Complete API documentation\n\n## Implementation Approach\n\nWhen user asks to integrate bodhi-js-sdk:\n\n1. **Check existing setup**: Look for package.json, existing React components\n2. **Install package**: Run `npm install @bodhiapp\u002Fbodhi-js-react`\n3. **Add BodhiProvider**: Wrap root component with provider\n4. **Create\u002Fupdate components**: Add useBodhi() hook to components\n5. **Test connection**: Verify extension detection or direct mode\n6. **Add OAuth**: Guide user to register at developer.getbodhi.app\n7. **Implement chat**: Create streaming chat interface\n8. **Handle errors**: Add proper error boundaries and user feedback\n\nWhen troubleshooting:\n\n1. **Check connection status**: Use `isOverallReady`, `isReady`, `isServerReady`\n2. **Verify auth state**: Check `isAuthenticated`, `auth` object\n3. **Inspect logs**: Look for `[Bodhi\u002FWeb]` prefixed logs in console\n4. **Review config**: Verify authClientId, redirectUri, basePath\n5. **Test backend**: Ensure local server at http:\u002F\u002Flocalhost:1135\n6. **Check extension**: Verify Bodhi Browser extension installed\n\n## Testing Integration\n\nAfter integration, verify:\n\n1. **Extension detection**: Check console for `[Bodhi\u002FWeb] Extension detected`\n2. **Server connection**: Verify `[Bodhi\u002FWeb] Server ready`\n3. **Setup flow**: Click \"Open Setup\" to test modal\n4. **Authentication**: Click \"Login\" to test OAuth flow\n5. **Model loading**: Verify models populate in dropdown\n6. **Streaming**: Send message and verify real-time response streaming\n7. **Error handling**: Test with server offline to verify error states\n\n## Common Integration Tasks\n\nWhen user requests:\n\n- **\"Add bodhi to my React app\"** → Follow basic integration steps\n- **\"Setup OAuth for bodhi\"** → Guide to oauth-setup.md, developer.getbodhi.app\n- **\"Deploy to GitHub Pages\"** → Reference github-pages.md for basePath and 404 hack\n- **\"Chat not working\"** → Check troubleshooting.md for connection\u002Fauth issues\n- **\"Models not loading\"** → Verify server, check models endpoint, review async iteration\n- **\"Streaming broken\"** → Verify stream:true, check AsyncGenerator pattern\n\n## Key Files to Reference\n\nWhen implementing integration:\n\n- `bodhi-js-sdk\u002Fdocs\u002Fquick-start.md` - Primary integration guide\n- `bodhi-js-sdk\u002Fdocs\u002Freact-integration.md` - React-specific patterns\n- `bodhi-js-sdk\u002Fdocs\u002F` - Comprehensive documentation and examples\n\n## Notes\n\n- Focus on React+Vite projects only\n- Always use `@bodhiapp\u002Fbodhi-js-react` preset package (simplest)\n- Auto-callback handling is enabled by default (`handleCallback={true}`)\n- OAuth callback happens automatically without custom routes\n- Default backend: http:\u002F\u002Flocalhost:1135\n- Extension mode is preferred over direct mode\n- AsyncGenerator pattern for streaming (OpenAI-compatible)\n","# Bodhi JS SDK React Integration\n\nGuide for integrating React+Vite applications with bodhi-js-sdk to enable local LLM chat capabilities through the Bodhi Browser ecosystem.\n\n## When to Use This Skill\n\n- User wants to integrate a React app with bodhi-js-sdk\n- User needs to add chat\u002FLLM capabilities to their React+Vite app\n- User is deploying a bodhi-integrated app to GitHub Pages\n- User is troubleshooting SDK connection, authentication, or streaming issues\n\n## Quick Integration Checklist\n\n1. **Install**: `npm install @bodhiapp\u002Fbodhi-js-react`\n2. **Register**: Create OAuth client at https:\u002F\u002Fdeveloper.getbodhi.app\n3. **Wrap App**: Add `\u003CBodhiProvider authClientId={...}>` around your app\n4. **Use Hook**: Access `useBodhi()` for client, auth state, and actions\n5. **Build UI**: Create chat interface with streaming support\n\n## Core Concepts\n\n### Package Architecture\n\n- `@bodhiapp\u002Fbodhi-js-react` - Preset package for web apps (auto-creates WebUIClient)\n- `@bodhiapp\u002Fbodhi-js-react-ext` - Preset package for Chrome extensions (auto-creates ExtUIClient)\n- Both include React bindings + OpenAI-compatible API\n\n### Connection Modes\n\n- **Extension mode**: Via Bodhi Browser extension (preferred)\n- **Direct mode**: Direct HTTP to local server (fallback)\n- SDK auto-detects and switches modes automatically\n\n### Authentication\n\n- OAuth 2.0 + PKCE flow\n- Two auth servers:\n  - **Dev**: `https:\u002F\u002Fmain-id.getbodhi.app\u002Frealms\u002Fbodhi` (allows localhost)\n  - **Prod**: `https:\u002F\u002Fid.getbodhi.app\u002Frealms\u002Fbodhi` (requires real domain)\n\n## Basic Integration Steps\n\n### Step 1: Install Package\n\n```bash\nnpm install @bodhiapp\u002Fbodhi-js-react\n```\n\n### Step 2: Wrap App with BodhiProvider\n\n```tsx\n\u002F\u002F App.tsx\nimport { BodhiProvider } from '@bodhiapp\u002Fbodhi-js-react';\nimport Chat from '.\u002FChat';\n\nconst CLIENT_ID = 'your-client-id-from-developer.getbodhi.app';\n\nfunction App() {\n  return (\n    \u003CBodhiProvider authClientId={CLIENT_ID}>\n      \u003Cdiv className=\"app\">\n        \u003Ch1>My Bodhi Chat App\u003C\u002Fh1>\n        \u003CChat \u002F>\n      \u003C\u002Fdiv>\n    \u003C\u002FBodhiProvider>\n  );\n}\n\nexport default App;\n```\n\n### Step 3: Create Chat Component\n\n```tsx\n\u002F\u002F Chat.tsx\nimport { useState, useEffect } from 'react';\nimport { useBodhi } from '@bodhiapp\u002Fbodhi-js-react';\n\nfunction Chat() {\n  const { client, isOverallReady, isAuthenticated, login, showSetup } = useBodhi();\n  const [prompt, setPrompt] = useState('');\n  const [response, setResponse] = useState('');\n  const [loading, setLoading] = useState(false);\n  const [models, setModels] = useState\u003Cstring[]>([]);\n  const [selectedModel, setSelectedModel] = useState('');\n\n  \u002F\u002F Load models on mount\n  useEffect(() => {\n    if (isOverallReady && isAuthenticated) {\n      loadModels();\n    }\n  }, [isOverallReady, isAuthenticated]);\n\n  const loadModels = async () => {\n    const modelList: string[] = [];\n    for await (const model of client.models.list()) {\n      modelList.push(model.id);\n    }\n    setModels(modelList);\n    if (modelList.length > 0) setSelectedModel(modelList[0]);\n  };\n\n  const handleSubmit = async (e: React.FormEvent) => {\n    e.preventDefault();\n    if (!prompt.trim() || !selectedModel) return;\n\n    setLoading(true);\n    setResponse('');\n\n    try {\n      const stream = client.chat.completions.create({\n        model: selectedModel,\n        messages: [{ role: 'user', content: prompt }],\n        stream: true,\n      });\n\n      for await (const chunk of stream) {\n        const content = chunk.choices?.[0]?.delta?.content || '';\n        setResponse(prev => prev + content);\n      }\n    } catch (err) {\n      setResponse(`Error: ${err instanceof Error ? err.message : String(err)}`);\n    } finally {\n      setLoading(false);\n    }\n  };\n\n  if (!isOverallReady) {\n    return \u003Cbutton onClick={showSetup}>Open Setup\u003C\u002Fbutton>;\n  }\n\n  if (!isAuthenticated) {\n    return \u003Cbutton onClick={login}>Login\u003C\u002Fbutton>;\n  }\n\n  return (\n    \u003Cdiv>\n      \u003Cselect value={selectedModel} onChange={e => setSelectedModel(e.target.value)}>\n        {models.map(model => (\n          \u003Coption key={model} value={model}>\n            {model}\n          \u003C\u002Foption>\n        ))}\n      \u003C\u002Fselect>\n      \u003Cform onSubmit={handleSubmit}>\n        \u003Cinput value={prompt} onChange={e => setPrompt(e.target.value)} \u002F>\n        \u003Cbutton type=\"submit\" disabled={loading}>\n          {loading ? 'Generating...' : 'Send'}\n        \u003C\u002Fbutton>\n      \u003C\u002Fform>\n      {response && \u003Cdiv>{response}\u003C\u002Fdiv>}\n    \u003C\u002Fdiv>\n  );\n}\n\nexport default Chat;\n```\n\n## Key APIs and Hooks\n\n### useBodhi() Hook\n\n```tsx\nconst {\n  client, \u002F\u002F SDK client instance (OpenAI-compatible API)\n  isOverallReady, \u002F\u002F Both client AND server ready (most common check)\n  isAuthenticated, \u002F\u002F User has valid OAuth token\n  login, \u002F\u002F Initiate OAuth login flow\n  logout, \u002F\u002F Logout and clear tokens\n  showSetup, \u002F\u002F Open setup wizard modal\n\n  \u002F\u002F Additional properties\n  isReady, \u002F\u002F Client initialized (extension or direct URL)\n  isServerReady, \u002F\u002F Server status is 'ready'\n  isInitializing, \u002F\u002F client.init() in progress\n  isExtension, \u002F\u002F Using extension mode\n  isDirect, \u002F\u002F Using direct HTTP mode\n  canLogin, \u002F\u002F isReady && !isAuthLoading\n  isAuthLoading, \u002F\u002F Auth operation in progress\n} = useBodhi();\n```\n\n### Client Methods (OpenAI-Compatible)\n\n```tsx\n\u002F\u002F List models (AsyncGenerator)\nfor await (const model of client.models.list()) {\n  console.log(model.id);\n}\n\n\u002F\u002F Streaming chat\nconst stream = client.chat.completions.create({\n  model: 'gemma-3n-e4b-it',\n  messages: [{ role: 'user', content: 'Hello!' }],\n  stream: true,\n});\n\nfor await (const chunk of stream) {\n  const content = chunk.choices?.[0]?.delta?.content || '';\n  \u002F\u002F Append to response\n}\n\n\u002F\u002F Non-streaming chat\nconst response = await client.chat.completions.create({\n  model: 'gemma-3n-e4b-it',\n  messages: [{ role: 'user', content: 'Hello!' }],\n  stream: false,\n});\n```\n\n## Advanced Configuration\n\n### Custom Client Config\n\n```tsx\n\u003CBodhiProvider\n  authClientId={CLIENT_ID}\n  clientConfig={{\n    redirectUri: 'https:\u002F\u002Fmyapp.com\u002Fcallback',\n    basePath: '\u002Fapp',\n    logLevel: 'debug',\n  }}\n>\n  \u003CApp \u002F>\n\u003C\u002FBodhiProvider>\n```\n\n### basePath for Sub-paths or GitHub Pages\n\nWhen your app runs on a sub-path (e.g., GitHub Pages at `\u002Frepo-name\u002F`):\n\n```tsx\n\u002F\u002F Vite config\nexport default defineConfig({\n  base: '\u002Frepo-name\u002F',\n});\n\n\u002F\u002F BodhiProvider\n\u003CBodhiProvider authClientId={CLIENT_ID} basePath=\"\u002Frepo-name\" callbackPath=\"\u002Frepo-name\u002Fcallback\">\n  \u003CApp \u002F>\n\u003C\u002FBodhiProvider>;\n```\n\n## Common Patterns\n\n### Conditional Rendering\n\n```tsx\nfunction App() {\n  const { isOverallReady, isAuthenticated, showSetup, login } = useBodhi();\n\n  if (!isOverallReady) {\n    return \u003Cbutton onClick={showSetup}>Setup Required\u003C\u002Fbutton>;\n  }\n\n  if (!isAuthenticated) {\n    return \u003Cbutton onClick={login}>Login Required\u003C\u002Fbutton>;\n  }\n\n  return \u003CChatInterface \u002F>;\n}\n```\n\n### Model Loading with Caching\n\n```tsx\nconst loadModels = async () => {\n  const cached = localStorage.getItem('bodhi_models');\n  if (cached) {\n    const { models: cachedModels, expiry } = JSON.parse(cached);\n    if (Date.now() \u003C expiry) {\n      setModels(cachedModels);\n      return;\n    }\n  }\n\n  const modelList: string[] = [];\n  for await (const model of client.models.list()) {\n    modelList.push(model.id);\n  }\n  setModels(modelList);\n\n  localStorage.setItem(\n    'bodhi_models',\n    JSON.stringify({\n      models: modelList,\n      expiry: Date.now() + 3600000, \u002F\u002F 1 hour\n    })\n  );\n};\n```\n\n### Error Handling\n\n```tsx\ntry {\n  const stream = client.chat.completions.create({ ... });\n  for await (const chunk of stream) {\n    \u002F\u002F Process chunk\n  }\n} catch (err) {\n  if (err instanceof Error) {\n    console.error('Chat error:', err.message);\n    setError(err.message);\n  }\n}\n```\n\n## Detailed Guides\n\nFor comprehensive information on specific topics, see the supporting documentation:\n\n- **[Quick Start Guide](.\u002Fquick-start.md)** - Complete 5-minute integration walkthrough\n- **[OAuth Setup](.\u002Foauth-setup.md)** - Dev vs prod environments, client registration\n- **[GitHub Pages Deployment](.\u002Fgithub-pages.md)** - basePath config, 404 hack, workflows\n- **[Troubleshooting](.\u002Ftroubleshooting.md)** - Common issues and solutions\n- **[Code Examples](.\u002Fcode-examples.md)** - Copy-paste snippets for common patterns\n\n## SDK Documentation Reference\n\nThe bodhi-js-sdk repository contains comprehensive documentation:\n\n- `bodhi-js-sdk\u002Fdocs\u002Fquick-start.md` - Official quick start\n- `bodhi-js-sdk\u002Fdocs\u002Freact-integration.md` - Deep dive into React integration\n- `bodhi-js-sdk\u002Fdocs\u002Fauthentication.md` - OAuth flow details\n- `bodhi-js-sdk\u002Fdocs\u002Fstreaming.md` - Streaming patterns\n- `bodhi-js-sdk\u002Fdocs\u002Fapi-reference.md` - Complete API documentation\n\n## Implementation Approach\n\nWhen user asks to integrate bodhi-js-sdk:\n\n1. **Check existing setup**: Look for package.json, existing React components\n2. **Install package**: Run `npm install @bodhiapp\u002Fbodhi-js-react`\n3. **Add BodhiProvider**: Wrap root component with provider\n4. **Create\u002Fupdate components**: Add useBodhi() hook to components\n5. **Test connection**: Verify extension detection or direct mode\n6. **Add OAuth**: Guide user to register at developer.getbodhi.app\n7. **Implement chat**: Create streaming chat interface\n8. **Handle errors**: Add proper error boundaries and user feedback\n\nWhen troubleshooting:\n\n1. **Check connection status**: Use `isOverallReady`, `isReady`, `isServerReady`\n2. **Verify auth state**: Check `isAuthenticated`, `auth` object\n3. **Inspect logs**: Look for `[Bodhi\u002FWeb]` prefixed logs in console\n4. **Review config**: Verify authClientId, redirectUri, basePath\n5. **Test backend**: Ensure local server at http:\u002F\u002Flocalhost:1135\n6. **Check extension**: Verify Bodhi Browser extension installed\n\n## Testing Integration\n\nAfter integration, verify:\n\n1. **Extension detection**: Check console for `[Bodhi\u002FWeb] Extension detected`\n2. **Server connection**: Verify `[Bodhi\u002FWeb] Server ready`\n3. **Setup flow**: Click \"Open Setup\" to test modal\n4. **Authentication**: Click \"Login\" to test OAuth flow\n5. **Model loading**: Verify models populate in dropdown\n6. **Streaming**: Send message and verify real-time response streaming\n7. **Error handling**: Test with server offline to verify error states\n\n## Common Integration Tasks\n\nWhen user requests:\n\n- **\"Add bodhi to my React app\"** → Follow basic integration steps\n- **\"Setup OAuth for bodhi\"** → Guide to oauth-setup.md, developer.getbodhi.app\n- **\"Deploy to GitHub Pages\"** → Reference github-pages.md for basePath and 404 hack\n- **\"Chat not working\"** → Check troubleshooting.md for connection\u002Fauth issues\n- **\"Models not loading\"** → Verify server, check models endpoint, review async iteration\n- **\"Streaming broken\"** → Verify stream:true, check AsyncGenerator pattern\n\n## Key Files to Reference\n\nWhen implementing integration:\n\n- `bodhi-js-sdk\u002Fdocs\u002Fquick-start.md` - Primary integration guide\n- `bodhi-js-sdk\u002Fdocs\u002Freact-integration.md` - React-specific patterns\n- `bodhi-js-sdk\u002Fdocs\u002F` - Comprehensive documentation and examples\n\n## Notes\n\n- Focus on React+Vite projects only\n- Always use `@bodhiapp\u002Fbodhi-js-react` preset package (simplest)\n- Auto-callback handling is enabled by default (`handleCallback={true}`)\n- OAuth callback happens automatically without custom routes\n- Default backend: http:\u002F\u002Flocalhost:1135\n- Extension mode is preferred over direct mode\n- AsyncGenerator pattern for streaming (OpenAI-compatible)\n",{"name":5,"description":49,"allowed-tools":50},"Integrate React+Vite web apps with bodhi-js-sdk for local LLM integration. Use when user asks to: \"integrate bodhi\", \"add bodhi sdk\", \"connect to bodhi\", \"setup bodhi provider\", \"bodhi react integration\", \"deploy bodhi to github pages\", or troubleshoot bodhi-js-sdk connection\u002Fauth issues.\n","Read, Grep, Glob, Edit, Write, Bash(npm:*), Bash(npx:*)","main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Faiskillstore\u002Fmarketplace\u002Fbodhisearch\u002Fbodhi-sdk-react-integration",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":61,"prompt":71,"zipAvailable":72},[62,65,68],{"tool":63,"command":64},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill bodhi-sdk-react-integration",{"tool":66,"command":67},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill bodhi-sdk-react-integration",{"tool":69,"command":70},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill bodhi-sdk-react-integration","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fbodhisearch\u002Fbodhi-sdk-react-integration\u002FSKILL.md",true,[74,80,84,88,92,96,100],{"path":75,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":78,"sourceHtmlUrl":79},"agents\u002Fopenai.yaml","REFERENCE","参考文档",121,"https:\u002F\u002Fgithub.com\u002FHigorAlves\u002Forc\u002Fblob\u002Fmain\u002Fcodex\u002Forc\u002Fskills\u002Forc-receiving-code-review\u002Fagents\u002Fopenai.yaml",{"path":81,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":82,"sourceHtmlUrl":83},"code-examples.md",17244,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fbodhisearch\u002Fbodhi-sdk-react-integration\u002Fcode-examples.md",{"path":85,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":86,"sourceHtmlUrl":87},"github-pages.md",6530,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fbodhisearch\u002Fbodhi-sdk-react-integration\u002Fgithub-pages.md",{"path":89,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":90,"sourceHtmlUrl":91},"oauth-setup.md",8936,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fbodhisearch\u002Fbodhi-sdk-react-integration\u002Foauth-setup.md",{"path":93,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":94,"sourceHtmlUrl":95},"quick-start.md",6236,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fbodhisearch\u002Fbodhi-sdk-react-integration\u002Fquick-start.md",{"path":97,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":98,"sourceHtmlUrl":99},"skill-report.json",53587,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fbodhisearch\u002Fbodhi-sdk-react-integration\u002Fskill-report.json",{"path":101,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":102,"sourceHtmlUrl":103},"troubleshooting.md",11898,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fbodhisearch\u002Fbodhi-sdk-react-integration\u002Ftroubleshooting.md",[105,117,127,134,140,147],{"skillNo":106,"name":107,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":108,"categories":109,"keywords":113,"publishedAt":44},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[110],{"categoryNo":111,"categoryName":112,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[114,115,116],"model","model hierarchy","model hierarchy skill",{"skillNo":118,"name":119,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":120,"categories":121,"keywords":123,"publishedAt":44},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[122],{"categoryNo":111,"categoryName":112,"taxonomyType":21,"taxonomyTypeDesc":22},[124,125,126],"MCP","zoom","zoom mcp",{"skillNo":128,"name":129,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":130,"categories":131,"keywords":132,"publishedAt":44},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[27,133,30,125],"zoom apps sdk",{"skillNo":135,"name":136,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":137,"categories":138,"keywords":139,"publishedAt":44},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[136],{"skillNo":141,"name":142,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":143,"categories":144,"keywords":146,"publishedAt":44},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[145],{"categoryNo":111,"categoryName":112,"taxonomyType":21,"taxonomyTypeDesc":22},[142],{"skillNo":148,"name":149,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":150,"categories":151,"keywords":152,"publishedAt":44},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[149]]