npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

cuoral-ionic

v0.1.1

Published

Cuoral Ionic Framework Library - silent churn prevention platform for Ionic apps. Provides screen recording, user interaction capture, and support widget integration for enhanced customer support experiences.

Readme

Cuoral Ionic Library

Proactive customer success platform integration for Ionic/Capacitor applications. Cuoral provides support ticketing, customer intelligence, screen recording, and comprehensive customer engagement tools.

Features

Customer Support - Integrated support ticketing system
Customer Intelligence - Track and analyze customer behavior automatically
Native Crash Tracking - Capture native Android & iOS crashes
Screen Recording - Native screen recording for issue reproduction
Modal Display - Full-screen modal with floating chat button
TypeScript Support - Full type definitions included
Simple API - Just pass your public key and optional user info
Zero Configuration - Everything handled automatically

Installation

npm install cuoral-ionic

After installing, sync your Capacitor project:

npx cap sync

Platform Setup

Android Setup

The Android plugin automatically declares required permissions. Ensure your AndroidManifest.xml includes:

<!-- Required for support widget and intelligence tracking -->
<uses-permission android:name="android.permission.INTERNET" />

<!-- Required for screen recording with audio -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />

<!-- Required for screen recording on Android 9 and below -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                 android:maxSdkVersion="28" />

Note: These permissions are automatically added by Capacitor when you run npx cap sync android. The INTERNET permission is auto-granted by the system. RECORD_AUDIO requires user approval at runtime.

iOS Setup

Add to your Info.plist:

<!-- Required for screen recording with audio -->
<key>NSMicrophoneUsageDescription</key>
<string>We need access to your microphone to record audio with screen recordings</string>

Note: Screen recording permission is requested automatically by iOS when recording starts. No additional permissions are needed for crash tracking or intelligence features.

📱 Google Play Store Declaration (Android Only)

IMPORTANT: When submitting your app to Google Play Store, you'll be asked to declare why your app uses FOREGROUND_SERVICE_MEDIA_PROJECTION (screen recording permission).

What to Do:

  1. Go to Play Console → PolicyApp ContentForeground Service Types
  2. Find FOREGROUND_SERVICE_MEDIA_PROJECTION → Click Manage
  3. Select category: Customer Support / Bug Reporting
  4. Provide justification:
Our app uses screen recording exclusively for customer support purposes when users 
initiate support requests. Users explicitly control when recording starts/stops via 
the support widget. All recordings are transmitted securely to our support platform 
to help resolve user-reported issues. No recording occurs without user action.

Why This is Required:

  • Screen recording is a "sensitive permission" that requires declaration
  • Google wants to ensure it's used appropriately (which it is - user-initiated support)
  • This is a simple form, not a special approval process
  • No code changes needed - your app is already configured correctly

Note: iOS App Store does not require this declaration.

Quick Start

Option 1: Modal with Floating Button (Recommended)

The easiest way to integrate Cuoral - displays a floating chat button that opens the widget in a full-screen modal.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Cuoral } from 'cuoral-ionic';

@Component({
  selector: 'app-home',
  template: `
    <ion-content>
      <!-- Your app content -->
      <h1>Welcome to Support</h1>

      <!-- Optional: Custom button to open widget -->
      <ion-button (click)="openSupport()"> Contact Support </ion-button>
    </ion-content>
  `,
})
export class HomePage implements OnInit, OnDestroy {
  private cuoral: Cuoral;

  constructor() {
    this.cuoral = new Cuoral({
      publicKey: 'your-public-key-here',
      email: '[email protected]', // Optional
      firstName: 'John', // Optional
      lastName: 'Doe', // Optional
      showFloatingButton: true, // Show floating chat button
      useModal: true, // Use modal display mode
    });
  }

  ngOnInit() {
    this.cuoral.initialize();
  }

  ngOnDestroy() {
    this.cuoral.destroy();
  }

  // Open modal programmatically
  openSupport() {
    this.cuoral.openModal();
  }
}

Features:

  • 🎈 Floating blue chat button in bottom-right corner
  • 📱 Opens widget in full-screen modal with small margins
  • ❌ Close button in top-right
  • 🎨 Smooth animations and transitions
  • 🖱️ Click backdrop to close

Option 2: Embedded Iframe - Not Recommended

For more control over widget placement:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { Cuoral } from 'cuoral-ionic';

@Component({
  selector: 'app-support',
  template: `
    <ion-content>
      <iframe [src]="widgetUrl" style="width: 100%; height: 600px; border: none;"> </iframe>
    </ion-content>
  `,
})
export class SupportPage implements OnInit, OnDestroy {
  widgetUrl: SafeResourceUrl;
  private cuoral: Cuoral;

  constructor(private sanitizer: DomSanitizer) {
    this.cuoral = new Cuoral({
      publicKey: 'your-public-key-here',
      useModal: false, // Disable modal mode
    });

    const url = this.cuoral.getWidgetUrl();
    this.widgetUrl = this.sanitizer.bypassSecurityTrustResourceUrl(url);
  }

  ngOnInit() {
    this.cuoral.initialize();
  }

  ngOnDestroy() {
    this.cuoral.destroy();
  }
}

Enable Page View Tracking (One-Time Setup)

To automatically track page views, add router tracking to your app.component.ts (or main app component):

import { Component } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { Cuoral } from 'cuoral-ionic';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
})
export class AppComponent {
  private cuoral: Cuoral;

  constructor(private router: Router) {
    this.cuoral = new Cuoral({
      publicKey: 'your-public-key-here',
      email: '[email protected]',
      firstName: 'John',
      lastName: 'Doe',
    });
  }

  async ngOnInit() {
    // Initialize Cuoral (intelligence auto-enabled from dashboard)
    await this.cuoral.initialize();

    // Track all page navigation automatically
    this.router.events.subscribe(event => {
      if (event instanceof NavigationEnd) {
        this.cuoral.trackPageView(event.url);
      }
    });
  }
}

That's it! All page views are now automatically tracked. Console errors, network errors, and native crashes are tracked with zero additional setup.

User Logout & Session Management

When users log out, properly clear and end the session before destroying the Cuoral instance:

async logout() {
  // Step 1: Clear and end the session
  await this.cuoral.clearSession();

  // Step 2: Destroy the Cuoral instance
  this.cuoral.destroy();

  // Step 3: Your logout logic
  // Navigate to login, clear user data, etc.
}

When a different user logs in on the same device, create a fresh Cuoral instance:

async login(email: string, firstName: string, lastName: string) {
  // Create new instance for the new user
  this.cuoral = new Cuoral({
    publicKey: 'your-public-key-here',
    email: email,
    firstName: firstName,
    lastName: lastName,
  });

  // Initialize for new user
  await this.cuoral.initialize();
}

Important Notes:

  • clearSession() marks the session as ended on the backend
  • destroy() cleans up local resources
  • Always call clearSession() before destroy() on logout
  • Create a new Cuoral instance for each user login
  • Email, first name, and last name are all required for identified users

Push Notifications

Cuoral integrates seamlessly with your existing push notification system using webhooks. When an agent sends a message, Cuoral sends a webhook to your backend, allowing you to send push notifications to users when the widget is closed.

How it works:

  1. Track widget state in your app (open/closed)
  2. Configure webhooks in your Cuoral dashboard
  3. Receive webhook when agent sends message
  4. Send FCM/APNs push if widget is closed

Example Implementation:

export class MyApp {
  private isCuoralWidgetOpen = false;

  // When user opens support
  openSupport() {
    this.isCuoralWidgetOpen = true;
    this.cuoral.openModal();
  }

  // When user closes support
  closeSupport() {
    this.isCuoralWidgetOpen = false;
    this.cuoral.closeModal();
  }

  // Handle incoming push notifications (your existing FCM handler)
  async handlePushNotification(message: any) {
    if (message.data.type === 'cuoral_message') {
      // Don't show notification if widget is already open
      if (this.isCuoralWidgetOpen) {
        return; // User already sees the message
      }

      // Show notification to user
      await this.showNotification({
        title: 'New message from Support',
        body: message.data.text,
      });

      // When user taps notification, open widget
      this.openSupport();
    }
  }
}

Setup:

  1. Configure your webhook URL in Cuoral Dashboard → Settings → Webhooks
  2. Cuoral sends webhooks for all new messages
  3. Your backend decides whether to send push based on your app's state
  4. User taps notification → your app calls cuoral.openModal()

This approach works with your existing FCM/APNs setup - no additional SDK configuration needed!

Configuration

CuoralOptions

interface CuoralOptions {
  publicKey: string; // Required: Your Cuoral public key
  email?: string; // Optional: User email
  firstName?: string; // Optional: User first name
  lastName?: string; // Optional: User last name
  debug?: boolean; // Optional: Enable debug logging (default: false)
  widgetBaseUrl?: string; // Optional: Custom widget URL (default: CDN)
  showFloatingButton?: boolean; // Optional: Show floating chat button (default: true)
  useModal?: boolean; // Optional: Use modal display mode (default: true)
  primaryColor?: string; // Optional: Custom primary color (auto-fetched from backend if not provided)
}

Branding & Colors

The floating button automatically uses your organization's brand color configured in the Cuoral dashboard. During initialize(), the SDK fetches your organization's configuration from the backend:

// Automatic (Recommended):
const cuoral = new Cuoral({ publicKey: 'your-key' });
await cuoral.initialize(); // Fetches your brand color from backend

// Manual override (Optional):
const cuoral = new Cuoral({
  publicKey: 'your-key',
  primaryColor: '#FF5733', // Force specific color
});
await cuoral.initialize();

How it works:

  • SDK fetches session configuration (POST /conversation/session/get) during initialization
  • Extracts color from configuration object in response
  • Applies color to floating button background and shadows
  • Falls back to default blue (#007AFF) if color not provided

Configure your brand color: Go to Cuoral Dashboard → Settings → Branding → Primary Color

Widget URL

By default, the widget loads from https://js.cuoral.com/mobile.html (production CDN).

Production (Default):

new Cuoral({ publicKey: 'your-key' });

Custom URL (Optional):

If you need to use a custom widget URL for testing or self-hosting:

new Cuoral({
  publicKey: 'your-key',
  widgetBaseUrl: 'https://your-domain.com/mobile.html',
});

API Reference

Cuoral Methods

// Initialize Cuoral
cuoral.initialize(): Promise<void>

// Track page/screen view (for intelligence)
cuoral.trackPageView(screen: string, metadata?: any): void

// Track error manually (for intelligence)
cuoral.trackError(message: string, stackTrace?: string, metadata?: any): void

// Start native screen recording programmatically
cuoral.startRecording(): Promise<boolean>

// Stop native screen recording programmatically
cuoral.stopRecording(): Promise<{filePath?: string; duration?: number} | null>

// Get widget URL for iframe embedding
cuoral.getWidgetUrl(): string

// Open modal programmatically
cuoral.openModal(): void

// Close modal programmatically
cuoral.closeModal(): void

// Check if modal is open
cuoral.isModalOpen(): boolean

// Clear and end current session (call before logout)
cuoral.clearSession(): Promise<void>

// Clean up resources
cuoral.destroy(): void

Programmatic Screen Recording

You can trigger native screen recording programmatically from your app code:

async startUserRecording() {
  const started = await this.cuoral.startRecording();
  if (started) {
    console.log('Recording started successfully');
    this.isRecording = true;
  } else {
    console.error('Failed to start recording');
  }
}

async stopUserRecording() {
  const result = await this.cuoral.stopRecording();
  if (result) {
    console.log('Recording stopped', {
      filePath: result.filePath,
      duration: result.duration
    });
    this.isRecording = false;
  } else {
    console.error('Failed to stop recording');
  }
}

Use Cases:

  • Allow users to record their issue before contacting support
  • Implement custom recording UI in your app
  • Record specific user flows programmatically
  • Create bug reporting features with automatic recording

Note: Recording still requires user permission on iOS (microphone access). The video file is automatically processed and available for playback in the support widget.

Dual Session Capture

Cuoral provides two complementary ways to capture user sessions:

📹 Native Video Screen Recording (Mobile)

When: User explicitly taps "Record" button in widget or via programmatic API

What it captures:

  • Exact visual output (pixel-perfect replay)
  • All UI elements including Shadow DOM components
  • Animations, transitions, gestures
  • Everything the user sees

📹 File Size: ~11 MB per minute (1.5 Mbps bitrate)
📤 Upload: Automatic to secure backend
🎯 Best for: Visual bug reproduction, UI issues, specific user flows

🎬 DOM Replay Recording (rrweb - Always On)

When: Automatically running in background during entire session

What it captures:

  • DOM structure and mutations
  • User interactions (clicks, scrolls, inputs)
  • Network requests and console errors
  • Page navigation and state changes
  • Custom events (rage clicks, form submissions)

💾 Data Size: Lightweight (~100-500 KB per session)
📤 Upload: Batched every 10 seconds
🎯 Best for: Understanding user journey, behavior analytics, session context

🔄 How They Work Together

Both features run simultaneously to give you complete visibility:

  1. DOM Replay (rrweb) - Always recording in background, captures user journey and interactions
  2. Video Recording - User-initiated, captures visual output when they hit an issue

Example Workflow:

  • User navigates app → rrweb captures all interactions
  • User hits a bug → User taps "Record" → Native video captures visual issue
  • Support team sees both: The journey (rrweb) + The exact visual problem (video)

Privacy: DOM replay masks password inputs. Other fields captured for support context. Native video captures exact screen content when user explicitly records.

🎨 Shadow DOM Handling (Ionic Components)

Problem: Ionic uses Shadow DOM which rrweb cannot natively access.
Solution: The SDK automatically captures Shadow DOM content every 5 seconds.

How it works:

  1. SDK scans all elements with Shadow DOM (ion-button, ion-content, etc.)
  2. Serializes Shadow DOM innerHTML to data-cuoral-shadow-* attributes
  3. rrweb captures these attributes (it can see regular attributes)
  4. Your replay dashboard uses these attributes to reconstruct the UI

To verify it's working: Open DevTools → Inspect any Ionic element → Should see data-cuoral-shadow-html attribute

If pages still show black: See SHADOW_DOM_CAPTURE_GUIDE.md for optimization tips:

  • Use open Shadow DOM (not closed)
  • Add data-content attributes to critical elements
  • Use semantic CSS classes
  • The guide includes code examples and troubleshooting

Note: Native video recording (user-initiated) always works perfectly - no Shadow DOM limitations.

Manual Intelligence Tracking

Track custom events beyond automatic tracking:

// Track page/screen view
this.cuoral.trackPageView('/checkout', {
  cart_items: 3,
  total_value: 99.99,
});

// Track error manually
this.cuoral.trackError('Payment failed', error.stack, {
  payment_method: 'credit_card',
  amount: 99.99,
});

What Gets Handled Automatically

  • ✅ Support ticket creation and management
  • ✅ Customer intelligence tracking (page views, errors, network failures)
  • ✅ Native crash tracking (Android & iOS)
  • ✅ Screen recording start/stop
  • ✅ File path conversion for video playback
  • ✅ Communication between widget and native code
  • ✅ Video upload to Cuoral backend
  • ✅ State management
  • ✅ Permissions handling

Intelligence Features (Backend-Controlled)

Intelligence tracking is automatically enabled/disabled from your Cuoral dashboard. When enabled, the library automatically captures:

  • Page Views - Track navigation (requires one-time router setup, see INTELLIGENCE.md)
  • Console Errors - JavaScript errors and unhandled exceptions
  • Network Errors - Failed API calls (4xx, 5xx responses)
  • Native Crashes - Native Android (Java/Kotlin) and iOS (Swift/Objective-C) crashes

For detailed intelligence setup and features, see INTELLIGENCE.md

Troubleshooting

Recording doesn't start

Check iOS permissions:

  • Verify NSMicrophoneUsageDescription is in Info.plist
  • System will prompt user automatically on first recording

Enable debug mode:

new Cuoral({
  publicKey: 'your-key',
  debug: true, // See console logs
});

Widget not appearing

Modal mode:

  • Make sure you called cuoral.initialize()
  • Check that useModal: true is set (default)
  • Look for the blue floating button in bottom-right

Iframe mode:

  • Verify widgetUrl is properly sanitized with DomSanitizer
  • Check browser console for CORS errors
  • Ensure iframe has proper dimensions in CSS

Build errors

iOS Pod Issues:

cd ios
pod deintegrate
pod install
cd ..
npx cap sync ios

Clean build:

rm -rf node_modules dist
npm install
npx cap sync

Support

  • 📧 Email: [email protected]
  • 📖 Docs: https://docs.cuoral.com
  • 🐛 Issues: https://github.com/cuoral/cuoral-ionic/issues

License

MIT License - see LICENSE file


Built with ❤️ by Cuoral