chatbot21 / wordpress code /addon.html
joedown11's picture
Upload 47 files
3910b9e verified
Raw
History Blame Contribute Delete
16 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Martechsol Assistant Widget</title>
<style>
/* Premium Design for Martechsol Assistant */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
#martech-chat-wrapper {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
}
/* Floating Action Button (FAB) */
#martech-chat-fab {
position: fixed;
right: 10px;
bottom: 25px;
width: auto;
height: auto;
background: transparent;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 999999;
transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.3s ease, box-shadow 0.3s ease;
-webkit-tap-highlight-color: transparent;
}
#martech-chat-fab:hover {
transform: scale(1.1);
}
#martech-chat-fab.is-active {
background: #1e293b;
box-shadow: 0 10px 25px rgba(30, 41, 59, 0.4);
transform: scale(0.8);
}
/* Chat Window Styling */
#martech-chat-box {
position: fixed;
right: 25px;
bottom: 100px;
width: 400px;
height: 550px;
background: #ffffff;
border-radius: 16px;
overflow: hidden;
display: none;
flex-direction: column;
box-shadow: 0 12px 50px rgba(0, 0, 0, 0.2);
z-index: 999998;
border: 1px solid #e2e8f0;
opacity: 0;
transform: translateY(20px);
transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
#martech-chat-box.is-visible {
display: flex;
opacity: 1;
transform: translateY(0);
}
#martech-chat-header {
background: #ffffff;
padding: 16px 20px;
border-bottom: 1px solid #f1f5f9;
display: flex;
align-items: center;
gap: 12px;
}
@keyframes pulse-dot {
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7); }
70% { transform: scale(1.1); box-shadow: 0 0 0 10px rgba(16, 185, 129, 0); }
100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
}
#martech-chat-header .dot {
width: 10px;
height: 10px;
background: #10b981;
border-radius: 50%;
animation: pulse-dot 2s infinite;
}
#martech-chat-header span {
font-weight: 600;
color: #1e293b;
font-size: 15px;
}
/* Custom Chat Area Styling */
#chat-messages {
flex: 1;
overflow-y: auto;
padding: 16px;
display: flex;
flex-direction: column;
gap: 12px;
background: #f8fafc;
scroll-behavior: smooth;
}
/* Custom Scrollbar for messages */
#chat-messages::-webkit-scrollbar { width: 6px; }
#chat-messages::-webkit-scrollbar-track { background: transparent; }
#chat-messages::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 10px; }
.chat-message {
max-width: 85%;
padding: 12px 16px;
border-radius: 16px;
font-size: 14px;
line-height: 1.5;
word-wrap: break-word;
white-space: pre-wrap;
}
.chat-message.user {
align-self: flex-end;
background: #1e293b;
color: white;
border-bottom-right-radius: 4px;
}
.chat-message.bot {
align-self: flex-start;
background: #ffffff;
color: #334155;
border-bottom-left-radius: 4px;
border: 1px solid #e2e8f0;
box-shadow: 0 2px 4px rgba(0,0,0,0.02);
}
#chat-input-area {
padding: 16px;
background: #ffffff;
border-top: 1px solid #f1f5f9;
display: flex;
gap: 8px;
align-items: center;
}
#chat-input {
flex: 1;
border: 1px solid #e2e8f0;
border-radius: 20px;
padding: 12px 16px;
font-size: 14px;
outline: none;
transition: border-color 0.2s;
font-family: inherit;
box-shadow: inset 0 1px 2px rgba(0,0,0,0.02);
}
#chat-input:focus {
border-color: #10b981;
}
#chat-input:disabled {
background: #f1f5f9;
cursor: not-allowed;
}
#chat-send {
background: #10b981;
color: white;
border: none;
width: 44px;
height: 44px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background 0.2s, transform 0.1s;
}
#chat-send:hover {
background: #059669;
}
#chat-send:active {
transform: scale(0.95);
}
#chat-send:disabled {
background: #cbd5e1;
cursor: not-allowed;
transform: none;
}
/* Loading / Typing Animation */
.typing-indicator {
display: flex;
gap: 4px;
padding: 4px 2px;
align-items: center;
height: 18px;
}
.typing-indicator span {
width: 6px;
height: 6px;
background: #94a3b8;
border-radius: 50%;
animation: typing-dots 1.4s infinite ease-in-out both;
}
.typing-indicator span:nth-child(1) { animation-delay: -0.32s; }
.typing-indicator span:nth-child(2) { animation-delay: -0.16s; }
@keyframes typing-dots {
0%, 80%, 100% { transform: scale(0); }
40% { transform: scale(1); }
}
@media (max-width: 480px) {
#martech-chat-box {
right: 15px;
bottom: 100px;
width: calc(100vw - 30px);
height: 70vh;
}
}
</style>
</head>
<body>
<!-- Lottie Player Script -->
<script src="https://unpkg.com/@dotlottie/player-component@latest/dist/dotlottie-player.mjs" type="module"></script>
<div id="martech-chat-wrapper">
<div id="martech-chat-fab">
<!-- Animated Chatbot Icon -->
<div id="chat-icon" style="width: 233px; height: 238px; display: flex; align-items: center; justify-content: center; pointer-events: none;">
<dotlottie-player
src="https://seashell-cattle-543014.hostingersite.com/wp-content/uploads/2026/04/KhloeSAC.lottie"
background="transparent" speed="1" style="width: 100%; height: 100%; will-change: transform;" loop autoplay>
</dotlottie-player>
</div>
<!-- Close Icon -->
<svg id="close-icon" viewBox="0 0 24 24" style="display:none; width: 60px; height: 60px;">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" fill="white" />
</svg>
</div>
<div id="martech-chat-box">
<div id="martech-chat-header">
<div class="dot"></div>
<span>Martechsol Assistant</span>
</div>
<!-- Custom Chat UI replacing iframe -->
<div id="chat-messages">
<div class="chat-message bot">Welcome! How can I help you today?</div>
</div>
<div id="chat-input-area">
<input type="text" id="chat-input" placeholder="Type your question here..." autocomplete="off" />
<button id="chat-send" title="Send">
<!-- Send Icon -->
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="22" y1="2" x2="11" y2="13"></line>
<polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
</svg>
</button>
</div>
</div>
</div>
<script>
(function () {
// UI Elements
const fab = document.getElementById('martech-chat-fab');
const box = document.getElementById('martech-chat-box');
const chatIcon = document.getElementById('chat-icon');
const closeIcon = document.getElementById('close-icon');
const messagesContainer = document.getElementById('chat-messages');
const inputField = document.getElementById('chat-input');
const sendButton = document.getElementById('chat-send');
// FAB Toggle Logic
fab.addEventListener('click', function () {
const isVisible = box.classList.contains('is-visible');
if (isVisible) {
box.classList.remove('is-visible');
setTimeout(() => { box.style.display = 'none'; }, 300);
chatIcon.style.display = 'block';
closeIcon.style.display = 'none';
fab.classList.remove('is-active');
} else {
box.style.display = 'flex';
setTimeout(() => { box.classList.add('is-visible'); inputField.focus(); }, 10);
chatIcon.style.display = 'none';
closeIcon.style.display = 'block';
fab.classList.add('is-active');
}
});
// Chat Logic
const API_URL = "https://joedown11-chatrag.hf.space/api/chat";
let chatHistory = [];
// ── Session ID: generate once, persist in localStorage ──
let sessionId = localStorage.getItem('martech_session_id');
if (!sessionId) {
sessionId = 'sess-' + Date.now() + '-' + Math.random().toString(36).slice(2, 9);
localStorage.setItem('martech_session_id', sessionId);
}
function appendMessage(role, content) {
const div = document.createElement('div');
div.className = `chat-message ${role}`;
div.textContent = content;
messagesContainer.appendChild(div);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
return div;
}
function showTypingIndicator() {
const div = document.createElement('div');
div.className = 'chat-message bot';
div.innerHTML = '<div class="typing-indicator"><span></span><span></span><span></span></div>';
messagesContainer.appendChild(div);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
return div;
}
// Typo Configuration for Human-like Typing
const NEARBY_KEYS = {
'a': 'sqwz', 'b': 'vghn', 'c': 'xdfv', 'd': 'sfecx', 'e': 'wrsdf',
'f': 'dgrtcv', 'g': 'fhtybn', 'h': 'gjyunb', 'i': 'uojkl', 'j': 'hkunmi',
'k': 'jlomi', 'l': 'kop', 'm': 'njk', 'n': 'bhjm', 'o': 'ipkl',
'p': 'ol', 'q': 'wa', 'r': 'edft', 's': 'awedxz', 't': 'rfgy',
'u': 'yihj', 'v': 'cfgb', 'w': 'qase', 'x': 'zsdc', 'y': 'tghu',
'z': 'xas',
};
const TYPO_CHANCE = 0.07;
function getNearbyChar(ch) {
const lower = ch.toLowerCase();
if (NEARBY_KEYS[lower]) {
const options = NEARBY_KEYS[lower];
const replacement = options.charAt(Math.floor(Math.random() * options.length));
return ch === ch.toUpperCase() ? replacement.toUpperCase() : replacement;
}
const abc = "abcdefghijklmnopqrstuvwxyz";
return abc.charAt(Math.floor(Math.random() * abc.length));
}
const sleep = ms => new Promise(r => setTimeout(r, ms));
async function typeMessageWithTypos(element, text) {
let displayed = "";
const words = text.split(" ");
for (let i = 0; i < words.length; i++) {
const word = words[i];
const separator = i > 0 ? " " : "";
const isAlpha = /^[a-zA-Z]+$/.test(word);
// Randomly insert a typo
if (i > 0 && word.length > 4 && Math.random() < TYPO_CHANCE && isAlpha) {
const typoPos = Math.floor(Math.random() * (word.length - 2)) + 2;
// 1. Type correct part
for (let char of (separator + word.substring(0, typoPos))) {
displayed += char;
element.textContent = displayed;
await sleep(Math.random() * 40 + 30);
}
// 2. Type wrong char
const wrongChar = getNearbyChar(word[typoPos]);
displayed += wrongChar;
element.textContent = displayed;
await sleep(Math.random() * 150 + 150); // Realize error
// 3. Backspace
await sleep(Math.random() * 200 + 200);
displayed = displayed.slice(0, -1);
element.textContent = displayed;
await sleep(Math.random() * 100 + 100);
// 4. Type rest
for (let char of word.substring(typoPos)) {
displayed += char;
element.textContent = displayed;
await sleep(Math.random() * 40 + 40);
}
} else {
// Type normally
for (let char of (separator + word)) {
displayed += char;
element.textContent = displayed;
await sleep(char === " " ? Math.random() * 70 + 80 : Math.random() * 40 + 20);
}
}
messagesContainer.scrollTop = messagesContainer.scrollHeight;
// Punctuation pauses
if (word && [".", "!", "?"].includes(word[word.length - 1])) {
await sleep(Math.random() * 400 + 400);
} else if (word && [",", ":", ";"].includes(word[word.length - 1])) {
await sleep(Math.random() * 200 + 200);
}
}
}
async function handleSend() {
const text = inputField.value.trim();
if (!text) return;
// UI state lock
inputField.value = '';
inputField.disabled = true;
sendButton.disabled = true;
appendMessage('user', text);
const typingEl = showTypingIndicator();
try {
// Call the FastAPI Backend directly
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: text,
history: chatHistory,
session_id: sessionId
})
});
if (!response.ok) {
if (response.status === 429) throw new Error("Rate limit exceeded.");
throw new Error("Server error.");
}
const data = await response.json();
const reply = data.reply;
typingEl.remove();
const botEl = appendMessage('bot', '');
// Update local memory
chatHistory.push({ role: "user", content: text });
chatHistory.push({ role: "assistant", content: reply });
// Stream text out
await typeMessageWithTypos(botEl, reply);
} catch (error) {
console.error(error);
typingEl.remove();
appendMessage('bot', "⚠️ I'm sorry, I encountered an error. Please try again.");
} finally {
inputField.disabled = false;
sendButton.disabled = false;
inputField.focus();
}
}
// Event Listeners
sendButton.addEventListener('click', handleSend);
inputField.addEventListener('keypress', function(e) {
if (e.key === 'Enter') handleSend();
});
})();
</script>
</body>
</html>