Itâs mine and CORS works just fine you can run this localy to check that the api works and the infinityfree hostings âsecurity preventionâ for some reason prevents the remote cookies.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Modern Chat Widget</title>
<!-- <link rel="stylesheet" href="styles.css"> -->
</head>
<body>
<!-- Chat Container -->
<div id="chat-container">
<div id="chat-header">
<h2>Chat</h2>
<button id="close-chat">â</button>
</div>
<div id="chat-messages"></div>
<div id="chat-input-container">
<input type="text" id="chat-input" placeholder="Type a message..." />
<button id="send-button">Send</button>
</div>
</div>
<!-- Expand Chat Button -->
<button id="expand-chat">
<svg fill="#ffffff" height="24px" width="24px" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg">
<path d="M30,1.5c-16.542,0-30,12.112-30,27c0,5.205,1.647,10.246,4.768,14.604c-0.591,6.537-2.175,11.39-4.475,13.689
c-0.304,0.304-0.38,0.769-0.188,1.153C0.276,58.289,0.625,58.5,1,58.5c0.046,0,0.093-0.003,0.14-0.01
c0.405-0.057,9.813-1.412,16.617-5.338C21.622,54.711,25.738,55.5,30,55.5c16.542,0,30-12.112,30-27S46.542,1.5,30,1.5z" />
</svg>
</button>
<!-- Code Snippet Template -->
<template id="code-snippet-template">
<div class="code-snippet">
<button class="copy-button">Copy</button>
<pre><code></code></pre>
</div>
</template>
<!-- Header and Bold Template -->
<template id="header-template">
<strong class="header"></strong>
</template>
<template id="bold-black-template">
<span class="bold-black"></span>
</template>
<script>
document.addEventListener('DOMContentLoaded', () => {
const chatContainer = document.getElementById('chat-container');
const chatMessages = document.getElementById('chat-messages');
const chatInput = document.getElementById('chat-input');
const chatForm = document.getElementById('chat-form');
const sendButton = document.getElementById('send-button');
const expandChatButton = document.getElementById('expand-chat');
const closeChatButton = document.getElementById('close-chat');
let lastMessage = '';
expandChatButton.addEventListener('click', () => {
chatContainer.classList.add('expanded');
expandChatButton.classList.add('hidden');
});
closeChatButton.addEventListener('click', () => {
chatContainer.classList.remove('expanded');
expandChatButton.classList.remove('hidden');
});
document.addEventListener('click', (e) => {
if (e.target.classList.contains('copy-button')) {
const codeSnippet = e.target.closest('.code-snippet');
const codeElement = codeSnippet.querySelector('code');
if (codeElement) {
const code = codeElement.textContent; // Get the text
navigator.clipboard.writeText(code).then(() => {
alert('Code copied to clipboard!');
}).catch(() => {
alert('Failed to copy code to clipboard.');
});
} else {
console.error('Code element not found!');
}
}
});
sendButton.addEventListener('click', async () => {
const message = chatInput.value.trim();
if (message) {
lastMessage = message;
addMessageToChat('user', message);
chatInput.value = '';
addTypingAnimation();
const MAX_RETRIES = 3; // Maximum number of retries
let retryCount = 0; // Retry counter
let timeoutReached = false;
const showRetryButtons = () => {
removeTypingAnimation();
const messageElement = document.createElement('div');
messageElement.className = 'message bot';
messageElement.innerHTML = `
<p>Äas vyprĆĄel. Zkusit znovu nebo smazat chat?</p>
<button id="retry-button">Zkusit znovu</button>
<button id="clear-button">Smazat</button>
`;
chatMessages.appendChild(messageElement);
chatMessages.scrollTop = chatMessages.scrollHeight;
document.getElementById('retry-button').addEventListener('click', () => {
messageElement.remove();
retryMessage();
});
document.getElementById('clear-button').addEventListener('click', () => {
chatMessages.innerHTML = '';
});
};
const timeoutId = setTimeout(() => {
timeoutReached = true;
showRetryButtons();
}, 20000);
const warningTimeoutId = setTimeout(() => {
if (!timeoutReached) {
addMessageToChat('bot', 'ProsĂm poÄkejte, generace zprĂĄvy zabrala dĂ©le neĆŸ obvykle.');
}
}, 15000);
while (retryCount < MAX_RETRIES) {
try {
const response = await fetch('https://aiceskekraje.vercel.app/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message }),
});
if (timeoutReached) return;
clearTimeout(timeoutId);
clearTimeout(warningTimeoutId);
removeTypingAnimation();
if (response.ok) {
const data = await response.json();
addMessageToChat('bot', data.response);
return; // Exit on success
} else {
console.warn(`Retrying... Attempt ${retryCount + 1}`);
retryCount++;
}
} catch (error) {
console.error(`Error sending message: ${error}`);
retryCount++;
if (retryCount >= MAX_RETRIES) {
removeTypingAnimation();
addMessageToChat(
'bot',
'DoĆĄlo k chybÄ pĆi odesĂlĂĄnĂ zprĂĄvy. ProsĂm zkuste to znovu.'
);
}
}
}
}
});
function addMessageToChat(role, content) {
const messageElement = document.createElement('div');
messageElement.className = `message ${role}`;
const parsedContent = parseMessageContent(content);
messageElement.appendChild(parsedContent);
chatMessages.appendChild(messageElement);
chatMessages.scrollTop = chatMessages.scrollHeight;
}
function parseMessageContent(content) {
const messageFragment = document.createDocumentFragment();
// Regex patterns
const codePattern = /```([\s\S]*?)```/g; // Matches ```...```
const headerPattern = /##(.+?)##/g; // Matches ###...###
const boldPattern = /\*\*(.+?)\*\*/g; // Matches ***...***
let lastIndex = 0;
// Replace code blocks
let match;
while ((match = codePattern.exec(content)) !== null) {
// Append preceding text
if (lastIndex < match.index) {
const text = content.substring(lastIndex, match.index);
messageFragment.appendChild(document.createTextNode(text));
}
// Create a code snippet
const codeSnippet = document.getElementById('code-snippet-template').content.cloneNode(true);
codeSnippet.querySelector('code').textContent = match[1]; // Add code content
messageFragment.appendChild(codeSnippet);
// Attach copy functionality to the new snippet
// setTimeout(() => attachCopyFunctionality(), 0); // Ensure DOM updates before attaching
lastIndex = codePattern.lastIndex;
}
// Append remaining content after code blocks
if (lastIndex < content.length) {
content = content.substring(lastIndex);
// Replace headers
content = content.replace(headerPattern, (_, headerText) => {
const headerElement = document.getElementById('header-template').content.cloneNode(true);
headerElement.querySelector('.header').textContent = headerText.trim();
messageFragment.appendChild(headerElement);
return '';
});
// Replace bold-black text
content = content.replace(boldPattern, (_, boldText) => {
const boldElement = document.getElementById('bold-black-template').content.cloneNode(true);
boldElement.querySelector('.bold-black').textContent = boldText.trim();
messageFragment.appendChild(boldElement);
return '';
});
// Add any remaining plain text
if (content.trim()) {
messageFragment.appendChild(document.createTextNode(content));
}
}
return messageFragment;
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function addTypingAnimation() {
const typingElement = document.createElement('div');
typingElement.className = 'message bot typing';
chatMessages.appendChild(typingElement);
chatMessages.scrollTop = chatMessages.scrollHeight;
let keepTyping = true;
const animateTyping = async () => {
while (keepTyping) {
typingElement.textContent = '.';
await delay(500);
typingElement.textContent = '..';
await delay(500);
typingElement.textContent = '...';
await delay(500);
}
};
animateTyping();
typingElement.cleanup = () => {
keepTyping = false;
typingElement.remove();
};
}
function removeTypingAnimation() {
const typingElement = chatMessages.querySelector('.typing');
if (typingElement && typeof typingElement.cleanup === 'function') {
typingElement.cleanup();
}
}
function retryMessage() {
chatInput.value = lastMessage;
sendButton.click();
}
});
</script>
<style>
body {
font-family: 'Arial', sans-serif;
background-color: #f9f9f9;
margin: 0;
padding: 0;
}
.code-snippet {
position: relative;
border: 1px solid #ddd;
background-color: #f8f8f8;
border-radius: 5px;
margin: 10px 0;
padding: 10px;
overflow: hidden;
}
#close-chat {
position: absolute;
top: 10px;
right: 10px;
background-color: #ffffff;
color: rgb(0, 0, 0);
border: none;
border-radius: 3px;
padding: 5px 10px;
font-size: 12px;
cursor: pointer;
z-index: 10;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.code-snippet pre {
margin: 0;
padding: 10px;
overflow-x: auto;
background: transparent;
}
.copy-button {
position: absolute;
top: 10px;
right: 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 3px;
padding: 5px 10px;
font-size: 12px;
cursor: pointer;
z-index: 10;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.copy-button:hover {
background-color: #0056b3;
}
#chat-container {
position: fixed;
bottom: 20px;
right: 20px;
width: 350px;
height: 500px;
background-color: #ffffff;
border-radius: 10px;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
display: none;
flex-direction: column;
overflow: hidden;
transition: all 0.3s ease;
z-index: 1000;
}
#chat-container.expanded {
display: flex;
}
#chat-header {
background-color: #007bff;
color: white;
padding: 15px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 16px;
}
#chat-messages {
flex: 1;
padding: 10px;
overflow-y: auto;
background-color: #f7f7f7;
}
.message {
margin-bottom: 10px;
padding: 10px;
border-radius: 15px;
max-width: 70%;
word-wrap: break-word;
}
.message.user {
background-color: #007bff;
color: white;
align-self: flex-end;
}
.message.bot {
background-color: #e5e5ea;
color: black;
align-self: flex-start;
}
#chat-input-container {
display: flex;
padding: 10px;
border-top: 1px solid #ddd;
background-color: #ffffff;
}
#chat-input {
flex-grow: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 14px;
}
#send-button {
margin-left: 10px;
padding: 10px 15px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
font-size: 14px;
cursor: pointer;
}
#expand-chat {
position: fixed;
bottom: 20px;
right: 20px;
width: 60px;
height: 60px;
background-color: #007bff;
color: white;
border: none;
border-radius: 50%;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
z-index: 1000;
}
#expand-chat svg {
width: 24px;
height: 24px;
fill: white;
}
.hidden {
display: none;
}
#expand-chat.hidden {
display: none !important;
}
.message.bot.typing {
font-style: italic;
color: gray;
}
</style>
</body>
</html>