<!-- This container ID links the HTML to the Custom CSS you added -->
<div id="nova-interface-container">
<!-- Load Tailwind CSS & Google Fonts -->
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<!-- Main Dashboard HTML Structure -->
<div class="w-full max-w-7xl mx-auto">
<div class="bg-white rounded-2xl shadow-lg flex flex-col h-[95vh]">
<!-- Header -->
<header class="p-4 border-b border-[var(--trm-border-gray)] flex justify-between items-center flex-shrink-0">
<div>
<h1 class="text-xl font-bold text-[var(--trm-dark-gray)]">TRM Nova Command Interface</h1>
<p class="text-sm text-[var(--trm-mid-gray)]">System Status: <span id="system-status" class="font-semibold text-green-600">Idle</span></p>
</div>
<img src="https://images.squarespace-cdn.com/content/v1/5f5f679483332c1c53b2039a/1600089531688-O5P9Y1J6J9Y2Z8J9Q8J9/TRM-Logo-Horizontal-Color.png?format=1500w" alt="Tether Rock Management Logo" class="h-10">
</header>
<div class="flex flex-1 min-h-0">
<!-- Left Panel: AI Team Roster -->
<aside class="w-1/4 p-4 border-r border-[var(--trm-border-gray)] flex flex-col">
<h2 class="text-lg font-semibold mb-4 text-[var(--trm-dark-gray)]">AI Team Roster</h2>
<div id="role-roster" class="space-y-2">
<button class="role-btn w-full text-left p-3 rounded-lg transition-all duration-200 hover:bg-gray-100 active" data-role="ronan">
<p class="font-semibold">Ronan (COO)</p>
<p class="text-xs text-[var(--trm-mid-gray)]">Trajectory & Logic</p>
</button>
<button class="role-btn w-full text-left p-3 rounded-lg transition-all duration-200 hover:bg-gray-100" data-role="gemini">
<p class="font-semibold">Gemini</p>
<p class="text-xs text-[var(--trm-mid-gray)]">Deviation & Research</p>
</button>
<button class="role-btn w-full text-left p-3 rounded-lg transition-all duration-200 hover:bg-gray-100" data-role="pm">
<p class="font-semibold">Project Manager</p>
<p class="text-xs text-[var(--trm-mid-gray)]">Triage & Tasking</p>
</button>
<button class="role-btn w-full text-left p-3 rounded-lg transition-all duration-200 hover:bg-gray-100" data-role="glenn">
<p class="font-semibold">Glenn</p>
<p class="text-xs text-[var(--trm-mid-gray)]">Legal Oversight</p>
</button>
</div>
<div class="mt-auto pt-4 border-t border-[var(--trm-border-gray)]">
<button id="board-meeting-btn" class="w-full text-left p-3 rounded-lg transition-all duration-200 bg-gray-800 text-white hover:bg-gray-700">
<p class="font-semibold">Board Meeting</p>
<p class="text-xs text-gray-300">Synthesize All Perspectives</p>
</button>
</div>
</aside>
<!-- Right Panel: Chat and Input -->
<main class="flex-1 flex flex-col">
<div id="chat-window" class="flex-1 p-6 overflow-y-auto">
<div class="ai-response ai-system p-4 rounded-lg mb-4">
<p class="font-semibold text-[var(--trm-dark-gray)]">Nova System</p>
<p class="text-sm text-gray-700 mt-1">Welcome to the Command Interface. Select a specialist from the roster, type your directive, and press send. All interactions are logged to the Brain.</p>
</div>
</div>
<div class="p-4 border-t border-[var(--trm-border-gray)] bg-white flex-shrink-0">
<div class="relative">
<textarea id="chat-input" class="w-full p-4 pr-16 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-[var(--trm-accent)] resize-none" placeholder="Enter directive, task, or prompt..." rows="2"></textarea>
<button id="send-button" class="absolute right-3 top-1/2 -translate-y-1/2 bg-[var(--trm-blue)] text-white rounded-lg p-2.5 hover:bg-[var(--trm-accent)] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[var(--trm-accent)] disabled:bg-gray-400 disabled:cursor-not-allowed">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
<path d="M3.105 2.289a.75.75 0 00-.826.95l1.414 4.949a.75.75 0 00.95.54l3.852-1.101a.75.75 0 010 1.392l-3.852-1.101a.75.75 0 00-.95.54l-1.414 4.95a.75.75 0 00.826.95L19 10 3.105 2.289z" />
</svg>
</button>
</div>
<div class="text-xs text-gray-400 mt-2">To include a document, paste its Google Drive link in your message.</div>
</div>
</main>
</div>
</div>
</div>
<!-- The JavaScript that makes the dashboard interactive -->
<script>
const BACKEND_URL = 'https://your-google-cloud-function-url-goes-here.cloudfunctions.net/nova-engine';
const chatWindow = document.getElementById('chat-window');
const chatInput = document.getElementById('chat-input');
const sendButton = document.getElementById('send-button');
const roleRoster = document.getElementById('role-roster');
const boardMeetingBtn = document.getElementById('board-meeting-btn');
const systemStatusEl = document.getElementById('system-status');
let activeRole = 'ronan';
function appendMessage(sender, text, roleClass) {
const senderP = document.createElement('p');
senderP.className = 'font-semibold text-gray-800';
senderP.textContent = sender;
const textP = document.createElement('p');
textP.className = 'text-sm text-gray-700 mt-1 whitespace-pre-wrap';
textP.textContent = text;
const messageDiv = document.createElement('div');
messageDiv.className = `ai-response p-4 rounded-lg mb-4 ${roleClass}`;
messageDiv.appendChild(senderP);
messageDiv.appendChild(textP);
chatWindow.appendChild(messageDiv);
chatWindow.scrollTop = chatWindow.scrollHeight;
}
function appendUserMessage(text) {
const messageDiv = document.createElement('div');
messageDiv.className = 'mb-4 flex justify-end';
messageDiv.innerHTML = `<div class="user-message p-4 rounded-lg max-w-lg"><p class="text-sm">${text.replace(/</g, "<").replace(/>/g, ">")}</p></div>`;
chatWindow.appendChild(messageDiv);
chatWindow.scrollTop = chatWindow.scrollHeight;
}
function setSystemStatus(status, isError = false) {
systemStatusEl.textContent = status;
systemStatusEl.className = `font-semibold ${isError ? 'text-red-600' : 'text-green-600'}`;
}
async function sendMessage() {
const messageText = chatInput.value.trim();
if (!messageText) return;
appendUserMessage(messageText);
chatInput.value = '';
chatInput.disabled = true;
sendButton.disabled = true;
setSystemStatus('Processing...');
const thinkingDiv = document.createElement('div');
thinkingDiv.id = 'thinking-indicator';
thinkingDiv.className = 'mb-4';
thinkingDiv.innerHTML = `<div class="ai-response ai-system p-4 rounded-lg"><p class="text-sm text-gray-700 animate-pulse">Nova engine is processing...</p></div>`;
chatWindow.appendChild(thinkingDiv);
chatWindow.scrollTop = chatWindow.scrollHeight;
try {
const payload = { prompt: messageText, role: activeRole };
const response = await fetch(BACKEND_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
document.getElementById('thinking-indicator')?.remove();
if (!response.ok) {
const errorResult = await response.json();
throw new Error(errorResult.error || `HTTP error! Status: ${response.status}`);
}
const result = await response.json();
if (Array.isArray(result.responses)) {
result.responses.forEach(res => appendMessage(res.roleName, res.text, `ai-${res.roleClass}`));
} else {
appendMessage(result.roleName, result.text, `ai-${result.roleClass}`);
}
setSystemStatus('Idle');
} catch (error) {
console.error('Error:', error);
document.getElementById('thinking-indicator')?.remove();
appendMessage('Nova System Error', error.message, 'ai-pm');
setSystemStatus('Error', true);
} finally {
chatInput.disabled = false;
sendButton.disabled = false;
chatInput.focus();
}
}
function selectRole(role) {
activeRole = role;
document.querySelectorAll('.role-btn').forEach(btn => btn.classList.remove('active'));
const selectedBtn = document.querySelector(`.role-btn[data-role='${role}']`);
if (selectedBtn) {
selectedBtn.classList.add('active');
} else {
boardMeetingBtn.classList.add('active');
}
}
sendButton.addEventListener('click', sendMessage);
chatInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
sendMessage();
}
});
roleRoster.addEventListener('click', (e) => {
const button = e.target.closest('.role-btn');
if (button) {
boardMeetingBtn.classList.remove('active');
selectRole(button.dataset.role);
}
});
boardMeetingBtn.addEventListener('click', () => {
selectRole('board_meeting');
});
</script>
</div>