Upload 7 modified files from chat-switchboard-v0.5.2.zip

This commit is contained in:
2026-02-18 22:14:35 +00:00
parent a6b55a0352
commit 92f4f87b6a
5 changed files with 750 additions and 788 deletions

View File

@@ -7,65 +7,89 @@
<link rel="icon" type="image/svg+xml" href="favicon.svg">
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32.png">
<link rel="apple-touch-icon" sizes="192x192" href="favicon-192.png">
<link rel="stylesheet" href="css/styles.css?v=0.5.1-2">
<link rel="stylesheet" href="css/styles.css?v=0.5.2">
</head>
<body>
<!-- ── App ─────────────────────────────────── -->
<div id="appContainer" style="display:none">
<header class="header">
<h1 class="header-title">🔀 Chat Switchboard</h1>
<div class="header-actions">
<div class="connection-status" id="connectionStatus">
<span class="status-dot"></span> Offline
</div>
<div class="dropdown">
<button class="btn-icon" id="exportBtn" title="Export">📥</button>
<div class="dropdown-menu" id="exportDropdown">
<button onclick="UI.exportChat('md')">Markdown</button>
<button onclick="UI.exportChat('json')">JSON</button>
<button onclick="UI.exportChat('txt')">Plain Text</button>
</div>
</div>
<button class="btn-icon" id="adminBtn" title="Admin" style="display:none">🛡️</button>
<button class="btn-icon" id="settingsBtn" title="Settings">⚙️</button>
<!-- ── App Shell ───────────────────────────── -->
<div id="appContainer" class="app" style="display:none">
<!-- Sidebar -->
<aside class="sidebar" id="sidebar">
<div class="sidebar-top">
<button class="sb-btn" id="sidebarToggle" title="Toggle sidebar">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="9" y1="3" x2="9" y2="21"/></svg>
</button>
<button class="sb-btn" id="newChatBtn" title="New Chat">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>
<span class="sb-label">New Chat</span>
</button>
</div>
</header>
<div class="main">
<aside class="sidebar" id="sidebar">
<button class="btn-primary new-chat-btn" id="newChatBtn">+ New Chat</button>
<div class="chat-history" id="chatHistory"></div>
</aside>
<div class="sidebar-chats" id="chatHistory"></div>
<main class="chat-area">
<div class="chat-messages" id="chatMessages">
<div class="empty-state">
<div class="empty-icon">💬</div>
<h2>Start a Conversation</h2>
<p>Type a message below to begin</p>
<!-- User area -->
<div class="sidebar-bottom">
<button class="user-btn" id="userMenuBtn">
<div class="user-avatar" id="userAvatar"><span id="avatarLetter">?</span><span class="avatar-dot"></span></div>
<span class="sb-label user-name" id="userName">User</span>
</button>
<div class="user-flyout" id="userFlyout">
<button class="flyout-item" id="menuSettings">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>
<span>Settings</span>
</button>
<button class="flyout-item" id="menuAdmin" style="display:none">
<svg width="16" height="16" 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"/></svg>
<span>Admin Panel</span>
</button>
<button class="flyout-item" id="menuDebug">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 11V6a2 2 0 0 0-2-2h-1a2 2 0 0 0-2 2"/><path d="M9 6a2 2 0 0 0-2 2v3"/><path d="M4 14h4"/><path d="M16 14h4"/><path d="M12 18v-6"/><circle cx="12" cy="14" r="6"/></svg>
<span>Debug Log</span>
</button>
<div class="flyout-divider"></div>
<button class="flyout-item flyout-danger" id="menuSignout">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
<span>Sign Out</span>
</button>
</div>
</div>
</aside>
<!-- Main Chat Area -->
<main class="chat-area">
<div class="model-bar">
<select id="modelSelect" title="Select model"><option value="">Select a model</option></select>
<button class="icon-btn" id="fetchModelsBtn" title="Refresh models">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg>
</button>
</div>
<div class="messages" id="chatMessages">
<div class="empty-state">
<div class="empty-logo">🔀</div>
<h2>Chat Switchboard</h2>
<p>Select a model and start chatting</p>
</div>
</div>
<div class="input-area">
<div class="input-wrap">
<textarea id="messageInput" placeholder="Send a message..." rows="1"></textarea>
<div class="input-actions">
<button class="action-btn" id="regenerateBtn" style="display:none" title="Regenerate">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg>
</button>
<button class="action-btn stop-btn" id="stopBtn" title="Stop">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="6" width="12" height="12" rx="2"/></svg>
</button>
<button class="action-btn send-btn" id="sendBtn" title="Send">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
</button>
</div>
</div>
<div class="input-area">
<div class="input-bar">
<div class="model-selector">
<label>Model:</label>
<select id="modelSelect"><option value=""></option></select>
<button class="btn-icon btn-small" id="fetchModelsBtn" title="Refresh models">🔄</button>
</div>
</div>
<div class="input-row">
<textarea id="messageInput" placeholder="Type your message..." rows="1"></textarea>
<div class="input-actions">
<button class="btn-regen" id="regenerateBtn" style="display:none" title="Regenerate">🔄</button>
<button class="btn-stop" id="stopBtn">⏹ Stop</button>
<button class="btn-send" id="sendBtn">Send ▸</button>
</div>
</div>
</div>
</main>
</div>
</div>
</main>
</div>
<!-- ── Auth Splash ─────────────────────────── -->
@@ -81,30 +105,13 @@
<button class="auth-tab" id="authTabRegister" onclick="switchAuthTab('register')">Register</button>
</div>
<div id="authLoginForm">
<div class="form-group">
<label>Username or Email</label>
<input type="text" id="authLogin" autocomplete="username">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" id="authPassword" autocomplete="current-password"
onkeydown="if(event.key==='Enter')handleLogin()">
</div>
<div class="form-group"><label>Username or Email</label><input type="text" id="authLogin" autocomplete="username"></div>
<div class="form-group"><label>Password</label><input type="password" id="authPassword" autocomplete="current-password" onkeydown="if(event.key==='Enter')handleLogin()"></div>
</div>
<div id="authRegisterForm" style="display:none">
<div class="form-group">
<label>Username</label>
<input type="text" id="authUsername" autocomplete="username">
</div>
<div class="form-group">
<label>Email</label>
<input type="email" id="authEmail" autocomplete="email">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" id="authRegPassword" autocomplete="new-password"
onkeydown="if(event.key==='Enter')handleRegister()">
</div>
<div class="form-group"><label>Username</label><input type="text" id="authUsername" autocomplete="username"></div>
<div class="form-group"><label>Email</label><input type="email" id="authEmail" autocomplete="email"></div>
<div class="form-group"><label>Password</label><input type="password" id="authRegPassword" autocomplete="new-password" onkeydown="if(event.key==='Enter')handleRegister()"></div>
</div>
<div class="auth-error" id="authError"></div>
<div class="splash-error" id="splashError"></div>
@@ -118,111 +125,51 @@
<!-- ── Settings Modal ──────────────────────── -->
<div class="modal-overlay" id="settingsModal">
<div class="modal">
<div class="modal-header">
<h2>⚙️ Settings</h2>
<button class="modal-close" id="settingsCloseBtn"></button>
</div>
<div class="modal-header"><h2>Settings</h2><button class="modal-close" id="settingsCloseBtn"></button></div>
<div class="modal-body">
<!-- Profile -->
<section class="settings-section">
<h3>Profile</h3>
<div class="form-group">
<label>Display Name</label>
<input type="text" id="profileDisplayName">
</div>
<div class="form-group">
<label>Email</label>
<input type="email" id="profileEmail">
</div>
<button class="btn-small" id="profileChangePwBtn">🔑 Change Password</button>
<div class="form-group"><label>Display Name</label><input type="text" id="profileDisplayName"></div>
<div class="form-group"><label>Email</label><input type="email" id="profileEmail"></div>
<button class="btn-small" id="profileChangePwBtn">Change Password</button>
<div id="profileChangePwForm" style="display:none">
<div class="form-group">
<label>Current Password</label>
<input type="password" id="profileCurrentPw">
</div>
<div class="form-group">
<label>New Password</label>
<input type="password" id="profileNewPw">
</div>
<div class="form-group"><label>Current Password</label><input type="password" id="profileCurrentPw"></div>
<div class="form-group"><label>New Password</label><input type="password" id="profileNewPw"></div>
<button class="btn-small btn-primary" id="profileSavePwBtn">Save Password</button>
</div>
</section>
<!-- Model & Chat -->
<section class="settings-section">
<h3>Chat Settings</h3>
<div class="form-group">
<label>Model</label>
<select id="settingsModel"></select>
</div>
<div class="form-group">
<label>System Prompt</label>
<textarea id="settingsSystemPrompt" rows="3"></textarea>
</div>
<h3>Chat</h3>
<div class="form-group"><label>Default Model</label><select id="settingsModel"></select></div>
<div class="form-group"><label>System Prompt</label><textarea id="settingsSystemPrompt" rows="3"></textarea></div>
<div class="form-row">
<div class="form-group">
<label>Max Tokens</label>
<input type="number" id="settingsMaxTokens" value="4096">
</div>
<div class="form-group">
<label>Temperature</label>
<input type="number" id="settingsTemp" value="0.7" step="0.1" min="0" max="2">
</div>
<div class="form-group"><label>Max Tokens</label><input type="number" id="settingsMaxTokens" value="4096"></div>
<div class="form-group"><label>Temperature</label><input type="number" id="settingsTemp" value="0.7" step="0.1" min="0" max="2"></div>
</div>
<label class="checkbox-label">
<input type="checkbox" id="settingsThinking" checked> Show thinking blocks
</label>
<label class="checkbox-label"><input type="checkbox" id="settingsThinking" checked> Show thinking blocks</label>
</section>
<!-- Providers -->
<section class="settings-section">
<h3>API Providers</h3>
<div id="providerList"></div>
<button class="btn-small" id="providerShowAddBtn">+ Add Provider</button>
<div id="providerAddForm" style="display:none">
<div class="form-group">
<label>Name</label>
<input type="text" id="providerName" placeholder="e.g. My OpenAI Key">
</div>
<div class="form-group">
<label>Provider</label>
<select id="providerType">
<option value="openai">OpenAI-compatible</option>
<option value="anthropic">Anthropic</option>
</select>
</div>
<div class="form-group">
<label>Endpoint</label>
<input type="text" id="providerEndpoint" placeholder="https://api.openai.com/v1">
</div>
<div class="form-group">
<label>API Key</label>
<input type="password" id="providerApiKey" placeholder="sk-...">
</div>
<div class="form-group">
<label>Default Model</label>
<input type="text" id="providerDefaultModel" placeholder="gpt-4o">
</div>
<div class="form-row">
<button class="btn-small btn-primary" id="providerCreateBtn">Save</button>
<button class="btn-small" id="providerCancelBtn">Cancel</button>
</div>
<div class="form-group"><label>Name</label><input type="text" id="providerName" placeholder="My OpenAI Key"></div>
<div class="form-group"><label>Provider</label><select id="providerType"><option value="openai">OpenAI-compatible</option><option value="anthropic">Anthropic</option></select></div>
<div class="form-group"><label>Endpoint</label><input type="text" id="providerEndpoint" placeholder="https://api.openai.com/v1"></div>
<div class="form-group"><label>API Key</label><input type="password" id="providerApiKey" placeholder="sk-..."></div>
<div class="form-group"><label>Default Model</label><input type="text" id="providerDefaultModel" placeholder="gpt-4o"></div>
<div class="form-row"><button class="btn-small btn-primary" id="providerCreateBtn">Save</button><button class="btn-small" id="providerCancelBtn">Cancel</button></div>
</div>
</section>
</div>
<div class="modal-footer">
<button class="btn-primary" id="settingsSaveBtn">💾 Save Settings</button>
</div>
<div class="modal-footer"><button class="btn-primary" id="settingsSaveBtn">Save Settings</button></div>
</div>
</div>
<!-- ── Admin Modal ─────────────────────────── -->
<div class="modal-overlay" id="adminModal">
<div class="modal modal-wide">
<div class="modal-header">
<h2>🛡️ Admin</h2>
<button class="modal-close" id="adminCloseBtn"></button>
</div>
<div class="modal-header"><h2>Admin Panel</h2><button class="modal-close" id="adminCloseBtn"></button></div>
<div class="admin-tabs">
<button class="admin-tab active" data-tab="users">Users</button>
<button class="admin-tab" data-tab="providers">Providers</button>
@@ -231,42 +178,25 @@
<button class="admin-tab" data-tab="stats">Stats</button>
</div>
<div class="modal-body">
<div class="admin-tab-content" id="adminUsersTab">
<div id="adminUserList"></div>
</div>
<div class="admin-tab-content" id="adminProvidersTab" style="display:none">
<div id="adminProviderList"></div>
</div>
<div class="admin-tab-content" id="adminModelsTab" style="display:none">
<div id="adminModelList"></div>
</div>
<div class="admin-tab-content" id="adminUsersTab"><div id="adminUserList"></div></div>
<div class="admin-tab-content" id="adminProvidersTab" style="display:none"><div id="adminProviderList"></div></div>
<div class="admin-tab-content" id="adminModelsTab" style="display:none"><div id="adminModelList"></div></div>
<div class="admin-tab-content" id="adminSettingsTab" style="display:none">
<label class="checkbox-label">
<input type="checkbox" id="adminRegToggle" checked> Allow registration
</label>
<label class="checkbox-label"><input type="checkbox" id="adminRegToggle" checked> Allow registration</label>
<button class="btn-primary btn-small" id="adminSaveSettings">Save</button>
</div>
<div class="admin-tab-content" id="adminStatsTab" style="display:none">
<div id="adminStats"></div>
</div>
<div class="admin-tab-content" id="adminStatsTab" style="display:none"><div id="adminStats"></div></div>
</div>
</div>
</div>
<!-- ── Debug Modal (Ctrl+Shift+L) ──────────── -->
<!-- Debug Modal (Ctrl+Shift+L) -->
<div class="modal-overlay" id="debugModal">
<div class="modal debug-modal">
<div class="modal-header">
<h2>🐛 Debug Log</h2>
<button class="modal-close" onclick="closeDebugModal()"></button>
</div>
<div class="modal-header"><h2>Debug Log</h2><button class="modal-close" onclick="closeDebugModal()"></button></div>
<div class="debug-tabs">
<button class="debug-tab active" data-tab="console" onclick="switchDebugTab('console')">
Console (<span id="debugConsoleCount">0</span>)
</button>
<button class="debug-tab" data-tab="network" onclick="switchDebugTab('network')">
Network (<span id="debugNetworkCount">0</span>)
</button>
<button class="debug-tab active" data-tab="console" onclick="switchDebugTab('console')">Console (<span id="debugConsoleCount">0</span>)</button>
<button class="debug-tab" data-tab="network" onclick="switchDebugTab('network')">Network (<span id="debugNetworkCount">0</span>)</button>
<button class="debug-tab" data-tab="state" onclick="switchDebugTab('state')">State</button>
</div>
<div class="modal-body debug-modal-body">
@@ -277,19 +207,15 @@
</div>
<div id="debugConsoleContent" class="debug-content"></div>
</div>
<div class="debug-tab-content" id="debugNetworkTab" style="display:none">
<div id="debugNetworkContent" class="debug-content"></div>
</div>
<div class="debug-tab-content" id="debugStateTab" style="display:none">
<div id="debugStateContent" class="debug-content"></div>
</div>
<div class="debug-tab-content" id="debugNetworkTab" style="display:none"><div id="debugNetworkContent" class="debug-content"></div></div>
<div class="debug-tab-content" id="debugStateTab" style="display:none"><div id="debugStateContent" class="debug-content"></div></div>
</div>
<div class="modal-footer debug-footer">
<button class="btn-primary btn-small" onclick="runDebugDiagnostics()">🔍 Diagnostics</button>
<button class="btn-primary btn-small" onclick="runDebugDiagnostics()">Diagnostics</button>
<div style="margin-left:auto;display:flex;gap:0.5rem">
<button class="btn-small" onclick="clearDebugLog()">🗑️ Clear</button>
<button class="btn-small" onclick="copyDebugLog()">📋 Copy</button>
<button class="btn-small" onclick="exportDebugLog()">📥 Export</button>
<button class="btn-small" onclick="clearDebugLog()">Clear</button>
<button class="btn-small" onclick="copyDebugLog()">Copy</button>
<button class="btn-small" onclick="exportDebugLog()">Export</button>
</div>
</div>
</div>
@@ -297,17 +223,13 @@
<div class="toast-container" id="toastContainer"></div>
<!-- Markdown: local vendor first, CDN fallback for connected envs -->
<script src="vendor/marked.min.js"
onerror="this.onerror=null; this.src='https://cdnjs.cloudflare.com/ajax/libs/marked/16.3.0/lib/marked.umd.min.js'">
</script>
<script src="vendor/purify.min.js"
onerror="this.onerror=null; this.src='https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.4/purify.min.js'">
</script>
<!-- Vendor libs -->
<script src="vendor/marked.min.js" onerror="this.onerror=null;this.src='https://cdnjs.cloudflare.com/ajax/libs/marked/16.3.0/lib/marked.umd.min.js'"></script>
<script src="vendor/purify.min.js" onerror="this.onerror=null;this.src='https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.4/purify.min.js'"></script>
<script src="js/debug.js?v=0.5.1-2"></script>
<script src="js/api.js?v=0.5.1-2"></script>
<script src="js/ui.js?v=0.5.1-2"></script>
<script src="js/app.js?v=0.5.1-2"></script>
<script src="js/debug.js?v=0.5.2"></script>
<script src="js/api.js?v=0.5.2"></script>
<script src="js/ui.js?v=0.5.2"></script>
<script src="js/app.js?v=0.5.2"></script>
</body>
</html>