@modochats/widget 0.1.0 → 0.1.1
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/src/models/conversation.js +1 -1
- package/dist/src/types/app.js +1 -0
- package/package.json +1 -1
- package/temp/app.dev.js +5101 -0
- package/temp/app.dev.js.map +1 -0
- package/temp/app.js +3570 -0
- package/.vscode/settings.json +0 -3
- package/dist/types/src/app.d.ts +0 -30
- package/dist/types/src/app.d.ts.map +0 -1
- package/dist/types/src/constants/index.d.ts +0 -10
- package/dist/types/src/constants/index.d.ts.map +0 -1
- package/dist/types/src/constants/regex.d.ts +0 -3
- package/dist/types/src/constants/regex.d.ts.map +0 -1
- package/dist/types/src/index.d.ts +0 -10
- package/dist/types/src/index.d.ts.map +0 -1
- package/dist/types/src/models/chatbot.d.ts +0 -24
- package/dist/types/src/models/chatbot.d.ts.map +0 -1
- package/dist/types/src/models/conversation.d.ts +0 -23
- package/dist/types/src/models/conversation.d.ts.map +0 -1
- package/dist/types/src/models/customer-data.d.ts +0 -32
- package/dist/types/src/models/customer-data.d.ts.map +0 -1
- package/dist/types/src/models/message-utils.d.ts +0 -13
- package/dist/types/src/models/message-utils.d.ts.map +0 -1
- package/dist/types/src/services/chat/conversation.d.ts +0 -23
- package/dist/types/src/services/chat/conversation.d.ts.map +0 -1
- package/dist/types/src/services/chat/message-utils.d.ts +0 -13
- package/dist/types/src/services/chat/message-utils.d.ts.map +0 -1
- package/dist/types/src/services/chat/model.d.ts +0 -28
- package/dist/types/src/services/chat/model.d.ts.map +0 -1
- package/dist/types/src/services/chatbot/chatbot.d.ts +0 -24
- package/dist/types/src/services/chatbot/chatbot.d.ts.map +0 -1
- package/dist/types/src/services/checker.d.ts +0 -4
- package/dist/types/src/services/checker.d.ts.map +0 -1
- package/dist/types/src/services/listeners/adders.d.ts +0 -4
- package/dist/types/src/services/listeners/adders.d.ts.map +0 -1
- package/dist/types/src/services/listeners/fn.d.ts +0 -4
- package/dist/types/src/services/listeners/fn.d.ts.map +0 -1
- package/dist/types/src/services/socket/utils.d.ts +0 -3
- package/dist/types/src/services/socket/utils.d.ts.map +0 -1
- package/dist/types/src/services/ui/fn.d.ts +0 -14
- package/dist/types/src/services/ui/fn.d.ts.map +0 -1
- package/dist/types/src/services/ui/html.d.ts +0 -4
- package/dist/types/src/services/ui/html.d.ts.map +0 -1
- package/dist/types/src/services/user/customer-data.d.ts +0 -32
- package/dist/types/src/services/user/customer-data.d.ts.map +0 -1
- package/dist/types/src/services/voice-chat/model.d.ts +0 -13
- package/dist/types/src/services/voice-chat/model.d.ts.map +0 -1
- package/dist/types/src/services/voice-chat/utils.d.ts +0 -10
- package/dist/types/src/services/voice-chat/utils.d.ts.map +0 -1
- package/dist/types/src/tools/fetch.d.ts +0 -3
- package/dist/types/src/tools/fetch.d.ts.map +0 -1
- package/dist/types/src/types/app.d.ts +0 -18
- package/dist/types/src/types/app.d.ts.map +0 -1
- package/dist/types/src/types/conversation.d.ts +0 -15
- package/dist/types/src/types/conversation.d.ts.map +0 -1
- package/dist/types/src/types/socket.d.ts +0 -7
- package/dist/types/src/types/socket.d.ts.map +0 -1
- package/dist/types/src/types/window.d.ts +0 -10
- package/dist/types/src/types/window.d.ts.map +0 -1
- package/dist/types/src/utils/audio.d.ts +0 -4
- package/dist/types/src/utils/audio.d.ts.map +0 -1
- package/dist/types/src/utils/browser.d.ts +0 -3
- package/dist/types/src/utils/browser.d.ts.map +0 -1
- package/dist/types/src/utils/fetch.d.ts +0 -19
- package/dist/types/src/utils/fetch.d.ts.map +0 -1
- package/dist/types/src/utils/uuid.d.ts +0 -7
- package/dist/types/src/utils/uuid.d.ts.map +0 -1
- package/src/app.ts +0 -117
- package/src/constants/index.ts +0 -21
- package/src/constants/regex.ts +0 -2
- package/src/index.ts +0 -16
- package/src/services/chat/conversation.ts +0 -135
- package/src/services/chat/message-utils.ts +0 -221
- package/src/services/chat/model.ts +0 -139
- package/src/services/chatbot/chatbot.ts +0 -66
- package/src/services/checker.ts +0 -10
- package/src/services/listeners/adders.ts +0 -178
- package/src/services/listeners/fn.ts +0 -77
- package/src/services/socket/utils.ts +0 -9
- package/src/services/ui/fn.ts +0 -254
- package/src/services/ui/html.ts +0 -192
- package/src/services/user/customer-data.ts +0 -78
- package/src/services/voice-chat/model.ts +0 -79
- package/src/services/voice-chat/utils.ts +0 -137
- package/src/tools/fetch.ts +0 -7
- package/src/types/app.ts +0 -17
- package/src/types/conversation.ts +0 -14
- package/src/types/socket.ts +0 -7
- package/src/types/window.ts +0 -12
- package/src/utils/audio.ts +0 -67
- package/src/utils/browser.ts +0 -4
- package/src/utils/fetch.ts +0 -98
- package/src/utils/uuid.ts +0 -13
- package/tsconfig.json +0 -119
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
class Chatbot {
|
|
2
|
-
name: string;
|
|
3
|
-
image: string;
|
|
4
|
-
shortDescription: string;
|
|
5
|
-
starters: string[] = [];
|
|
6
|
-
voiceChat: boolean;
|
|
7
|
-
createdAt: string;
|
|
8
|
-
updatedAt: string;
|
|
9
|
-
deletedAt: string | null;
|
|
10
|
-
uuid: string;
|
|
11
|
-
allowedHosts: string[] = [];
|
|
12
|
-
id: number;
|
|
13
|
-
greetingMessage?: string;
|
|
14
|
-
uiConfig: {
|
|
15
|
-
primaryColor: string;
|
|
16
|
-
foregroundColor: string;
|
|
17
|
-
theme: "dark" | "light";
|
|
18
|
-
};
|
|
19
|
-
constructor(data: Record<string, any>) {
|
|
20
|
-
this.name = data.name;
|
|
21
|
-
this.image = data.image;
|
|
22
|
-
this.shortDescription = data.short_description;
|
|
23
|
-
this.starters = data.starters;
|
|
24
|
-
this.voiceChat = data.voice_agent;
|
|
25
|
-
this.createdAt = data.setting.created_at;
|
|
26
|
-
this.updatedAt = data.setting.updated_at;
|
|
27
|
-
this.deletedAt = data.setting.deleted_at;
|
|
28
|
-
this.uuid = data.setting.unique_id;
|
|
29
|
-
this.allowedHosts = data.setting.allow_hosts?.split(",") ?? [];
|
|
30
|
-
this.id = data.setting.chatbot;
|
|
31
|
-
this.allowedHosts.push("modochats.com");
|
|
32
|
-
this.uiConfig = {
|
|
33
|
-
primaryColor: data.primary_color,
|
|
34
|
-
foregroundColor: data.foreground_color,
|
|
35
|
-
theme: data.theme
|
|
36
|
-
};
|
|
37
|
-
this.greetingMessage = data.greeting_message;
|
|
38
|
-
}
|
|
39
|
-
showTooltip() {
|
|
40
|
-
const widget = window.getMWidget?.();
|
|
41
|
-
const tooltip = widget?.container?.querySelector(".mw-toggle-tooltip");
|
|
42
|
-
const tooltipText = widget?.container?.querySelector(".mw-toggle-tooltip-text");
|
|
43
|
-
const hasSeen = localStorage.getItem(`modochats:${widget?.publicKey}-has-seen-greeting-message`) === "true";
|
|
44
|
-
|
|
45
|
-
if (tooltip && tooltipText && this.greetingMessage && !hasSeen) {
|
|
46
|
-
// Show the tooltip
|
|
47
|
-
tooltip.classList.remove("mw-hidden");
|
|
48
|
-
|
|
49
|
-
// Update tooltip text with greeting message
|
|
50
|
-
tooltipText.textContent = this.greetingMessage;
|
|
51
|
-
|
|
52
|
-
// Auto-hide after 5 seconds
|
|
53
|
-
// setTimeout(() => {
|
|
54
|
-
// tooltip.classList.add("mw-hidden");
|
|
55
|
-
// }, 5000);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
hideTooltip() {
|
|
59
|
-
const widget = window.getMWidget?.();
|
|
60
|
-
const tooltip = widget?.container?.querySelector(".mw-toggle-tooltip");
|
|
61
|
-
if (tooltip) {
|
|
62
|
-
tooltip.classList.add("mw-hidden");
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
export {Chatbot};
|
package/src/services/checker.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import {Widget} from "#src/app.js";
|
|
2
|
-
import {parse} from "tldts";
|
|
3
|
-
|
|
4
|
-
const checkIfHostIsAllowed = (widget: Widget) => {
|
|
5
|
-
const currentHost = parse(window.location.origin).hostname;
|
|
6
|
-
const allowedHosts = widget.chatbot?.allowedHosts || [];
|
|
7
|
-
if (currentHost) return allowedHosts.includes(currentHost);
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export {checkIfHostIsAllowed};
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import {getMessageElement} from "#src/services/chat/message-utils.js";
|
|
2
|
-
import {sendMessage, submitPhoneNumberForm} from "./fn.js";
|
|
3
|
-
|
|
4
|
-
const registerListeners = (widgetContainer: HTMLDivElement) => {
|
|
5
|
-
let chatBody = widgetContainer.querySelector(".mw-chat-body");
|
|
6
|
-
const toggleChatBtn = widgetContainer.querySelector(".mw-toggle-chat-btn") as HTMLButtonElement;
|
|
7
|
-
let isBodyOpen = false;
|
|
8
|
-
|
|
9
|
-
// Set footer link URL with origin parameter and version title
|
|
10
|
-
const footerLink = widgetContainer.querySelector(".mw-footer-link") as HTMLAnchorElement;
|
|
11
|
-
if (footerLink) {
|
|
12
|
-
const widget = window.getMWidget?.();
|
|
13
|
-
footerLink.href = `https://modochats.com?utm_source=${encodeURIComponent(window.location.origin)}`;
|
|
14
|
-
footerLink.title = `مودوچت v${widget?.version || "0.1"}`;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// toggle chat body visibility (only if not in fullscreen mode)
|
|
18
|
-
if (toggleChatBtn) {
|
|
19
|
-
toggleChatBtn.addEventListener(
|
|
20
|
-
"click",
|
|
21
|
-
() => {
|
|
22
|
-
const widget = window.getMWidget?.();
|
|
23
|
-
isBodyOpen = !isBodyOpen;
|
|
24
|
-
if (isBodyOpen) widget?.onOpen();
|
|
25
|
-
else widget?.onClose();
|
|
26
|
-
chatBody?.classList.toggle("mw-hidden");
|
|
27
|
-
toggleChatBtn.classList.toggle("mw-chat-open", isBodyOpen);
|
|
28
|
-
},
|
|
29
|
-
{capture: false}
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
registerSendMessageListener(widgetContainer);
|
|
34
|
-
registerPhoneNumberFormListeners(widgetContainer);
|
|
35
|
-
registerNewConversationListener(widgetContainer);
|
|
36
|
-
registerFileUploadListener(widgetContainer);
|
|
37
|
-
registerReplyPreviewListener(widgetContainer);
|
|
38
|
-
registerTooltipCloseListener(widgetContainer);
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const registerSendMessageListener = (widgetContainer: HTMLDivElement) => {
|
|
42
|
-
const chatInput = widgetContainer.querySelector(".mw-chat-input") as HTMLInputElement;
|
|
43
|
-
const sendMessageBtn = widgetContainer.querySelector(".mw-send-message-btn") as HTMLButtonElement;
|
|
44
|
-
|
|
45
|
-
let isDisabled = false;
|
|
46
|
-
function toggleLoading() {
|
|
47
|
-
isDisabled = !isDisabled;
|
|
48
|
-
chatInput.disabled = isDisabled;
|
|
49
|
-
sendMessageBtn.disabled = isDisabled;
|
|
50
|
-
sendMessageBtn.setAttribute("data-is-loading", String(isDisabled));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
chatInput.addEventListener("keydown", e => {
|
|
54
|
-
if (e.key === "Enter") {
|
|
55
|
-
e.preventDefault();
|
|
56
|
-
const message = chatInput.value;
|
|
57
|
-
toggleLoading();
|
|
58
|
-
sendMessage(message)
|
|
59
|
-
.then(() => {
|
|
60
|
-
chatInput.value = "";
|
|
61
|
-
})
|
|
62
|
-
.finally(toggleLoading);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
sendMessageBtn.addEventListener("click", e => {
|
|
66
|
-
e.preventDefault();
|
|
67
|
-
const message = chatInput.value;
|
|
68
|
-
toggleLoading();
|
|
69
|
-
sendMessage(message)
|
|
70
|
-
.then(() => {
|
|
71
|
-
chatInput.value = "";
|
|
72
|
-
})
|
|
73
|
-
.finally(toggleLoading);
|
|
74
|
-
});
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const registerPhoneNumberFormListeners = (widgetContainer: HTMLDivElement) => {
|
|
78
|
-
const formOverlay = widgetContainer.querySelector(".mw-form-overlay") as HTMLDivElement;
|
|
79
|
-
const phoneInput = widgetContainer.querySelector(".mw-phone-input") as HTMLInputElement;
|
|
80
|
-
const formSubmitBtn = widgetContainer.querySelector(".mw-form-submit-btn") as HTMLButtonElement;
|
|
81
|
-
const formCancelBtn = widgetContainer.querySelector(".mw-form-cancel-btn") as HTMLButtonElement;
|
|
82
|
-
formSubmitBtn.addEventListener("click", () => {
|
|
83
|
-
const phoneNumber = phoneInput.value;
|
|
84
|
-
submitPhoneNumberForm(phoneNumber);
|
|
85
|
-
});
|
|
86
|
-
formCancelBtn.addEventListener("click", () => {
|
|
87
|
-
formOverlay.classList.add("mw-hidden");
|
|
88
|
-
});
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const registerNewConversationListener = (widgetContainer: HTMLDivElement) => {
|
|
92
|
-
const newBtn = widgetContainer.querySelector(".mw-new-conversation-btn") as HTMLButtonElement;
|
|
93
|
-
|
|
94
|
-
newBtn.addEventListener("click", () => {
|
|
95
|
-
const widget = window.getMWidget?.();
|
|
96
|
-
if (widget) {
|
|
97
|
-
widget.chat!.clear();
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const registerFileUploadListener = (widgetContainer: HTMLDivElement) => {
|
|
103
|
-
const fileUploadBtn = widgetContainer.querySelector(".mw-file-upload-btn") as HTMLButtonElement;
|
|
104
|
-
const fileInput = widgetContainer.querySelector(".mw-file-input") as HTMLInputElement;
|
|
105
|
-
const widget = window?.getMWidget?.();
|
|
106
|
-
|
|
107
|
-
// Trigger file input when button is clicked
|
|
108
|
-
fileUploadBtn.addEventListener("click", () => {
|
|
109
|
-
if (widget?.chat.fileMaster.file) {
|
|
110
|
-
// If a file is selected, remove it
|
|
111
|
-
widget?.chat.fileMaster.clearFile();
|
|
112
|
-
} else {
|
|
113
|
-
// Otherwise, open file picker
|
|
114
|
-
fileInput.click();
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// Handle file selection
|
|
119
|
-
fileInput.addEventListener("change", () => {
|
|
120
|
-
if (fileInput.files && fileInput.files.length > 0) {
|
|
121
|
-
widget?.chat.fileMaster.setFile(fileInput.files[0]);
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const registerReplyPreviewListener = (widgetContainer: HTMLDivElement) => {
|
|
127
|
-
const replyPreview = widgetContainer.querySelector(".mw-reply-preview") as HTMLDivElement;
|
|
128
|
-
const replyPreviewClose = widgetContainer.querySelector(".mw-reply-preview-close") as HTMLButtonElement;
|
|
129
|
-
const replyPreviewInfo = widgetContainer.querySelector(".mw-reply-preview-info") as HTMLDivElement;
|
|
130
|
-
|
|
131
|
-
if (!replyPreview || !replyPreviewClose || !replyPreviewInfo) return;
|
|
132
|
-
|
|
133
|
-
// Close button - clear reply
|
|
134
|
-
replyPreviewClose.addEventListener("click", e => {
|
|
135
|
-
e.stopPropagation();
|
|
136
|
-
const widget = window.getMWidget?.();
|
|
137
|
-
if (widget?.chat) {
|
|
138
|
-
widget.chat.replyMaster.clearReply();
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// Click on preview info - scroll to message
|
|
143
|
-
replyPreviewInfo.addEventListener("click", () => {
|
|
144
|
-
const widget = window.getMWidget?.();
|
|
145
|
-
const replyingToEl = getMessageElement(widget?.chat.replyMaster.replyingTo! || {});
|
|
146
|
-
|
|
147
|
-
if (replyingToEl) {
|
|
148
|
-
// Scroll to the message
|
|
149
|
-
const messagesContainer = widgetContainer.querySelector(".mw-chat-messages-con") as HTMLDivElement;
|
|
150
|
-
if (messagesContainer) {
|
|
151
|
-
replyingToEl.scrollIntoView({behavior: "smooth", block: "center"});
|
|
152
|
-
|
|
153
|
-
// Add a highlight effect
|
|
154
|
-
replyingToEl.classList.add("mw-message-highlight");
|
|
155
|
-
setTimeout(() => {
|
|
156
|
-
replyingToEl?.classList.remove("mw-message-highlight");
|
|
157
|
-
}, 2000);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const registerTooltipCloseListener = (widgetContainer: HTMLDivElement) => {
|
|
164
|
-
const widget = window.getMWidget?.();
|
|
165
|
-
const tooltipCloseBtn = widgetContainer.querySelector(".mw-toggle-tooltip-close") as HTMLButtonElement;
|
|
166
|
-
const tooltip = widgetContainer.querySelector(".mw-toggle-tooltip") as HTMLDivElement;
|
|
167
|
-
if (tooltipCloseBtn) {
|
|
168
|
-
tooltipCloseBtn.addEventListener("click", e => {
|
|
169
|
-
localStorage.setItem(`modochats:${widget?.publicKey}-has-seen-greeting-message`, "true");
|
|
170
|
-
e.stopPropagation();
|
|
171
|
-
if (tooltip) {
|
|
172
|
-
tooltip.classList.add("mw-hidden");
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
export {registerListeners, registerNewConversationListener};
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import {PhoneNumberRegex} from "#src/constants/regex.js";
|
|
2
|
-
|
|
3
|
-
const sendMessage = async (message: string) => {
|
|
4
|
-
if (message.trim().length) {
|
|
5
|
-
if (checkIfUserHasPhoneNumber()) {
|
|
6
|
-
const widget = window.getMWidget?.();
|
|
7
|
-
|
|
8
|
-
if (widget) {
|
|
9
|
-
const savedFile = widget.chat.fileMaster.file;
|
|
10
|
-
const savedReply = widget.chat.replyMaster.replyingTo?.id;
|
|
11
|
-
if (widget?.conversation?.d?.uuid) {
|
|
12
|
-
const chatInput = widget.container?.querySelector(".mw-chat-input") as HTMLInputElement;
|
|
13
|
-
if (chatInput) chatInput.value = "";
|
|
14
|
-
}
|
|
15
|
-
widget.chat.fileMaster.clearFile();
|
|
16
|
-
widget.chat.replyMaster.clearReply();
|
|
17
|
-
await widget?.chat.sendMessage(message, {file: savedFile, replyTo: savedReply});
|
|
18
|
-
} else {
|
|
19
|
-
console.error("Widget instance not found");
|
|
20
|
-
}
|
|
21
|
-
} else {
|
|
22
|
-
throw new Error("User has not submitted the phone number form");
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const checkIfUserHasPhoneNumber = () => {
|
|
28
|
-
const widget = window.getMWidget?.();
|
|
29
|
-
if (widget?.customerData?.hasSubmittedPhoneForm()) {
|
|
30
|
-
// User has already submitted the phone number form (whether empty or with phone number)
|
|
31
|
-
return true;
|
|
32
|
-
} else {
|
|
33
|
-
// Show phone number form
|
|
34
|
-
switchToPhoneNumberFormView();
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const switchToPhoneNumberFormView = () => {
|
|
40
|
-
const formOverlay = window.getMWidget?.().container?.querySelector(".mw-form-overlay");
|
|
41
|
-
if (formOverlay) {
|
|
42
|
-
formOverlay.classList.remove("mw-hidden");
|
|
43
|
-
formOverlay.classList.add("mw-active");
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
const submitPhoneNumberForm = (phoneNumber: string) => {
|
|
47
|
-
// Allow empty phone number or valid phone number
|
|
48
|
-
const parsedPhoneNumber = phoneNumber.replace(/[۰-۹٠-٩]/g, ch => {
|
|
49
|
-
const fa = "۰۱۲۳۴۵۶۷۸۹".indexOf(ch);
|
|
50
|
-
if (fa > -1) return String(fa);
|
|
51
|
-
const ar = "٠١٢٣٤٥٦٧٨٩".indexOf(ch);
|
|
52
|
-
if (ar > -1) return String(ar);
|
|
53
|
-
return ch;
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
if (parsedPhoneNumber.trim() === "" || PhoneNumberRegex.test(parsedPhoneNumber)) {
|
|
57
|
-
const widget = window.getMWidget?.();
|
|
58
|
-
if (widget) {
|
|
59
|
-
// Update the phone number
|
|
60
|
-
widget.customerData.savePhoneNumber(phoneNumber.trim() || undefined);
|
|
61
|
-
|
|
62
|
-
const formOverlay = widget.container?.querySelector(".mw-form-overlay");
|
|
63
|
-
if (formOverlay) {
|
|
64
|
-
formOverlay.classList.remove("mw-active");
|
|
65
|
-
formOverlay.classList.add("mw-hidden");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
(widget.container?.querySelector(".mw-send-message-btn") as HTMLButtonElement)?.click();
|
|
69
|
-
} else {
|
|
70
|
-
console.error("Widget instance not found");
|
|
71
|
-
}
|
|
72
|
-
} else {
|
|
73
|
-
alert("لطفا شماره تلفن معتبر وارد کنید یا فیلد را خالی بگذارید.");
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export {sendMessage, submitPhoneNumberForm};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
const onSocketConnectionUpdate = (connected: boolean) => {
|
|
2
|
-
const widget = window.getMWidget?.();
|
|
3
|
-
const connectionIndicator = widget?.container?.querySelector(".mw-connection-status");
|
|
4
|
-
|
|
5
|
-
if (connectionIndicator) {
|
|
6
|
-
connectionIndicator.className = `mw-connection-status ${connected ? "mw-connected" : "mw-disconnected"}`;
|
|
7
|
-
}
|
|
8
|
-
};
|
|
9
|
-
export {onSocketConnectionUpdate};
|
package/src/services/ui/fn.ts
DELETED
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
import {BASE_STORAGE_URL, isDev} from "#src/constants/index.js";
|
|
2
|
-
import {ConversationStatus} from "#src/types/conversation.js";
|
|
3
|
-
|
|
4
|
-
function switchToConversationLayout() {
|
|
5
|
-
const widget = window.getMWidget?.();
|
|
6
|
-
widget?.container?.querySelector(".mw-new-conversation-btn")?.classList.remove("mw-hidden");
|
|
7
|
-
widget?.container?.querySelector(".mw-starters-con")?.classList.add("mw-hidden");
|
|
8
|
-
}
|
|
9
|
-
function switchToStarterLayout() {
|
|
10
|
-
const widget = window.getMWidget?.();
|
|
11
|
-
widget?.container?.querySelector(".mw-new-conversation-btn")?.classList.add("mw-hidden");
|
|
12
|
-
widget?.container?.querySelector(".mw-starters-con")?.classList.remove("mw-hidden");
|
|
13
|
-
widget?.container?.querySelector(".mw-conversation-status-icon")?.classList.add("mw-hidden");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function setConversationType(type: keyof typeof ConversationStatus) {
|
|
17
|
-
const widget = window.getMWidget?.();
|
|
18
|
-
widget?.container?.querySelector(".mw-conversation-status-icon")?.classList.remove("mw-hidden");
|
|
19
|
-
const statusIcon = widget?.container?.querySelector(".mw-conversation-status-icon");
|
|
20
|
-
|
|
21
|
-
if (statusIcon) {
|
|
22
|
-
// Remove existing mode classes
|
|
23
|
-
statusIcon.classList.remove("mw-ai-mode", "mw-human-mode");
|
|
24
|
-
|
|
25
|
-
// Add appropriate mode class
|
|
26
|
-
if (type === "AI_CHAT") {
|
|
27
|
-
statusIcon.classList.add("mw-ai-mode");
|
|
28
|
-
} else {
|
|
29
|
-
statusIcon.classList.add("mw-human-mode");
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function loadStarters() {
|
|
35
|
-
const widget = window.getMWidget?.();
|
|
36
|
-
const startersContainer = widget?.container?.querySelector(".mw-starters-con");
|
|
37
|
-
const starterItemsContainer = widget?.container?.querySelector(".mw-starter-items");
|
|
38
|
-
|
|
39
|
-
startersContainer?.classList.remove("mw-hidden");
|
|
40
|
-
|
|
41
|
-
for (const starter of widget?.chatbot?.starters || []) {
|
|
42
|
-
const starterElement = document.createElement("div");
|
|
43
|
-
starterElement.className = "mw-starter-item";
|
|
44
|
-
starterElement.textContent = starter;
|
|
45
|
-
starterElement.addEventListener("click", async () => {
|
|
46
|
-
const inputEl = widget?.container?.querySelector(".mw-chat-input") as HTMLInputElement;
|
|
47
|
-
const sendMsgBtnEl = widget?.container?.querySelector(".mw-send-message-btn") as HTMLButtonElement;
|
|
48
|
-
switchToConversationLayout();
|
|
49
|
-
if (inputEl) {
|
|
50
|
-
inputEl.value = starter;
|
|
51
|
-
sendMsgBtnEl.click();
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
starterItemsContainer?.appendChild(starterElement);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function updateChatToggleImage() {
|
|
59
|
-
const widget = window.getMWidget?.();
|
|
60
|
-
const toggleImageEl = widget?.container?.querySelector(".mw-chat-toggle-image") as HTMLImageElement;
|
|
61
|
-
const starterLogoEl = widget?.container?.querySelector(".mw-starter-logo") as HTMLImageElement;
|
|
62
|
-
|
|
63
|
-
const defaultSvg =
|
|
64
|
-
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M12 2C13.1 2 14 2.9 14 4C14 5.1 13.1 6 12 6C10.9 6 10 5.1 10 4C10 2.9 10.9 2 12 2ZM21 9V7L15 1H5C3.89 1 3 1.89 3 3V21C3 22.1 3.9 23 5 23H11V21H5V3H13V9H21ZM23 18V16H15V18L19 22L15 26V28H23V26H19L23 22L19 18H23Z'/%3E%3C/svg%3E";
|
|
65
|
-
|
|
66
|
-
// Update toggle button image
|
|
67
|
-
if (toggleImageEl) {
|
|
68
|
-
if (widget?.chatbot?.image) {
|
|
69
|
-
toggleImageEl.src = widget.chatbot.image;
|
|
70
|
-
toggleImageEl.alt = widget.chatbot.name || "شروع گفتگو";
|
|
71
|
-
|
|
72
|
-
// Add error handling for failed image loads
|
|
73
|
-
toggleImageEl.onerror = () => {
|
|
74
|
-
toggleImageEl.src = defaultSvg;
|
|
75
|
-
toggleImageEl.alt = "پشتیبانی چت";
|
|
76
|
-
};
|
|
77
|
-
} else {
|
|
78
|
-
// Use default avatar if no image is provided
|
|
79
|
-
toggleImageEl.src = defaultSvg;
|
|
80
|
-
toggleImageEl.alt = "پشتیبانی چت";
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Update starter logo
|
|
85
|
-
if (starterLogoEl) {
|
|
86
|
-
if (widget?.chatbot?.image) {
|
|
87
|
-
starterLogoEl.src = widget.chatbot.image;
|
|
88
|
-
starterLogoEl.alt = widget.chatbot.name || "لوگو چت بات";
|
|
89
|
-
starterLogoEl.style.display = "block";
|
|
90
|
-
|
|
91
|
-
// Add error handling for failed image loads
|
|
92
|
-
starterLogoEl.onerror = () => {
|
|
93
|
-
starterLogoEl.style.display = "none";
|
|
94
|
-
};
|
|
95
|
-
} else {
|
|
96
|
-
// Hide logo if no image is provided
|
|
97
|
-
starterLogoEl.style.display = "none";
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function updateChatTitle() {
|
|
103
|
-
const widget = window.getMWidget?.();
|
|
104
|
-
const chatTitleEl = widget?.container?.querySelector(".mw-chat-title") as HTMLElement;
|
|
105
|
-
const starterTitleEl = widget?.container?.querySelector(".mw-starter-title") as HTMLElement;
|
|
106
|
-
|
|
107
|
-
if (chatTitleEl || starterTitleEl) {
|
|
108
|
-
// Use options title if no chatbot name is available
|
|
109
|
-
const displayTitle = widget?.options?.title || widget?.chatbot?.name || "Modo";
|
|
110
|
-
|
|
111
|
-
if (chatTitleEl) {
|
|
112
|
-
chatTitleEl.textContent = displayTitle;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (starterTitleEl) {
|
|
116
|
-
starterTitleEl.textContent = displayTitle;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function applyModoOptions() {
|
|
122
|
-
const widget = window.getMWidget?.();
|
|
123
|
-
if (!widget?.container || !widget?.options) return;
|
|
124
|
-
|
|
125
|
-
const container = widget.container;
|
|
126
|
-
const options = widget.options;
|
|
127
|
-
|
|
128
|
-
// Apply position option
|
|
129
|
-
applyPositionOption(container, options.position!);
|
|
130
|
-
|
|
131
|
-
// Apply theme option
|
|
132
|
-
applyThemeOption(container, options.theme!);
|
|
133
|
-
|
|
134
|
-
// Apply primary color option
|
|
135
|
-
applyPrimaryColorOption(container, options.primaryColor!);
|
|
136
|
-
|
|
137
|
-
// Apply foreground color option
|
|
138
|
-
applyForegroundColorOption(container, options.foregroundColor!);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function applyPositionOption(container: HTMLDivElement, position: "left" | "right") {
|
|
142
|
-
const widget = window.getMWidget?.()?.container as HTMLElement;
|
|
143
|
-
if (widget) {
|
|
144
|
-
if (position === "left") {
|
|
145
|
-
widget.style.right = "auto";
|
|
146
|
-
widget.style.left = "32px";
|
|
147
|
-
widget.style.direction = "ltr";
|
|
148
|
-
|
|
149
|
-
// Update chat body position for left alignment
|
|
150
|
-
const chatBody = widget.querySelector(".mw-chat-body") as HTMLElement;
|
|
151
|
-
if (chatBody) {
|
|
152
|
-
chatBody.style.right = "auto";
|
|
153
|
-
chatBody.style.left = "0";
|
|
154
|
-
}
|
|
155
|
-
} else {
|
|
156
|
-
// widget.style.left = "auto";
|
|
157
|
-
// widget.style.right = "32px";
|
|
158
|
-
// widget.style.direction = "rtl";
|
|
159
|
-
|
|
160
|
-
// Update chat body position for right alignment
|
|
161
|
-
const chatBody = widget.querySelector(".mw-chat-body") as HTMLElement;
|
|
162
|
-
if (chatBody) {
|
|
163
|
-
chatBody.style.left = "auto";
|
|
164
|
-
chatBody.style.right = "0";
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function applyThemeOption(container: HTMLDivElement, theme: "dark" | "light") {
|
|
171
|
-
// Set the theme attribute on the container or document
|
|
172
|
-
const widget = document.querySelector(".modo-widget");
|
|
173
|
-
if (theme === "light") {
|
|
174
|
-
widget?.setAttribute("data-theme", "light");
|
|
175
|
-
} else {
|
|
176
|
-
widget?.removeAttribute("data-theme");
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Store theme preference
|
|
180
|
-
localStorage.setItem("modo-component:theme", theme);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function applyPrimaryColorOption(container: HTMLDivElement, primaryColor: string) {
|
|
184
|
-
// Create CSS custom properties for the primary color
|
|
185
|
-
const root = document.querySelector(".modo-widget") as HTMLDivElement;
|
|
186
|
-
if (root) {
|
|
187
|
-
// Set the primary color
|
|
188
|
-
root?.style.setProperty("--primary-color", primaryColor);
|
|
189
|
-
|
|
190
|
-
// Generate hover color (slightly darker)
|
|
191
|
-
const hoverColor = adjustColorBrightness(primaryColor, -20);
|
|
192
|
-
root?.style.setProperty("--primary-hover", hoverColor);
|
|
193
|
-
|
|
194
|
-
// Generate gradient using the primary color
|
|
195
|
-
const gradientColor = adjustColorBrightness(primaryColor, 15);
|
|
196
|
-
root?.style.setProperty("--primary-gradient", `linear-gradient(135deg, ${primaryColor} 0%, ${gradientColor} 100%)`);
|
|
197
|
-
} else console.error("modo chat widget not found");
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function applyForegroundColorOption(container: HTMLDivElement, foregroundColor: string) {
|
|
201
|
-
// Create CSS custom properties for the foreground color
|
|
202
|
-
const root = document.querySelector(".modo-widget") as HTMLDivElement;
|
|
203
|
-
if (root) {
|
|
204
|
-
// Set the foreground color
|
|
205
|
-
root?.style.setProperty("--foreground-color", foregroundColor);
|
|
206
|
-
|
|
207
|
-
// Set white color to use foreground color for white text elements
|
|
208
|
-
root?.style.setProperty("--white", foregroundColor);
|
|
209
|
-
} else console.error("modo chat widget not found");
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function adjustColorBrightness(hex: string, percent: number): string {
|
|
213
|
-
// Remove # if present
|
|
214
|
-
hex = hex.replace(/^#/, "");
|
|
215
|
-
|
|
216
|
-
// Parse r, g, b values
|
|
217
|
-
const num = parseInt(hex, 16);
|
|
218
|
-
const amt = Math.round(2.55 * percent);
|
|
219
|
-
const R = (num >> 16) + amt;
|
|
220
|
-
const G = ((num >> 8) & 0x00ff) + amt;
|
|
221
|
-
const B = (num & 0x0000ff) + amt;
|
|
222
|
-
|
|
223
|
-
// Ensure values stay within 0-255 range
|
|
224
|
-
const newR = Math.max(0, Math.min(255, R));
|
|
225
|
-
const newG = Math.max(0, Math.min(255, G));
|
|
226
|
-
const newB = Math.max(0, Math.min(255, B));
|
|
227
|
-
|
|
228
|
-
return `#${((newR << 16) | (newG << 8) | newB).toString(16).padStart(6, "0")}`;
|
|
229
|
-
}
|
|
230
|
-
async function loadCss() {
|
|
231
|
-
return await new Promise(resolve => {
|
|
232
|
-
const link = document.createElement("link");
|
|
233
|
-
const source = isDev ? "/css/index.css" : `${BASE_STORAGE_URL}/index.css`;
|
|
234
|
-
link.rel = "stylesheet";
|
|
235
|
-
link.href = source;
|
|
236
|
-
document.head.appendChild(link);
|
|
237
|
-
link.addEventListener("load", () => {
|
|
238
|
-
resolve("css loaded");
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
export {
|
|
243
|
-
switchToConversationLayout,
|
|
244
|
-
switchToStarterLayout,
|
|
245
|
-
setConversationType,
|
|
246
|
-
loadStarters,
|
|
247
|
-
loadCss,
|
|
248
|
-
updateChatToggleImage,
|
|
249
|
-
updateChatTitle,
|
|
250
|
-
applyModoOptions,
|
|
251
|
-
applyPositionOption,
|
|
252
|
-
applyThemeOption,
|
|
253
|
-
applyPrimaryColorOption
|
|
254
|
-
};
|