Skip to content

Advanced Features

The SiteAssist widget includes several advanced features that enhance user experience and provide powerful capabilities beyond basic chat functionality.

One of the most powerful features of the SiteAssist widget is intelligent text selection. Users can select any text on your page and instantly ask the AI about it.

  1. User selects text on your page
  2. An “Ask AI” popover appears near the selection
  3. User clicks the popover
  4. Widget opens with the selected text as context
  5. AI provides answers based on both the selection and page content

The widget automatically shares page context with the AI, including:

  • Page URL: Current page location
  • Page Title: Document title
  • Page Content: Cleaned, text-only version of page content
  • Selected Text: Any text the user has selected (if using text selection feature)

What gets filtered out:

  • Scripts and styles
  • Navigation menus
  • Advertisements
  • SVG and canvas elements
  • Media elements (audio, video, images)
  • The widget itself

Documentation Sites:

User selects: "To install the package, run npm install..."
User asks: "What does this command do?"
AI responds with context-aware explanation

E-commerce:

User selects: "Free shipping on orders over $50"
User asks: "How can I get free shipping?"
AI explains the shipping policy

Blog Posts:

User selects: "The React useEffect hook..."
User asks: "Can you explain this in simpler terms?"
AI provides a simplified explanation

You can control where the text selection popover appears using the contentSelector option:

// Default - only show popover in .main-content elements
SiteAssist("init", {
apiKey: "YOUR_API_KEY",
contentSelector: true, // or omit entirely
});
// Show popover everywhere on the page
SiteAssist("init", {
apiKey: "YOUR_API_KEY",
contentSelector: false,
});
// Custom selector - only in specific areas
SiteAssist("init", {
apiKey: "YOUR_API_KEY",
contentSelector: ".main-content",
});

Use Cases:

  • Documentation sites: Restrict to main content areas to avoid confusion
  • Blogs: Only allow selection in article content, not navigation
  • E-commerce: Enable everywhere for product help and support
  • Admin panels: Restrict to specific content sections

Example HTML Structure:

<div class="sidebar">
<nav>
<a href="/">Home</a>
<a href="/docs">Docs</a>
</nav>
<!-- Text selection popover won't appear here -->
</div>
<div class="main-content">
<h1>Documentation</h1>
<p>Select this text to ask AI questions!</p>
<!-- Text selection popover WILL appear here -->
</div>

The widget supports fullscreen mode for immersive chat experiences.

Fullscreen is automatically activated on:

  • Mobile devices (screens < 768px wide)
  • When user clicks fullscreen button in widget

The widget can request fullscreen mode, which is handled automatically:

// Widget internally handles fullscreen requests
// No manual API needed - users control via widget UI

When fullscreen is active:

  • Widget expands to fill entire viewport
  • Page scrolling is disabled (overflow: hidden)
  • Close button remains accessible
  • User can exit via close button or ESC key

When fullscreen exits:

  • Widget returns to normal size
  • Page scrolling is restored
  • Content layout returns to normal

The widget uses these breakpoints:

  • Mobile (< 768px): Always fullscreen when open
  • Tablet (768px - 1024px): Standard size, can go fullscreen
  • Desktop (≥ 1024px): Standard size with sidepanel support

The widget automatically integrates with your page content to provide intelligent, context-aware responses.

When a user interacts with the widget, it automatically extracts:

{
url: "https://yoursite.com/page",
title: "Page Title",
content: "Clean text content of the page...",
textSelection: "Selected text (if any)"
}

The widget intelligently cleans page content:

  1. Clone the document body
  2. Remove unnecessary elements (scripts, styles, widgets, media)
  3. Extract link text and URLs: "Link Text (https://url)"
  4. Normalize whitespace and line breaks
  5. Send to AI for context-aware responses

User visits: https://example.com/pricing

// Automatically shared context:
{
url: "https://example.com/pricing",
title: "Pricing - Example SaaS",
content: "Pricing Plans Starter $9/month Basic features... Pro $29/month Advanced features...",
textSelection: null
}

User asks: “What’s included in the Pro plan?”

AI receives context and can answer specifically about that page’s Pro plan.

What is shared:

  • Publicly visible text content
  • Page URL and title
  • User’s selected text

What is NOT shared:

  • Form inputs or user data
  • Hidden or password-protected content
  • Images or media files
  • Scripts or technical implementation

The widget dispatches custom events that you can listen to for advanced integrations.

All custom events are prefixed with sa: and dispatched on the window object:

window.addEventListener("sa:event_name", (event) => {
console.log("Event detail:", event.detail);
});

While specific events depend on your widget configuration, you can listen for custom events:

// Example: Listen for chat opened
window.addEventListener("sa:opened", () => {
console.log("Chat widget opened");
// Track in your analytics
analytics.track("Chat Opened");
});
// Example: Listen for chat closed
window.addEventListener("sa:closed", () => {
console.log("Chat widget closed");
});
// Example: Custom event from widget
window.addEventListener("sa:custom_action", (event) => {
console.log("Custom action:", event.detail);
});

Example - Track widget usage:

let chatOpenedAt = null;
window.addEventListener("sa:opened", () => {
chatOpenedAt = Date.now();
console.log("Chat opened");
});
window.addEventListener("sa:closed", () => {
if (chatOpenedAt) {
const duration = Date.now() - chatOpenedAt;
console.log(`Chat was open for ${duration}ms`);
chatOpenedAt = null;
}
});

Example - Trigger other UI elements:

window.addEventListener("sa:opened", () => {
// Hide other overlays when chat opens
document.getElementById("promo-banner").style.display = "none";
});
window.addEventListener("sa:closed", () => {
// Show promo banner again
document.getElementById("promo-banner").style.display = "block";
});

The widget automatically adapts to different screen sizes and orientations.

On mobile devices (< 768px):

  • Widget opens in fullscreen
  • “Ask AI” text selection popover is optimized for touch
  • Page scrolling disabled when widget is open
  • Swipe gestures for closing (native behavior)

On tablets (768px - 1024px):

  • Standard widget size
  • Sidepanel floats over content (doesn’t push)
  • Touch-optimized interactions
  • Responsive text selection

On desktop (≥ 1024px):

  • Full-featured experience
  • Sidepanel can push content
  • Keyboard shortcuts work
  • Precise text selection

The widget automatically handles orientation changes:

// Automatically handled - no code needed
// Widget adjusts when device rotates

The sidepanel widget type offers advanced integration with your application layout.

When sidepanel opens on desktop screens:

SiteAssist("init", {
apiKey: "YOUR_API_KEY",
type: "sidepanel",
appContainerSelector: ".main-content",
});

Desktop (≥ 1024px):

  • Container margin-right: 400px
  • Smooth transition animation
  • Content visible alongside widget

Tablet (< 1024px):

  • Sidepanel floats over content
  • No content push
  • Overlay-style interaction

Mobile (< 768px):

  • Full-screen mode
  • Content hidden while widget open

You can have multiple containers that respond to the sidepanel:

<div class="siteassist-container">
<header>Header content</header>
</div>
<div class="siteassist-container">
<main>Main content</main>
</div>
<div class="siteassist-container">
<footer>Footer content</footer>
</div>

All containers with the class will be pushed when the sidepanel opens.

The widget applies default transitions, but you can customize:

.siteassist-container {
transition-property: margin-right;
transition-duration: 200ms;
transition-timing-function: ease-in-out;
}

The widget is designed for optimal performance:

The widget loads asynchronously and doesn’t block page rendering:

// Non-blocking async load
o.async = !0;
o.src = "https://...widget.js";
e.head.appendChild(o);

The iframe content only loads when:

  • Widget is initialized
  • User first interacts with the widget
  • Small bundle size: ~10KB gzipped
  • No external dependencies: Self-contained
  • Efficient event handlers: Debounced and throttled
  • Smart content extraction: Only when needed

// ✅ Good - use environment variables
SiteAssist("init", {
apiKey: process.env.NEXT_PUBLIC_SITEASSIST_PUBLISHABLE_KEY,
});
// ❌ Avoid - hardcoded keys in public repos
SiteAssist("init", {
apiKey: "pk_live_hardcoded_key_123", // Don't commit this!
});

If you use CSP headers, whitelist these domains:

script-src 'self' https://cnrib24ur3hk4b49.public.blob.vercel-storage.com;
frame-src https://widgets.siteassist.io;
connect-src https://api.siteassist.io;

The widget uses secure postMessage communication:

// Widget validates message origin
if (ev.origin !== options.widgetUrl) return;

Replace default floating button with your own:

<style>
.sa-button {
display: none !important;
} /* Hide default button */
</style>
<button id="custom-chat-button" class="your-button-class">
💬 Chat with AI
</button>
<script>
SiteAssist("init", {
apiKey: "YOUR_API_KEY",
type: "floating-bubble",
});
document
.getElementById("custom-chat-button")
.addEventListener("click", () => {
SiteAssist("toggle");
});
</script>

Load widget only for specific pages or users:

// Only load on product pages
if (window.location.pathname.startsWith("/products/")) {
SiteAssist("init", {
apiKey: "YOUR_API_KEY",
});
}
// Only for logged-in users
if (userIsLoggedIn) {
SiteAssist("init", {
apiKey: "YOUR_API_KEY",
externalId: currentUser.id,
});
}

Sync widget theme with your site:

// Watch for site theme changes
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === "data-theme") {
const theme = document.documentElement.getAttribute("data-theme");
SiteAssist("changeTheme", theme);
}
});
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-theme"],
});

Check:

  • Widget is properly initialized
  • No conflicting JavaScript on the page
  • Browser supports Selection API
  • Content selector is correctly configured

Debug:

window.addEventListener("mouseup", () => {
const selection = window.getSelection();
console.log("Selected text:", selection.toString());
// Check if selection is in the right area
const range = selection.getRangeAt(0);
const container = range.commonAncestorContainer;
console.log("Selection container:", container);
});

Common Issues:

  • Popover not appearing: Check your contentSelector setting
  • Popover appears in wrong areas: Verify your CSS selector is correct
  • Selection not working at all: Ensure widget is properly initialized

Example debugging:

// Test if contentSelector is working
SiteAssist("init", {
apiKey: "YOUR_API_KEY",
contentSelector: ".test-area", // Test with a specific area
});
// Check if the selector exists
console.log("Content element found:", document.querySelector(".test-area"));

Common causes:

  • Browser restrictions on fullscreen
  • Conflicting CSS overflow properties
  • Z-index conflicts

Solution:

/* Ensure widget has high z-index */
.sa-frame.fullscreen {
z-index: 999999 !important;
}

Check:

  • Page content isn’t in an iframe
  • Content is rendered (not dynamically loaded after widget init)
  • No security restrictions

Need help with advanced features? Contact us at support@siteassist.io