demo
This commit is contained in:
406
web/index.html
Normal file
406
web/index.html
Normal file
@@ -0,0 +1,406 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>GoCoder</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<style>
|
||||
.conversation-item {
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
.conversation-item:hover {
|
||||
border-left-color: #cbd5e1;
|
||||
}
|
||||
.conversation-item.active {
|
||||
border-left-color: #3b82f6;
|
||||
background-color: #eff6ff;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
.conversation-item.active .text-gray-400 {
|
||||
color: #3b82f6;
|
||||
}
|
||||
.loading-dots {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
.loading-dots .dot {
|
||||
animation: blink 1.4s infinite both;
|
||||
animation-duration: 1.4s;
|
||||
}
|
||||
.loading-dots .dot:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
.loading-dots .dot:nth-child(3) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
@keyframes blink {
|
||||
0%, 80%, 100% { opacity: 0; }
|
||||
40% { opacity: 1; }
|
||||
}
|
||||
.sidebar {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
z-index: 50;
|
||||
height: 100vh;
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
.sidebar.open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
.sidebar-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0,0,0,0.3);
|
||||
z-index: 40;
|
||||
}
|
||||
.sidebar-overlay.open {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="min-h-screen bg-gray-50">
|
||||
<div class="flex h-screen">
|
||||
<!-- Sidebar -->
|
||||
<div class="sidebar w-72 bg-white border-r border-gray-200 flex flex-col" id="sidebar">
|
||||
<div class="p-4 border-b border-gray-200">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<h2 class="text-lg font-semibold text-gray-800">Workspaces</h2>
|
||||
<div class="flex gap-1">
|
||||
<button id="newGroupBtn" class="p-1.5 rounded-lg hover:bg-gray-100 text-gray-600 transition-colors" title="New workspace">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>
|
||||
</button>
|
||||
<button id="newConversationBtn" class="p-1.5 rounded-lg hover:bg-gray-100 text-gray-600 transition-colors" title="New conversation">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="conversationList" class="flex-1 overflow-y-auto p-2 space-y-3">
|
||||
</div>
|
||||
<div class="p-3 border-t border-gray-200">
|
||||
<button id="mobileMenuBtn" class="md:hidden w-full text-center text-sm text-gray-500 hover:text-gray-700 py-2">
|
||||
Close sidebar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar overlay for mobile -->
|
||||
<div class="sidebar-overlay" id="sidebarOverlay"></div>
|
||||
|
||||
<!-- Group settings modal -->
|
||||
<div id="groupSettingsModal" class="fixed inset-0 z-50 hidden items-center justify-center" style="background: rgba(0,0,0,0.4);">
|
||||
<div class="bg-white rounded-2xl shadow-xl w-full max-w-md mx-4 overflow-hidden">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Workspace Settings</h3>
|
||||
<button id="closeGroupSettingsModal" class="p-1.5 rounded-lg hover:bg-gray-100 text-gray-400 hover:text-gray-600 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<form id="groupSettingsForm">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5">Workspace name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="groupSettingsNameInput"
|
||||
class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800"
|
||||
required
|
||||
>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5 mt-4">Workspace folder</label>
|
||||
<div id="groupSettingsLocation" class="hidden mb-3 px-3 py-2 bg-gray-50 border border-gray-200 rounded-xl">
|
||||
<div class="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-gray-400 flex-shrink-0"><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"/><line x1="7" y1="7" x2="7.01" y2="7"/></svg>
|
||||
<span id="groupSettingsLocationText" class="text-sm text-gray-600 truncate"></span>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" id="groupSettingsPickBtn" class="w-full px-4 py-2.5 border border-gray-300 rounded-xl text-gray-700 font-medium hover:bg-gray-50 transition-colors flex items-center justify-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg>
|
||||
Pick Folder
|
||||
</button>
|
||||
<div class="flex gap-3 mt-5">
|
||||
<button type="button" id="cancelGroupSettingsBtn" class="flex-1 px-4 py-2.5 border border-gray-300 rounded-xl text-gray-700 font-medium hover:bg-gray-50 transition-colors">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="flex-1 px-4 py-2.5 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-xl transition-colors">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Runtime settings modal -->
|
||||
<div id="settingsModal" class="fixed inset-0 z-50 hidden items-center justify-center p-4" style="background: rgba(0,0,0,0.4);">
|
||||
<div class="bg-white rounded-2xl shadow-xl w-full max-w-2xl max-h-[90vh] overflow-hidden">
|
||||
<div class="max-h-[90vh] overflow-y-auto">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-800">Runtime Settings</h3>
|
||||
<p class="mt-1 text-sm text-gray-500">Edit the model endpoint, generation params, server settings, and compaction budget used by the chat runtime.</p>
|
||||
</div>
|
||||
<button id="closeSettingsModal" class="p-1.5 rounded-lg hover:bg-gray-100 text-gray-400 hover:text-gray-600 transition-colors flex-shrink-0">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<form id="settingsForm">
|
||||
<div class="space-y-4">
|
||||
<div class="rounded-2xl border border-gray-200 bg-gray-50/70 p-4">
|
||||
<div class="mb-3 flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.2em] text-gray-500">
|
||||
<span>Connection</span>
|
||||
</div>
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5" for="settingsProvider">Provider</label>
|
||||
<input type="text" id="settingsProvider" required class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5" for="settingsModel">Model</label>
|
||||
<input type="text" id="settingsModel" required class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800">
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5" for="settingsBaseURL">Base URL</label>
|
||||
<input type="url" id="settingsBaseURL" required class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800">
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5" for="settingsAPIKey">API key</label>
|
||||
<input type="password" id="settingsAPIKey" autocomplete="off" class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800">
|
||||
<p class="mt-1 text-xs text-gray-500">Leave as-is to keep the current key or replace it with a new one.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-gray-200 bg-gray-50/70 p-4">
|
||||
<div class="mb-3 flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.2em] text-gray-500">
|
||||
<span>Server</span>
|
||||
</div>
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5" for="settingsServerHost">Host</label>
|
||||
<input type="text" id="settingsServerHost" required placeholder="localhost" class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5" for="settingsServerAddr">Address</label>
|
||||
<input type="text" id="settingsServerAddr" required placeholder=":8080" class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800">
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<p class="text-xs text-gray-500">Server settings are saved immediately but only take effect after you restart the app.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-gray-200 bg-gray-50/70 p-4">
|
||||
<div class="mb-3 flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.2em] text-gray-500">
|
||||
<span>Generation</span>
|
||||
</div>
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5" for="settingsTemperature">Temperature</label>
|
||||
<input type="number" id="settingsTemperature" min="0" step="0.01" required class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5" for="settingsMaxTokens">Max tokens</label>
|
||||
<input type="number" id="settingsMaxTokens" min="1" step="1" required class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5" for="settingsTopP">Top P</label>
|
||||
<input type="number" id="settingsTopP" min="0.01" max="1" step="0.01" required class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5" for="settingsFrequencyPenalty">Frequency penalty</label>
|
||||
<input type="number" id="settingsFrequencyPenalty" step="0.1" class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800">
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5" for="settingsPresencePenalty">Presence penalty</label>
|
||||
<input type="number" id="settingsPresencePenalty" step="0.1" class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800">
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5" for="settingsReasoningEffort">Reasoning effort</label>
|
||||
<select id="settingsReasoningEffort" class="w-full px-4 py-2.5 border border-gray-300 rounded-xl bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800">
|
||||
<option value="">Default</option>
|
||||
<option value="low">Low</option>
|
||||
<option value="medium">Medium</option>
|
||||
<option value="high">High</option>
|
||||
</select>
|
||||
<p class="mt-1 text-xs text-gray-500">Forwarded to LM Studio when the backend supports reasoning controls.</p>
|
||||
</div>
|
||||
<div class="md:col-span-2 flex items-start gap-3 rounded-xl border border-gray-200 bg-white px-4 py-3">
|
||||
<input type="checkbox" id="settingsAutoOpenReasoning" class="mt-1 h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500">
|
||||
<div class="min-w-0">
|
||||
<label class="block text-sm font-medium text-gray-700" for="settingsAutoOpenReasoning">Auto-open reasoning</label>
|
||||
<p class="mt-1 text-xs text-gray-500">When enabled, live reasoning starts expanded. You can still collapse it while the model is thinking.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-gray-200 bg-gray-50/70 p-4">
|
||||
<div class="mb-3 flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.2em] text-gray-500">
|
||||
<span>Context compaction</span>
|
||||
</div>
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div class="md:col-span-2">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5" for="settingsContextTokens">Prompt budget (tokens)</label>
|
||||
<input type="number" id="settingsContextTokens" min="1" step="1" required class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800">
|
||||
<p class="mt-1 text-xs text-gray-500">This is the estimated prompt size budget used before compaction starts.</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5" for="settingsTriggerRatio">Compaction trigger</label>
|
||||
<input type="number" id="settingsTriggerRatio" min="0.01" max="0.99" step="0.01" required class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800">
|
||||
<p class="mt-1 text-xs text-gray-500">Compaction starts above this fraction of the budget.</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5" for="settingsTargetRatio">Compaction target</label>
|
||||
<input type="number" id="settingsTargetRatio" min="0.01" max="0.99" step="0.01" required class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800">
|
||||
<p class="mt-1 text-xs text-gray-500">The summarizer tries to shrink history below this fraction.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p id="settingsFormError" class="mt-4 hidden text-sm text-red-600"></p>
|
||||
<div class="flex gap-3 mt-5">
|
||||
<button type="button" id="cancelSettingsBtn" class="flex-1 px-4 py-2.5 border border-gray-300 rounded-xl text-gray-700 font-medium hover:bg-gray-50 transition-colors">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="flex-1 px-4 py-2.5 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-xl transition-colors">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Group creation modal -->
|
||||
<div id="groupModal" class="fixed inset-0 z-50 hidden items-center justify-center" style="background: rgba(0,0,0,0.4);">
|
||||
<div class="bg-white rounded-2xl shadow-xl w-full max-w-md mx-4 overflow-hidden">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<h3 class="text-lg font-semibold text-gray-800">New Workspace</h3>
|
||||
<button id="closeGroupModal" class="p-1.5 rounded-lg hover:bg-gray-100 text-gray-400 hover:text-gray-600 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<form id="groupForm">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1.5">Workspace name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="groupNameInput"
|
||||
placeholder="e.g. My Project"
|
||||
class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800 placeholder:text-gray-400"
|
||||
required
|
||||
autofocus
|
||||
>
|
||||
<div class="flex gap-3 mt-5">
|
||||
<button type="button" id="cancelGroupBtn" class="flex-1 px-4 py-2.5 border border-gray-300 rounded-xl text-gray-700 font-medium hover:bg-gray-50 transition-colors">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="flex-1 px-4 py-2.5 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-xl transition-colors">
|
||||
Create
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main content -->
|
||||
<div class="flex-1 flex flex-col overflow-hidden">
|
||||
<!-- Header -->
|
||||
<div class="bg-white border-b border-gray-200 px-6 py-4 flex items-center gap-4">
|
||||
<button id="mobileMenuBtnDesktop" class="md:hidden p-2 rounded-lg hover:bg-gray-100 text-gray-600 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
|
||||
</button>
|
||||
<div class="flex items-center gap-2 flex-1 min-w-0">
|
||||
<h1 class="text-xl font-bold text-gray-800">GoCoder</h1>
|
||||
<span class="text-gray-300 text-lg leading-none mx-1 flex-shrink-0">›</span>
|
||||
<div id="headerLocation" class="hidden items-center gap-1.5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-gray-400"><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"/><line x1="7" y1="7" x2="7.01" y2="7"/></svg>
|
||||
<span id="headerLocationText" class="text-sm text-gray-500 truncate max-w-xs"></span>
|
||||
<button id="changeLocationBtn" class="hidden p-1 rounded hover:bg-gray-100 text-gray-400 hover:text-gray-600 transition-colors" title="Change location">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<button id="pickLocationBtn" class="hidden items-center gap-1 px-2.5 py-1 text-xs bg-gray-100 hover:bg-gray-200 text-gray-600 rounded-lg transition-colors border border-gray-200">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg>
|
||||
Pick Location
|
||||
</button>
|
||||
</div>
|
||||
<button id="openSettingsBtn" class="p-2 rounded-lg hover:bg-gray-100 text-gray-500 hover:text-gray-700 transition-colors" title="Runtime settings">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Chat area -->
|
||||
<div id="chatScrollArea" class="flex-1 overflow-y-auto px-4 md:px-8 lg:px-16 py-6">
|
||||
<div id="result" class="hidden">
|
||||
<div id="resultText" class="space-y-4"></div>
|
||||
</div>
|
||||
<div id="emptyState" class="flex flex-col items-center justify-center h-full text-center">
|
||||
<div class="w-20 h-20 bg-gray-100 rounded-2xl flex items-center justify-center mb-6">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
||||
</div>
|
||||
<h2 class="text-2xl font-semibold text-gray-700 mb-2">Start a conversation</h2>
|
||||
<p class="text-gray-500 mb-6">Type a message to begin, or select an existing conversation</p>
|
||||
<button id="newConversationBtnDesktop" class="bg-blue-600 hover:bg-blue-700 text-white font-medium px-6 py-3 rounded-lg transition-colors">
|
||||
New Conversation
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Input area -->
|
||||
<div class="bg-white border-t border-gray-200 px-4 md:px-8 lg:px-16 py-4">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span class="text-xs text-gray-500 font-medium">Agent:</span>
|
||||
<div id="agentSelector" class="flex gap-1.5 flex-wrap">
|
||||
<button type="button" data-agent="none" class="agent-btn px-3 py-1 text-xs font-medium rounded-lg border border-gray-300 text-gray-600 hover:bg-gray-100 transition-colors bg-blue-50 border-blue-300 text-blue-700">None</button>
|
||||
<button type="button" data-agent="dadjokes" class="agent-btn px-3 py-1 text-xs font-medium rounded-lg border border-gray-300 text-gray-600 hover:bg-gray-100 transition-colors">Dadjokes</button>
|
||||
<button type="button" data-agent="poet" class="agent-btn px-3 py-1 text-xs font-medium rounded-lg border border-gray-300 text-gray-600 hover:bg-gray-100 transition-colors">Poet</button>
|
||||
<button type="button" data-agent="robot" class="agent-btn px-3 py-1 text-xs font-medium rounded-lg border border-gray-300 text-gray-600 hover:bg-gray-100 transition-colors">Robot</button>
|
||||
</div>
|
||||
</div>
|
||||
<form id="messageForm" class="flex items-end gap-3">
|
||||
<textarea
|
||||
id="messageInput"
|
||||
name="message"
|
||||
rows="1"
|
||||
placeholder="Type a message... Enter sends, Shift+Enter adds a new line"
|
||||
aria-describedby="messageInputHint"
|
||||
class="message-input flex-1 px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-800"
|
||||
required
|
||||
></textarea>
|
||||
<button
|
||||
type="submit"
|
||||
id="sendBtn"
|
||||
class="bg-blue-600 hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed text-white font-medium px-6 py-3 rounded-xl transition-colors"
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
id="cancelBtn"
|
||||
class="hidden border border-red-200 bg-white px-5 py-3 font-medium text-red-600 rounded-xl transition-colors hover:bg-red-50 disabled:cursor-not-allowed disabled:opacity-60"
|
||||
>
|
||||
Stop
|
||||
</button>
|
||||
</form>
|
||||
<div id="messageInputHint" class="mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-gray-500">
|
||||
<span><kbd class="rounded border border-gray-200 bg-gray-100 px-1.5 py-0.5 font-mono text-[10px] text-gray-700">Enter</kbd> sends</span>
|
||||
<span><kbd class="rounded border border-gray-200 bg-gray-100 px-1.5 py-0.5 font-mono text-[10px] text-gray-700">Shift</kbd>+<kbd class="rounded border border-gray-200 bg-gray-100 px-1.5 py-0.5 font-mono text-[10px] text-gray-700">Enter</kbd> adds a new line</span>
|
||||
<span><kbd class="rounded border border-gray-200 bg-gray-100 px-1.5 py-0.5 font-mono text-[10px] text-gray-700">Alt</kbd>, <kbd class="rounded border border-gray-200 bg-gray-100 px-1.5 py-0.5 font-mono text-[10px] text-gray-700">Ctrl</kbd>, or <kbd class="rounded border border-gray-200 bg-gray-100 px-1.5 py-0.5 font-mono text-[10px] text-gray-700">Cmd</kbd>+<kbd class="rounded border border-gray-200 bg-gray-100 px-1.5 py-0.5 font-mono text-[10px] text-gray-700">Enter</kbd> also add a new line</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user