autonomy-labs / static /dashboard.html
ArunKr's picture
Upload folder using huggingface_hub
6c13b5e verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard - autonomy-labs</title>
<script src="https://cdn.tailwindcss.com?plugins=typography"></script>
<script src="/static/vendor/supabase-loader.js" defer></script>
<link href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.1.6/dist/purify.min.js"></script>
<!-- KaTeX -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"></script>
<!-- Highlight.js -->
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/tokyo-night-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<link rel="stylesheet" href="/static/theme.css">
<link rel="stylesheet" href="/static/dashboard.css">
</head>
<body class="h-screen flex flex-col overflow-hidden">
<!-- Navbar -->
<nav class="bg-gray-800 border-b border-gray-700 shrink-0">
<div class="px-4 py-3 flex items-center gap-3">
<!-- Left: brand + text nav -->
<div class="text-xl font-bold text-blue-500 flex items-center gap-2 shrink-0">
<span class="md:hidden cursor-pointer text-gray-400"
onclick="document.getElementById('mobile-menu').classList.toggle('hidden')"></span>
autonomy-labs
</div>
<div class="hidden md:flex space-x-1 shrink-0">
<button onclick="switchMode('dashboard')"
class="px-3 py-1 rounded hover:bg-gray-700 text-gray-300 transition"
id="nav-dashboard">Dashboard</button>
<button onclick="switchMode('chat')"
class="px-3 py-1 rounded hover:bg-gray-700 text-gray-300 transition" id="nav-chat">Chat</button>
<button onclick="switchMode('rooms')"
class="px-3 py-1 rounded hover:bg-gray-700 text-gray-300 transition" id="nav-rooms">Rooms</button>
<button onclick="switchMode('notes')"
class="px-3 py-1 rounded hover:bg-gray-700 text-gray-300 transition" id="nav-notes">Notes</button>
</div>
<!-- Center: welcome -->
<div class="hidden md:flex flex-1 justify-center min-w-0">
<div class="text-sm text-gray-400 truncate">Welcome to autonomy-labs</div>
</div>
<!-- Right: icons + user -->
<div class="flex items-center gap-2 min-w-0 ml-auto">
<button onclick="switchMode('terminal')"
class="p-2 rounded hover:bg-gray-700 text-gray-300 transition inline-flex items-center justify-center"
id="nav-terminal" title="Terminal" aria-label="Terminal">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5h16v14H4V5z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 9l3 3-3 3" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 15h6" />
</svg>
</button>
<button onclick="toggleTheme()" id="nav-theme" title="Toggle theme" aria-label="Toggle theme"
class="p-2 rounded hover:bg-gray-700 text-gray-300 transition inline-flex items-center justify-center">
<span id="theme-icon"></span>
</button>
<button onclick="openAdmin()" id="nav-admin" title="Admin" aria-label="Admin"
class="hidden p-2 rounded hover:bg-gray-700 text-gray-300 transition inline-flex items-center justify-center">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 3l8 4v6c0 5-3.5 9-8 9s-8-4-8-9V7l8-4z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 12l2 2 4-5" />
</svg>
</button>
<button onclick="openSettings()" id="nav-settings" title="Settings" aria-label="Settings"
class="p-2 rounded hover:bg-gray-700 text-gray-300 transition inline-flex items-center justify-center">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37.608-.996.07-2.296-1.065-2.572-1.756-.426-1.756-2.924 0-3.35.996-.242 1.53-1.53 1.066-2.573-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</button>
<div id="user-badge" class="hidden md:flex flex-col items-end leading-tight min-w-0">
<div id="user-name" class="text-sm text-gray-200 font-medium truncate max-w-[20rem]"></div>
<div id="user-email" class="text-xs text-gray-400 truncate max-w-[20rem]"></div>
</div>
<button id="logout-btn"
class="px-3 py-1 bg-red-600 rounded hover:bg-red-700 text-sm font-medium transition">Logout</button>
</div>
</div>
<!-- Mobile Menu -->
<div id="mobile-menu" class="hidden md:hidden bg-gray-800 border-t border-gray-700 p-2 space-y-1">
<button onclick="switchMode('dashboard')"
class="block w-full text-left px-3 py-2 rounded hover:bg-gray-700 text-gray-300">Dashboard</button>
<button onclick="switchMode('chat')"
class="block w-full text-left px-3 py-2 rounded hover:bg-gray-700 text-gray-300">Chat</button>
<button onclick="switchMode('rooms')" id="mobile-rooms"
class="block w-full text-left px-3 py-2 rounded hover:bg-gray-700 text-gray-300">Rooms</button>
<button onclick="switchMode('notes')"
class="block w-full text-left px-3 py-2 rounded hover:bg-gray-700 text-gray-300">Notes</button>
<button onclick="switchMode('terminal')"
class="block w-full text-left px-3 py-2 rounded hover:bg-gray-700 text-gray-300">Terminal</button>
<button onclick="openAdmin()" id="mobile-admin"
class="hidden block w-full text-left px-3 py-2 rounded hover:bg-gray-700 text-gray-300">Admin</button>
</div>
</nav>
<!-- Main Content Area (Full Screen) -->
<div class="flex-grow relative overflow-hidden">
<!-- Dashboard View -->
<div id="dashboard-view"
class="view-section w-full h-full flex flex-col items-center justify-start p-8 text-center overflow-y-auto">
<h1
class="text-4xl font-bold mb-4 bg-gradient-to-r from-blue-400 to-purple-500 bg-clip-text text-transparent">
Welcome to autonomy-labs</h1>
<p class="text-gray-400 max-w-md mx-auto mb-2">Your AI-powered development environment. Access the Chat for
coding assistance or the Terminal for execution.</p>
<p class="text-gray-500 text-sm max-w-md mx-auto mb-8">Build faster with chat + runnable terminals.</p>
<div class="flex gap-4 mb-8">
<button onclick="switchMode('chat')"
class="px-6 py-3 bg-blue-600 rounded-lg hover:bg-blue-700 font-semibold transition">Open
Chat</button>
<button onclick="switchMode('terminal')"
class="px-6 py-3 bg-gray-700 rounded-lg hover:bg-gray-600 font-semibold transition">Open
Terminal</button>
</div>
<div class="w-full max-w-3xl text-left bg-gray-800/60 border border-gray-700 rounded-2xl p-5 shadow-xl">
<div class="flex items-start justify-between gap-4">
<div>
<h2 class="text-lg font-semibold text-gray-100">Getting started</h2>
<p class="text-sm text-gray-400 mt-1">A quick checklist to get productive in under a minute.</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 mt-4">
<div class="p-3 rounded-xl bg-gray-900/40 border border-gray-700">
<div class="text-sm font-semibold text-gray-200">1) Configure provider</div>
<div class="text-sm text-gray-400 mt-1">Set <code>DEFAULT_BASE_URL</code> + <code>DEFAULT_API_KEY</code> in deployment secrets. Use the Model box in chat to override.</div>
</div>
<div class="p-3 rounded-xl bg-gray-900/40 border border-gray-700">
<div class="text-sm font-semibold text-gray-200">2) Start chatting</div>
<div class="text-sm text-gray-400 mt-1">Use Stop to cancel a response. Attach images or take a screenshot for multimodal prompts.</div>
</div>
<div class="p-3 rounded-xl bg-gray-900/40 border border-gray-700">
<div class="text-sm font-semibold text-gray-200">3) Run commands</div>
<div class="text-sm text-gray-400 mt-1">Open Terminal and run shell commands; resize panes as needed.</div>
</div>
<div class="p-3 rounded-xl bg-gray-900/40 border border-gray-700">
<div class="text-sm font-semibold text-gray-200">4) Autonomous mode</div>
<div class="text-sm text-gray-400 mt-1">Use “Run in Terminal” buttons on code blocks and resize chat/terminal split.</div>
</div>
</div>
</div>
</div>
<!-- Chat View -->
<div id="chat-view" class="view-section hidden w-full h-full flex overflow-hidden">
<!-- Sidebar for History (Desktop) -->
<div id="chat-sidebar" class="w-64 bg-gray-800 border-r border-gray-700 flex flex-col hidden md:flex shrink-0">
<div class="p-3 border-b border-gray-700 flex items-center gap-2">
<button onclick="startNewChat()"
class="flex-grow bg-blue-600 hover:bg-blue-700 text-white py-2 rounded flex items-center justify-center gap-2 text-sm font-medium transition"
title="New chat" aria-label="New chat">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 5v14M5 12h14" />
</svg>
</button>
<button onclick="toggleChatSidebar()"
class="hidden md:inline-flex p-2 rounded hover:bg-gray-700 text-gray-300 transition"
title="Collapse history" aria-label="Collapse history">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
</div>
<div id="history-list" class="flex-grow overflow-y-auto p-2 space-y-1"></div>
</div>
<!-- Chat Area -->
<div class="flex-grow flex flex-col h-full bg-gray-900 relative min-w-0">
<!-- Header -->
<div
class="bg-gray-800 p-3 border-b border-gray-700 flex justify-between items-center shadow-sm shrink-0">
<div class="flex items-center gap-3 overflow-hidden">
<button onclick="toggleChatSidebar()"
class="text-gray-400 hover:text-white p-1 rounded hover:bg-gray-700 transition"
title="Toggle history" aria-label="Toggle history">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
<h2 class="text-lg font-semibold truncate" id="current-chat-title">New Chat</h2>
</div>
<div class="flex items-center gap-2">
<div class="hidden md:flex bg-gray-700/60 rounded-lg p-1">
<button id="chat-mode-chat" onclick="setChatMode('chat')"
class="px-2 py-1 text-xs rounded-md text-gray-200 hover:bg-white/10 transition">Chat</button>
<button id="chat-mode-auto" onclick="setChatMode('autonomous')"
class="px-2 py-1 text-xs rounded-md text-gray-200 hover:bg-white/10 transition">Autonomous</button>
</div>
</div>
</div>
<!-- Settings Drawer (Unified) -->
<div id="settings-overlay" class="settings-overlay settings-hidden" onclick="closeSettings()"></div>
<div id="settings-drawer" class="settings-drawer settings-hidden" role="dialog" aria-label="Settings">
<div class="p-4 border-b border-gray-700 flex items-center justify-between">
<div class="font-semibold text-gray-100">Settings</div>
<button onclick="closeSettings()" class="text-gray-400 hover:text-white px-2 py-1 rounded hover:bg-white/10">&times;</button>
</div>
<div id="settings-content" class="p-4 space-y-4 overflow-y-auto" style="max-height: calc(100% - 56px);">
<div class="bg-gray-800/40 border border-gray-700 rounded-lg px-3 py-2">
<div class="text-sm text-gray-100">Chat provider</div>
<div class="text-xs text-gray-400 mt-0.5">Configured via deployment secrets (<code>DEFAULT_BASE_URL</code>, <code>DEFAULT_API_KEY</code>, <code>DEFAULT_MODEL</code>).</div>
<div class="text-xs text-gray-500 mt-1">Use the Model input in the chat bar to override per-message.</div>
</div>
<div class="pt-4 border-t border-gray-700">
<div class="flex items-center justify-between gap-2 mb-2">
<div class="font-semibold text-gray-200">Access</div>
</div>
<label class="flex items-center justify-between gap-3 bg-gray-800/40 border border-gray-700 rounded-lg px-3 py-2">
<div>
<div class="text-sm text-gray-100">Enable Sign Up / Register</div>
<div class="text-xs text-gray-400 mt-0.5">Default disabled. Affects the login page “Register” button.</div>
</div>
<input id="auth-allow-signup" type="checkbox" onchange="setAllowSignup(this.checked)"
class="h-4 w-4 accent-blue-600">
</label>
</div>
<div class="pt-4 border-t border-gray-700">
<div class="flex items-center justify-between gap-2 mb-2">
<div class="font-semibold text-gray-200">Account</div>
</div>
<div class="bg-gray-900/30 border border-gray-700 rounded-lg p-3 space-y-3">
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<div>
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Display name</label>
<input id="account-display-name" type="text" placeholder="Your name"
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
<div class="text-xs text-gray-500 mt-1">Saved to Supabase user metadata.</div>
</div>
<div>
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Device name (Rooms)</label>
<input id="account-device-name" type="text" placeholder="Laptop / Phone / ..."
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
<div class="text-xs text-gray-500 mt-1">Stored in this browser only.</div>
</div>
</div>
<div class="flex flex-wrap items-center justify-between gap-2">
<div class="text-xs text-gray-400">
<span class="font-semibold text-gray-200">Device ID:</span>
<span id="account-device-id" class="font-mono"></span>
</div>
<div class="flex gap-2">
<button onclick="saveAccountSettings()"
class="bg-blue-600 hover:bg-blue-700 text-white px-2 py-1 rounded text-xs">Save</button>
<button onclick="regenerateDeviceId()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">New device</button>
</div>
</div>
<div id="account-status" class="text-xs text-gray-500"></div>
</div>
</div>
<div class="pt-4 border-t border-gray-700">
<div class="flex items-center justify-between gap-2 mb-2">
<div class="font-semibold text-gray-200">Codex SDK</div>
<div class="flex gap-2">
<button onclick="resetCodexThread()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Reset Thread</button>
<button onclick="refreshCodexMcpServers()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">List MCP</button>
</div>
</div>
<div class="text-xs text-gray-400 mb-2">
Uses OpenAI Codex SDK locally (separate from chat). Device auth is available in the terminal: <code>codex login --device-auth</code>.
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Codex Base URL</label>
<input type="text" id="codex-base-url" placeholder="https://api.openai.com/v1"
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500"
onchange="saveCodexSdkSettings()">
</div>
<div>
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Codex API Key</label>
<input type="password" id="codex-api-key" placeholder="sk-..."
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500"
onchange="saveCodexSdkSettings()">
</div>
</div>
<div class="mt-3">
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Codex Model (optional)</label>
<input type="text" id="codex-model" placeholder="(leave blank for default)"
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500"
onchange="saveCodexSdkSettings()">
<div class="text-xs text-gray-500 mt-1">Prevents chat-provider models (e.g. DeepSeek) from being sent to Codex.</div>
</div>
<div class="mt-3">
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Codex Sandbox</label>
<select id="codex-sandbox-mode" onchange="saveCodexSandboxMode()"
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
<option value="read-only">read-only</option>
<option value="workspace-write">workspace-write</option>
<option value="danger-full-access">danger-full-access</option>
</select>
<div class="text-xs text-gray-500 mt-1">Use <code>danger-full-access</code> only if you fully trust the model and this environment.</div>
</div>
<div class="mt-3">
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Codex Workspace Directory (optional)</label>
<input type="text" id="codex-workdir" placeholder="/data/codex/workspace"
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500"
onchange="saveCodexWorkdir()">
<div class="text-xs text-gray-500 mt-1">Used as <code>--cd</code> for Codex runs. Restricted to <code>/data/codex/workspace</code> when available.</div>
</div>
<div class="mt-3">
<label class="flex items-center justify-between gap-3 bg-gray-800/40 border border-gray-700 rounded-lg px-3 py-2">
<div>
<div class="text-sm text-gray-100">Show raw Codex JSON events</div>
<div class="text-xs text-gray-400 mt-0.5">Appends raw JSON events to the progress view while Codex runs.</div>
</div>
<input id="codex-show-jsonl" type="checkbox" onchange="setCodexShowJsonl(this.checked)"
class="h-4 w-4 accent-blue-600">
</label>
</div>
<div id="codex-mcp-list" class="mt-3 text-xs text-gray-400"></div>
</div>
<div class="pt-4 border-t border-gray-700">
<div class="flex items-center justify-between gap-2 mb-2">
<div class="font-semibold text-gray-200">Autonomous Tools</div>
</div>
<label class="flex items-center justify-between gap-3 bg-gray-800/40 border border-gray-700 rounded-lg px-3 py-2">
<div>
<div class="text-sm text-gray-100">Use Codex CLI in Autonomous mode</div>
<div class="text-xs text-gray-400 mt-0.5">Lets the agent access configured MCP tools (when available).</div>
</div>
<input id="agent-use-codex-cli" type="checkbox" onchange="setAgentUseCodexCli(this.checked)"
class="h-4 w-4 accent-blue-600">
</label>
</div>
<div class="pt-4 border-t border-gray-700">
<div class="flex items-center justify-between gap-2 mb-2">
<div class="font-semibold text-gray-200">MCP</div>
<button onclick="addMcpServerPrompt()"
class="bg-blue-600 hover:bg-blue-700 text-white px-2 py-1 rounded text-xs">+ Add</button>
</div>
<div class="text-xs text-gray-400 mb-2">
Configure MCP servers (URLs + auth). Stored in <code>localStorage</code>. Import/export via <code>mcp.json</code>.
</div>
<div class="flex gap-2 mb-3">
<button onclick="exportMcpConfig()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Export</button>
<input id="mcp-import-input" type="file" accept="application/json" class="hidden" />
<button onclick="document.getElementById('mcp-import-input').click()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Import</button>
<button onclick="loadMcpRegistryFromServer()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Load (server)</button>
<button onclick="saveMcpRegistryToServer()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Save (server)</button>
</div>
<div class="bg-gray-900/30 border border-gray-700 rounded-lg p-3 mb-3 space-y-2">
<div class="flex items-center justify-between gap-2">
<div class="text-xs font-semibold text-gray-300 uppercase">Tool Policy</div>
<div class="flex gap-2">
<button onclick="loadMcpPolicyFromServer()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Load</button>
<button onclick="saveMcpPolicyToServer()"
class="bg-blue-600 hover:bg-blue-700 text-white px-2 py-1 rounded text-xs">Save</button>
</div>
</div>
<div class="text-xs text-gray-400">Optional allow/deny list enforced server-side for <code>/api/mcp/call</code> and tool listing.</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<div>
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Allow tools (one per line)</label>
<textarea id="mcp-allow-tools" rows="3"
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500 font-mono"
placeholder="filesystem.read\nfilesystem.write"></textarea>
<div class="text-xs text-gray-500 mt-1">If non-empty, only these tools are allowed.</div>
</div>
<div>
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Deny tools (one per line)</label>
<textarea id="mcp-deny-tools" rows="3"
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500 font-mono"
placeholder="filesystem.write"></textarea>
<div class="text-xs text-gray-500 mt-1">Always blocked (overrides allow).</div>
</div>
</div>
<div id="mcp-policy-status" class="text-xs text-gray-500"></div>
</div>
<div class="flex gap-2 mb-3">
<input id="mcp-direct-url" type="text" placeholder="Direct MCP URL (https://...)"
class="flex-grow bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
<button onclick="addMcpServerByUrl()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 rounded text-xs">Add</button>
</div>
<div id="settings-mcp-server-list" class="space-y-3"></div>
</div>
<div id="admin-panel" class="hidden pt-4 border-t border-gray-700">
<div class="flex items-center justify-between gap-2 mb-2">
<div class="font-semibold text-gray-200">Admin</div>
</div>
<div class="text-xs text-gray-400 mb-2">Read-only admin status and server feature flags.</div>
<div id="admin-status" class="text-sm text-gray-200">Loading...</div>
<div id="admin-features" class="mt-2 grid grid-cols-2 gap-2 text-xs text-gray-300"></div>
<div class="mt-3 bg-gray-900/30 border border-gray-700 rounded-lg p-3 space-y-2">
<div class="flex items-center justify-between gap-2">
<div class="text-xs font-semibold text-gray-300 uppercase">Feature Overrides</div>
<div class="flex gap-2">
<button onclick="loadAdminFeatureOverrides()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Load</button>
<button onclick="saveAdminFeatureOverrides()"
class="bg-blue-600 hover:bg-blue-700 text-white px-2 py-1 rounded text-xs">Save</button>
</div>
</div>
<div class="text-xs text-gray-400">
Overrides are persisted server-side (requires admin). Useful for temporarily disabling high-risk features without a redeploy.
</div>
<div class="grid grid-cols-2 gap-2 text-xs text-gray-200">
<label class="flex items-center gap-2 bg-gray-800/40 border border-gray-700 rounded-lg px-3 py-2">
<input id="admin-override-terminal" type="checkbox" class="h-4 w-4 accent-blue-600">
Terminal
</label>
<label class="flex items-center gap-2 bg-gray-800/40 border border-gray-700 rounded-lg px-3 py-2">
<input id="admin-override-codex" type="checkbox" class="h-4 w-4 accent-blue-600">
Codex
</label>
<label class="flex items-center gap-2 bg-gray-800/40 border border-gray-700 rounded-lg px-3 py-2">
<input id="admin-override-mcp" type="checkbox" class="h-4 w-4 accent-blue-600">
MCP
</label>
<label class="flex items-center gap-2 bg-gray-800/40 border border-gray-700 rounded-lg px-3 py-2">
<input id="admin-override-indexing" type="checkbox" class="h-4 w-4 accent-blue-600">
Indexing
</label>
<label class="flex items-center gap-2 bg-gray-800/40 border border-gray-700 rounded-lg px-3 py-2">
<input id="admin-override-rooms" type="checkbox" class="h-4 w-4 accent-blue-600">
Rooms
</label>
<label class="flex items-center gap-2 bg-gray-800/40 border border-gray-700 rounded-lg px-3 py-2">
<input id="admin-override-vault" type="checkbox" class="h-4 w-4 accent-blue-600">
Vault
</label>
</div>
<div id="admin-overrides-status" class="text-xs text-gray-500"></div>
</div>
<div class="mt-3 bg-gray-900/30 border border-gray-700 rounded-lg p-3 space-y-2">
<div class="flex items-center justify-between gap-2">
<div class="text-xs font-semibold text-gray-300 uppercase">Users</div>
<div class="flex gap-2">
<button onclick="loadAdminUsers()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Load</button>
</div>
</div>
<div class="text-xs text-gray-400">
Manage Supabase users. Requires <code>SUPABASE_SERVICE_ROLE_KEY</code> on the server.
</div>
<div class="flex flex-wrap items-center gap-2 text-xs text-gray-200">
<input id="admin-users-prune-days" type="number" min="1" value="90"
class="w-24 bg-gray-800 text-xs rounded border border-gray-700 p-1 text-white outline-none focus:border-blue-500"
title="Older than (days)">
<input id="admin-users-prune-max" type="number" min="1" value="50"
class="w-20 bg-gray-800 text-xs rounded border border-gray-700 p-1 text-white outline-none focus:border-blue-500"
title="Max delete">
<label class="flex items-center gap-1">
<input id="admin-users-prune-inactive" type="checkbox" class="accent-blue-600" checked>
Inactive only
</label>
<button onclick="pruneAdminUsers()"
class="bg-red-600 hover:bg-red-700 text-white px-2 py-1 rounded text-xs">Prune old users</button>
</div>
<div id="admin-users-status" class="text-xs text-gray-500"></div>
<div id="admin-users-list" class="space-y-2"></div>
</div>
<div class="mt-3">
<div class="text-xs font-semibold text-gray-400 mb-1 uppercase">MCP Templates (server)</div>
<textarea id="admin-mcp-templates" rows="6"
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500 font-mono"
placeholder='{\"version\":1,\"templates\":[]}'></textarea>
<div class="mt-2 flex gap-2">
<button onclick="loadAdminMcpTemplates()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Load</button>
<button onclick="saveAdminMcpTemplates()"
class="bg-blue-600 hover:bg-blue-700 text-white px-2 py-1 rounded text-xs">Save</button>
</div>
<div class="text-xs text-gray-500 mt-1">Templates are admin-managed and persisted server-side; use to share common MCP server presets.</div>
</div>
</div>
<div class="pt-4 border-t border-gray-700">
<div class="flex items-center justify-between gap-2 mb-2">
<div class="font-semibold text-gray-200">Notes</div>
<button onclick="switchMode('notes'); closeSettings();"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Open Notes</button>
</div>
<div class="text-xs text-gray-400">Notes editor is available in the Notes tab.</div>
</div>
<div id="rag-panel" class="pt-4 border-t border-gray-700">
<div class="flex items-center justify-between gap-2 mb-2">
<div class="font-semibold text-gray-200">RAG (Indexing)</div>
<button onclick="loadRagDocuments()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Refresh</button>
</div>
<div class="text-xs text-gray-400 mb-2">
Upload plain text documents and run basic keyword search. Stored per user. Enable with <code>ENABLE_INDEXING=1</code>.
</div>
<div id="rag-disabled" class="hidden text-xs bg-yellow-900/30 border border-yellow-800 text-yellow-200 rounded-lg px-3 py-2">
Indexing is disabled on this deployment.
</div>
<div id="rag-status" class="text-xs text-gray-500 mt-2"></div>
<div id="rag-controls" class="mt-3 space-y-3">
<div class="flex items-center gap-2">
<input id="rag-file-input" type="file" accept=".txt,.md,.markdown,text/plain"
class="flex-grow bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
<button onclick="uploadRagDocument()"
class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-2 rounded text-xs">Upload</button>
</div>
<div>
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Search</label>
<div class="flex gap-2">
<input id="rag-query" type="text" placeholder="Search query..."
class="flex-grow bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
<button onclick="searchRag()"
class="bg-green-600 hover:bg-green-700 text-white px-3 py-2 rounded text-xs">Go</button>
</div>
<div id="rag-search-results" class="mt-2 space-y-2"></div>
</div>
<div class="bg-gray-900/30 border border-gray-700 rounded-lg p-3 space-y-2">
<div class="flex items-center justify-between gap-2">
<div class="text-xs font-semibold text-gray-300 uppercase">Website Indexing</div>
<button onclick="loadIndexingJobs()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Refresh Jobs</button>
</div>
<div class="text-xs text-gray-400">Indexes same-origin HTML pages into RAG with depth/page limits. Blocks private/localhost targets.</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-2">
<input id="crawl-url" type="text" placeholder="https://example.com"
class="md:col-span-2 bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
<button onclick="startWebCrawlJob()"
class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-2 rounded text-xs">Start</button>
</div>
<div class="grid grid-cols-3 gap-2">
<div>
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Max Pages</label>
<input id="crawl-max-pages" type="number" min="1" max="150" value="25"
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
</div>
<div>
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Max Depth</label>
<input id="crawl-max-depth" type="number" min="0" max="6" value="2"
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
</div>
<div>
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Rate (sec)</label>
<input id="crawl-rate" type="number" min="0" max="5" step="0.05" value="0.25"
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
</div>
</div>
<div class="flex items-center gap-2">
<input id="crawl-respect-robots" type="checkbox" checked class="h-4 w-4 accent-blue-600">
<label for="crawl-respect-robots" class="text-xs text-gray-300">Respect robots.txt (basic)</label>
</div>
<div id="indexing-status" class="text-xs text-gray-500"></div>
<div id="indexing-jobs" class="space-y-2"></div>
</div>
<div class="bg-gray-900/30 border border-gray-700 rounded-lg p-3 space-y-2">
<div class="text-xs font-semibold text-gray-300 uppercase">GitHub Repo Indexing</div>
<div class="text-xs text-gray-400">Indexes repository text files into RAG (uses `GITHUB_TOKEN`/`GITHUB_PAT` for private repos).</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-2">
<input id="gh-repo" type="text" placeholder="owner/repo or https://github.com/owner/repo"
class="md:col-span-2 bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
<button onclick="startGithubRepoJob()"
class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-2 rounded text-xs">Start</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-2">
<div>
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Ref</label>
<input id="gh-ref" type="text" placeholder="main"
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
</div>
<div>
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Path Prefix</label>
<input id="gh-path" type="text" placeholder="(optional) src/"
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
</div>
<div>
<label class="block text-xs font-semibold text-gray-400 mb-1 uppercase">Max Files</label>
<input id="gh-max-files" type="number" min="1" max="400" value="60"
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
</div>
</div>
<div id="github-indexing-status" class="text-xs text-gray-500"></div>
</div>
<div>
<div class="text-xs font-semibold text-gray-400 mb-1 uppercase">Documents</div>
<div id="rag-documents" class="space-y-2"></div>
</div>
</div>
</div>
<div id="vault-panel" class="pt-4 border-t border-gray-700">
<div class="flex items-center justify-between gap-2 mb-2">
<div class="font-semibold text-gray-200">Vault (Encrypted)</div>
<div class="flex gap-2">
<button onclick="loadVaultFromServer()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Load</button>
<button onclick="lockVault()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Lock</button>
</div>
</div>
<div class="text-xs text-gray-400">
Client-side encrypted vault stored per user. Enable with <code>ENABLE_VAULT=1</code>. Do not rely on this for production security.
</div>
<div id="vault-disabled" class="hidden text-xs bg-yellow-900/30 border border-yellow-800 text-yellow-200 rounded-lg px-3 py-2 mt-2">
Vault is disabled on this deployment.
</div>
<div id="vault-status" class="text-xs text-gray-500 mt-2"></div>
<div class="mt-3 space-y-3">
<div class="bg-gray-900/30 border border-gray-700 rounded-lg p-3 space-y-2">
<div class="text-xs font-semibold text-gray-300 uppercase">Unlock</div>
<div class="flex gap-2">
<input id="vault-password" type="password" placeholder="Master password"
class="flex-grow bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
<button onclick="unlockVault()"
class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-2 rounded text-xs">Unlock</button>
<button onclick="createNewVault()"
class="bg-indigo-600 hover:bg-indigo-700 text-white px-3 py-2 rounded text-xs">New</button>
</div>
<div class="text-xs text-gray-500">Your password is never sent to the server.</div>
</div>
<div class="bg-gray-900/30 border border-gray-700 rounded-lg p-3 space-y-2">
<div class="flex items-center justify-between gap-2">
<div class="text-xs font-semibold text-gray-300 uppercase">Entries</div>
<button onclick="openVaultNewEntry()"
class="bg-green-600 hover:bg-green-700 text-white px-2 py-1 rounded text-xs">+ Add</button>
</div>
<input id="vault-search" type="text" placeholder="Search..."
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
<div id="vault-entries" class="space-y-2"></div>
</div>
</div>
</div>
</div>
</div>
<div id="agent-split-root" class="split-root flex-grow min-h-0">
<div id="agent-pane-chat" class="split-pane min-h-0 flex flex-col border-r border-gray-700 agent-pane-surface">
<div id="agent-terminal-collapsed-bar" class="hidden px-3 py-2 border-b border-gray-700/70 flex items-center justify-end bg-gray-900/30">
<button onclick="toggleAgentTerminalCollapsed()"
class="px-2 py-1 text-xs rounded-md text-gray-200 hover:bg-white/10 transition"
title="Show terminal">Show terminal</button>
</div>
<div id="chat-standard" class="flex-grow flex flex-col min-h-0">
<!-- Messages -->
<div id="chat-history" class="flex-grow p-4 overflow-y-auto space-y-4 scroll-smooth">
<div id="chat-empty-state" class="text-center text-gray-500 mt-10">
<div class="text-lg font-semibold text-gray-300">Start a conversation</div>
<div class="text-sm text-gray-500 mt-1" id="chat-empty-hint">Configure <code>DEFAULT_BASE_URL</code>, then send a message.</div>
</div>
</div>
<!-- Input -->
<div class="p-3 bg-gray-800 border-t border-gray-700 shrink-0">
<div id="chat-attachments" class="max-w-5xl mx-auto mb-2 hidden"></div>
<div class="flex gap-2 max-w-5xl mx-auto items-center flex-nowrap overflow-x-auto">
<select id="chat-agent-target" onchange="setChatAgentTarget(this.value)"
class="bg-gray-700 h-10 rounded-lg border border-gray-600 px-2 text-sm text-white outline-none focus:border-blue-500"
title="Target">
<option value="chat">Chat</option>
<option value="codex">@codex</option>
</select>
<input id="chat-input" type="text" autocomplete="off"
class="flex-grow bg-gray-700 h-10 px-3 rounded-lg border border-gray-600 focus:ring-2 focus:ring-blue-500 focus:outline-none text-white"
placeholder="Message..."
onkeydown="handleEnter(event)" />
<div class="hidden sm:flex items-center gap-2">
<input id="chat-model-quick" type="text" list="model-list" placeholder="Model"
class="bg-gray-700 h-10 w-44 px-2 rounded-lg border border-gray-600 text-sm text-white outline-none focus:border-blue-500"
oninput="setQuickModel(this.value)" title="Model" />
<button onclick="fetchModels({ quiet: false }).catch(() => {})"
class="bg-gray-700 h-10 w-10 rounded-lg hover:bg-gray-600 transition inline-flex items-center justify-center text-gray-200"
title="Refresh models" aria-label="Refresh models">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M20 12a8 8 0 1 1-2.343-5.657M20 4v6h-6" />
</svg>
</button>
</div>
<datalist id="model-list"></datalist>
<input id="chat-file-input" type="file" accept="image/*" multiple class="hidden" />
<button onclick="document.getElementById('chat-file-input').click()"
class="bg-gray-700 h-10 w-10 rounded-lg hover:bg-gray-600 transition inline-flex items-center justify-center text-gray-200"
title="Attach images" aria-label="Attach images">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21.44 11.05l-8.49 8.49a5 5 0 0 1-7.07-7.07l9.2-9.2a3.5 3.5 0 0 1 4.95 4.95l-9.2 9.2a2 2 0 0 1-2.83-2.83l8.49-8.49" />
</svg>
</button>
<button onclick="captureScreenshotToChat()"
class="bg-gray-700 h-10 w-10 rounded-lg hover:bg-gray-600 transition inline-flex items-center justify-center text-gray-200"
title="Take screenshot" aria-label="Take screenshot">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7h4l2-2h4l2 2h4v12H4V7z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 17a4 4 0 1 0 0-8 4 4 0 0 0 0 8z" />
</svg>
</button>
<button id="chat-mic-btn" onclick="toggleChatDictation()"
class="bg-gray-700 h-10 w-10 rounded-lg hover:bg-gray-600 transition inline-flex items-center justify-center text-gray-200"
title="Voice input" aria-label="Voice input">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 14a3 3 0 0 0 3-3V6a3 3 0 0 0-6 0v5a3 3 0 0 0 3 3z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11a7 7 0 0 1-14 0" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19v3" />
</svg>
</button>
<button id="chat-stop-btn" onclick="stopChatGeneration()" disabled
class="bg-gray-600 h-10 w-10 rounded-lg hover:bg-gray-700 font-bold transition inline-flex items-center justify-center disabled:opacity-40 disabled:cursor-not-allowed"
title="Stop generating">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<circle cx="12" cy="12" r="9" stroke-width="2" />
<rect x="9" y="9" width="6" height="6" rx="1" fill="currentColor" stroke="none" />
</svg>
</button>
<button onclick="sendChatBarMessage()"
class="bg-blue-600 h-10 w-10 rounded-lg hover:bg-blue-700 font-bold transition inline-flex items-center justify-center">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10 14l11-11M21 3l-7 20-4-9-9-4 20-7z" />
</svg>
</button>
</div>
</div>
</div>
</div>
<div id="agent-split-divider" class="split-divider hidden" title="Drag to resize panes"></div>
<div id="agent-pane-terminal" class="split-pane hidden min-h-0 agent-terminal-chrome flex flex-col">
<div class="shrink-0 flex items-center bg-gray-900/60 border-b border-gray-700 overflow-x-auto">
<div id="agent-terminal-tabs" class="flex"></div>
<button onclick="toggleAgentTerminalCollapsed()"
class="ml-auto px-2 py-2 text-gray-300 hover:text-white hover:bg-white/10 transition font-bold"
title="Collapse terminal" aria-label="Collapse terminal"></button>
<button onclick="createTerminalTab({ scope: 'agent' })"
class="px-3 py-2 text-gray-300 hover:text-white hover:bg-white/10 transition font-bold"
title="New Terminal">+</button>
</div>
<div id="agent-terminals-container" class="flex-grow relative w-full h-full"></div>
</div>
</div>
</div>
</div>
<!-- Terminal View (Multi-Tab) -->
<div id="terminal-view" class="view-section hidden w-full h-full flex flex-col overflow-hidden bg-black">
<!-- Tabs Bar -->
<div class="flex items-center bg-gray-800 border-b border-gray-700 shrink-0 overflow-x-auto">
<div id="terminal-tabs" class="flex">
<!-- Tab Items will be injected here -->
</div>
<div class="ml-auto flex items-center">
<button onclick="copyActiveTerminalOutput()"
class="px-2 py-2 text-gray-400 hover:text-white hover:bg-gray-700 transition"
title="Copy terminal output"></button>
<button onclick="setTerminalLayout('single')" id="layout-single"
class="px-2 py-2 text-gray-400 hover:text-white hover:bg-gray-700 transition"
title="Single pane"></button>
<button onclick="setTerminalLayout('vsplit')" id="layout-vsplit"
class="px-2 py-2 text-gray-400 hover:text-white hover:bg-gray-700 transition"
title="Vertical split"></button>
<button onclick="setTerminalLayout('hsplit')" id="layout-hsplit"
class="px-2 py-2 text-gray-400 hover:text-white hover:bg-gray-700 transition"
title="Horizontal split"></button>
<button onclick="setTerminalLayout('quad')" id="layout-quad"
class="px-2 py-2 text-gray-400 hover:text-white hover:bg-gray-700 transition"
title="2x2 grid"></button>
<button onclick="createTerminalTab()"
class="px-3 py-2 text-gray-400 hover:text-white hover:bg-gray-700 transition font-bold"
title="New Terminal">
+
</button>
</div>
</div>
<!-- Terminal Containers Area -->
<div id="terminals-container" class="flex-grow w-full h-full terminal-grid single">
<div id="terminal-pane-1" class="terminal-pane"></div>
<div id="terminal-pane-2" class="terminal-pane hidden"></div>
<div id="terminal-pane-3" class="terminal-pane hidden"></div>
<div id="terminal-pane-4" class="terminal-pane hidden"></div>
</div>
</div>
<!-- Notes View -->
<div id="notes-view" class="view-section hidden w-full h-full flex overflow-hidden bg-gray-900">
<div id="notes-sidebar" class="w-72 bg-gray-800 border-r border-gray-700 flex flex-col shrink-0">
<div class="p-3 border-b border-gray-700 flex items-center justify-between gap-2">
<div class="font-semibold text-gray-200">Notes</div>
<div class="flex gap-2">
<button onclick="createNotesFolder()" class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">+ Folder</button>
<button onclick="createNotesDoc()" class="bg-blue-600 hover:bg-blue-700 text-white px-2 py-1 rounded text-xs">+ Note</button>
</div>
</div>
<div class="p-2 border-b border-gray-700">
<button onclick="refreshNotesTree()" class="text-xs text-gray-300 hover:text-white">↻ Refresh</button>
</div>
<div id="notes-tree" class="flex-grow overflow-y-auto p-2 text-sm space-y-1"></div>
</div>
<div class="flex-grow min-w-0 flex flex-col">
<div class="p-3 border-b border-gray-700 bg-gray-800 flex items-center justify-between gap-2">
<div class="flex items-center gap-2 min-w-0 flex-1">
<button onclick="toggleNotesSidebar()" class="p-2 rounded hover:bg-gray-700 text-gray-300 transition inline-flex items-center justify-center"
title="Toggle folders" aria-label="Toggle folders">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
<input id="notes-title" type="text" placeholder="Title"
class="w-full bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500" />
</div>
<div class="flex items-center gap-2 shrink-0">
<div class="hidden md:flex bg-gray-700/60 rounded-lg p-1 mr-2">
<button id="notes-mode-split" onclick="setNotesMode('split')"
class="px-2 py-1 text-xs rounded-md text-gray-200 hover:bg-white/10 transition">Split</button>
<button id="notes-mode-edit" onclick="setNotesMode('edit')"
class="px-2 py-1 text-xs rounded-md text-gray-200 hover:bg-white/10 transition">Edit</button>
<button id="notes-mode-preview" onclick="setNotesMode('preview')"
class="px-2 py-1 text-xs rounded-md text-gray-200 hover:bg-white/10 transition">Preview</button>
</div>
<button onclick="aiWriteNotes()" class="bg-indigo-600 hover:bg-indigo-700 text-white px-3 py-2 rounded text-xs" title="Use AI to write in this note">@AI</button>
<button onclick="saveActiveNote()" class="bg-green-600 hover:bg-green-700 text-white px-3 py-2 rounded text-xs">Save</button>
<button onclick="renameActiveNote()" class="bg-gray-700 hover:bg-gray-600 text-white px-3 py-2 rounded text-xs">Rename</button>
<button onclick="deleteActiveNote()" class="bg-red-600 hover:bg-red-700 text-white px-3 py-2 rounded text-xs">Delete</button>
</div>
</div>
<div id="notes-main" class="flex-grow min-h-0 grid grid-cols-1 lg:grid-cols-2 gap-0">
<div id="notes-editor-pane" class="min-h-0 flex flex-col border-r border-gray-700">
<div class="px-3 py-2 text-xs text-gray-400 border-b border-gray-700 bg-gray-900/30 flex items-center justify-between">
<span>Editor</span>
<button id="notes-toggle-editor" onclick="toggleNotesPane('editor')" class="text-gray-300 hover:text-white">Hide</button>
</div>
<textarea id="notes-editor"
class="flex-grow min-h-0 w-full bg-gray-900 text-sm p-3 text-white outline-none font-mono"
placeholder="# Notes..."></textarea>
</div>
<div id="notes-preview-pane" class="min-h-0 flex flex-col">
<div class="px-3 py-2 text-xs text-gray-400 border-b border-gray-700 bg-gray-900/30 flex items-center justify-between">
<span>Preview</span>
<button id="notes-toggle-preview" onclick="toggleNotesPane('preview')" class="text-gray-300 hover:text-white">Hide</button>
</div>
<div id="notes-preview" class="flex-grow min-h-0 overflow-y-auto prose prose-invert max-w-none p-4"></div>
</div>
</div>
<div class="px-3 py-2 text-xs text-gray-400 border-t border-gray-700 bg-gray-800" id="notes-status"></div>
</div>
</div>
<!-- Rooms View (P2P PubSub MVP) -->
<div id="rooms-view" class="view-section hidden w-full h-full flex overflow-hidden bg-gray-900">
<div class="w-80 bg-gray-800 border-r border-gray-700 flex flex-col shrink-0">
<div class="p-3 border-b border-gray-700 flex items-center justify-between gap-2">
<div class="font-semibold text-gray-200">Rooms</div>
<button onclick="createRoom()"
class="bg-blue-600 hover:bg-blue-700 text-white px-2 py-1 rounded text-xs">+ New</button>
</div>
<div class="p-3 border-b border-gray-700 space-y-2">
<div class="text-xs font-semibold text-gray-400 uppercase">Join by ID</div>
<div class="flex gap-2">
<input id="rooms-join-id" type="text" placeholder="room UUID"
class="flex-grow bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500">
<button onclick="joinRoomFromInput()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 rounded text-xs">Join</button>
</div>
<div class="text-xs text-gray-500">Tip: share a room ID to invite others.</div>
</div>
<div class="p-2 border-b border-gray-700 flex items-center justify-between">
<button onclick="loadRoomsList()" class="text-xs text-gray-300 hover:text-white">↻ Refresh</button>
<div id="rooms-status" class="text-xs text-gray-500"></div>
</div>
<div id="rooms-list" class="flex-grow overflow-y-auto p-2 text-sm space-y-1"></div>
</div>
<div class="flex-grow min-w-0 flex flex-col">
<div class="p-3 border-b border-gray-700 bg-gray-800 flex items-center justify-between gap-2">
<div class="min-w-0">
<div class="text-sm font-semibold text-gray-200 truncate" id="rooms-active-title">Select a room</div>
<div class="text-xs text-gray-400 truncate" id="rooms-active-subtitle">Create or join a room to start chatting.</div>
</div>
<div class="flex items-center gap-2 shrink-0">
<label class="flex items-center gap-2 text-xs text-gray-300 bg-gray-700/60 px-2 py-1 rounded-lg">
<input id="rooms-prefer-p2p" type="checkbox" class="h-4 w-4 accent-blue-600">
Prefer P2P
</label>
<button onclick="copyActiveRoomId()"
class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Copy ID</button>
<button onclick="leaveActiveRoom()"
class="bg-red-600 hover:bg-red-700 text-white px-2 py-1 rounded text-xs">Leave</button>
</div>
</div>
<div class="px-3 py-2 border-b border-gray-700 bg-gray-900/40 flex items-center justify-between gap-3">
<div class="text-xs text-gray-400">
<span class="font-semibold text-gray-200" id="rooms-my-role">role: —</span>
<span class="mx-2"></span>
<button onclick="toggleRoomsMembers()" class="text-gray-300 hover:text-white underline underline-offset-2">members</button>
</div>
<div class="flex items-center gap-2">
<button onclick="loadRoomMembers()" class="bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 rounded text-xs">Refresh</button>
</div>
</div>
<div id="rooms-members-panel" class="hidden border-b border-gray-700 bg-gray-900/30 p-3">
<div id="rooms-members" class="grid grid-cols-1 md:grid-cols-2 gap-2 text-xs"></div>
<div id="rooms-members-status" class="text-xs text-gray-500 mt-2"></div>
</div>
<div id="rooms-peers-panel" class="hidden border-b border-gray-700 bg-gray-900/30 p-3">
<div class="text-xs font-semibold text-gray-400 uppercase mb-2">Peers</div>
<div id="rooms-peers-list" class="grid grid-cols-1 md:grid-cols-2 gap-2 text-xs"></div>
</div>
<div class="flex-grow min-h-0 overflow-y-auto p-4 space-y-3" id="rooms-messages">
<div class="text-sm text-gray-500">No room selected.</div>
</div>
<div class="border-t border-gray-700 bg-gray-800 p-3">
<div class="flex items-end gap-2">
<textarea id="rooms-input" rows="2" placeholder="Message…"
class="flex-grow bg-gray-700 text-sm rounded border border-gray-600 p-2 text-white outline-none focus:border-blue-500"></textarea>
<button onclick="sendRoomMessage()"
class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-2 rounded text-sm font-semibold">Send</button>
</div>
<div class="mt-2 text-xs text-gray-500">
<span id="rooms-conn-status">Disconnected</span>
<span class="mx-2"></span>
<span id="rooms-peers">Peers: 0</span>
<span class="mx-2"></span>
<button onclick="toggleRoomsPeers()" class="text-gray-300 hover:text-white underline underline-offset-2">peers</button>
</div>
</div>
</div>
</div>
<!-- Autonomous Mode View removed (now embedded in Chat) -->
</div>
<!-- Footer -->
<footer class="shrink-0 border-t border-gray-700 bg-gray-800 px-4 py-1 text-xs text-gray-400 flex items-center justify-between">
<div>autonomy-labs — ship ideas faster.</div>
<div class="hidden md:block">Chat • Autonomous • Terminal</div>
</footer>
<script src="/static/dashboard.js" defer></script>
</body>
</html>