@pocketping/widget 0.3.3 → 0.3.4

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.
@@ -1,9 +1,48 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
1
20
  // src/index.ts
2
- import { render, h as h2 } from "preact";
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ close: () => close,
24
+ default: () => index_default,
25
+ destroy: () => destroy,
26
+ getIdentity: () => getIdentity,
27
+ getTrackedElements: () => getTrackedElements,
28
+ identify: () => identify,
29
+ init: () => init,
30
+ offEvent: () => offEvent,
31
+ on: () => on,
32
+ onEvent: () => onEvent,
33
+ open: () => open,
34
+ reset: () => reset,
35
+ sendMessage: () => sendMessage,
36
+ setupTrackedElements: () => setupTrackedElements,
37
+ toggle: () => toggle,
38
+ trigger: () => trigger
39
+ });
40
+ module.exports = __toCommonJS(index_exports);
41
+ var import_preact2 = require("preact");
3
42
 
4
43
  // src/components/ChatWidget.tsx
5
- import { Fragment } from "preact";
6
- import { useState, useEffect, useRef, useCallback } from "preact/hooks";
44
+ var import_preact = require("preact");
45
+ var import_hooks = require("preact/hooks");
7
46
 
8
47
  // src/components/styles.ts
9
48
  function styles(primaryColor, theme) {
@@ -348,17 +387,17 @@ function styles(primaryColor, theme) {
348
387
  }
349
388
 
350
389
  // src/components/ChatWidget.tsx
351
- import { Fragment as Fragment2, jsx, jsxs } from "preact/jsx-runtime";
390
+ var import_jsx_runtime = require("preact/jsx-runtime");
352
391
  function ChatWidget({ client: client2, config }) {
353
- const [isOpen, setIsOpen] = useState(false);
354
- const [messages, setMessages] = useState([]);
355
- const [inputValue, setInputValue] = useState("");
356
- const [isTyping, setIsTyping] = useState(false);
357
- const [operatorOnline, setOperatorOnline] = useState(false);
358
- const [isConnected, setIsConnected] = useState(false);
359
- const messagesEndRef = useRef(null);
360
- const inputRef = useRef(null);
361
- useEffect(() => {
392
+ const [isOpen, setIsOpen] = (0, import_hooks.useState)(false);
393
+ const [messages, setMessages] = (0, import_hooks.useState)([]);
394
+ const [inputValue, setInputValue] = (0, import_hooks.useState)("");
395
+ const [isTyping, setIsTyping] = (0, import_hooks.useState)(false);
396
+ const [operatorOnline, setOperatorOnline] = (0, import_hooks.useState)(false);
397
+ const [isConnected, setIsConnected] = (0, import_hooks.useState)(false);
398
+ const messagesEndRef = (0, import_hooks.useRef)(null);
399
+ const inputRef = (0, import_hooks.useRef)(null);
400
+ (0, import_hooks.useEffect)(() => {
362
401
  const unsubOpen = client2.on("openChange", setIsOpen);
363
402
  const unsubMessage = client2.on("message", () => {
364
403
  setMessages([...client2.getMessages()]);
@@ -387,15 +426,15 @@ function ChatWidget({ client: client2, config }) {
387
426
  unsubConnect();
388
427
  };
389
428
  }, [client2]);
390
- useEffect(() => {
429
+ (0, import_hooks.useEffect)(() => {
391
430
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
392
431
  }, [messages]);
393
- useEffect(() => {
432
+ (0, import_hooks.useEffect)(() => {
394
433
  if (isOpen) {
395
434
  inputRef.current?.focus();
396
435
  }
397
436
  }, [isOpen]);
398
- const markMessagesAsRead = useCallback(() => {
437
+ const markMessagesAsRead = (0, import_hooks.useCallback)(() => {
399
438
  if (!isOpen || !isConnected) return;
400
439
  const unreadMessages = messages.filter(
401
440
  (msg) => msg.sender !== "visitor" && msg.status !== "read"
@@ -405,14 +444,14 @@ function ChatWidget({ client: client2, config }) {
405
444
  client2.sendReadStatus(messageIds, "read");
406
445
  }
407
446
  }, [isOpen, isConnected, messages, client2]);
408
- useEffect(() => {
447
+ (0, import_hooks.useEffect)(() => {
409
448
  if (!isOpen || !isConnected) return;
410
449
  const timer = setTimeout(() => {
411
450
  markMessagesAsRead();
412
451
  }, 1e3);
413
452
  return () => clearTimeout(timer);
414
453
  }, [isOpen, isConnected, messages, markMessagesAsRead]);
415
- useEffect(() => {
454
+ (0, import_hooks.useEffect)(() => {
416
455
  const handleVisibilityChange = () => {
417
456
  if (document.visibilityState === "visible" && isOpen) {
418
457
  markMessagesAsRead();
@@ -421,7 +460,7 @@ function ChatWidget({ client: client2, config }) {
421
460
  document.addEventListener("visibilitychange", handleVisibilityChange);
422
461
  return () => document.removeEventListener("visibilitychange", handleVisibilityChange);
423
462
  }, [isOpen, markMessagesAsRead]);
424
- useEffect(() => {
463
+ (0, import_hooks.useEffect)(() => {
425
464
  const unsubRead = client2.on(
426
465
  "read",
427
466
  () => {
@@ -451,71 +490,71 @@ function ChatWidget({ client: client2, config }) {
451
490
  const position = config.position ?? "bottom-right";
452
491
  const theme = getTheme(config.theme ?? "auto");
453
492
  const primaryColor = config.primaryColor ?? "#6366f1";
454
- return /* @__PURE__ */ jsxs(Fragment, { children: [
455
- /* @__PURE__ */ jsx("style", { children: styles(primaryColor, theme) }),
456
- /* @__PURE__ */ jsxs(
493
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_preact.Fragment, { children: [
494
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: styles(primaryColor, theme) }),
495
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
457
496
  "button",
458
497
  {
459
498
  class: `pp-toggle pp-${position}`,
460
499
  onClick: () => client2.toggleOpen(),
461
500
  "aria-label": isOpen ? "Close chat" : "Open chat",
462
501
  children: [
463
- isOpen ? /* @__PURE__ */ jsx(CloseIcon, {}) : /* @__PURE__ */ jsx(ChatIcon, {}),
464
- !isOpen && operatorOnline && /* @__PURE__ */ jsx("span", { class: "pp-online-dot" })
502
+ isOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatIcon, {}),
503
+ !isOpen && operatorOnline && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-online-dot" })
465
504
  ]
466
505
  }
467
506
  ),
468
- isOpen && /* @__PURE__ */ jsxs("div", { class: `pp-window pp-${position} pp-theme-${theme}`, children: [
469
- /* @__PURE__ */ jsxs("div", { class: "pp-header", children: [
470
- /* @__PURE__ */ jsxs("div", { class: "pp-header-info", children: [
471
- config.operatorAvatar && /* @__PURE__ */ jsx("img", { src: config.operatorAvatar, alt: "", class: "pp-avatar" }),
472
- /* @__PURE__ */ jsxs("div", { children: [
473
- /* @__PURE__ */ jsx("div", { class: "pp-header-title", children: config.operatorName ?? "Support" }),
474
- /* @__PURE__ */ jsx("div", { class: "pp-header-status", children: operatorOnline ? /* @__PURE__ */ jsxs(Fragment2, { children: [
475
- /* @__PURE__ */ jsx("span", { class: "pp-status-dot pp-online" }),
507
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: `pp-window pp-${position} pp-theme-${theme}`, children: [
508
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-header", children: [
509
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-header-info", children: [
510
+ config.operatorAvatar && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: config.operatorAvatar, alt: "", class: "pp-avatar" }),
511
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
512
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-header-title", children: config.operatorName ?? "Support" }),
513
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-header-status", children: operatorOnline ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
514
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-status-dot pp-online" }),
476
515
  " Online"
477
- ] }) : /* @__PURE__ */ jsxs(Fragment2, { children: [
478
- /* @__PURE__ */ jsx("span", { class: "pp-status-dot" }),
516
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
517
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-status-dot" }),
479
518
  " Away"
480
519
  ] }) })
481
520
  ] })
482
521
  ] }),
483
- /* @__PURE__ */ jsx(
522
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
484
523
  "button",
485
524
  {
486
525
  class: "pp-close-btn",
487
526
  onClick: () => client2.setOpen(false),
488
527
  "aria-label": "Close chat",
489
- children: /* @__PURE__ */ jsx(CloseIcon, {})
528
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {})
490
529
  }
491
530
  )
492
531
  ] }),
493
- /* @__PURE__ */ jsxs("div", { class: "pp-messages", children: [
494
- config.welcomeMessage && messages.length === 0 && /* @__PURE__ */ jsx("div", { class: "pp-welcome", children: config.welcomeMessage }),
495
- messages.map((msg) => /* @__PURE__ */ jsxs(
532
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-messages", children: [
533
+ config.welcomeMessage && messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-welcome", children: config.welcomeMessage }),
534
+ messages.map((msg) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
496
535
  "div",
497
536
  {
498
537
  class: `pp-message pp-message-${msg.sender}`,
499
538
  children: [
500
- /* @__PURE__ */ jsx("div", { class: "pp-message-content", children: msg.content }),
501
- /* @__PURE__ */ jsxs("div", { class: "pp-message-time", children: [
539
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-message-content", children: msg.content }),
540
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-message-time", children: [
502
541
  formatTime(msg.timestamp),
503
- msg.sender === "ai" && /* @__PURE__ */ jsx("span", { class: "pp-ai-badge", children: "AI" }),
504
- msg.sender === "visitor" && /* @__PURE__ */ jsx("span", { class: `pp-status pp-status-${msg.status ?? "sent"}`, children: /* @__PURE__ */ jsx(StatusIcon, { status: msg.status }) })
542
+ msg.sender === "ai" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-ai-badge", children: "AI" }),
543
+ msg.sender === "visitor" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: `pp-status pp-status-${msg.status ?? "sent"}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusIcon, { status: msg.status }) })
505
544
  ] })
506
545
  ]
507
546
  },
508
547
  msg.id
509
548
  )),
510
- isTyping && /* @__PURE__ */ jsxs("div", { class: "pp-message pp-message-operator pp-typing", children: [
511
- /* @__PURE__ */ jsx("span", {}),
512
- /* @__PURE__ */ jsx("span", {}),
513
- /* @__PURE__ */ jsx("span", {})
549
+ isTyping && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-message pp-message-operator pp-typing", children: [
550
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
551
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
552
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {})
514
553
  ] }),
515
- /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
554
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: messagesEndRef })
516
555
  ] }),
517
- /* @__PURE__ */ jsxs("form", { class: "pp-input-form", onSubmit: handleSubmit, children: [
518
- /* @__PURE__ */ jsx(
556
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { class: "pp-input-form", onSubmit: handleSubmit, children: [
557
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
519
558
  "input",
520
559
  {
521
560
  ref: inputRef,
@@ -527,20 +566,20 @@ function ChatWidget({ client: client2, config }) {
527
566
  disabled: !isConnected
528
567
  }
529
568
  ),
530
- /* @__PURE__ */ jsx(
569
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
531
570
  "button",
532
571
  {
533
572
  type: "submit",
534
573
  class: "pp-send-btn",
535
574
  disabled: !inputValue.trim() || !isConnected,
536
575
  "aria-label": "Send message",
537
- children: /* @__PURE__ */ jsx(SendIcon, {})
576
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SendIcon, {})
538
577
  }
539
578
  )
540
579
  ] }),
541
- /* @__PURE__ */ jsxs("div", { class: "pp-footer", children: [
580
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-footer", children: [
542
581
  "Powered by ",
543
- /* @__PURE__ */ jsx("a", { href: "https://pocketping.io", target: "_blank", rel: "noopener", children: "PocketPing" })
582
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: "https://pocketping.io", target: "_blank", rel: "noopener", children: "PocketPing" })
544
583
  ] })
545
584
  ] })
546
585
  ] });
@@ -566,41 +605,41 @@ function formatTime(timestamp) {
566
605
  return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
567
606
  }
568
607
  function ChatIcon() {
569
- return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: /* @__PURE__ */ jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
608
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
570
609
  }
571
610
  function CloseIcon() {
572
- return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
573
- /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
574
- /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
611
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
612
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
613
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
575
614
  ] });
576
615
  }
577
616
  function SendIcon() {
578
- return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
579
- /* @__PURE__ */ jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
580
- /* @__PURE__ */ jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
617
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
618
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
619
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
581
620
  ] });
582
621
  }
583
622
  function StatusIcon({ status }) {
584
623
  if (!status || status === "sending" || status === "sent") {
585
- return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", "stroke-width": "2", class: "pp-check", children: /* @__PURE__ */ jsx("polyline", { points: "3 8 7 12 13 4" }) });
624
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", "stroke-width": "2", class: "pp-check", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "3 8 7 12 13 4" }) });
586
625
  }
587
626
  if (status === "delivered") {
588
- return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 20 16", fill: "none", stroke: "currentColor", "stroke-width": "2", class: "pp-check-double", children: [
589
- /* @__PURE__ */ jsx("polyline", { points: "1 8 5 12 11 4" }),
590
- /* @__PURE__ */ jsx("polyline", { points: "7 8 11 12 17 4" })
627
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 20 16", fill: "none", stroke: "currentColor", "stroke-width": "2", class: "pp-check-double", children: [
628
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "1 8 5 12 11 4" }),
629
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "7 8 11 12 17 4" })
591
630
  ] });
592
631
  }
593
632
  if (status === "read") {
594
- return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 20 16", fill: "none", stroke: "currentColor", "stroke-width": "2", class: "pp-check-double pp-check-read", children: [
595
- /* @__PURE__ */ jsx("polyline", { points: "1 8 5 12 11 4" }),
596
- /* @__PURE__ */ jsx("polyline", { points: "7 8 11 12 17 4" })
633
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 20 16", fill: "none", stroke: "currentColor", "stroke-width": "2", class: "pp-check-double pp-check-read", children: [
634
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "1 8 5 12 11 4" }),
635
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "7 8 11 12 17 4" })
597
636
  ] });
598
637
  }
599
638
  return null;
600
639
  }
601
640
 
602
641
  // src/version.ts
603
- var VERSION = "0.3.3";
642
+ var VERSION = "0.3.4";
604
643
 
605
644
  // src/client.ts
606
645
  var PocketPingClient = class {
@@ -613,6 +652,10 @@ var PocketPingClient = class {
613
652
  this.reconnectAttempts = 0;
614
653
  this.maxReconnectAttempts = 5;
615
654
  this.reconnectTimeout = null;
655
+ this.trackedElementCleanups = [];
656
+ this.currentTrackedElements = [];
657
+ this.inspectorMode = false;
658
+ this.inspectorCleanup = null;
616
659
  this.config = config;
617
660
  }
618
661
  // ─────────────────────────────────────────────────────────────────
@@ -622,11 +665,14 @@ var PocketPingClient = class {
622
665
  const visitorId = this.getOrCreateVisitorId();
623
666
  const storedSessionId = this.getStoredSessionId();
624
667
  const storedIdentity = this.getStoredIdentity();
668
+ const urlParams = new URLSearchParams(window.location.search);
669
+ const inspectorToken = urlParams.get("pp_inspector");
625
670
  const response = await this.fetch("/connect", {
626
671
  method: "POST",
627
672
  body: JSON.stringify({
628
673
  visitorId,
629
674
  sessionId: storedSessionId,
675
+ inspectorToken: inspectorToken || void 0,
630
676
  metadata: {
631
677
  url: window.location.href,
632
678
  referrer: document.referrer || void 0,
@@ -649,6 +695,11 @@ var PocketPingClient = class {
649
695
  };
650
696
  this.storeSessionId(response.sessionId);
651
697
  this.connectWebSocket();
698
+ if (response.inspectorMode) {
699
+ this.enableInspectorMode();
700
+ } else if (response.trackedElements?.length) {
701
+ this.setupTrackedElements(response.trackedElements);
702
+ }
652
703
  this.emit("connect", this.session);
653
704
  this.config.onConnect?.(response.sessionId);
654
705
  return this.session;
@@ -660,6 +711,8 @@ var PocketPingClient = class {
660
711
  if (this.reconnectTimeout) {
661
712
  clearTimeout(this.reconnectTimeout);
662
713
  }
714
+ this.cleanupTrackedElements();
715
+ this.disableInspectorMode();
663
716
  }
664
717
  async sendMessage(content) {
665
718
  if (!this.session) {
@@ -879,10 +932,15 @@ var PocketPingClient = class {
879
932
  * Trigger a custom event to the backend
880
933
  * @param eventName - The name of the event (e.g., 'clicked_pricing', 'viewed_demo')
881
934
  * @param data - Optional payload to send with the event
935
+ * @param options - Optional trigger options (widgetMessage to open chat)
882
936
  * @example
883
- * PocketPing.trigger('clicked_cta', { button: 'signup', page: '/pricing' })
937
+ * // Silent event (just notify bridges)
938
+ * PocketPing.trigger('clicked_cta', { button: 'signup' })
939
+ *
940
+ * // Open widget with message
941
+ * PocketPing.trigger('clicked_pricing', { plan: 'pro' }, { widgetMessage: 'Need help choosing a plan?' })
884
942
  */
885
- trigger(eventName, data) {
943
+ trigger(eventName, data, options) {
886
944
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
887
945
  console.warn("[PocketPing] Cannot trigger event: WebSocket not connected");
888
946
  return;
@@ -897,6 +955,10 @@ var PocketPingClient = class {
897
955
  data: event
898
956
  }));
899
957
  this.emit(`event:${eventName}`, event);
958
+ if (options?.widgetMessage) {
959
+ this.setOpen(true);
960
+ this.emit("triggerMessage", { message: options.widgetMessage, eventName });
961
+ }
900
962
  }
901
963
  /**
902
964
  * Subscribe to custom events from the backend
@@ -934,6 +996,317 @@ var PocketPingClient = class {
934
996
  this.emit(`event:${event.name}`, event);
935
997
  }
936
998
  // ─────────────────────────────────────────────────────────────────
999
+ // Tracked Elements (SaaS auto-tracking)
1000
+ // ─────────────────────────────────────────────────────────────────
1001
+ /**
1002
+ * Setup tracked elements from config (used by SaaS dashboard)
1003
+ * @param elements - Array of tracked element configurations
1004
+ */
1005
+ setupTrackedElements(elements) {
1006
+ this.cleanupTrackedElements();
1007
+ this.currentTrackedElements = elements;
1008
+ for (const config of elements) {
1009
+ const eventType = config.event || "click";
1010
+ const handler = (domEvent) => {
1011
+ const elementData = {
1012
+ ...config.data,
1013
+ selector: config.selector,
1014
+ elementText: domEvent.target?.textContent?.trim().slice(0, 100),
1015
+ url: window.location.href
1016
+ };
1017
+ this.trigger(config.name, elementData, {
1018
+ widgetMessage: config.widgetMessage
1019
+ });
1020
+ };
1021
+ const delegatedHandler = (domEvent) => {
1022
+ const target = domEvent.target;
1023
+ if (target?.closest(config.selector)) {
1024
+ handler(domEvent);
1025
+ }
1026
+ };
1027
+ document.addEventListener(eventType, delegatedHandler, true);
1028
+ this.trackedElementCleanups.push(() => {
1029
+ document.removeEventListener(eventType, delegatedHandler, true);
1030
+ });
1031
+ }
1032
+ if (elements.length > 0) {
1033
+ console.info(`[PocketPing] Tracking ${elements.length} element(s)`);
1034
+ }
1035
+ }
1036
+ /**
1037
+ * Cleanup all tracked element listeners
1038
+ */
1039
+ cleanupTrackedElements() {
1040
+ for (const cleanup of this.trackedElementCleanups) {
1041
+ cleanup();
1042
+ }
1043
+ this.trackedElementCleanups = [];
1044
+ this.currentTrackedElements = [];
1045
+ }
1046
+ /**
1047
+ * Get current tracked elements configuration
1048
+ */
1049
+ getTrackedElements() {
1050
+ return [...this.currentTrackedElements];
1051
+ }
1052
+ // ─────────────────────────────────────────────────────────────────
1053
+ // Inspector Mode (SaaS visual element selector)
1054
+ // ─────────────────────────────────────────────────────────────────
1055
+ /**
1056
+ * Enable inspector mode for visual element selection
1057
+ * This shows an overlay that allows clicking on elements to get their CSS selector
1058
+ */
1059
+ enableInspectorMode() {
1060
+ if (this.inspectorMode) return;
1061
+ this.inspectorMode = true;
1062
+ console.info("[PocketPing] \u{1F50D} Inspector mode active - click on any element to select it");
1063
+ const overlay = document.createElement("div");
1064
+ overlay.id = "pp-inspector-overlay";
1065
+ overlay.innerHTML = `
1066
+ <style>
1067
+ #pp-inspector-overlay {
1068
+ position: fixed;
1069
+ top: 0;
1070
+ left: 0;
1071
+ right: 0;
1072
+ bottom: 0;
1073
+ z-index: 999999;
1074
+ pointer-events: none;
1075
+ }
1076
+ #pp-inspector-overlay * {
1077
+ pointer-events: auto;
1078
+ }
1079
+ #pp-inspector-banner {
1080
+ position: fixed;
1081
+ top: 16px;
1082
+ left: 50%;
1083
+ transform: translateX(-50%);
1084
+ background: linear-gradient(135deg, #6366f1, #8b5cf6);
1085
+ color: white;
1086
+ padding: 12px 24px;
1087
+ border-radius: 12px;
1088
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1089
+ font-size: 14px;
1090
+ font-weight: 500;
1091
+ box-shadow: 0 4px 20px rgba(99, 102, 241, 0.4);
1092
+ display: flex;
1093
+ align-items: center;
1094
+ gap: 12px;
1095
+ z-index: 1000000;
1096
+ }
1097
+ #pp-inspector-banner svg {
1098
+ animation: pp-pulse 1.5s ease-in-out infinite;
1099
+ }
1100
+ @keyframes pp-pulse {
1101
+ 0%, 100% { opacity: 1; }
1102
+ 50% { opacity: 0.5; }
1103
+ }
1104
+ #pp-inspector-exit {
1105
+ background: rgba(255,255,255,0.2);
1106
+ border: none;
1107
+ color: white;
1108
+ padding: 6px 12px;
1109
+ border-radius: 6px;
1110
+ cursor: pointer;
1111
+ font-size: 12px;
1112
+ margin-left: 8px;
1113
+ }
1114
+ #pp-inspector-exit:hover {
1115
+ background: rgba(255,255,255,0.3);
1116
+ }
1117
+ .pp-inspector-highlight {
1118
+ outline: 3px dashed #6366f1 !important;
1119
+ outline-offset: 2px !important;
1120
+ background: rgba(99, 102, 241, 0.1) !important;
1121
+ }
1122
+ #pp-inspector-tooltip {
1123
+ position: fixed;
1124
+ background: #1f2937;
1125
+ color: white;
1126
+ padding: 8px 12px;
1127
+ border-radius: 8px;
1128
+ font-family: ui-monospace, SFMono-Regular, monospace;
1129
+ font-size: 12px;
1130
+ pointer-events: none;
1131
+ z-index: 1000001;
1132
+ max-width: 400px;
1133
+ word-break: break-all;
1134
+ display: none;
1135
+ }
1136
+ </style>
1137
+ <div id="pp-inspector-banner">
1138
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1139
+ <circle cx="12" cy="12" r="10"/>
1140
+ <line x1="22" y1="22" x2="16.65" y2="16.65"/>
1141
+ <line x1="12" y1="8" x2="12" y2="12"/>
1142
+ <line x1="12" y1="16" x2="12" y2="16"/>
1143
+ </svg>
1144
+ <span>Inspector Mode - Click an element to capture its selector</span>
1145
+ <button id="pp-inspector-exit">Exit</button>
1146
+ </div>
1147
+ <div id="pp-inspector-tooltip"></div>
1148
+ `;
1149
+ document.body.appendChild(overlay);
1150
+ const tooltip = document.getElementById("pp-inspector-tooltip");
1151
+ let currentHighlight = null;
1152
+ const getSelector = (element) => {
1153
+ if (element.id && !element.id.startsWith("pp-")) {
1154
+ return `#${CSS.escape(element.id)}`;
1155
+ }
1156
+ const classes = Array.from(element.classList).filter((c) => !c.startsWith("pp-"));
1157
+ if (classes.length > 0) {
1158
+ const classSelector = "." + classes.map((c) => CSS.escape(c)).join(".");
1159
+ if (document.querySelectorAll(classSelector).length === 1) {
1160
+ return classSelector;
1161
+ }
1162
+ }
1163
+ for (const attr of Array.from(element.attributes)) {
1164
+ if (attr.name.startsWith("data-") && attr.value) {
1165
+ const dataSelector = `[${attr.name}="${CSS.escape(attr.value)}"]`;
1166
+ if (document.querySelectorAll(dataSelector).length === 1) {
1167
+ return dataSelector;
1168
+ }
1169
+ }
1170
+ }
1171
+ const path = [];
1172
+ let current = element;
1173
+ while (current && current !== document.body) {
1174
+ let selector = current.tagName.toLowerCase();
1175
+ if (current.id && !current.id.startsWith("pp-")) {
1176
+ selector = `#${CSS.escape(current.id)}`;
1177
+ path.unshift(selector);
1178
+ break;
1179
+ }
1180
+ const parent = current.parentElement;
1181
+ if (parent) {
1182
+ const currentTagName = current.tagName;
1183
+ const siblings = Array.from(parent.children).filter(
1184
+ (c) => c.tagName === currentTagName
1185
+ );
1186
+ if (siblings.length > 1) {
1187
+ const index = siblings.indexOf(current) + 1;
1188
+ selector += `:nth-of-type(${index})`;
1189
+ }
1190
+ }
1191
+ path.unshift(selector);
1192
+ current = parent;
1193
+ }
1194
+ return path.join(" > ");
1195
+ };
1196
+ const handleMouseOver = (e) => {
1197
+ const target = e.target;
1198
+ if (target.closest("#pp-inspector-overlay") || target.closest("#pocketping-widget")) {
1199
+ return;
1200
+ }
1201
+ if (currentHighlight) {
1202
+ currentHighlight.classList.remove("pp-inspector-highlight");
1203
+ }
1204
+ target.classList.add("pp-inspector-highlight");
1205
+ currentHighlight = target;
1206
+ const selector = getSelector(target);
1207
+ tooltip.textContent = selector;
1208
+ tooltip.style.display = "block";
1209
+ tooltip.style.left = `${e.clientX + 15}px`;
1210
+ tooltip.style.top = `${e.clientY + 15}px`;
1211
+ const rect = tooltip.getBoundingClientRect();
1212
+ if (rect.right > window.innerWidth) {
1213
+ tooltip.style.left = `${e.clientX - rect.width - 15}px`;
1214
+ }
1215
+ if (rect.bottom > window.innerHeight) {
1216
+ tooltip.style.top = `${e.clientY - rect.height - 15}px`;
1217
+ }
1218
+ };
1219
+ const handleMouseOut = (e) => {
1220
+ const target = e.target;
1221
+ if (target.closest("#pp-inspector-overlay")) return;
1222
+ target.classList.remove("pp-inspector-highlight");
1223
+ tooltip.style.display = "none";
1224
+ };
1225
+ const handleClick = (e) => {
1226
+ const target = e.target;
1227
+ if (target.id === "pp-inspector-exit") {
1228
+ this.disableInspectorMode();
1229
+ return;
1230
+ }
1231
+ if (target.closest("#pp-inspector-overlay") || target.closest("#pocketping-widget")) {
1232
+ return;
1233
+ }
1234
+ e.preventDefault();
1235
+ e.stopPropagation();
1236
+ const selector = getSelector(target);
1237
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
1238
+ this.ws.send(JSON.stringify({
1239
+ type: "inspector_select",
1240
+ data: {
1241
+ selector,
1242
+ tagName: target.tagName.toLowerCase(),
1243
+ text: target.textContent?.trim().slice(0, 50) || "",
1244
+ url: window.location.href
1245
+ }
1246
+ }));
1247
+ }
1248
+ this.emit("inspectorSelect", { selector, element: target });
1249
+ target.classList.remove("pp-inspector-highlight");
1250
+ target.classList.add("pp-inspector-highlight");
1251
+ setTimeout(() => {
1252
+ target.classList.remove("pp-inspector-highlight");
1253
+ }, 500);
1254
+ const banner = document.getElementById("pp-inspector-banner");
1255
+ if (banner) {
1256
+ const originalHTML = banner.innerHTML;
1257
+ banner.innerHTML = `
1258
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1259
+ <polyline points="20 6 9 17 4 12"/>
1260
+ </svg>
1261
+ <span>Selector captured: <code style="background:rgba(255,255,255,0.2);padding:2px 6px;border-radius:4px;font-family:monospace;">${selector}</code></span>
1262
+ `;
1263
+ setTimeout(() => {
1264
+ if (banner && this.inspectorMode) {
1265
+ banner.innerHTML = originalHTML;
1266
+ document.getElementById("pp-inspector-exit")?.addEventListener("click", () => {
1267
+ this.disableInspectorMode();
1268
+ });
1269
+ }
1270
+ }, 2e3);
1271
+ }
1272
+ console.info(`[PocketPing] \u{1F4CC} Selector captured: ${selector}`);
1273
+ };
1274
+ document.addEventListener("mouseover", handleMouseOver, true);
1275
+ document.addEventListener("mouseout", handleMouseOut, true);
1276
+ document.addEventListener("click", handleClick, true);
1277
+ this.inspectorCleanup = () => {
1278
+ document.removeEventListener("mouseover", handleMouseOver, true);
1279
+ document.removeEventListener("mouseout", handleMouseOut, true);
1280
+ document.removeEventListener("click", handleClick, true);
1281
+ overlay.remove();
1282
+ if (currentHighlight) {
1283
+ currentHighlight.classList.remove("pp-inspector-highlight");
1284
+ }
1285
+ };
1286
+ document.getElementById("pp-inspector-exit")?.addEventListener("click", () => {
1287
+ this.disableInspectorMode();
1288
+ });
1289
+ }
1290
+ /**
1291
+ * Disable inspector mode
1292
+ */
1293
+ disableInspectorMode() {
1294
+ if (!this.inspectorMode) return;
1295
+ this.inspectorMode = false;
1296
+ if (this.inspectorCleanup) {
1297
+ this.inspectorCleanup();
1298
+ this.inspectorCleanup = null;
1299
+ }
1300
+ console.info("[PocketPing] Inspector mode disabled");
1301
+ this.emit("inspectorDisabled", null);
1302
+ }
1303
+ /**
1304
+ * Check if inspector mode is active
1305
+ */
1306
+ isInspectorModeActive() {
1307
+ return this.inspectorMode;
1308
+ }
1309
+ // ─────────────────────────────────────────────────────────────────
937
1310
  // WebSocket
938
1311
  // ─────────────────────────────────────────────────────────────────
939
1312
  connectWebSocket() {
@@ -1039,6 +1412,13 @@ var PocketPingClient = class {
1039
1412
  const versionWarning = event.data;
1040
1413
  this.handleVersionWarning(versionWarning);
1041
1414
  break;
1415
+ case "config_update":
1416
+ const configData = event.data;
1417
+ if (configData.trackedElements) {
1418
+ this.setupTrackedElements(configData.trackedElements);
1419
+ this.emit("configUpdate", configData);
1420
+ }
1421
+ break;
1042
1422
  }
1043
1423
  }
1044
1424
  // ─────────────────────────────────────────────────────────────────
@@ -1208,7 +1588,7 @@ function init(config) {
1208
1588
  container = document.createElement("div");
1209
1589
  container.id = "pocketping-container";
1210
1590
  document.body.appendChild(container);
1211
- render(h2(ChatWidget, { client, config }), container);
1591
+ (0, import_preact2.render)((0, import_preact2.h)(ChatWidget, { client, config }), container);
1212
1592
  client.connect().catch((err) => {
1213
1593
  console.error("[PocketPing] Failed to connect:", err);
1214
1594
  });
@@ -1216,7 +1596,7 @@ function init(config) {
1216
1596
  }
1217
1597
  function destroy() {
1218
1598
  if (container) {
1219
- render(null, container);
1599
+ (0, import_preact2.render)(null, container);
1220
1600
  container.remove();
1221
1601
  container = null;
1222
1602
  }
@@ -1240,12 +1620,22 @@ function sendMessage(content) {
1240
1620
  }
1241
1621
  return client.sendMessage(content);
1242
1622
  }
1243
- function trigger(eventName, data) {
1623
+ function trigger(eventName, data, options) {
1244
1624
  if (!client) {
1245
1625
  console.warn("[PocketPing] Not initialized, cannot trigger event");
1246
1626
  return;
1247
1627
  }
1248
- client.trigger(eventName, data);
1628
+ client.trigger(eventName, data, options);
1629
+ }
1630
+ function setupTrackedElements(elements) {
1631
+ if (!client) {
1632
+ console.warn("[PocketPing] Not initialized, cannot setup tracked elements");
1633
+ return;
1634
+ }
1635
+ client.setupTrackedElements(elements);
1636
+ }
1637
+ function getTrackedElements() {
1638
+ return client?.getTrackedElements() || [];
1249
1639
  }
1250
1640
  function onEvent(eventName, handler) {
1251
1641
  if (!client) {
@@ -1297,12 +1687,13 @@ if (typeof document !== "undefined") {
1297
1687
  }
1298
1688
  }
1299
1689
  }
1300
- var index_default = { init, destroy, open, close, toggle, sendMessage, trigger, onEvent, offEvent, on, identify, reset, getIdentity };
1301
- export {
1690
+ var index_default = { init, destroy, open, close, toggle, sendMessage, trigger, onEvent, offEvent, on, identify, reset, getIdentity, setupTrackedElements, getTrackedElements };
1691
+ // Annotate the CommonJS export names for ESM import in node:
1692
+ 0 && (module.exports = {
1302
1693
  close,
1303
- index_default as default,
1304
1694
  destroy,
1305
1695
  getIdentity,
1696
+ getTrackedElements,
1306
1697
  identify,
1307
1698
  init,
1308
1699
  offEvent,
@@ -1311,6 +1702,7 @@ export {
1311
1702
  open,
1312
1703
  reset,
1313
1704
  sendMessage,
1705
+ setupTrackedElements,
1314
1706
  toggle,
1315
1707
  trigger
1316
- };
1708
+ });