207 lines
10 KiB
HTML
207 lines
10 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Chat Switchboard</title>
|
|
<link rel="stylesheet" href="css/styles.css">
|
|
</head>
|
|
<body>
|
|
<header class="header">
|
|
<h1>🔀 Chat Switchboard</h1>
|
|
<div class="header-actions">
|
|
<button class="btn btn-secondary" id="toggleSidebarBtn" title="Toggle Sidebar (Ctrl+B)">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
|
<line x1="9" y1="3" x2="9" y2="21"></line>
|
|
</svg>
|
|
</button>
|
|
<div class="dropdown">
|
|
<button class="btn btn-secondary" id="exportBtn" title="Export Chat">
|
|
📥 Export
|
|
</button>
|
|
<div class="dropdown-content" id="exportDropdown">
|
|
<button class="dropdown-item" onclick="exportChat('markdown')">📄 Markdown</button>
|
|
<button class="dropdown-item" onclick="exportChat('json')">📋 JSON</button>
|
|
<button class="dropdown-item" onclick="exportChat('text')">📝 Plain Text</button>
|
|
</div>
|
|
</div>
|
|
<button class="btn btn-secondary" id="settingsBtn" title="Settings (Ctrl+,)">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<circle cx="12" cy="12" r="3"></circle>
|
|
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
|
|
</svg>
|
|
Settings
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="main-container">
|
|
<aside class="sidebar" id="sidebar">
|
|
<div class="sidebar-header">
|
|
<span class="sidebar-title">Chat History</span>
|
|
</div>
|
|
<button class="btn btn-primary new-chat-btn" id="newChatBtn">
|
|
<svg width="16" height="16" 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>
|
|
New Chat
|
|
</button>
|
|
<div class="chat-history" id="chatHistory"></div>
|
|
</aside>
|
|
|
|
<main class="chat-area">
|
|
<div class="chat-messages" id="chatMessages">
|
|
<div class="empty-state" id="emptyState">
|
|
<div class="empty-state-icon">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
|
</svg>
|
|
</div>
|
|
<h2>Start a Conversation</h2>
|
|
<p>Configure your API settings and start chatting</p>
|
|
<button class="btn btn-primary" style="margin-top: 1.5rem;" id="configureBtn">⚙️ Configure API</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="input-area">
|
|
<div class="input-container">
|
|
<div class="input-top-bar">
|
|
<div class="model-selector">
|
|
<label for="quickModel">Model:</label>
|
|
<select id="quickModel">
|
|
<option value="">-- Configure in Settings --</option>
|
|
</select>
|
|
<button class="btn btn-small btn-secondary" id="quickFetchBtn" title="Refresh models">🔄</button>
|
|
</div>
|
|
<div class="input-shortcuts">
|
|
<kbd>Enter</kbd> send · <kbd>Shift+Enter</kbd> newline · <kbd>Esc</kbd> stop
|
|
</div>
|
|
</div>
|
|
<div class="input-wrapper">
|
|
<textarea id="messageInput" placeholder="Type your message..." rows="1"></textarea>
|
|
<div class="input-actions">
|
|
<div class="input-left">
|
|
<button class="btn btn-small btn-secondary" id="regenerateBtn" title="Regenerate last response" style="display: none;">🔄 Regenerate</button>
|
|
</div>
|
|
<div style="display: flex; gap: 0.5rem;">
|
|
<button class="stop-btn" id="stopBtn">⏹ Stop</button>
|
|
<button class="send-btn" id="sendBtn">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<line x1="22" y1="2" x2="11" y2="13"></line>
|
|
<polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
|
|
</svg>
|
|
Send
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<!-- Settings Modal -->
|
|
<div class="modal-overlay" id="settingsModal">
|
|
<div class="modal">
|
|
<div class="modal-header">
|
|
<h2>⚙️ Settings</h2>
|
|
<button class="modal-close" id="closeModalBtn">✕</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label for="apiEndpoint">API Endpoint</label>
|
|
<input type="text" id="apiEndpoint" placeholder="https://api.openai.com/v1">
|
|
<small>Base URL for the OpenAI-compatible API</small>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="apiKey">API Key</label>
|
|
<input type="password" id="apiKey" placeholder="sk-...">
|
|
<small>Your API key for authentication</small>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="model">Default Model</label>
|
|
<div style="display: flex; gap: 0.5rem;">
|
|
<select id="model" style="flex: 1;">
|
|
<option value="">-- Select or type below --</option>
|
|
</select>
|
|
<button type="button" class="btn btn-secondary" id="fetchModelsBtn" title="Fetch available models">
|
|
🔄 Fetch
|
|
</button>
|
|
</div>
|
|
<input type="text" id="modelCustom" placeholder="Or type custom model name..." style="margin-top: 0.5rem;">
|
|
<small>Select from list or enter custom model name</small>
|
|
</div>
|
|
|
|
<div class="toggle-group">
|
|
<span class="toggle-label">Stream Responses</span>
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="streamResponse" checked>
|
|
<span class="toggle-slider"></span>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="toggle-group">
|
|
<span class="toggle-label">Save Chat History</span>
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="saveHistory" checked>
|
|
<span class="toggle-slider"></span>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="toggle-group">
|
|
<span class="toggle-label">Show Thinking Blocks</span>
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="showThinking" checked>
|
|
<span class="toggle-slider"></span>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="systemPrompt">System Prompt (Optional)</label>
|
|
<textarea id="systemPrompt" rows="3" placeholder="You are a helpful AI assistant."></textarea>
|
|
<small>Instructions that define the AI's behavior</small>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label for="maxTokens">Max Tokens</label>
|
|
<input type="number" id="maxTokens" value="4096" min="1" max="32768">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="temperature">Temperature</label>
|
|
<input type="number" id="temperature" value="0.7" min="0" max="2" step="0.1">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label for="topP">Top P</label>
|
|
<input type="number" id="topP" value="1" min="0" max="1" step="0.1">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="presencePenalty">Presence Penalty</label>
|
|
<input type="number" id="presencePenalty" value="0" min="-2" max="2" step="0.1">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-secondary" id="cancelBtn">Cancel</button>
|
|
<button class="btn btn-primary" id="saveBtn">💾 Save Settings</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="toast-container" id="toastContainer"></div>
|
|
|
|
<script src="js/storage.js"></script>
|
|
<script src="js/state.js"></script>
|
|
<script src="js/api.js"></script>
|
|
<script src="js/ui.js"></script>
|
|
<script src="js/app.js"></script>
|
|
</body>
|
|
</html>
|