2025-12-15 16:48:36 -03:00
|
|
|
<link rel="stylesheet" href="chat/chat.css" />
|
2026-01-03 17:19:17 -03:00
|
|
|
|
|
|
|
|
<div class="chat-layout" id="chat-app">
|
|
|
|
|
<main id="messages"></main>
|
|
|
|
|
|
|
|
|
|
<footer>
|
|
|
|
|
<div class="suggestions-container" id="suggestions"></div>
|
|
|
|
|
<form class="input-container" id="chatForm">
|
|
|
|
|
<input
|
|
|
|
|
name="content"
|
|
|
|
|
id="messageInput"
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="Message..."
|
|
|
|
|
autofocus
|
|
|
|
|
/>
|
|
|
|
|
<button type="button" id="voiceBtn" title="Voice">🎤</button>
|
|
|
|
|
<button type="submit" id="sendBtn" title="Send">↑</button>
|
|
|
|
|
</form>
|
|
|
|
|
</footer>
|
|
|
|
|
<button class="scroll-to-bottom" id="scrollToBottom">↓</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-12-10 22:58:09 -03:00
|
|
|
<script>
|
2026-01-03 17:19:17 -03:00
|
|
|
(function () {
|
|
|
|
|
"use strict";
|
2025-12-15 16:48:36 -03:00
|
|
|
|
2026-01-03 17:19:17 -03:00
|
|
|
// ==========================================================================
|
|
|
|
|
// NOTIFICATION HELPER - Uses GBAlerts bell system
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
function notify(message, type) {
|
|
|
|
|
type = type || "info";
|
|
|
|
|
if (window.GBAlerts) {
|
|
|
|
|
if (type === "success") {
|
|
|
|
|
window.GBAlerts.info("Chat", message);
|
|
|
|
|
} else if (type === "error") {
|
|
|
|
|
window.GBAlerts.warning("Chat", message);
|
|
|
|
|
} else {
|
|
|
|
|
window.GBAlerts.info("Chat", message);
|
2025-12-10 22:58:09 -03:00
|
|
|
}
|
|
|
|
|
}
|
2026-01-03 17:19:17 -03:00
|
|
|
}
|
2025-12-15 16:48:36 -03:00
|
|
|
|
2026-01-03 17:19:17 -03:00
|
|
|
// ==========================================================================
|
|
|
|
|
// CONFIGURATION
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
var WS_BASE_URL =
|
|
|
|
|
window.location.protocol === "https:" ? "wss://" : "ws://";
|
|
|
|
|
var WS_URL = WS_BASE_URL + window.location.host;
|
|
|
|
|
|
|
|
|
|
var MessageType = {
|
|
|
|
|
EXTERNAL: 0,
|
|
|
|
|
USER: 1,
|
|
|
|
|
BOT_RESPONSE: 2,
|
|
|
|
|
CONTINUE: 3,
|
|
|
|
|
SUGGESTION: 4,
|
|
|
|
|
CONTEXT_CHANGE: 5,
|
2025-12-10 22:58:09 -03:00
|
|
|
};
|
2025-12-15 16:48:36 -03:00
|
|
|
|
2026-01-03 17:19:17 -03:00
|
|
|
// ==========================================================================
|
|
|
|
|
// STATE
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
var ws = null;
|
|
|
|
|
var currentSessionId = null;
|
|
|
|
|
var currentUserId = null;
|
|
|
|
|
var currentBotId = "default";
|
|
|
|
|
var isStreaming = false;
|
|
|
|
|
var streamingMessageId = null;
|
|
|
|
|
var currentStreamingContent = "";
|
|
|
|
|
var reconnectAttempts = 0;
|
|
|
|
|
var maxReconnectAttempts = 5;
|
|
|
|
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
// MESSAGE FUNCTIONS
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
function escapeHtml(text) {
|
|
|
|
|
var div = document.createElement("div");
|
|
|
|
|
div.textContent = text;
|
|
|
|
|
return div.innerHTML;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function addMessage(sender, content, msgId) {
|
|
|
|
|
var messages = document.getElementById("messages");
|
|
|
|
|
if (!messages) return;
|
|
|
|
|
|
|
|
|
|
var div = document.createElement("div");
|
|
|
|
|
div.className = "message " + sender;
|
|
|
|
|
if (msgId) div.id = msgId;
|
2025-12-15 16:48:36 -03:00
|
|
|
|
2026-01-03 17:19:17 -03:00
|
|
|
if (sender === "user") {
|
|
|
|
|
div.innerHTML =
|
|
|
|
|
'<div class="message-content user-message">' +
|
|
|
|
|
escapeHtml(content) +
|
|
|
|
|
"</div>";
|
2025-12-10 22:58:09 -03:00
|
|
|
} else {
|
2026-01-03 17:19:17 -03:00
|
|
|
var parsed =
|
|
|
|
|
typeof marked !== "undefined" && marked.parse
|
|
|
|
|
? marked.parse(content)
|
|
|
|
|
: escapeHtml(content);
|
|
|
|
|
div.innerHTML =
|
|
|
|
|
'<div class="message-content bot-message">' +
|
|
|
|
|
parsed +
|
|
|
|
|
"</div>";
|
2025-12-10 22:58:09 -03:00
|
|
|
}
|
2026-01-03 17:19:17 -03:00
|
|
|
|
|
|
|
|
messages.appendChild(div);
|
|
|
|
|
messages.scrollTop = messages.scrollHeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateStreaming(content) {
|
|
|
|
|
var el = document.getElementById(streamingMessageId);
|
|
|
|
|
if (el) {
|
|
|
|
|
var parsed =
|
|
|
|
|
typeof marked !== "undefined" && marked.parse
|
|
|
|
|
? marked.parse(content)
|
|
|
|
|
: escapeHtml(content);
|
|
|
|
|
el.querySelector(".message-content").innerHTML = parsed;
|
2025-12-10 22:58:09 -03:00
|
|
|
}
|
|
|
|
|
}
|
2026-01-03 17:19:17 -03:00
|
|
|
|
|
|
|
|
function finalizeStreaming() {
|
|
|
|
|
var el = document.getElementById(streamingMessageId);
|
|
|
|
|
if (el) {
|
|
|
|
|
var parsed =
|
|
|
|
|
typeof marked !== "undefined" && marked.parse
|
|
|
|
|
? marked.parse(currentStreamingContent)
|
|
|
|
|
: escapeHtml(currentStreamingContent);
|
|
|
|
|
el.querySelector(".message-content").innerHTML = parsed;
|
|
|
|
|
el.removeAttribute("id");
|
|
|
|
|
}
|
|
|
|
|
streamingMessageId = null;
|
|
|
|
|
currentStreamingContent = "";
|
2025-12-10 22:58:09 -03:00
|
|
|
}
|
2026-01-03 17:19:17 -03:00
|
|
|
|
|
|
|
|
function processMessage(data) {
|
|
|
|
|
if (data.is_complete) {
|
|
|
|
|
if (isStreaming) {
|
|
|
|
|
finalizeStreaming();
|
|
|
|
|
} else {
|
|
|
|
|
addMessage("bot", data.content);
|
|
|
|
|
}
|
|
|
|
|
isStreaming = false;
|
|
|
|
|
} else {
|
|
|
|
|
if (!isStreaming) {
|
|
|
|
|
isStreaming = true;
|
|
|
|
|
streamingMessageId = "streaming-" + Date.now();
|
|
|
|
|
currentStreamingContent = data.content || "";
|
|
|
|
|
addMessage(
|
|
|
|
|
"bot",
|
|
|
|
|
currentStreamingContent,
|
|
|
|
|
streamingMessageId,
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
currentStreamingContent += data.content || "";
|
|
|
|
|
updateStreaming(currentStreamingContent);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-10 22:58:09 -03:00
|
|
|
}
|
2025-12-15 16:48:36 -03:00
|
|
|
|
2026-01-03 17:19:17 -03:00
|
|
|
// ==========================================================================
|
|
|
|
|
// SEND MESSAGE - GLOBAL FUNCTION
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
function sendMessage() {
|
|
|
|
|
var input = document.getElementById("messageInput");
|
|
|
|
|
if (!input) {
|
|
|
|
|
console.error("Chat input not found");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var content = input.value.trim();
|
|
|
|
|
if (!content) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Always show user message locally
|
|
|
|
|
addMessage("user", content);
|
|
|
|
|
input.value = "";
|
|
|
|
|
input.focus();
|
|
|
|
|
|
|
|
|
|
// Try to send via WebSocket if connected
|
|
|
|
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
|
|
|
ws.send(
|
|
|
|
|
JSON.stringify({
|
|
|
|
|
bot_id: currentBotId,
|
|
|
|
|
user_id: currentUserId,
|
|
|
|
|
session_id: currentSessionId,
|
|
|
|
|
channel: "web",
|
|
|
|
|
content: content,
|
|
|
|
|
message_type: MessageType.USER,
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
notify("Not connected to server. Message not sent.", "warning");
|
|
|
|
|
}
|
2025-12-10 22:58:09 -03:00
|
|
|
}
|
2025-12-15 16:48:36 -03:00
|
|
|
|
2026-01-03 17:19:17 -03:00
|
|
|
// Make sendMessage globally available immediately
|
|
|
|
|
window.sendMessage = sendMessage;
|
|
|
|
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
// WEBSOCKET CONNECTION
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
function connectWebSocket() {
|
|
|
|
|
if (ws) {
|
|
|
|
|
ws.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var url =
|
|
|
|
|
WS_URL +
|
|
|
|
|
"/ws?session_id=" +
|
|
|
|
|
currentSessionId +
|
|
|
|
|
"&user_id=" +
|
|
|
|
|
currentUserId;
|
|
|
|
|
ws = new WebSocket(url);
|
|
|
|
|
|
|
|
|
|
ws.onopen = function () {
|
|
|
|
|
console.log("WebSocket connected");
|
|
|
|
|
reconnectAttempts = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ws.onmessage = function (event) {
|
|
|
|
|
try {
|
|
|
|
|
var data = JSON.parse(event.data);
|
|
|
|
|
if (data.type === "connected") return;
|
|
|
|
|
if (data.message_type === MessageType.BOT_RESPONSE) {
|
|
|
|
|
processMessage(data);
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("WS message error:", e);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ws.onclose = function () {
|
|
|
|
|
notify("Disconnected from chat server", "error");
|
|
|
|
|
if (reconnectAttempts < maxReconnectAttempts) {
|
|
|
|
|
reconnectAttempts++;
|
|
|
|
|
setTimeout(connectWebSocket, 1000 * reconnectAttempts);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ws.onerror = function (e) {
|
|
|
|
|
console.error("WebSocket error:", e);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
// INITIALIZATION
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
function initChat() {
|
|
|
|
|
var botName = "default";
|
|
|
|
|
fetch("/api/auth?bot_name=" + encodeURIComponent(botName))
|
|
|
|
|
.then(function (response) {
|
|
|
|
|
return response.json();
|
|
|
|
|
})
|
|
|
|
|
.then(function (auth) {
|
|
|
|
|
currentUserId = auth.user_id;
|
|
|
|
|
currentSessionId = auth.session_id;
|
|
|
|
|
currentBotId = auth.bot_id || "default";
|
|
|
|
|
console.log("Auth:", {
|
|
|
|
|
currentUserId: currentUserId,
|
|
|
|
|
currentSessionId: currentSessionId,
|
|
|
|
|
currentBotId: currentBotId,
|
|
|
|
|
});
|
|
|
|
|
connectWebSocket();
|
|
|
|
|
})
|
|
|
|
|
.catch(function (e) {
|
|
|
|
|
console.error("Auth failed:", e);
|
|
|
|
|
notify("Failed to connect to chat server", "error");
|
|
|
|
|
setTimeout(initChat, 3000);
|
|
|
|
|
});
|
2025-12-10 22:58:09 -03:00
|
|
|
}
|
2026-01-03 17:19:17 -03:00
|
|
|
|
|
|
|
|
function setupEventHandlers() {
|
|
|
|
|
var form = document.getElementById("chatForm");
|
|
|
|
|
var input = document.getElementById("messageInput");
|
|
|
|
|
var sendBtn = document.getElementById("sendBtn");
|
|
|
|
|
|
|
|
|
|
if (form) {
|
|
|
|
|
form.onsubmit = function (e) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
sendMessage();
|
|
|
|
|
return false;
|
|
|
|
|
};
|
2025-12-10 22:58:09 -03:00
|
|
|
}
|
|
|
|
|
|
2026-01-03 17:19:17 -03:00
|
|
|
if (input) {
|
|
|
|
|
input.onkeydown = function (e) {
|
|
|
|
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
sendMessage();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
2025-12-03 18:42:22 -03:00
|
|
|
|
2026-01-03 17:19:17 -03:00
|
|
|
if (sendBtn) {
|
|
|
|
|
sendBtn.onclick = function (e) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
sendMessage();
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run setup immediately
|
|
|
|
|
setupEventHandlers();
|
|
|
|
|
initChat();
|
|
|
|
|
|
|
|
|
|
console.log(
|
|
|
|
|
"Chat module initialized, sendMessage is:",
|
|
|
|
|
typeof window.sendMessage,
|
|
|
|
|
);
|
|
|
|
|
})();
|
|
|
|
|
</script>
|