<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Warmplane Control Deck</title>
<link rel="stylesheet" href="./src/styles/theme.css">
<style>
.ambient-glow {
position: fixed;
top: -150px;
right: 15%;
width: 450px;
height: 450px;
background: radial-gradient(circle, rgba(245, 158, 11, 0.07) 0%, rgba(12, 13, 16, 0) 70%);
pointer-events: none;
z-index: 0;
}
aside.sidebar {
width: 240px;
background: var(--surface);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
padding: 16px 12px;
z-index: 10;
}
.brand {
display: flex;
align-items: center;
gap: 10px;
padding: 6px 8px 18px;
border-bottom: 1px solid var(--border);
margin-bottom: 16px;
}
.brand-icon {
width: 28px;
height: 28px;
border-radius: var(--radius-sm);
background: linear-gradient(135deg, var(--amber-400), var(--amber-500));
display: flex;
align-items: center;
justify-content: center;
font-weight: 900;
color: #000;
font-size: 15px;
}
.nav-group-label {
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
color: var(--text-dim);
padding: 8px 10px 4px;
letter-spacing: 0.5px;
}
.nav-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 10px;
border-radius: var(--radius-sm);
color: var(--text-muted);
cursor: pointer;
font-size: 12.5px;
font-weight: 500;
transition: background-color var(--transition-fast), color var(--transition-fast), border-left-color var(--transition-fast);
margin-bottom: 2px;
border-left: 2.5px solid transparent;
}
.nav-item:hover {
background: var(--surface-hover);
color: var(--text-main);
}
.nav-item.active {
background: var(--surface-card);
color: var(--amber-400);
font-weight: 600;
border-left-color: var(--amber-400);
}
.nav-item svg {
width: 16px;
height: 16px;
transition: transform var(--transition-fast);
}
.main-wrapper {
flex: 1;
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
position: relative;
z-index: 1;
}
.top-bar {
height: 52px;
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
background: rgba(19, 21, 26, 0.7);
backdrop-filter: blur(8px);
}
.content-viewport {
flex: 1;
overflow-y: auto;
padding: 24px;
}
.cap-item {
padding: 9px 10px;
border-radius: var(--radius-sm);
cursor: pointer;
margin-bottom: 2px;
transition: background-color var(--transition-fast), border-left-color var(--transition-fast);
border-left: 2px solid transparent;
}
.cap-item:hover {
background: var(--surface-hover);
}
.cap-item.active {
background: var(--surface-card);
border-left-color: var(--amber-400);
}
.feed-row {
display: grid;
padding: 8px 14px;
border-bottom: 1px solid rgba(255,255,255,0.03);
align-items: center;
font-size: 11.5px;
transition: background-color var(--transition-fast);
}
.feed-row:hover {
background: var(--surface-hover);
}
</style>
</head>
<body>
<div class="ambient-glow"></div>
<aside class="sidebar">
<div class="brand">
<div class="brand-icon">W</div>
<div>
<div style="font-weight: 800; font-size: 13px; letter-spacing: 0.5px;">WARMPLANE</div>
<div style="font-size: 10px; color: var(--text-dim);">CONTROL DECK</div>
</div>
<span class="brand-badge" style="margin-left: auto;">v0.23.0</span>
</div>
<div class="nav-group-label">Mission Control</div>
<div class="nav-item active" data-tab="overview" onclick="window.app.switchTab('overview')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg>
Overview Cockpit
</div>
<div class="nav-item" data-tab="servers" onclick="window.app.switchTab('servers')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="8" rx="2"></rect><rect x="2" y="14" width="20" height="8" rx="2"></rect><line x1="6" y1="6" x2="6.01" y2="6"></line><line x1="6" y1="18" x2="6.01" y2="18"></line></svg>
Server Hub
</div>
<div class="nav-item" data-tab="playground" onclick="window.app.switchTab('playground')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path></svg>
MCP Playground
</div>
<div class="nav-item" data-tab="approvals" onclick="window.app.switchTab('approvals')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path><path d="m9 12 2 2 4-4"></path></svg>
<span>HITL Approvals</span>
<span id="nav-approvals-badge" class="brand-badge" style="margin-left: auto; display: none; background: rgba(245, 158, 11, 0.2); color: var(--amber-400); border-color: rgba(245, 158, 11, 0.4); font-size: 10px; padding: 1px 6px;"></span>
</div>
<div class="nav-group-label" style="margin-top: 14px;">Governance</div>
<div class="nav-item" data-tab="audit" onclick="window.app.switchTab('audit')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
WORM Audit Trail
</div>
<div class="nav-item" data-tab="policy" onclick="window.app.switchTab('policy')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>
Policy & Redaction
</div>
<div class="nav-item" data-tab="aliases" onclick="window.app.switchTab('aliases')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"></path><line x1="7" y1="7" x2="7.01" y2="7"></line></svg>
Alias Studio
</div>
<div class="nav-item" data-tab="profiles" onclick="window.app.switchTab('profiles')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
Profiles Studio
</div>
<div style="margin-top: auto; padding: 10px; background: var(--surface-card); border-radius: var(--radius-sm); border: 1px solid var(--border); display: flex; justify-content: space-between; align-items: center; font-size: 11px;">
<span style="display: flex; align-items: center; gap: 6px;">
<span style="width: 7px; height: 7px; border-radius: 50%; background: var(--green-400);"></span>
<span id="daemon-port-label">Daemon</span>
</span>
<span style="color: var(--green-400); font-weight: 700;">WARM</span>
</div>
</aside>
<main class="main-wrapper">
<header class="top-bar">
<div style="display: flex; align-items: center; gap: 14px;">
<div style="font-size: 16px; font-weight: 700; color: var(--text-main);" id="top-title">Overview Cockpit</div>
<div style="display: flex; align-items: center; gap: 6px; background: var(--surface-card); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 2px 8px;">
<span style="font-size: 11px; color: var(--text-dim); font-weight: 600;">PROFILE:</span>
<select id="top-profile-selector" class="form-input" style="height: 24px; padding: 0 6px; font-size: 11.5px; border: none; background: transparent; color: var(--amber-400); font-weight: 600; cursor: pointer;" onchange="window.app.setActiveProfile(this.value || null)">
<option value="">All Servers (Unrestricted)</option>
</select>
</div>
</div>
<div style="display: flex; gap: 8px;">
<button class="btn btn-ghost" onclick="window.app.openImportModal()">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
Import Config
</button>
<button class="btn btn-ghost" onclick="window.app.openAddServerModal()">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
Add Custom
</button>
<button class="btn btn-primary" onclick="window.app.openTemplateCatalog()">
✨ Browse Templates
</button>
</div>
</header>
<div class="content-viewport" id="app-main">
</div>
</main>
<div class="modal-backdrop" id="modal-add-server" onclick="if(event.target === this) window.app.closeModals()">
<div class="modal-box">
<div class="modal-header">
<div class="modal-title" id="modal-srv-title">Add Upstream MCP Server</div>
<button class="btn btn-ghost" onclick="window.app.closeModals()" style="padding: 2px 6px;">✕</button>
</div>
<div id="modal-srv-template-banner" style="background: var(--surface-card); border: 1px dashed var(--border); border-radius: var(--radius-sm); padding: 10px 14px; margin-bottom: 14px; display: flex; justify-content: space-between; align-items: center;">
<div style="font-size: 11.5px; color: var(--text-muted);">Looking for popular pre-configured servers (Postgres, GitHub, Playwright, etc.)?</div>
<button class="btn btn-primary" style="font-size: 11px; padding: 3px 8px;" onclick="window.app.openTemplateCatalog()">Browse 25 Templates →</button>
</div>
<div class="form-group">
<label class="form-label">Server Identifier</label>
<input type="text" class="form-input" id="modal-srv-name" placeholder="e.g. postgres, weather, linear">
</div>
<div class="form-group">
<label class="form-label">Transport Type</label>
<select class="form-input" id="modal-srv-transport" onchange="document.getElementById('modal-group-cmd').style.display = this.value === 'stdio' ? 'block' : 'none'; document.getElementById('modal-group-url').style.display = this.value === 'http' ? 'block' : 'none';">
<option value="stdio">Stdio (Local Process / CLI)</option>
<option value="http">HTTP / SSE (Remote Endpoint)</option>
</select>
</div>
<div class="form-group" id="modal-group-cmd">
<label class="form-label">Executable Command & Args</label>
<input type="text" class="form-input" id="modal-srv-command" placeholder="npx -y @modelcontextprotocol/server-postgres postgresql://...">
</div>
<div class="form-group" id="modal-group-url" style="display: none;">
<label class="form-label">Remote Endpoint URL</label>
<input type="text" class="form-input" id="modal-srv-url" placeholder="https://api.example.com/sse">
</div>
<details style="margin-top: 14px; background: rgba(0,0,0,0.2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 8px 12px;">
<summary style="font-size: 11.5px; font-weight: 600; color: var(--amber-400); cursor: pointer;">
🛡️ Fault Tolerance & Process Supervision (Optional)
</summary>
<div style="margin-top: 10px; display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
<div>
<label class="form-label" style="font-size: 10.5px;">Failure Threshold</label>
<input type="number" class="form-input" id="modal-srv-ft" placeholder="3" value="3">
</div>
<div>
<label class="form-label" style="font-size: 10.5px;">Cooldown (ms)</label>
<input type="number" class="form-input" id="modal-srv-cd" placeholder="30000" value="30000">
</div>
<div>
<label class="form-label" style="font-size: 10.5px;">Auto-Restart</label>
<select class="form-input" id="modal-srv-autorestart">
<option value="true">Enabled (Default)</option>
<option value="false">Disabled</option>
</select>
</div>
<div>
<label class="form-label" style="font-size: 10.5px;">Max Restarts</label>
<input type="number" class="form-input" id="modal-srv-maxrestarts" placeholder="5" value="5">
</div>
</div>
</details>
<div style="display: flex; justify-content: flex-end; gap: 8px; margin-top: 20px;">
<button class="btn btn-ghost" onclick="window.app.closeModals()">Cancel</button>
<button class="btn btn-primary" onclick="window.app.submitAddServer()">Save & Connect</button>
</div>
</div>
</div>
<div class="modal-backdrop" id="modal-import" onclick="if(event.target === this) window.app.closeModals()">
<div class="modal-box" style="width: 560px;">
<div class="modal-header">
<div class="modal-title">Import Existing MCP Configurations</div>
<button class="btn btn-ghost" onclick="window.app.closeModals()" style="padding: 2px 6px;">✕</button>
</div>
<p style="color: var(--text-dim); font-size: 12px; margin-bottom: 14px;">
Discovered standard MCP configurations from your IDE ecosystem:
</p>
<div style="display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px;" id="modal-eco-list">
</div>
<div style="display: flex; justify-content: flex-end; gap: 8px;">
<button class="btn btn-ghost" onclick="window.app.closeModals()">Cancel</button>
<button class="btn btn-primary" onclick="window.app.submitImport()">Import Discovered Servers</button>
</div>
</div>
</div>
<div class="modal-backdrop" id="modal-templates" onclick="if(event.target === this) window.app.closeModals()">
<div class="modal-box" style="width: 880px; max-width: 95vw; max-height: 88vh; display: flex; flex-direction: column;">
<div class="modal-header" style="margin-bottom: 12px;">
<div>
<div class="modal-title" style="display: flex; align-items: center; gap: 8px;">
<span>✨ MCP Server Template Catalog</span>
<span class="brand-badge">25 Curated Servers</span>
</div>
<div style="font-size: 11.5px; color: var(--text-dim); margin-top: 2px;">
1-Click configurations for the most popular tools, browsers, databases, and services in the MCP ecosystem.
</div>
</div>
<button class="btn btn-ghost" onclick="window.app.closeModals()" style="padding: 2px 6px;">✕</button>
</div>
<div style="display: flex; gap: 10px; align-items: center; margin-bottom: 14px;">
<input type="text" class="form-input" id="tmpl-search-input" placeholder="Search templates (e.g. postgres, github, playwright, redis)..." oninput="window.app.filterTemplates(this.value)" style="flex: 1; min-width: 0;">
<div style="display: flex; gap: 4px; flex-shrink: 0;" id="tmpl-category-tabs">
<button class="btn btn-ghost tmpl-cat-btn active" data-category="all" onclick="window.app.setTemplateCategory('all')">All</button>
<button class="btn btn-ghost tmpl-cat-btn" data-category="devtools" onclick="window.app.setTemplateCategory('devtools')">DevTools</button>
<button class="btn btn-ghost tmpl-cat-btn" data-category="browser" onclick="window.app.setTemplateCategory('browser')">Browser & Search</button>
<button class="btn btn-ghost tmpl-cat-btn" data-category="database" onclick="window.app.setTemplateCategory('database')">Databases</button>
<button class="btn btn-ghost tmpl-cat-btn" data-category="productivity" onclick="window.app.setTemplateCategory('productivity')">Productivity</button>
<button class="btn btn-ghost tmpl-cat-btn" data-category="cloud" onclick="window.app.setTemplateCategory('cloud')">Cloud & Infra</button>
</div>
</div>
<div style="flex: 1; overflow-y: auto; overflow-x: hidden; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; padding-right: 4px;" id="tmpl-grid">
</div>
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 14px; padding-top: 10px; border-top: 1px solid var(--border); font-size: 11px; color: var(--text-dim);">
<div>Need a custom MCP command or SSE URL?</div>
<button class="btn btn-ghost" onclick="window.app.openAddServerModal()">+ Custom Manual Server</button>
</div>
</div>
</div>
<div class="modal-backdrop" id="modal-configure-template" onclick="if(event.target === this) window.app.closeModals()">
<div class="modal-box" style="width: 580px;">
<div class="modal-header">
<div>
<div class="modal-title" id="cfg-tmpl-title">Configure Server Template</div>
<div style="font-size: 11px; color: var(--text-dim);" id="cfg-tmpl-desc">Fill in required credentials and launch.</div>
</div>
<button class="btn btn-ghost" onclick="window.app.closeModals()" style="padding: 2px 6px;">✕</button>
</div>
<div id="cfg-tmpl-form">
</div>
<div style="display: flex; justify-content: flex-end; gap: 8px; margin-top: 20px;">
<button class="btn btn-ghost" onclick="window.app.openTemplateCatalog()">← Back to Catalog</button>
<button class="btn btn-primary" onclick="window.app.submitTemplateServer()">Save & Connect Server</button>
</div>
</div>
</div>
<div class="modal-backdrop" id="modal-add-profile" onclick="if(event.target === this) window.app.closeModals()">
<div class="modal-box" style="width: 520px;">
<div class="modal-header">
<div class="modal-title" id="modal-prof-title">Create Server Constellation Profile</div>
<button class="btn btn-ghost" onclick="window.app.closeModals()" style="padding: 2px 6px;">✕</button>
</div>
<input type="hidden" id="modal-prof-mode" value="create">
<div class="form-group">
<label class="form-label">Profile Identifier (e.g. coding, support, data)</label>
<input type="text" class="form-input" id="modal-prof-name" placeholder="e.g. coding">
</div>
<div class="form-group">
<label class="form-label">Description (Optional)</label>
<input type="text" class="form-input" id="modal-prof-desc" placeholder="e.g. Software engineering and database inspection tools">
</div>
<div class="form-group">
<label class="form-label">Included MCP Upstream Servers</label>
<div id="modal-prof-servers-list" style="max-height: 180px; overflow-y: auto; background: var(--surface-elevated); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 8px; display: flex; flex-direction: column; gap: 4px;">
</div>
</div>
<div style="display: flex; justify-content: flex-end; gap: 8px; margin-top: 20px;">
<button class="btn btn-ghost" onclick="window.app.closeModals()">Cancel</button>
<button class="btn btn-primary" onclick="window.app.saveProfile()">Save Profile</button>
</div>
</div>
</div>
<script type="module" src="./src/main.ts"></script>
</body>
</html>