Integration Guide
Everything you need to integrate the Pernoex cognition layer into your product
Pernoex offers three UI integration modes: Floating for a chat bubble experience,Embedded for custom layouts where you want the intelligence always visible, and Inline Explainer for contextual "Ask AI" functionality on text selection that opens an AI sidebar with full conversational support. (For IDE integration, see our MCP documentation).
Floating Mode
The default integration. A floating interface appears in the corner of your page that users can click to open and close.
Basic Setup
Add this before the closing </body> tag:
<script src="https://cdn.pernoex.com/widget.js"data-api-key="YOUR_API_KEY"></script>
Custom API URL
For self-hosted instances:
<script src="https://cdn.pernoex.com/widget.js"data-api-key="YOUR_API_KEY"data-api-url="https://your-api.example.com/api/v1"></script>
Live Preview
Click the chat bubble to preview
Embedded Mode
Render the intelligence inside a specific container. Perfect for dedicated help pages, sidebars, or custom layouts.
height (or flex/grid sizing). The widget will automatically set position: relative on it if needed. If the container has zero height, a console warning will help you diagnose the issue.Setup
<div id="chat-container" style="height: 600px;"></div><script src="https://cdn.pernoex.com/widget.js"data-manual-init="true"></script><script>Pernoex.init({apiKey: 'YOUR_API_KEY',mode: 'embedded',container: '#chat-container',showHeader: true,borderRadius: '12px'});</script>
Live Preview
Embedded mode - always visible
Configuration Options
Inline Explainer Mode
Add contextual AI explanations to any text in your product. Users can highlight text and click "Ask AI" to get explanations grounded in your knowledge base in a sidebar that pushes page content, keeping the reading context visible.
Basic Setup
<!-- Inline explainer is controlled from your dashboard settings --><!-- Once enabled, it works automatically with text selection --><script src="https://cdn.pernoex.com/widget.js"data-api-key="YOUR_API_KEY"></script>
How It Works
Our RAG pipeline ensures accurate responses grounded in your documentation.
Click "RAG pipeline" above to preview
Advanced Configuration
// For programmatic control, you can override dashboard settingsPernoex.init({apiKey: 'YOUR_API_KEY',inline: {enabled: true, // Override dashboard settingtriggerSelector: 'article, .docs-content', // Only in these areasexcludeSelector: 'nav, .code-block', // Not in these areasposition: 'auto', // 'auto' | 'above' | 'below'showFollowUp: true, // Allow follow-up questionstheme: 'auto' // Match system theme}});
Configuration Options
Combined with Floating Integration
The inline explainer works alongside your main floating integration automatically. When enabled from the dashboard, both the floating interface and text selection explainer are available to your users:
// Inline explainer works alongside the floating widget automatically// Just enable it in your dashboard settings, or override via init:Pernoex.init({apiKey: 'YOUR_API_KEY',inline: {enabled: true // Works alongside the floating chat widget}});
Theme Customization
Fully customize the widget's visual appearance to match your brand. Pass a theme object with any combination of color overrides. All properties are optional — only specify what you want to change.
Example: Transparent Background (No Avatars)
Pernoex.init({apiKey: 'YOUR_API_KEY',showAvatars: false,theme: {background: 'transparent',assistantBubbleBg: '#f0f1f3',assistantTextColor: '#1a1d23',userBubbleBg: '#0f172a',userTextColor: '#ffffff',linkColor: '#0f172a',inputBg: '#ffffff',inputBorderColor: '#e5e7eb',inputTextColor: '#1a1d23',inputPlaceholderColor: '#9ca3af',sendButtonBg: '#0f172a',sendButtonColor: '#ffffff',fontFamily: '"Inter", sans-serif'}});
Example: Dark Theme
Pernoex.init({apiKey: 'YOUR_API_KEY',theme: {background: '#1a1a2e',assistantBubbleBg: '#16213e',assistantTextColor: '#e2e8f0',userBubbleBg: '#0f172a',userTextColor: '#ffffff',linkColor: '#818cf8',inputBg: '#16213e',inputBorderColor: '#2a2a4a',inputTextColor: '#e2e8f0',inputPlaceholderColor: '#64748b',sendButtonBg: '#0f172a',sendButtonColor: '#ffffff'}});
Theme Properties
Common Use Cases
Full-Page Intelligence
Create a dedicated support or chat page:
<!DOCTYPE html><html><head><style>body, html { margin: 0; height: 100%; }#chat-container { height: 100vh; width: 100%; }</style></head><body><div id="chat-container"></div><script src="https://cdn.pernoex.com/widget.js"data-manual-init="true"></script><script>Pernoex.init({apiKey: 'YOUR_API_KEY',mode: 'embedded',container: '#chat-container'});</script></body></html>
Embedded mode - always visible
Sidebar Integration
Add a persistent intelligence sidebar to your application:
<div style="display: flex; height: 100vh;"><main style="flex: 1;"><!-- Your main content --></main><aside id="chat-sidebar"style="width: 400px; border-left: 1px solid #e5e7eb;"></aside></div><script src="https://cdn.pernoex.com/widget.js"data-manual-init="true"></script><script>Pernoex.init({apiKey: 'YOUR_API_KEY',mode: 'embedded',container: '#chat-sidebar',showHeader: true});</script>
Sidebar integration layout
JavaScript API Reference
Control Methods
Available for floating mode:
// Open the chat windowPernoex.open()// Close the chat windowPernoex.close()// Toggle open/closed statePernoex.toggle()// Check if widget is currently openconst isOpen = Pernoex.isOpen()// Clear all messages and start a new conversationPernoex.clearMessages()// Get explanation for text programmaticallyconst explanation = await Pernoex.explain("text to explain")// Destroy the widget and clean upPernoex.destroy()
Event Callbacks
Listen to integration lifecycle events:
Pernoex.init({apiKey: 'YOUR_API_KEY',onReady: () => {console.log('Widget is ready');},onOpen: () => {console.log('Widget opened');},onClose: () => {console.log('Widget closed');},onMessage: (message) => {console.log('New message:', message);},onError: (error) => {console.error('Widget error:', error);},onExplain: (text, explanation) => {console.log('Explained:', text, explanation);},onExplainError: (error) => {console.error('Explain error:', error);}});
Framework Examples
import { useEffect, useRef } from 'react';function ChatWidget() {const containerRef = useRef(null);useEffect(() => {const script = document.createElement('script');script.src = 'https://cdn.pernoex.com/widget.js';script.setAttribute('data-manual-init', 'true');document.body.appendChild(script);script.onload = () => {window.Pernoex.init({apiKey: 'YOUR_API_KEY',mode: 'embedded',container: containerRef.current,showHeader: true});};return () => {window.Pernoex?.destroy();document.body.removeChild(script);};}, []);return <div ref={containerRef} style={{ height: '600px' }} />;}
Security & Best Practices
- Your page's CSS cannot affect the widget, and widget styles cannot leak into your page
- Host-page scripts cannot access widget DOM internals (e.g. conversation messages)
- Styles are injected via
adoptedStyleSheets— nostyle-src 'unsafe-inline'CSP directive needed - Fonts are loaded via
@font-faceinside the shadow root — no<link>tags are injected into your<head>
Domain Restrictions
Domain restrictions control which sites are allowed to load your widget. The behavior depends on whether you have configured any allowed domains yet:
- No domains configured (default): The API key is open — any origin is accepted, including
localhost. This lets you develop and test locally without any extra setup, and allows third-party integrators to get started immediately. - At least one domain configured: Enforcement becomes strict. Only the exact domains you have listed are accepted.
localhostis not automatically allowed once you lock down your project.
localhost or your specific local port (e.g. localhost:3000) to your project's allowed domains list. Remove it before going to production.Content Security Policy
If you use CSP headers, allow the following. Note that style-src 'unsafe-inline' is not required — the widget uses Shadow DOM with adoptedStyleSheets for CSP-compliant style injection.
script-src 'self' https://cdn.pernoex.com;connect-src 'self' https://api.pernoex.com;style-src 'self';img-src 'self' https:;font-src 'self' data: https://fonts.gstatic.com;frame-src 'none';object-src 'none';base-uri 'self';
Troubleshooting
Widget Blocked After Adding Domain Restrictions
- Once you add at least one allowed domain,
localhostis no longer automatically permitted - Add
localhostorlocalhost:PORT(e.g.localhost:3000) to your allowed domains list for local development - Remove
localhostfrom your allowed domains before deploying to production if you want to restrict access to your live domains only - The browser console error will show
origin_not_allowedwhen this occurs
Integration Not Appearing
- Verify your API key is correct
- If you have configured allowed domains, check that the current domain is in the list
- If no domains are configured yet, the widget accepts all origins automatically
- Open browser console for errors
- Ensure the script tag is loaded (check the Network tab)
Embedded Mode Container Empty
- Confirm container element exists before calling
Pernoex.init() - Set an explicit height on the container element
- Check console for initialization errors
- Verify
data-manual-init="true"is set on the script tag
Styling Issues
- The widget renders inside a Shadow DOM boundary, so host page styles cannot affect it and widget styles cannot leak into your page
- Ensure the container has defined dimensions for embedded mode
- If the widget appears behind other elements, use the
zIndexoption inPernoex.init()to adjust the stacking order
Inline Explainer Not Appearing
- Verify inline explainer is enabled in your project's Settings page on the dashboard
- Check
triggerSelectormatches elements on your page - Ensure selection is at least 3 characters
- Check console for JavaScript errors
- Verify the selected area is not excluded by
excludeSelector
Explanations Are Generic
- Ensure your knowledge base is indexed (check Crawl status in dashboard)
- The selected text should relate to content in your indexed pages
- Try selecting more specific terms from your documentation
- Check that your project has sufficient credit balance
Sidebar Not Pushing Content
- The sidebar pushes page content to the left when it opens
- Ensure your page layout can accommodate the sidebar width
- Check for fixed positioning elements that may not respond to the push
Need Help?
Have questions about integration? Reach out to our support team at [email protected] or explore more examples in your dashboard.