@pocketping/widget 0.3.2 → 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.
- package/dist/{index.mjs → index.cjs} +471 -79
- package/dist/{index.d.mts → index.d.cts} +79 -5
- package/dist/index.d.ts +79 -5
- package/dist/index.js +432 -116
- package/dist/pocketping.min.global.js +90 -2
- package/package.json +9 -8
package/dist/index.js
CHANGED
|
@@ -1,46 +1,9 @@
|
|
|
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
|
-
|
|
20
1
|
// src/index.ts
|
|
21
|
-
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
close: () => close,
|
|
24
|
-
default: () => index_default,
|
|
25
|
-
destroy: () => destroy,
|
|
26
|
-
getIdentity: () => getIdentity,
|
|
27
|
-
identify: () => identify,
|
|
28
|
-
init: () => init,
|
|
29
|
-
offEvent: () => offEvent,
|
|
30
|
-
on: () => on,
|
|
31
|
-
onEvent: () => onEvent,
|
|
32
|
-
open: () => open,
|
|
33
|
-
reset: () => reset,
|
|
34
|
-
sendMessage: () => sendMessage,
|
|
35
|
-
toggle: () => toggle,
|
|
36
|
-
trigger: () => trigger
|
|
37
|
-
});
|
|
38
|
-
module.exports = __toCommonJS(index_exports);
|
|
39
|
-
var import_preact2 = require("preact");
|
|
2
|
+
import { render, h as h2 } from "preact";
|
|
40
3
|
|
|
41
4
|
// src/components/ChatWidget.tsx
|
|
42
|
-
|
|
43
|
-
|
|
5
|
+
import { Fragment } from "preact";
|
|
6
|
+
import { useState, useEffect, useRef, useCallback } from "preact/hooks";
|
|
44
7
|
|
|
45
8
|
// src/components/styles.ts
|
|
46
9
|
function styles(primaryColor, theme) {
|
|
@@ -385,17 +348,17 @@ function styles(primaryColor, theme) {
|
|
|
385
348
|
}
|
|
386
349
|
|
|
387
350
|
// src/components/ChatWidget.tsx
|
|
388
|
-
|
|
351
|
+
import { Fragment as Fragment2, jsx, jsxs } from "preact/jsx-runtime";
|
|
389
352
|
function ChatWidget({ client: client2, config }) {
|
|
390
|
-
const [isOpen, setIsOpen] =
|
|
391
|
-
const [messages, setMessages] =
|
|
392
|
-
const [inputValue, setInputValue] =
|
|
393
|
-
const [isTyping, setIsTyping] =
|
|
394
|
-
const [operatorOnline, setOperatorOnline] =
|
|
395
|
-
const [isConnected, setIsConnected] =
|
|
396
|
-
const messagesEndRef =
|
|
397
|
-
const inputRef =
|
|
398
|
-
|
|
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(() => {
|
|
399
362
|
const unsubOpen = client2.on("openChange", setIsOpen);
|
|
400
363
|
const unsubMessage = client2.on("message", () => {
|
|
401
364
|
setMessages([...client2.getMessages()]);
|
|
@@ -424,15 +387,15 @@ function ChatWidget({ client: client2, config }) {
|
|
|
424
387
|
unsubConnect();
|
|
425
388
|
};
|
|
426
389
|
}, [client2]);
|
|
427
|
-
|
|
390
|
+
useEffect(() => {
|
|
428
391
|
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
429
392
|
}, [messages]);
|
|
430
|
-
|
|
393
|
+
useEffect(() => {
|
|
431
394
|
if (isOpen) {
|
|
432
395
|
inputRef.current?.focus();
|
|
433
396
|
}
|
|
434
397
|
}, [isOpen]);
|
|
435
|
-
const markMessagesAsRead =
|
|
398
|
+
const markMessagesAsRead = useCallback(() => {
|
|
436
399
|
if (!isOpen || !isConnected) return;
|
|
437
400
|
const unreadMessages = messages.filter(
|
|
438
401
|
(msg) => msg.sender !== "visitor" && msg.status !== "read"
|
|
@@ -442,14 +405,14 @@ function ChatWidget({ client: client2, config }) {
|
|
|
442
405
|
client2.sendReadStatus(messageIds, "read");
|
|
443
406
|
}
|
|
444
407
|
}, [isOpen, isConnected, messages, client2]);
|
|
445
|
-
|
|
408
|
+
useEffect(() => {
|
|
446
409
|
if (!isOpen || !isConnected) return;
|
|
447
410
|
const timer = setTimeout(() => {
|
|
448
411
|
markMessagesAsRead();
|
|
449
412
|
}, 1e3);
|
|
450
413
|
return () => clearTimeout(timer);
|
|
451
414
|
}, [isOpen, isConnected, messages, markMessagesAsRead]);
|
|
452
|
-
|
|
415
|
+
useEffect(() => {
|
|
453
416
|
const handleVisibilityChange = () => {
|
|
454
417
|
if (document.visibilityState === "visible" && isOpen) {
|
|
455
418
|
markMessagesAsRead();
|
|
@@ -458,7 +421,7 @@ function ChatWidget({ client: client2, config }) {
|
|
|
458
421
|
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
459
422
|
return () => document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
460
423
|
}, [isOpen, markMessagesAsRead]);
|
|
461
|
-
|
|
424
|
+
useEffect(() => {
|
|
462
425
|
const unsubRead = client2.on(
|
|
463
426
|
"read",
|
|
464
427
|
() => {
|
|
@@ -488,71 +451,71 @@ function ChatWidget({ client: client2, config }) {
|
|
|
488
451
|
const position = config.position ?? "bottom-right";
|
|
489
452
|
const theme = getTheme(config.theme ?? "auto");
|
|
490
453
|
const primaryColor = config.primaryColor ?? "#6366f1";
|
|
491
|
-
return /* @__PURE__ */
|
|
492
|
-
/* @__PURE__ */
|
|
493
|
-
/* @__PURE__ */
|
|
454
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
455
|
+
/* @__PURE__ */ jsx("style", { children: styles(primaryColor, theme) }),
|
|
456
|
+
/* @__PURE__ */ jsxs(
|
|
494
457
|
"button",
|
|
495
458
|
{
|
|
496
459
|
class: `pp-toggle pp-${position}`,
|
|
497
460
|
onClick: () => client2.toggleOpen(),
|
|
498
461
|
"aria-label": isOpen ? "Close chat" : "Open chat",
|
|
499
462
|
children: [
|
|
500
|
-
isOpen ? /* @__PURE__ */
|
|
501
|
-
!isOpen && operatorOnline && /* @__PURE__ */
|
|
463
|
+
isOpen ? /* @__PURE__ */ jsx(CloseIcon, {}) : /* @__PURE__ */ jsx(ChatIcon, {}),
|
|
464
|
+
!isOpen && operatorOnline && /* @__PURE__ */ jsx("span", { class: "pp-online-dot" })
|
|
502
465
|
]
|
|
503
466
|
}
|
|
504
467
|
),
|
|
505
|
-
isOpen && /* @__PURE__ */
|
|
506
|
-
/* @__PURE__ */
|
|
507
|
-
/* @__PURE__ */
|
|
508
|
-
config.operatorAvatar && /* @__PURE__ */
|
|
509
|
-
/* @__PURE__ */
|
|
510
|
-
/* @__PURE__ */
|
|
511
|
-
/* @__PURE__ */
|
|
512
|
-
/* @__PURE__ */
|
|
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" }),
|
|
513
476
|
" Online"
|
|
514
|
-
] }) : /* @__PURE__ */
|
|
515
|
-
/* @__PURE__ */
|
|
477
|
+
] }) : /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
478
|
+
/* @__PURE__ */ jsx("span", { class: "pp-status-dot" }),
|
|
516
479
|
" Away"
|
|
517
480
|
] }) })
|
|
518
481
|
] })
|
|
519
482
|
] }),
|
|
520
|
-
/* @__PURE__ */
|
|
483
|
+
/* @__PURE__ */ jsx(
|
|
521
484
|
"button",
|
|
522
485
|
{
|
|
523
486
|
class: "pp-close-btn",
|
|
524
487
|
onClick: () => client2.setOpen(false),
|
|
525
488
|
"aria-label": "Close chat",
|
|
526
|
-
children: /* @__PURE__ */
|
|
489
|
+
children: /* @__PURE__ */ jsx(CloseIcon, {})
|
|
527
490
|
}
|
|
528
491
|
)
|
|
529
492
|
] }),
|
|
530
|
-
/* @__PURE__ */
|
|
531
|
-
config.welcomeMessage && messages.length === 0 && /* @__PURE__ */
|
|
532
|
-
messages.map((msg) => /* @__PURE__ */
|
|
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(
|
|
533
496
|
"div",
|
|
534
497
|
{
|
|
535
498
|
class: `pp-message pp-message-${msg.sender}`,
|
|
536
499
|
children: [
|
|
537
|
-
/* @__PURE__ */
|
|
538
|
-
/* @__PURE__ */
|
|
500
|
+
/* @__PURE__ */ jsx("div", { class: "pp-message-content", children: msg.content }),
|
|
501
|
+
/* @__PURE__ */ jsxs("div", { class: "pp-message-time", children: [
|
|
539
502
|
formatTime(msg.timestamp),
|
|
540
|
-
msg.sender === "ai" && /* @__PURE__ */
|
|
541
|
-
msg.sender === "visitor" && /* @__PURE__ */
|
|
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
505
|
] })
|
|
543
506
|
]
|
|
544
507
|
},
|
|
545
508
|
msg.id
|
|
546
509
|
)),
|
|
547
|
-
isTyping && /* @__PURE__ */
|
|
548
|
-
/* @__PURE__ */
|
|
549
|
-
/* @__PURE__ */
|
|
550
|
-
/* @__PURE__ */
|
|
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", {})
|
|
551
514
|
] }),
|
|
552
|
-
/* @__PURE__ */
|
|
515
|
+
/* @__PURE__ */ jsx("div", { ref: messagesEndRef })
|
|
553
516
|
] }),
|
|
554
|
-
/* @__PURE__ */
|
|
555
|
-
/* @__PURE__ */
|
|
517
|
+
/* @__PURE__ */ jsxs("form", { class: "pp-input-form", onSubmit: handleSubmit, children: [
|
|
518
|
+
/* @__PURE__ */ jsx(
|
|
556
519
|
"input",
|
|
557
520
|
{
|
|
558
521
|
ref: inputRef,
|
|
@@ -564,20 +527,20 @@ function ChatWidget({ client: client2, config }) {
|
|
|
564
527
|
disabled: !isConnected
|
|
565
528
|
}
|
|
566
529
|
),
|
|
567
|
-
/* @__PURE__ */
|
|
530
|
+
/* @__PURE__ */ jsx(
|
|
568
531
|
"button",
|
|
569
532
|
{
|
|
570
533
|
type: "submit",
|
|
571
534
|
class: "pp-send-btn",
|
|
572
535
|
disabled: !inputValue.trim() || !isConnected,
|
|
573
536
|
"aria-label": "Send message",
|
|
574
|
-
children: /* @__PURE__ */
|
|
537
|
+
children: /* @__PURE__ */ jsx(SendIcon, {})
|
|
575
538
|
}
|
|
576
539
|
)
|
|
577
540
|
] }),
|
|
578
|
-
/* @__PURE__ */
|
|
541
|
+
/* @__PURE__ */ jsxs("div", { class: "pp-footer", children: [
|
|
579
542
|
"Powered by ",
|
|
580
|
-
/* @__PURE__ */
|
|
543
|
+
/* @__PURE__ */ jsx("a", { href: "https://pocketping.io", target: "_blank", rel: "noopener", children: "PocketPing" })
|
|
581
544
|
] })
|
|
582
545
|
] })
|
|
583
546
|
] });
|
|
@@ -603,41 +566,41 @@ function formatTime(timestamp) {
|
|
|
603
566
|
return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
604
567
|
}
|
|
605
568
|
function ChatIcon() {
|
|
606
|
-
return /* @__PURE__ */
|
|
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" }) });
|
|
607
570
|
}
|
|
608
571
|
function CloseIcon() {
|
|
609
|
-
return /* @__PURE__ */
|
|
610
|
-
/* @__PURE__ */
|
|
611
|
-
/* @__PURE__ */
|
|
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" })
|
|
612
575
|
] });
|
|
613
576
|
}
|
|
614
577
|
function SendIcon() {
|
|
615
|
-
return /* @__PURE__ */
|
|
616
|
-
/* @__PURE__ */
|
|
617
|
-
/* @__PURE__ */
|
|
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" })
|
|
618
581
|
] });
|
|
619
582
|
}
|
|
620
583
|
function StatusIcon({ status }) {
|
|
621
584
|
if (!status || status === "sending" || status === "sent") {
|
|
622
|
-
return /* @__PURE__ */
|
|
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" }) });
|
|
623
586
|
}
|
|
624
587
|
if (status === "delivered") {
|
|
625
|
-
return /* @__PURE__ */
|
|
626
|
-
/* @__PURE__ */
|
|
627
|
-
/* @__PURE__ */
|
|
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" })
|
|
628
591
|
] });
|
|
629
592
|
}
|
|
630
593
|
if (status === "read") {
|
|
631
|
-
return /* @__PURE__ */
|
|
632
|
-
/* @__PURE__ */
|
|
633
|
-
/* @__PURE__ */
|
|
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" })
|
|
634
597
|
] });
|
|
635
598
|
}
|
|
636
599
|
return null;
|
|
637
600
|
}
|
|
638
601
|
|
|
639
602
|
// src/version.ts
|
|
640
|
-
var VERSION = "0.3.
|
|
603
|
+
var VERSION = "0.3.4";
|
|
641
604
|
|
|
642
605
|
// src/client.ts
|
|
643
606
|
var PocketPingClient = class {
|
|
@@ -650,6 +613,10 @@ var PocketPingClient = class {
|
|
|
650
613
|
this.reconnectAttempts = 0;
|
|
651
614
|
this.maxReconnectAttempts = 5;
|
|
652
615
|
this.reconnectTimeout = null;
|
|
616
|
+
this.trackedElementCleanups = [];
|
|
617
|
+
this.currentTrackedElements = [];
|
|
618
|
+
this.inspectorMode = false;
|
|
619
|
+
this.inspectorCleanup = null;
|
|
653
620
|
this.config = config;
|
|
654
621
|
}
|
|
655
622
|
// ─────────────────────────────────────────────────────────────────
|
|
@@ -659,11 +626,14 @@ var PocketPingClient = class {
|
|
|
659
626
|
const visitorId = this.getOrCreateVisitorId();
|
|
660
627
|
const storedSessionId = this.getStoredSessionId();
|
|
661
628
|
const storedIdentity = this.getStoredIdentity();
|
|
629
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
630
|
+
const inspectorToken = urlParams.get("pp_inspector");
|
|
662
631
|
const response = await this.fetch("/connect", {
|
|
663
632
|
method: "POST",
|
|
664
633
|
body: JSON.stringify({
|
|
665
634
|
visitorId,
|
|
666
635
|
sessionId: storedSessionId,
|
|
636
|
+
inspectorToken: inspectorToken || void 0,
|
|
667
637
|
metadata: {
|
|
668
638
|
url: window.location.href,
|
|
669
639
|
referrer: document.referrer || void 0,
|
|
@@ -686,6 +656,11 @@ var PocketPingClient = class {
|
|
|
686
656
|
};
|
|
687
657
|
this.storeSessionId(response.sessionId);
|
|
688
658
|
this.connectWebSocket();
|
|
659
|
+
if (response.inspectorMode) {
|
|
660
|
+
this.enableInspectorMode();
|
|
661
|
+
} else if (response.trackedElements?.length) {
|
|
662
|
+
this.setupTrackedElements(response.trackedElements);
|
|
663
|
+
}
|
|
689
664
|
this.emit("connect", this.session);
|
|
690
665
|
this.config.onConnect?.(response.sessionId);
|
|
691
666
|
return this.session;
|
|
@@ -697,6 +672,8 @@ var PocketPingClient = class {
|
|
|
697
672
|
if (this.reconnectTimeout) {
|
|
698
673
|
clearTimeout(this.reconnectTimeout);
|
|
699
674
|
}
|
|
675
|
+
this.cleanupTrackedElements();
|
|
676
|
+
this.disableInspectorMode();
|
|
700
677
|
}
|
|
701
678
|
async sendMessage(content) {
|
|
702
679
|
if (!this.session) {
|
|
@@ -916,10 +893,15 @@ var PocketPingClient = class {
|
|
|
916
893
|
* Trigger a custom event to the backend
|
|
917
894
|
* @param eventName - The name of the event (e.g., 'clicked_pricing', 'viewed_demo')
|
|
918
895
|
* @param data - Optional payload to send with the event
|
|
896
|
+
* @param options - Optional trigger options (widgetMessage to open chat)
|
|
919
897
|
* @example
|
|
920
|
-
*
|
|
898
|
+
* // Silent event (just notify bridges)
|
|
899
|
+
* PocketPing.trigger('clicked_cta', { button: 'signup' })
|
|
900
|
+
*
|
|
901
|
+
* // Open widget with message
|
|
902
|
+
* PocketPing.trigger('clicked_pricing', { plan: 'pro' }, { widgetMessage: 'Need help choosing a plan?' })
|
|
921
903
|
*/
|
|
922
|
-
trigger(eventName, data) {
|
|
904
|
+
trigger(eventName, data, options) {
|
|
923
905
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
924
906
|
console.warn("[PocketPing] Cannot trigger event: WebSocket not connected");
|
|
925
907
|
return;
|
|
@@ -934,6 +916,10 @@ var PocketPingClient = class {
|
|
|
934
916
|
data: event
|
|
935
917
|
}));
|
|
936
918
|
this.emit(`event:${eventName}`, event);
|
|
919
|
+
if (options?.widgetMessage) {
|
|
920
|
+
this.setOpen(true);
|
|
921
|
+
this.emit("triggerMessage", { message: options.widgetMessage, eventName });
|
|
922
|
+
}
|
|
937
923
|
}
|
|
938
924
|
/**
|
|
939
925
|
* Subscribe to custom events from the backend
|
|
@@ -971,6 +957,317 @@ var PocketPingClient = class {
|
|
|
971
957
|
this.emit(`event:${event.name}`, event);
|
|
972
958
|
}
|
|
973
959
|
// ─────────────────────────────────────────────────────────────────
|
|
960
|
+
// Tracked Elements (SaaS auto-tracking)
|
|
961
|
+
// ─────────────────────────────────────────────────────────────────
|
|
962
|
+
/**
|
|
963
|
+
* Setup tracked elements from config (used by SaaS dashboard)
|
|
964
|
+
* @param elements - Array of tracked element configurations
|
|
965
|
+
*/
|
|
966
|
+
setupTrackedElements(elements) {
|
|
967
|
+
this.cleanupTrackedElements();
|
|
968
|
+
this.currentTrackedElements = elements;
|
|
969
|
+
for (const config of elements) {
|
|
970
|
+
const eventType = config.event || "click";
|
|
971
|
+
const handler = (domEvent) => {
|
|
972
|
+
const elementData = {
|
|
973
|
+
...config.data,
|
|
974
|
+
selector: config.selector,
|
|
975
|
+
elementText: domEvent.target?.textContent?.trim().slice(0, 100),
|
|
976
|
+
url: window.location.href
|
|
977
|
+
};
|
|
978
|
+
this.trigger(config.name, elementData, {
|
|
979
|
+
widgetMessage: config.widgetMessage
|
|
980
|
+
});
|
|
981
|
+
};
|
|
982
|
+
const delegatedHandler = (domEvent) => {
|
|
983
|
+
const target = domEvent.target;
|
|
984
|
+
if (target?.closest(config.selector)) {
|
|
985
|
+
handler(domEvent);
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
document.addEventListener(eventType, delegatedHandler, true);
|
|
989
|
+
this.trackedElementCleanups.push(() => {
|
|
990
|
+
document.removeEventListener(eventType, delegatedHandler, true);
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
if (elements.length > 0) {
|
|
994
|
+
console.info(`[PocketPing] Tracking ${elements.length} element(s)`);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* Cleanup all tracked element listeners
|
|
999
|
+
*/
|
|
1000
|
+
cleanupTrackedElements() {
|
|
1001
|
+
for (const cleanup of this.trackedElementCleanups) {
|
|
1002
|
+
cleanup();
|
|
1003
|
+
}
|
|
1004
|
+
this.trackedElementCleanups = [];
|
|
1005
|
+
this.currentTrackedElements = [];
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Get current tracked elements configuration
|
|
1009
|
+
*/
|
|
1010
|
+
getTrackedElements() {
|
|
1011
|
+
return [...this.currentTrackedElements];
|
|
1012
|
+
}
|
|
1013
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1014
|
+
// Inspector Mode (SaaS visual element selector)
|
|
1015
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1016
|
+
/**
|
|
1017
|
+
* Enable inspector mode for visual element selection
|
|
1018
|
+
* This shows an overlay that allows clicking on elements to get their CSS selector
|
|
1019
|
+
*/
|
|
1020
|
+
enableInspectorMode() {
|
|
1021
|
+
if (this.inspectorMode) return;
|
|
1022
|
+
this.inspectorMode = true;
|
|
1023
|
+
console.info("[PocketPing] \u{1F50D} Inspector mode active - click on any element to select it");
|
|
1024
|
+
const overlay = document.createElement("div");
|
|
1025
|
+
overlay.id = "pp-inspector-overlay";
|
|
1026
|
+
overlay.innerHTML = `
|
|
1027
|
+
<style>
|
|
1028
|
+
#pp-inspector-overlay {
|
|
1029
|
+
position: fixed;
|
|
1030
|
+
top: 0;
|
|
1031
|
+
left: 0;
|
|
1032
|
+
right: 0;
|
|
1033
|
+
bottom: 0;
|
|
1034
|
+
z-index: 999999;
|
|
1035
|
+
pointer-events: none;
|
|
1036
|
+
}
|
|
1037
|
+
#pp-inspector-overlay * {
|
|
1038
|
+
pointer-events: auto;
|
|
1039
|
+
}
|
|
1040
|
+
#pp-inspector-banner {
|
|
1041
|
+
position: fixed;
|
|
1042
|
+
top: 16px;
|
|
1043
|
+
left: 50%;
|
|
1044
|
+
transform: translateX(-50%);
|
|
1045
|
+
background: linear-gradient(135deg, #6366f1, #8b5cf6);
|
|
1046
|
+
color: white;
|
|
1047
|
+
padding: 12px 24px;
|
|
1048
|
+
border-radius: 12px;
|
|
1049
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
1050
|
+
font-size: 14px;
|
|
1051
|
+
font-weight: 500;
|
|
1052
|
+
box-shadow: 0 4px 20px rgba(99, 102, 241, 0.4);
|
|
1053
|
+
display: flex;
|
|
1054
|
+
align-items: center;
|
|
1055
|
+
gap: 12px;
|
|
1056
|
+
z-index: 1000000;
|
|
1057
|
+
}
|
|
1058
|
+
#pp-inspector-banner svg {
|
|
1059
|
+
animation: pp-pulse 1.5s ease-in-out infinite;
|
|
1060
|
+
}
|
|
1061
|
+
@keyframes pp-pulse {
|
|
1062
|
+
0%, 100% { opacity: 1; }
|
|
1063
|
+
50% { opacity: 0.5; }
|
|
1064
|
+
}
|
|
1065
|
+
#pp-inspector-exit {
|
|
1066
|
+
background: rgba(255,255,255,0.2);
|
|
1067
|
+
border: none;
|
|
1068
|
+
color: white;
|
|
1069
|
+
padding: 6px 12px;
|
|
1070
|
+
border-radius: 6px;
|
|
1071
|
+
cursor: pointer;
|
|
1072
|
+
font-size: 12px;
|
|
1073
|
+
margin-left: 8px;
|
|
1074
|
+
}
|
|
1075
|
+
#pp-inspector-exit:hover {
|
|
1076
|
+
background: rgba(255,255,255,0.3);
|
|
1077
|
+
}
|
|
1078
|
+
.pp-inspector-highlight {
|
|
1079
|
+
outline: 3px dashed #6366f1 !important;
|
|
1080
|
+
outline-offset: 2px !important;
|
|
1081
|
+
background: rgba(99, 102, 241, 0.1) !important;
|
|
1082
|
+
}
|
|
1083
|
+
#pp-inspector-tooltip {
|
|
1084
|
+
position: fixed;
|
|
1085
|
+
background: #1f2937;
|
|
1086
|
+
color: white;
|
|
1087
|
+
padding: 8px 12px;
|
|
1088
|
+
border-radius: 8px;
|
|
1089
|
+
font-family: ui-monospace, SFMono-Regular, monospace;
|
|
1090
|
+
font-size: 12px;
|
|
1091
|
+
pointer-events: none;
|
|
1092
|
+
z-index: 1000001;
|
|
1093
|
+
max-width: 400px;
|
|
1094
|
+
word-break: break-all;
|
|
1095
|
+
display: none;
|
|
1096
|
+
}
|
|
1097
|
+
</style>
|
|
1098
|
+
<div id="pp-inspector-banner">
|
|
1099
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1100
|
+
<circle cx="12" cy="12" r="10"/>
|
|
1101
|
+
<line x1="22" y1="22" x2="16.65" y2="16.65"/>
|
|
1102
|
+
<line x1="12" y1="8" x2="12" y2="12"/>
|
|
1103
|
+
<line x1="12" y1="16" x2="12" y2="16"/>
|
|
1104
|
+
</svg>
|
|
1105
|
+
<span>Inspector Mode - Click an element to capture its selector</span>
|
|
1106
|
+
<button id="pp-inspector-exit">Exit</button>
|
|
1107
|
+
</div>
|
|
1108
|
+
<div id="pp-inspector-tooltip"></div>
|
|
1109
|
+
`;
|
|
1110
|
+
document.body.appendChild(overlay);
|
|
1111
|
+
const tooltip = document.getElementById("pp-inspector-tooltip");
|
|
1112
|
+
let currentHighlight = null;
|
|
1113
|
+
const getSelector = (element) => {
|
|
1114
|
+
if (element.id && !element.id.startsWith("pp-")) {
|
|
1115
|
+
return `#${CSS.escape(element.id)}`;
|
|
1116
|
+
}
|
|
1117
|
+
const classes = Array.from(element.classList).filter((c) => !c.startsWith("pp-"));
|
|
1118
|
+
if (classes.length > 0) {
|
|
1119
|
+
const classSelector = "." + classes.map((c) => CSS.escape(c)).join(".");
|
|
1120
|
+
if (document.querySelectorAll(classSelector).length === 1) {
|
|
1121
|
+
return classSelector;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
for (const attr of Array.from(element.attributes)) {
|
|
1125
|
+
if (attr.name.startsWith("data-") && attr.value) {
|
|
1126
|
+
const dataSelector = `[${attr.name}="${CSS.escape(attr.value)}"]`;
|
|
1127
|
+
if (document.querySelectorAll(dataSelector).length === 1) {
|
|
1128
|
+
return dataSelector;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
const path = [];
|
|
1133
|
+
let current = element;
|
|
1134
|
+
while (current && current !== document.body) {
|
|
1135
|
+
let selector = current.tagName.toLowerCase();
|
|
1136
|
+
if (current.id && !current.id.startsWith("pp-")) {
|
|
1137
|
+
selector = `#${CSS.escape(current.id)}`;
|
|
1138
|
+
path.unshift(selector);
|
|
1139
|
+
break;
|
|
1140
|
+
}
|
|
1141
|
+
const parent = current.parentElement;
|
|
1142
|
+
if (parent) {
|
|
1143
|
+
const currentTagName = current.tagName;
|
|
1144
|
+
const siblings = Array.from(parent.children).filter(
|
|
1145
|
+
(c) => c.tagName === currentTagName
|
|
1146
|
+
);
|
|
1147
|
+
if (siblings.length > 1) {
|
|
1148
|
+
const index = siblings.indexOf(current) + 1;
|
|
1149
|
+
selector += `:nth-of-type(${index})`;
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
path.unshift(selector);
|
|
1153
|
+
current = parent;
|
|
1154
|
+
}
|
|
1155
|
+
return path.join(" > ");
|
|
1156
|
+
};
|
|
1157
|
+
const handleMouseOver = (e) => {
|
|
1158
|
+
const target = e.target;
|
|
1159
|
+
if (target.closest("#pp-inspector-overlay") || target.closest("#pocketping-widget")) {
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
if (currentHighlight) {
|
|
1163
|
+
currentHighlight.classList.remove("pp-inspector-highlight");
|
|
1164
|
+
}
|
|
1165
|
+
target.classList.add("pp-inspector-highlight");
|
|
1166
|
+
currentHighlight = target;
|
|
1167
|
+
const selector = getSelector(target);
|
|
1168
|
+
tooltip.textContent = selector;
|
|
1169
|
+
tooltip.style.display = "block";
|
|
1170
|
+
tooltip.style.left = `${e.clientX + 15}px`;
|
|
1171
|
+
tooltip.style.top = `${e.clientY + 15}px`;
|
|
1172
|
+
const rect = tooltip.getBoundingClientRect();
|
|
1173
|
+
if (rect.right > window.innerWidth) {
|
|
1174
|
+
tooltip.style.left = `${e.clientX - rect.width - 15}px`;
|
|
1175
|
+
}
|
|
1176
|
+
if (rect.bottom > window.innerHeight) {
|
|
1177
|
+
tooltip.style.top = `${e.clientY - rect.height - 15}px`;
|
|
1178
|
+
}
|
|
1179
|
+
};
|
|
1180
|
+
const handleMouseOut = (e) => {
|
|
1181
|
+
const target = e.target;
|
|
1182
|
+
if (target.closest("#pp-inspector-overlay")) return;
|
|
1183
|
+
target.classList.remove("pp-inspector-highlight");
|
|
1184
|
+
tooltip.style.display = "none";
|
|
1185
|
+
};
|
|
1186
|
+
const handleClick = (e) => {
|
|
1187
|
+
const target = e.target;
|
|
1188
|
+
if (target.id === "pp-inspector-exit") {
|
|
1189
|
+
this.disableInspectorMode();
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
if (target.closest("#pp-inspector-overlay") || target.closest("#pocketping-widget")) {
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
e.preventDefault();
|
|
1196
|
+
e.stopPropagation();
|
|
1197
|
+
const selector = getSelector(target);
|
|
1198
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
1199
|
+
this.ws.send(JSON.stringify({
|
|
1200
|
+
type: "inspector_select",
|
|
1201
|
+
data: {
|
|
1202
|
+
selector,
|
|
1203
|
+
tagName: target.tagName.toLowerCase(),
|
|
1204
|
+
text: target.textContent?.trim().slice(0, 50) || "",
|
|
1205
|
+
url: window.location.href
|
|
1206
|
+
}
|
|
1207
|
+
}));
|
|
1208
|
+
}
|
|
1209
|
+
this.emit("inspectorSelect", { selector, element: target });
|
|
1210
|
+
target.classList.remove("pp-inspector-highlight");
|
|
1211
|
+
target.classList.add("pp-inspector-highlight");
|
|
1212
|
+
setTimeout(() => {
|
|
1213
|
+
target.classList.remove("pp-inspector-highlight");
|
|
1214
|
+
}, 500);
|
|
1215
|
+
const banner = document.getElementById("pp-inspector-banner");
|
|
1216
|
+
if (banner) {
|
|
1217
|
+
const originalHTML = banner.innerHTML;
|
|
1218
|
+
banner.innerHTML = `
|
|
1219
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1220
|
+
<polyline points="20 6 9 17 4 12"/>
|
|
1221
|
+
</svg>
|
|
1222
|
+
<span>Selector captured: <code style="background:rgba(255,255,255,0.2);padding:2px 6px;border-radius:4px;font-family:monospace;">${selector}</code></span>
|
|
1223
|
+
`;
|
|
1224
|
+
setTimeout(() => {
|
|
1225
|
+
if (banner && this.inspectorMode) {
|
|
1226
|
+
banner.innerHTML = originalHTML;
|
|
1227
|
+
document.getElementById("pp-inspector-exit")?.addEventListener("click", () => {
|
|
1228
|
+
this.disableInspectorMode();
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
}, 2e3);
|
|
1232
|
+
}
|
|
1233
|
+
console.info(`[PocketPing] \u{1F4CC} Selector captured: ${selector}`);
|
|
1234
|
+
};
|
|
1235
|
+
document.addEventListener("mouseover", handleMouseOver, true);
|
|
1236
|
+
document.addEventListener("mouseout", handleMouseOut, true);
|
|
1237
|
+
document.addEventListener("click", handleClick, true);
|
|
1238
|
+
this.inspectorCleanup = () => {
|
|
1239
|
+
document.removeEventListener("mouseover", handleMouseOver, true);
|
|
1240
|
+
document.removeEventListener("mouseout", handleMouseOut, true);
|
|
1241
|
+
document.removeEventListener("click", handleClick, true);
|
|
1242
|
+
overlay.remove();
|
|
1243
|
+
if (currentHighlight) {
|
|
1244
|
+
currentHighlight.classList.remove("pp-inspector-highlight");
|
|
1245
|
+
}
|
|
1246
|
+
};
|
|
1247
|
+
document.getElementById("pp-inspector-exit")?.addEventListener("click", () => {
|
|
1248
|
+
this.disableInspectorMode();
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Disable inspector mode
|
|
1253
|
+
*/
|
|
1254
|
+
disableInspectorMode() {
|
|
1255
|
+
if (!this.inspectorMode) return;
|
|
1256
|
+
this.inspectorMode = false;
|
|
1257
|
+
if (this.inspectorCleanup) {
|
|
1258
|
+
this.inspectorCleanup();
|
|
1259
|
+
this.inspectorCleanup = null;
|
|
1260
|
+
}
|
|
1261
|
+
console.info("[PocketPing] Inspector mode disabled");
|
|
1262
|
+
this.emit("inspectorDisabled", null);
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Check if inspector mode is active
|
|
1266
|
+
*/
|
|
1267
|
+
isInspectorModeActive() {
|
|
1268
|
+
return this.inspectorMode;
|
|
1269
|
+
}
|
|
1270
|
+
// ─────────────────────────────────────────────────────────────────
|
|
974
1271
|
// WebSocket
|
|
975
1272
|
// ─────────────────────────────────────────────────────────────────
|
|
976
1273
|
connectWebSocket() {
|
|
@@ -1076,6 +1373,13 @@ var PocketPingClient = class {
|
|
|
1076
1373
|
const versionWarning = event.data;
|
|
1077
1374
|
this.handleVersionWarning(versionWarning);
|
|
1078
1375
|
break;
|
|
1376
|
+
case "config_update":
|
|
1377
|
+
const configData = event.data;
|
|
1378
|
+
if (configData.trackedElements) {
|
|
1379
|
+
this.setupTrackedElements(configData.trackedElements);
|
|
1380
|
+
this.emit("configUpdate", configData);
|
|
1381
|
+
}
|
|
1382
|
+
break;
|
|
1079
1383
|
}
|
|
1080
1384
|
}
|
|
1081
1385
|
// ─────────────────────────────────────────────────────────────────
|
|
@@ -1245,7 +1549,7 @@ function init(config) {
|
|
|
1245
1549
|
container = document.createElement("div");
|
|
1246
1550
|
container.id = "pocketping-container";
|
|
1247
1551
|
document.body.appendChild(container);
|
|
1248
|
-
|
|
1552
|
+
render(h2(ChatWidget, { client, config }), container);
|
|
1249
1553
|
client.connect().catch((err) => {
|
|
1250
1554
|
console.error("[PocketPing] Failed to connect:", err);
|
|
1251
1555
|
});
|
|
@@ -1253,7 +1557,7 @@ function init(config) {
|
|
|
1253
1557
|
}
|
|
1254
1558
|
function destroy() {
|
|
1255
1559
|
if (container) {
|
|
1256
|
-
|
|
1560
|
+
render(null, container);
|
|
1257
1561
|
container.remove();
|
|
1258
1562
|
container = null;
|
|
1259
1563
|
}
|
|
@@ -1277,12 +1581,22 @@ function sendMessage(content) {
|
|
|
1277
1581
|
}
|
|
1278
1582
|
return client.sendMessage(content);
|
|
1279
1583
|
}
|
|
1280
|
-
function trigger(eventName, data) {
|
|
1584
|
+
function trigger(eventName, data, options) {
|
|
1281
1585
|
if (!client) {
|
|
1282
1586
|
console.warn("[PocketPing] Not initialized, cannot trigger event");
|
|
1283
1587
|
return;
|
|
1284
1588
|
}
|
|
1285
|
-
client.trigger(eventName, data);
|
|
1589
|
+
client.trigger(eventName, data, options);
|
|
1590
|
+
}
|
|
1591
|
+
function setupTrackedElements(elements) {
|
|
1592
|
+
if (!client) {
|
|
1593
|
+
console.warn("[PocketPing] Not initialized, cannot setup tracked elements");
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
client.setupTrackedElements(elements);
|
|
1597
|
+
}
|
|
1598
|
+
function getTrackedElements() {
|
|
1599
|
+
return client?.getTrackedElements() || [];
|
|
1286
1600
|
}
|
|
1287
1601
|
function onEvent(eventName, handler) {
|
|
1288
1602
|
if (!client) {
|
|
@@ -1334,12 +1648,13 @@ if (typeof document !== "undefined") {
|
|
|
1334
1648
|
}
|
|
1335
1649
|
}
|
|
1336
1650
|
}
|
|
1337
|
-
var index_default = { init, destroy, open, close, toggle, sendMessage, trigger, onEvent, offEvent, on, identify, reset, getIdentity };
|
|
1338
|
-
|
|
1339
|
-
0 && (module.exports = {
|
|
1651
|
+
var index_default = { init, destroy, open, close, toggle, sendMessage, trigger, onEvent, offEvent, on, identify, reset, getIdentity, setupTrackedElements, getTrackedElements };
|
|
1652
|
+
export {
|
|
1340
1653
|
close,
|
|
1654
|
+
index_default as default,
|
|
1341
1655
|
destroy,
|
|
1342
1656
|
getIdentity,
|
|
1657
|
+
getTrackedElements,
|
|
1343
1658
|
identify,
|
|
1344
1659
|
init,
|
|
1345
1660
|
offEvent,
|
|
@@ -1348,6 +1663,7 @@ var index_default = { init, destroy, open, close, toggle, sendMessage, trigger,
|
|
|
1348
1663
|
open,
|
|
1349
1664
|
reset,
|
|
1350
1665
|
sendMessage,
|
|
1666
|
+
setupTrackedElements,
|
|
1351
1667
|
toggle,
|
|
1352
1668
|
trigger
|
|
1353
|
-
}
|
|
1669
|
+
};
|