botui/ui/suite/js/chat-agent-mode.js

303 lines
9.9 KiB
JavaScript
Raw Normal View History

2026-02-26 12:40:44 -03:00
/**
* Chat Agent Mode handles toggling between Agent and Chat mode,
* multi-panel layout management, and WebSocket message routing
* for thought process, terminal output, browser preview, and step tracking.
*/
(function () {
"use strict";
var agentMode = false;
var currentStep = 0;
var totalSteps = 0;
var terminalLineCount = 0;
function initAgentMode() {
setupModeToggle();
setupToggleSwitches();
setupStepNavigation();
setupQuickActions();
setupSidebarItems();
}
function setupModeToggle() {
var agentBtn = document.getElementById("modeAgentBtn");
var chatBtn = document.getElementById("modeChatBtn");
if (!agentBtn || !chatBtn) return;
agentBtn.addEventListener("click", function () {
setMode("agent");
});
chatBtn.addEventListener("click", function () {
setMode("chat");
});
}
function setMode(mode) {
var chatApp = document.getElementById("chat-app");
var agentBtn = document.getElementById("modeAgentBtn");
var chatBtn = document.getElementById("modeChatBtn");
var quickActions = document.getElementById("quickActions");
if (!chatApp || !agentBtn || !chatBtn) return;
agentMode = mode === "agent";
agentBtn.classList.toggle("active", agentMode);
chatBtn.classList.toggle("active", !agentMode);
if (agentMode) {
chatApp.classList.add("agent-mode");
if (quickActions) quickActions.style.display = "none";
} else {
chatApp.classList.remove("agent-mode");
if (quickActions) quickActions.style.display = "";
}
}
function setupToggleSwitches() {
var planToggle = document.getElementById("togglePlan");
var yoloToggle = document.getElementById("toggleYolo");
if (planToggle) {
planToggle.addEventListener("click", function () {
this.classList.toggle("on");
emitModeChange();
});
}
if (yoloToggle) {
yoloToggle.addEventListener("click", function () {
this.classList.toggle("on");
emitModeChange();
});
}
}
function emitModeChange() {
var planOn = document.getElementById("togglePlan");
var yoloOn = document.getElementById("toggleYolo");
var mode = "plan";
if (yoloOn && yoloOn.classList.contains("on")) {
mode = "yolo";
}
if (window.ws && window.ws.readyState === WebSocket.OPEN) {
window.ws.send(JSON.stringify({
type: "toggle_mode",
mode: mode
}));
}
}
function setupStepNavigation() {
var prevBtn = document.getElementById("stepPrev");
var nextBtn = document.getElementById("stepNext");
if (prevBtn) {
prevBtn.addEventListener("click", function () {
if (currentStep > 1) {
currentStep--;
updateStepCounter();
}
});
}
if (nextBtn) {
nextBtn.addEventListener("click", function () {
if (currentStep < totalSteps) {
currentStep++;
updateStepCounter();
}
});
}
}
function updateStepCounter() {
var display = document.getElementById("stepCounterText");
if (display) {
display.textContent = currentStep + " / " + totalSteps;
}
}
function setupQuickActions() {
var chips = document.querySelectorAll(".quick-action-chip");
chips.forEach(function (chip) {
chip.addEventListener("click", function () {
var action = this.getAttribute("data-action");
var prompts = {
"full-stack": "Create a full-stack web application",
"writing": "Help me write ",
"data-insight": "Analyze data and provide insights",
"magic-design": "Design a beautiful UI for "
};
var input = document.getElementById("messageInput");
if (input && prompts[action]) {
input.value = prompts[action];
input.focus();
}
});
});
}
function setupSidebarItems() {
var items = document.querySelectorAll(".agent-sidebar-item");
items.forEach(function (item) {
item.addEventListener("click", function () {
items.forEach(function (i) { i.classList.remove("active"); });
this.classList.add("active");
});
});
}
/* ===========================================
Agent Mode WebSocket Message Handlers
=========================================== */
function handleAgentMessage(data) {
if (!agentMode) return;
switch (data.type) {
case "thought_process":
renderThoughtProcess(data.content);
break;
case "terminal_output":
appendTerminalLine(data.line, data.stream);
break;
case "browser_ready":
showBrowserPreview(data.url);
break;
case "step_progress":
currentStep = data.current;
totalSteps = data.total;
updateStepCounter();
break;
case "step_complete":
break;
case "todo_update":
renderTodoList(data.todos);
break;
case "agent_status":
updateAgentInfo(data);
break;
case "file_created":
incrementBadge("explorerBadge");
break;
}
}
function renderThoughtProcess(content) {
var messages = document.getElementById("messages");
if (!messages) return;
var block = document.createElement("div");
block.className = "thought-process";
block.innerHTML =
'<button class="thought-process-header">' +
'<span class="thought-process-toggle">▶</span>' +
'<span>Thought Process</span>' +
"</button>" +
'<div class="thought-process-body">' + escapeForHtml(content) + "</div>";
var header = block.querySelector(".thought-process-header");
header.addEventListener("click", function () {
block.classList.toggle("expanded");
});
messages.appendChild(block);
}
function appendTerminalLine(text, stream) {
var terminal = document.getElementById("terminalPanelContent");
if (!terminal) return;
var line = document.createElement("div");
line.className = "terminal-line " + (stream || "stdout");
line.textContent = text;
terminal.appendChild(line);
terminal.scrollTop = terminal.scrollHeight;
terminalLineCount++;
incrementBadge("terminalBadge");
}
function showBrowserPreview(url) {
var content = document.getElementById("browserPanelContent");
var urlBar = document.getElementById("browserUrlBar");
if (!content || !urlBar) return;
urlBar.value = url;
content.innerHTML = '<iframe src="' + url + '" sandbox="allow-scripts allow-same-origin"></iframe>';
}
function renderTodoList(todos) {
var messages = document.getElementById("messages");
if (!messages) return;
var existing = messages.querySelector(".agent-todo-list:last-child");
if (existing) existing.remove();
var list = document.createElement("div");
list.className = "agent-todo-list";
var headerHtml = '<div class="agent-todo-header">' +
'<span>📋 Todos</span>' +
'<span class="agent-todo-count">' + todos.length + "</span>" +
"</div>";
var itemsHtml = todos.map(function (todo) {
var doneClass = todo.done ? " done" : "";
var checkMark = todo.done ? "✓" : "";
return '<div class="agent-todo-item' + doneClass + '">' +
'<span class="agent-todo-check">' + checkMark + "</span>" +
'<span>' + escapeForHtml(todo.text) + "</span>" +
"</div>";
}).join("");
list.innerHTML = headerHtml + itemsHtml;
messages.appendChild(list);
}
function updateAgentInfo(data) {
var nameEl = document.getElementById("agentNameDisplay");
var levelEl = document.getElementById("agentLevelBadge");
var modelEl = document.getElementById("agentModelDisplay");
if (nameEl && data.name) nameEl.textContent = data.name;
if (levelEl && data.level) {
levelEl.textContent = data.level;
levelEl.className = "agent-level-badge badge-" + data.level.toLowerCase();
}
if (modelEl && data.model) {
modelEl.textContent = data.model + " — " + (data.usage || 0) + "%";
}
}
function incrementBadge(badgeId) {
var badge = document.getElementById(badgeId);
if (!badge) return;
var count = parseInt(badge.textContent, 10) || 0;
badge.textContent = count + 1;
badge.style.display = "";
}
function escapeForHtml(text) {
var div = document.createElement("div");
div.textContent = text || "";
return div.innerHTML;
}
/* ===========================================
Expose to global scope
=========================================== */
window.AgentMode = {
init: initAgentMode,
handleMessage: handleAgentMessage,
setMode: setMode,
isActive: function () { return agentMode; }
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initAgentMode);
} else {
initAgentMode();
}
})();