Overview

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

Text Selection & Context

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.

How It Works

  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

Automatic Context Sharing

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

Example Use Cases

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

Controlling Text Selection Areas

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>

Fullscreen Mode

The widget supports fullscreen mode for immersive chat experiences.

Automatic Fullscreen

Fullscreen is automatically activated on:

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

Manual Fullscreen Control

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

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

Fullscreen Behavior

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

Responsive Breakpoints

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

Page Context Integration

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

Context Extraction

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)"
}

How Content is Cleaned

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

Example Context Usage

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.

Privacy Considerations

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

Custom Events

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

Event Format

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);
});

Common Events

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);
});

Event-Driven Integrations

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";
});

Responsive Behavior

The widget automatically adapts to different screen sizes and orientations.

Mobile Optimization

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)

Tablet Behavior

On tablets (768px - 1024px):

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

Desktop Experience

On desktop (≥ 1024px):

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

Orientation Changes

The widget automatically handles orientation changes:

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

Sidepanel Integration

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

Content Push Behavior

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

Multiple Containers

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.

Custom Transition

The widget applies default transitions, but you can customize:

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

Performance Optimization

The widget is designed for optimal performance:

Async Loading

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);

Lazy Initialization

The iframe content only loads when:

  • Widget is initialized
  • User first interacts with the widget

Resource Optimization

  • Small bundle size: ~10KB gzipped
  • No external dependencies: Self-contained
  • Efficient event handlers: Debounced and throttled
  • Smart content extraction: Only when needed

Security Best Practices

Publishable Key Protection

// ✅ 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!
});

Content Security Policy (CSP)

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;

Cross-Origin Communication

The widget uses secure postMessage communication:

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

Advanced Customization Examples

Custom Trigger Button

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>

Conditional Loading

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,
  });
}

Theme Synchronization

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"],
});

Troubleshooting

Text Selection Not Working

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"));

Fullscreen Issues

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;
}

Content Not Being Captured

Check:

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

Next Steps

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