cuoral-ionic 0.0.9 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -382,10 +382,11 @@ class CuoralBridge {
382
382
  * Handles full-screen modal display with floating button
383
383
  */
384
384
  class CuoralModal {
385
- constructor(widgetUrl, showFloatingButton = true) {
385
+ constructor(widgetUrl, showFloatingButton = true, primaryColor = '#007AFF') {
386
386
  this.isOpen = false;
387
387
  this.widgetUrl = widgetUrl;
388
388
  this.showFloatingButton = showFloatingButton;
389
+ this.primaryColor = primaryColor;
389
390
  }
390
391
  /**
391
392
  * Update the widget URL (e.g., to include new session_id)
@@ -453,12 +454,12 @@ class CuoralModal {
453
454
  width: '56px',
454
455
  height: '56px',
455
456
  borderRadius: '50%',
456
- backgroundColor: '#007AFF',
457
+ backgroundColor: this.primaryColor,
457
458
  display: 'flex',
458
459
  alignItems: 'center',
459
460
  justifyContent: 'center',
460
461
  cursor: 'pointer',
461
- boxShadow: '0 4px 12px rgba(0, 122, 255, 0.4)',
462
+ boxShadow: `0 4px 12px ${this.primaryColor}66`, // 40% opacity
462
463
  zIndex: '999999',
463
464
  transition: 'transform 0.2s, box-shadow 0.2s'
464
465
  });
@@ -466,13 +467,13 @@ class CuoralModal {
466
467
  this.floatingButton.addEventListener('mouseenter', () => {
467
468
  if (this.floatingButton) {
468
469
  this.floatingButton.style.transform = 'scale(1.1)';
469
- this.floatingButton.style.boxShadow = '0 6px 16px rgba(0, 122, 255, 0.5)';
470
+ this.floatingButton.style.boxShadow = `0 6px 16px ${this.primaryColor}80`; // 50% opacity
470
471
  }
471
472
  });
472
473
  this.floatingButton.addEventListener('mouseleave', () => {
473
474
  if (this.floatingButton) {
474
475
  this.floatingButton.style.transform = 'scale(1)';
475
- this.floatingButton.style.boxShadow = '0 4px 12px rgba(0, 122, 255, 0.4)';
476
+ this.floatingButton.style.boxShadow = `0 4px 12px ${this.primaryColor}66`; // 40% opacity
476
477
  }
477
478
  });
478
479
  // Click to open modal
@@ -671,11 +672,13 @@ class CuoralIntelligence {
671
672
  // Session replay state
672
673
  this.rrwebStopFn = null;
673
674
  this.rrwebEvents = [];
675
+ this.rrwebEmit = null; // Store emit function for custom events
674
676
  this.customEvents = [];
675
677
  this.sessionReplayTimer = null;
676
678
  this.clickTimestamps = new Map();
677
679
  this.rageClickThreshold = 5; // 5 rapid clicks
678
680
  this.rageClickWindowMs = 2000; // Within 2 seconds
681
+ this.shadowDOMNodeMap = new Map(); // Map elements to unique IDs
679
682
  this.sessionId = sessionId;
680
683
  }
681
684
  /**
@@ -1185,27 +1188,65 @@ class CuoralIntelligence {
1185
1188
  }
1186
1189
  /**
1187
1190
  * Setup session replay with rrweb
1191
+ * Captures everything for maximum replay fidelity
1188
1192
  */
1189
1193
  setupSessionReplay() {
1190
1194
  if (!this.sessionId) {
1191
1195
  console.warn('[Cuoral Intelligence] Session replay requires a session ID');
1192
1196
  return;
1193
1197
  }
1194
- // Start rrweb recording
1198
+ // Start rrweb recording with aggressive capture settings
1195
1199
  this.rrwebStopFn = rrweb__namespace.record({
1196
1200
  emit: (event) => {
1197
1201
  this.rrwebEvents.push(event);
1202
+ this.rrwebEmit = this.rrwebEmit || ((e) => this.rrwebEvents.push(e)); // Store emit reference
1198
1203
  },
1199
- checkoutEveryNms: 60000, // Full snapshot every minute
1204
+ // Take full snapshots more frequently to catch dynamic content
1205
+ checkoutEveryNms: 30000, // Every 30 seconds (was 60s)
1206
+ // Capture everything - minimize sampling throttling
1200
1207
  sampling: {
1201
- scroll: 150, // Throttle scroll events
1202
- media: 800,
1203
- input: 'last', // Only record final input value (privacy)
1208
+ scroll: 50, // Capture scroll very frequently (was 150)
1209
+ media: 200, // Capture media interactions frequently (was 800)
1210
+ input: 'last', // Only record final input value (privacy for passwords)
1211
+ mousemove: true, // Capture all mouse movements
1212
+ mouseInteraction: true, // Capture all mouse interactions
1213
+ },
1214
+ // Privacy: Only mask sensitive inputs (passwords, credit cards)
1215
+ // Everything else is captured for maximum replay fidelity
1216
+ maskAllInputs: false, // Don't mask all inputs
1217
+ maskInputOptions: {
1218
+ password: true, // Mask password fields
1219
+ email: false, // Capture emails
1220
+ text: false, // Capture text inputs
1221
+ textarea: false, // Capture textareas
1222
+ select: false, // Capture select dropdowns
1223
+ // Mask credit card patterns
1224
+ },
1225
+ // Capture all content types
1226
+ recordCanvas: true, // Capture canvas elements (charts, games, etc.)
1227
+ // Inline everything for perfect replay
1228
+ inlineStylesheet: true, // Inline all external stylesheets
1229
+ inlineImages: true, // Inline images as base64 (larger but more reliable)
1230
+ collectFonts: true, // Capture custom fonts
1231
+ // Block/ignore classes - customers can add these to sensitive elements
1232
+ blockClass: 'cuoral-block', // Add to elements that should be blocked (replaced with placeholder)
1233
+ ignoreClass: 'cuoral-ignore', // Add to elements that should be ignored (not captured)
1234
+ // Capture mutations aggressively
1235
+ slimDOMOptions: {
1236
+ // Don't slim anything - capture full fidelity
1237
+ script: false, // Keep scripts
1238
+ comment: false, // Keep comments
1239
+ headFavicon: false, // Keep favicons
1240
+ headWhitespace: false, // Keep whitespace
1241
+ headMetaSocial: false, // Keep meta tags
1242
+ headMetaRobots: false, // Keep robots meta
1243
+ headMetaHttpEquiv: false, // Keep http-equiv
1244
+ headMetaAuthorship: false, // Keep authorship
1245
+ headMetaDescKeywords: false, // Keep description/keywords
1204
1246
  },
1205
- maskAllInputs: true, // Mask sensitive inputs (privacy)
1206
- blockClass: 'cuoral-block',
1207
- ignoreClass: 'cuoral-ignore',
1208
1247
  });
1248
+ // Setup Shadow DOM observer to capture Ionic components
1249
+ this.setupShadowDOMObserver();
1209
1250
  // Setup custom event tracking
1210
1251
  this.setupClickTracking();
1211
1252
  this.setupScrollTracking();
@@ -1357,6 +1398,159 @@ class CuoralIntelligence {
1357
1398
  });
1358
1399
  });
1359
1400
  }
1401
+ /**
1402
+ * Setup Shadow DOM observer to capture Ionic component content
1403
+ * Emits custom rrweb events with Shadow DOM snapshots for viewer reconstruction
1404
+ */
1405
+ setupShadowDOMObserver() {
1406
+ let shadowNodeIdCounter = 1;
1407
+ let captureTimeout = null;
1408
+ const captureShadowDOM = (immediate = false) => {
1409
+ // Debounce rapid calls (e.g., during scroll)
1410
+ if (!immediate && captureTimeout) {
1411
+ clearTimeout(captureTimeout);
1412
+ }
1413
+ const doCapture = () => {
1414
+ if (!this.rrwebEmit)
1415
+ return;
1416
+ const shadowDOMData = [];
1417
+ const elementsWithShadowDOM = document.querySelectorAll('*');
1418
+ elementsWithShadowDOM.forEach((element) => {
1419
+ if (element.shadowRoot) {
1420
+ try {
1421
+ // Assign unique ID to element if it doesn't have one
1422
+ if (!this.shadowDOMNodeMap.has(element)) {
1423
+ this.shadowDOMNodeMap.set(element, shadowNodeIdCounter++);
1424
+ }
1425
+ const nodeId = this.shadowDOMNodeMap.get(element);
1426
+ const tagName = element.tagName.toLowerCase();
1427
+ // Capture Shadow DOM HTML
1428
+ const shadowHTML = element.shadowRoot.innerHTML;
1429
+ // Capture ALL styles from Shadow DOM
1430
+ const styleSheets = [];
1431
+ // 1. Capture inline <style> tags
1432
+ const shadowStyles = element.shadowRoot.querySelectorAll('style');
1433
+ shadowStyles.forEach((styleEl) => {
1434
+ if (styleEl.textContent) {
1435
+ styleSheets.push(styleEl.textContent);
1436
+ }
1437
+ });
1438
+ // 2. Capture adoptedStyleSheets (modern CSS API)
1439
+ if (element.shadowRoot.adoptedStyleSheets) {
1440
+ try {
1441
+ element.shadowRoot.adoptedStyleSheets.forEach((sheet) => {
1442
+ try {
1443
+ const rules = Array.from(sheet.cssRules).map(rule => rule.cssText).join('\n');
1444
+ if (rules)
1445
+ styleSheets.push(rules);
1446
+ }
1447
+ catch (e) {
1448
+ // CORS blocked stylesheet
1449
+ }
1450
+ });
1451
+ }
1452
+ catch (e) {
1453
+ // Browser doesn't support adoptedStyleSheets
1454
+ }
1455
+ }
1456
+ // 3. Capture external <link> stylesheets in Shadow DOM
1457
+ const shadowLinks = element.shadowRoot.querySelectorAll('link[rel="stylesheet"]');
1458
+ shadowLinks.forEach((linkEl) => {
1459
+ const href = linkEl.href;
1460
+ if (href) {
1461
+ // Store link href so dashboard can also load it
1462
+ styleSheets.push(`/* @import url("${href}"); */`);
1463
+ }
1464
+ });
1465
+ // 4. Capture CSS custom properties from host element
1466
+ // Ionic components use CSS variables extensively (--ion-color-*, etc.)
1467
+ const computedStyle = window.getComputedStyle(element);
1468
+ const cssVariables = [];
1469
+ // Get all CSS custom properties from host
1470
+ for (let i = 0; i < computedStyle.length; i++) {
1471
+ const propertyName = computedStyle[i];
1472
+ if (propertyName.startsWith('--')) {
1473
+ const propertyValue = computedStyle.getPropertyValue(propertyName).trim();
1474
+ if (propertyValue) {
1475
+ cssVariables.push(`${propertyName}: ${propertyValue};`);
1476
+ }
1477
+ }
1478
+ }
1479
+ // Add CSS variables as a :host rule
1480
+ if (cssVariables.length > 0) {
1481
+ styleSheets.push(`:host { ${cssVariables.join(' ')} }`);
1482
+ }
1483
+ // Get element's CSS selector for reconstruction
1484
+ const selector = this.getElementSelector(element);
1485
+ // Capture element attributes (Ionic uses color="primary", mode="ios", etc.)
1486
+ const attributes = {};
1487
+ for (let i = 0; i < element.attributes.length; i++) {
1488
+ const attr = element.attributes[i];
1489
+ // Skip internal attributes
1490
+ if (!attr.name.startsWith('data-cuoral') && !attr.name.startsWith('ng-')) {
1491
+ attributes[attr.name] = attr.value;
1492
+ }
1493
+ }
1494
+ // Create Shadow DOM snapshot
1495
+ shadowDOMData.push({
1496
+ nodeId,
1497
+ tagName,
1498
+ selector,
1499
+ attributes, // NEW: Include element attributes
1500
+ shadowHTML,
1501
+ styleSheets,
1502
+ timestamp: Date.now(),
1503
+ });
1504
+ // Also set data attribute as fallback
1505
+ element.setAttribute('data-cuoral-shadow-id', String(nodeId));
1506
+ }
1507
+ catch (error) {
1508
+ // Shadow DOM might be closed
1509
+ console.warn('[Cuoral Intelligence] Failed to capture Shadow DOM:', error);
1510
+ }
1511
+ }
1512
+ });
1513
+ // Emit custom event with Shadow DOM data
1514
+ if (shadowDOMData.length > 0 && this.rrwebEmit) {
1515
+ this.rrwebEmit({
1516
+ type: 5, // CustomEvent type in rrweb
1517
+ data: {
1518
+ tag: 'cuoral-shadow-dom',
1519
+ payload: {
1520
+ shadows: shadowDOMData,
1521
+ url: window.location.href,
1522
+ },
1523
+ },
1524
+ timestamp: Date.now(),
1525
+ });
1526
+ console.log(`[Cuoral Intelligence] Captured ${shadowDOMData.length} Shadow DOM elements`);
1527
+ }
1528
+ };
1529
+ if (immediate) {
1530
+ doCapture();
1531
+ }
1532
+ else {
1533
+ // Debounce: wait 500ms after last call
1534
+ captureTimeout = setTimeout(doCapture, 500);
1535
+ }
1536
+ };
1537
+ // Run immediately on init
1538
+ captureShadowDOM(true);
1539
+ // Observe DOM mutations to catch dynamically added Shadow DOM elements
1540
+ const observer = new MutationObserver(() => {
1541
+ captureShadowDOM(false); // Debounced
1542
+ });
1543
+ observer.observe(document.body, {
1544
+ childList: true,
1545
+ subtree: true,
1546
+ });
1547
+ // Capture on scroll events (debounced)
1548
+ window.addEventListener('scroll', () => captureShadowDOM(false), { passive: true });
1549
+ // Also listen for Ionic scroll events
1550
+ document.addEventListener('ionScroll', () => captureShadowDOM(false), { passive: true });
1551
+ // Periodic capture as fallback (every 5 seconds now, since we have scroll capture)
1552
+ setInterval(() => captureShadowDOM(true), 5000);
1553
+ }
1360
1554
  /**
1361
1555
  * Track custom business events (flows, features, etc.)
1362
1556
  */
@@ -1485,10 +1679,7 @@ class Cuoral {
1485
1679
  if (options.lastName)
1486
1680
  params.set('last_name', options.lastName);
1487
1681
  const widgetUrl = `${baseUrl}?${params.toString()}`;
1488
- // Initialize modal if enabled
1489
- if (this.options.useModal) {
1490
- this.modal = new CuoralModal(widgetUrl, this.options.showFloatingButton);
1491
- }
1682
+ // Note: Modal will be created in initialize() after fetching primary color from backend
1492
1683
  // Initialize bridge and recorder
1493
1684
  this.bridge = new CuoralBridge({
1494
1685
  widgetUrl,
@@ -1509,6 +1700,7 @@ class Cuoral {
1509
1700
  */
1510
1701
  async initialize() {
1511
1702
  // Fetch session configuration and initialize intelligence if enabled by backend
1703
+ // This also fetches the organization's primary color
1512
1704
  await this.initializeIntelligence();
1513
1705
  console.log('[Cuoral] Initialize - Session ID:', localStorage.getItem('__x_loadID'));
1514
1706
  // Setup localStorage listener to detect when widget changes session
@@ -1517,7 +1709,7 @@ class Cuoral {
1517
1709
  // Recreate modal if it was destroyed (e.g., after clearSession)
1518
1710
  if (this.options.useModal && !this.modal) {
1519
1711
  const widgetUrl = this.getWidgetUrl();
1520
- this.modal = new CuoralModal(widgetUrl, this.options.showFloatingButton);
1712
+ this.modal = new CuoralModal(widgetUrl, this.options.showFloatingButton, this.options.primaryColor);
1521
1713
  }
1522
1714
  // Update modal URL with session ID
1523
1715
  if (this.modal) {
@@ -1542,6 +1734,15 @@ class Cuoral {
1542
1734
  }
1543
1735
  // Fetch session configuration
1544
1736
  const config = await this.fetchSessionConfiguration(sessionId);
1737
+ // Extract organization's primary color from config if available
1738
+ if (!this.options.primaryColor && config?.color) {
1739
+ this.options.primaryColor = config.color;
1740
+ console.log('[Cuoral] Using organization primary color:', this.options.primaryColor);
1741
+ }
1742
+ else if (!this.options.primaryColor) {
1743
+ this.options.primaryColor = '#007AFF';
1744
+ console.log('[Cuoral] Using default primary color');
1745
+ }
1545
1746
  // If session was invalid/expired, try to initiate a new one
1546
1747
  if (!config && !localStorage.getItem('__x_loadID')) {
1547
1748
  sessionId = await this.initiateSession();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -11,11 +11,13 @@ export declare class CuoralIntelligence {
11
11
  private originalXMLHttpRequest;
12
12
  private rrwebStopFn;
13
13
  private rrwebEvents;
14
+ private rrwebEmit;
14
15
  private customEvents;
15
16
  private sessionReplayTimer;
16
17
  private clickTimestamps;
17
18
  private rageClickThreshold;
18
19
  private rageClickWindowMs;
20
+ private shadowDOMNodeMap;
19
21
  constructor(sessionId: string);
20
22
  /**
21
23
  * Update the session ID (e.g., when user logs in)
@@ -75,6 +77,7 @@ export declare class CuoralIntelligence {
75
77
  private setupNativeErrorCapture;
76
78
  /**
77
79
  * Setup session replay with rrweb
80
+ * Captures everything for maximum replay fidelity
78
81
  */
79
82
  private setupSessionReplay;
80
83
  /**
@@ -89,6 +92,11 @@ export declare class CuoralIntelligence {
89
92
  * Setup form tracking
90
93
  */
91
94
  private setupFormTracking;
95
+ /**
96
+ * Setup Shadow DOM observer to capture Ionic component content
97
+ * Emits custom rrweb events with Shadow DOM snapshots for viewer reconstruction
98
+ */
99
+ private setupShadowDOMObserver;
92
100
  /**
93
101
  * Track custom business events (flows, features, etc.)
94
102
  */
@@ -1 +1 @@
1
- {"version":3,"file":"intelligence.d.ts","sourceRoot":"","sources":["../src/intelligence.ts"],"names":[],"mappings":"AA0EA,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAWZ;IAEF,OAAO,CAAC,MAAM,CAKZ;IAEF,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,qBAAqB,CAAc;IAC3C,OAAO,CAAC,kBAAkB,CAAM;IAChC,OAAO,CAAC,aAAa,CAAa;IAGlC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,sBAAsB,CAAa;IAG3C,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,eAAe,CAAoC;IAC3D,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,iBAAiB,CAAQ;gBAErB,SAAS,EAAE,MAAM;IAI7B;;OAEG;IACI,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAkBlD;;OAEG;IACI,IAAI,IAAI,IAAI;IAgBnB;;OAEG;IACI,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI;IAc1D;;OAEG;IACI,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI;IAS7E;;OAEG;IACI,KAAK,IAAI,IAAI;IAMpB;;OAEG;IACI,OAAO,IAAI,IAAI;IA0CtB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAoFjC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA8H9B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAgB7B;;OAEG;IACH,OAAO,CAAC,YAAY;IAyCpB;;OAEG;IACH,OAAO,CAAC,UAAU;IAqFlB;;OAEG;YACW,aAAa;IAiC3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAU1B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAgB/B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAiC1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAoD1B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAwC3B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA6DzB;;OAEG;IACI,gBAAgB,CACrB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EACpC,eAAe,CAAC,EAAE,MAAM,EACxB,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI;IAgBP;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAwB/B;;OAEG;YACW,sBAAsB;IAkBpC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAe1B;;OAEG;IACH,OAAO,CAAC,WAAW;CAOpB"}
1
+ {"version":3,"file":"intelligence.d.ts","sourceRoot":"","sources":["../src/intelligence.ts"],"names":[],"mappings":"AA0EA,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAWZ;IAEF,OAAO,CAAC,MAAM,CAKZ;IAEF,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,qBAAqB,CAAc;IAC3C,OAAO,CAAC,kBAAkB,CAAM;IAChC,OAAO,CAAC,aAAa,CAAa;IAGlC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,sBAAsB,CAAa;IAG3C,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,SAAS,CAAuC;IACxD,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,eAAe,CAAoC;IAC3D,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,iBAAiB,CAAQ;IACjC,OAAO,CAAC,gBAAgB,CAAmC;gBAE/C,SAAS,EAAE,MAAM;IAI7B;;OAEG;IACI,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAkBlD;;OAEG;IACI,IAAI,IAAI,IAAI;IAgBnB;;OAEG;IACI,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI;IAc1D;;OAEG;IACI,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI;IAS7E;;OAEG;IACI,KAAK,IAAI,IAAI;IAMpB;;OAEG;IACI,OAAO,IAAI,IAAI;IA0CtB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAoFjC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA8H9B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAgB7B;;OAEG;IACH,OAAO,CAAC,YAAY;IAyCpB;;OAEG;IACH,OAAO,CAAC,UAAU;IAqFlB;;OAEG;YACW,aAAa;IAiC3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAU1B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAgB/B;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IA8E1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAoD1B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAwC3B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA6DzB;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IA2K9B;;OAEG;IACI,gBAAgB,CACrB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EACpC,eAAe,CAAC,EAAE,MAAM,EACxB,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI;IAgBP;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAwB/B;;OAEG;YACW,sBAAsB;IAkBpC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAe1B;;OAEG;IACH,OAAO,CAAC,WAAW;CAOpB"}
@@ -35,11 +35,13 @@ export class CuoralIntelligence {
35
35
  // Session replay state
36
36
  this.rrwebStopFn = null;
37
37
  this.rrwebEvents = [];
38
+ this.rrwebEmit = null; // Store emit function for custom events
38
39
  this.customEvents = [];
39
40
  this.sessionReplayTimer = null;
40
41
  this.clickTimestamps = new Map();
41
42
  this.rageClickThreshold = 5; // 5 rapid clicks
42
43
  this.rageClickWindowMs = 2000; // Within 2 seconds
44
+ this.shadowDOMNodeMap = new Map(); // Map elements to unique IDs
43
45
  this.sessionId = sessionId;
44
46
  }
45
47
  /**
@@ -549,27 +551,65 @@ export class CuoralIntelligence {
549
551
  }
550
552
  /**
551
553
  * Setup session replay with rrweb
554
+ * Captures everything for maximum replay fidelity
552
555
  */
553
556
  setupSessionReplay() {
554
557
  if (!this.sessionId) {
555
558
  console.warn('[Cuoral Intelligence] Session replay requires a session ID');
556
559
  return;
557
560
  }
558
- // Start rrweb recording
561
+ // Start rrweb recording with aggressive capture settings
559
562
  this.rrwebStopFn = rrweb.record({
560
563
  emit: (event) => {
561
564
  this.rrwebEvents.push(event);
565
+ this.rrwebEmit = this.rrwebEmit || ((e) => this.rrwebEvents.push(e)); // Store emit reference
562
566
  },
563
- checkoutEveryNms: 60000, // Full snapshot every minute
567
+ // Take full snapshots more frequently to catch dynamic content
568
+ checkoutEveryNms: 30000, // Every 30 seconds (was 60s)
569
+ // Capture everything - minimize sampling throttling
564
570
  sampling: {
565
- scroll: 150, // Throttle scroll events
566
- media: 800,
567
- input: 'last', // Only record final input value (privacy)
571
+ scroll: 50, // Capture scroll very frequently (was 150)
572
+ media: 200, // Capture media interactions frequently (was 800)
573
+ input: 'last', // Only record final input value (privacy for passwords)
574
+ mousemove: true, // Capture all mouse movements
575
+ mouseInteraction: true, // Capture all mouse interactions
576
+ },
577
+ // Privacy: Only mask sensitive inputs (passwords, credit cards)
578
+ // Everything else is captured for maximum replay fidelity
579
+ maskAllInputs: false, // Don't mask all inputs
580
+ maskInputOptions: {
581
+ password: true, // Mask password fields
582
+ email: false, // Capture emails
583
+ text: false, // Capture text inputs
584
+ textarea: false, // Capture textareas
585
+ select: false, // Capture select dropdowns
586
+ // Mask credit card patterns
587
+ },
588
+ // Capture all content types
589
+ recordCanvas: true, // Capture canvas elements (charts, games, etc.)
590
+ // Inline everything for perfect replay
591
+ inlineStylesheet: true, // Inline all external stylesheets
592
+ inlineImages: true, // Inline images as base64 (larger but more reliable)
593
+ collectFonts: true, // Capture custom fonts
594
+ // Block/ignore classes - customers can add these to sensitive elements
595
+ blockClass: 'cuoral-block', // Add to elements that should be blocked (replaced with placeholder)
596
+ ignoreClass: 'cuoral-ignore', // Add to elements that should be ignored (not captured)
597
+ // Capture mutations aggressively
598
+ slimDOMOptions: {
599
+ // Don't slim anything - capture full fidelity
600
+ script: false, // Keep scripts
601
+ comment: false, // Keep comments
602
+ headFavicon: false, // Keep favicons
603
+ headWhitespace: false, // Keep whitespace
604
+ headMetaSocial: false, // Keep meta tags
605
+ headMetaRobots: false, // Keep robots meta
606
+ headMetaHttpEquiv: false, // Keep http-equiv
607
+ headMetaAuthorship: false, // Keep authorship
608
+ headMetaDescKeywords: false, // Keep description/keywords
568
609
  },
569
- maskAllInputs: true, // Mask sensitive inputs (privacy)
570
- blockClass: 'cuoral-block',
571
- ignoreClass: 'cuoral-ignore',
572
610
  });
611
+ // Setup Shadow DOM observer to capture Ionic components
612
+ this.setupShadowDOMObserver();
573
613
  // Setup custom event tracking
574
614
  this.setupClickTracking();
575
615
  this.setupScrollTracking();
@@ -721,6 +761,159 @@ export class CuoralIntelligence {
721
761
  });
722
762
  });
723
763
  }
764
+ /**
765
+ * Setup Shadow DOM observer to capture Ionic component content
766
+ * Emits custom rrweb events with Shadow DOM snapshots for viewer reconstruction
767
+ */
768
+ setupShadowDOMObserver() {
769
+ let shadowNodeIdCounter = 1;
770
+ let captureTimeout = null;
771
+ const captureShadowDOM = (immediate = false) => {
772
+ // Debounce rapid calls (e.g., during scroll)
773
+ if (!immediate && captureTimeout) {
774
+ clearTimeout(captureTimeout);
775
+ }
776
+ const doCapture = () => {
777
+ if (!this.rrwebEmit)
778
+ return;
779
+ const shadowDOMData = [];
780
+ const elementsWithShadowDOM = document.querySelectorAll('*');
781
+ elementsWithShadowDOM.forEach((element) => {
782
+ if (element.shadowRoot) {
783
+ try {
784
+ // Assign unique ID to element if it doesn't have one
785
+ if (!this.shadowDOMNodeMap.has(element)) {
786
+ this.shadowDOMNodeMap.set(element, shadowNodeIdCounter++);
787
+ }
788
+ const nodeId = this.shadowDOMNodeMap.get(element);
789
+ const tagName = element.tagName.toLowerCase();
790
+ // Capture Shadow DOM HTML
791
+ const shadowHTML = element.shadowRoot.innerHTML;
792
+ // Capture ALL styles from Shadow DOM
793
+ const styleSheets = [];
794
+ // 1. Capture inline <style> tags
795
+ const shadowStyles = element.shadowRoot.querySelectorAll('style');
796
+ shadowStyles.forEach((styleEl) => {
797
+ if (styleEl.textContent) {
798
+ styleSheets.push(styleEl.textContent);
799
+ }
800
+ });
801
+ // 2. Capture adoptedStyleSheets (modern CSS API)
802
+ if (element.shadowRoot.adoptedStyleSheets) {
803
+ try {
804
+ element.shadowRoot.adoptedStyleSheets.forEach((sheet) => {
805
+ try {
806
+ const rules = Array.from(sheet.cssRules).map(rule => rule.cssText).join('\n');
807
+ if (rules)
808
+ styleSheets.push(rules);
809
+ }
810
+ catch (e) {
811
+ // CORS blocked stylesheet
812
+ }
813
+ });
814
+ }
815
+ catch (e) {
816
+ // Browser doesn't support adoptedStyleSheets
817
+ }
818
+ }
819
+ // 3. Capture external <link> stylesheets in Shadow DOM
820
+ const shadowLinks = element.shadowRoot.querySelectorAll('link[rel="stylesheet"]');
821
+ shadowLinks.forEach((linkEl) => {
822
+ const href = linkEl.href;
823
+ if (href) {
824
+ // Store link href so dashboard can also load it
825
+ styleSheets.push(`/* @import url("${href}"); */`);
826
+ }
827
+ });
828
+ // 4. Capture CSS custom properties from host element
829
+ // Ionic components use CSS variables extensively (--ion-color-*, etc.)
830
+ const computedStyle = window.getComputedStyle(element);
831
+ const cssVariables = [];
832
+ // Get all CSS custom properties from host
833
+ for (let i = 0; i < computedStyle.length; i++) {
834
+ const propertyName = computedStyle[i];
835
+ if (propertyName.startsWith('--')) {
836
+ const propertyValue = computedStyle.getPropertyValue(propertyName).trim();
837
+ if (propertyValue) {
838
+ cssVariables.push(`${propertyName}: ${propertyValue};`);
839
+ }
840
+ }
841
+ }
842
+ // Add CSS variables as a :host rule
843
+ if (cssVariables.length > 0) {
844
+ styleSheets.push(`:host { ${cssVariables.join(' ')} }`);
845
+ }
846
+ // Get element's CSS selector for reconstruction
847
+ const selector = this.getElementSelector(element);
848
+ // Capture element attributes (Ionic uses color="primary", mode="ios", etc.)
849
+ const attributes = {};
850
+ for (let i = 0; i < element.attributes.length; i++) {
851
+ const attr = element.attributes[i];
852
+ // Skip internal attributes
853
+ if (!attr.name.startsWith('data-cuoral') && !attr.name.startsWith('ng-')) {
854
+ attributes[attr.name] = attr.value;
855
+ }
856
+ }
857
+ // Create Shadow DOM snapshot
858
+ shadowDOMData.push({
859
+ nodeId,
860
+ tagName,
861
+ selector,
862
+ attributes, // NEW: Include element attributes
863
+ shadowHTML,
864
+ styleSheets,
865
+ timestamp: Date.now(),
866
+ });
867
+ // Also set data attribute as fallback
868
+ element.setAttribute('data-cuoral-shadow-id', String(nodeId));
869
+ }
870
+ catch (error) {
871
+ // Shadow DOM might be closed
872
+ console.warn('[Cuoral Intelligence] Failed to capture Shadow DOM:', error);
873
+ }
874
+ }
875
+ });
876
+ // Emit custom event with Shadow DOM data
877
+ if (shadowDOMData.length > 0 && this.rrwebEmit) {
878
+ this.rrwebEmit({
879
+ type: 5, // CustomEvent type in rrweb
880
+ data: {
881
+ tag: 'cuoral-shadow-dom',
882
+ payload: {
883
+ shadows: shadowDOMData,
884
+ url: window.location.href,
885
+ },
886
+ },
887
+ timestamp: Date.now(),
888
+ });
889
+ console.log(`[Cuoral Intelligence] Captured ${shadowDOMData.length} Shadow DOM elements`);
890
+ }
891
+ };
892
+ if (immediate) {
893
+ doCapture();
894
+ }
895
+ else {
896
+ // Debounce: wait 500ms after last call
897
+ captureTimeout = setTimeout(doCapture, 500);
898
+ }
899
+ };
900
+ // Run immediately on init
901
+ captureShadowDOM(true);
902
+ // Observe DOM mutations to catch dynamically added Shadow DOM elements
903
+ const observer = new MutationObserver(() => {
904
+ captureShadowDOM(false); // Debounced
905
+ });
906
+ observer.observe(document.body, {
907
+ childList: true,
908
+ subtree: true,
909
+ });
910
+ // Capture on scroll events (debounced)
911
+ window.addEventListener('scroll', () => captureShadowDOM(false), { passive: true });
912
+ // Also listen for Ionic scroll events
913
+ document.addEventListener('ionScroll', () => captureShadowDOM(false), { passive: true });
914
+ // Periodic capture as fallback (every 5 seconds now, since we have scroll capture)
915
+ setInterval(() => captureShadowDOM(true), 5000);
916
+ }
724
917
  /**
725
918
  * Track custom business events (flows, features, etc.)
726
919
  */
package/dist/modal.d.ts CHANGED
@@ -9,8 +9,9 @@ export declare class CuoralModal {
9
9
  private isOpen;
10
10
  private widgetUrl;
11
11
  private showFloatingButton;
12
+ private primaryColor;
12
13
  private lastLoadedUrl?;
13
- constructor(widgetUrl: string, showFloatingButton?: boolean);
14
+ constructor(widgetUrl: string, showFloatingButton?: boolean, primaryColor?: string);
14
15
  /**
15
16
  * Update the widget URL (e.g., to include new session_id)
16
17
  * Forces complete iframe recreation if URL changed to clear all cached state
@@ -1 +1 @@
1
- {"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["../src/modal.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,YAAY,CAAC,CAAiB;IACtC,OAAO,CAAC,aAAa,CAAC,CAAoB;IAC1C,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,kBAAkB,CAAU;IACpC,OAAO,CAAC,aAAa,CAAC,CAAS;gBAEnB,SAAS,EAAE,MAAM,EAAE,kBAAkB,UAAO;IAKxD;;;OAGG;IACI,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IA4C5C;;OAEG;IACI,UAAU,IAAI,IAAI;IAOzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAkD5B;;OAEG;IACH,OAAO,CAAC,WAAW;IAmGnB;;OAEG;IACI,IAAI,IAAI,IAAI;IAsBnB;;OAEG;IACI,KAAK,IAAI,IAAI;IAqBpB;;OAEG;IACI,WAAW,IAAI,OAAO;IAI7B;;OAEG;IACI,OAAO,IAAI,IAAI;IAUtB;;OAEG;IACI,SAAS,IAAI,iBAAiB,GAAG,SAAS;CAGlD"}
1
+ {"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["../src/modal.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,YAAY,CAAC,CAAiB;IACtC,OAAO,CAAC,aAAa,CAAC,CAAoB;IAC1C,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,kBAAkB,CAAU;IACpC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,aAAa,CAAC,CAAS;gBAEnB,SAAS,EAAE,MAAM,EAAE,kBAAkB,UAAO,EAAE,YAAY,SAAY;IAMlF;;;OAGG;IACI,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IA4C5C;;OAEG;IACI,UAAU,IAAI,IAAI;IAOzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAkD5B;;OAEG;IACH,OAAO,CAAC,WAAW;IAmGnB;;OAEG;IACI,IAAI,IAAI,IAAI;IAsBnB;;OAEG;IACI,KAAK,IAAI,IAAI;IAqBpB;;OAEG;IACI,WAAW,IAAI,OAAO;IAI7B;;OAEG;IACI,OAAO,IAAI,IAAI;IAUtB;;OAEG;IACI,SAAS,IAAI,iBAAiB,GAAG,SAAS;CAGlD"}