Spaces:
Running
Running
Update index.html
Browse files- index.html +139 -57
index.html
CHANGED
|
@@ -104,38 +104,6 @@ tailwind.config = {
|
|
| 104 |
--pre-bg: #d8eaf8;
|
| 105 |
}
|
| 106 |
|
| 107 |
-
body { background: var(--bg); color: var(--pearl); }
|
| 108 |
-
#sidebar { background: var(--surface); border-color: var(--border); }
|
| 109 |
-
header { background: var(--surface); border-color: var(--border); }
|
| 110 |
-
.bg-smoke { background: var(--surface) !important; }
|
| 111 |
-
.bg-ash { background: var(--mist) !important; }
|
| 112 |
-
.bg-mist { background: var(--mist) !important; }
|
| 113 |
-
.hover\:bg-ash:hover { background: var(--border) !important; }
|
| 114 |
-
.hover\:bg-mist:hover { background: var(--mist) !important; }
|
| 115 |
-
.border-ash { border-color: var(--border) !important; }
|
| 116 |
-
.border-mist { border-color: var(--mist) !important; }
|
| 117 |
-
.text-fog { color: var(--fog) !important; }
|
| 118 |
-
.text-ghost { color: var(--ghost) !important; }
|
| 119 |
-
.text-pearl { color: var(--pearl) !important; }
|
| 120 |
-
.text-cream { color: var(--cream) !important; }
|
| 121 |
-
.text-amber { color: var(--accent) !important; }
|
| 122 |
-
.text-ink { color: var(--user-text) !important; }
|
| 123 |
-
.text-rose { color: var(--danger) !important; }
|
| 124 |
-
.text-sage { color: var(--safe) !important; }
|
| 125 |
-
.bg-amber { background: var(--user-bg) !important; }
|
| 126 |
-
.hover\:bg-ember:hover { background: var(--accent2) !important; }
|
| 127 |
-
.bg-amber\/10 { background: color-mix(in srgb, var(--accent) 10%, transparent) !important; }
|
| 128 |
-
.bg-amber\/20 { background: color-mix(in srgb, var(--accent) 20%, transparent) !important; }
|
| 129 |
-
.border-amber\/25 { border-color: color-mix(in srgb, var(--accent) 25%, transparent) !important; }
|
| 130 |
-
.from-amber { --tw-gradient-from: var(--accent) !important; }
|
| 131 |
-
.to-ember { --tw-gradient-to: var(--accent2) !important; }
|
| 132 |
-
.bg-rose { background: var(--danger) !important; }
|
| 133 |
-
.hover\:text-rose:hover { color: var(--danger) !important; }
|
| 134 |
-
.hover\:text-sage:hover { color: var(--safe) !important; }
|
| 135 |
-
.hover\:text-pearl:hover { color: var(--pearl) !important; }
|
| 136 |
-
textarea, input, select { background: var(--mist); color: var(--cream); }
|
| 137 |
-
|
| 138 |
-
/* Subtle starfield in dark mode */
|
| 139 |
body:not(.light) {
|
| 140 |
background-image:
|
| 141 |
radial-gradient(1px 1px at 20% 30%, rgba(160,200,255,0.4) 0%, transparent 100%),
|
|
@@ -145,7 +113,6 @@ tailwind.config = {
|
|
| 145 |
radial-gradient(1px 1px at 10% 85%, rgba(160,200,255,0.2) 0%, transparent 100%);
|
| 146 |
}
|
| 147 |
|
| 148 |
-
/* Glowing accent on sidebar top */
|
| 149 |
body:not(.light) #sidebar::before {
|
| 150 |
content: '';
|
| 151 |
position: absolute;
|
|
@@ -154,7 +121,7 @@ tailwind.config = {
|
|
| 154 |
background: linear-gradient(90deg, transparent, var(--accent), transparent);
|
| 155 |
opacity: 0.5;
|
| 156 |
}
|
| 157 |
-
#sidebar { position: relative; }
|
| 158 |
|
| 159 |
.font-display { font-family: 'Syne', sans-serif !important; }
|
| 160 |
|
|
@@ -188,7 +155,108 @@ tailwind.config = {
|
|
| 188 |
.dot-3 { animation: blink 1.2s 0.4s infinite; }
|
| 189 |
select { -webkit-appearance: none; appearance: none; }
|
| 190 |
textarea:focus, input:focus, select:focus { outline: none; box-shadow: 0 0 0 1.5px var(--accent), 0 0 12px color-mix(in srgb, var(--accent) 20%, transparent); }
|
| 191 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
</style>
|
| 193 |
</head>
|
| 194 |
<body class="h-screen flex overflow-hidden">
|
|
@@ -272,9 +340,9 @@ tailwind.config = {
|
|
| 272 |
<div class="relative">
|
| 273 |
<select id="model-select" class="w-full bg-ash border border-mist rounded-md px-3 py-2 pr-7 text-xs font-mono text-cream focus:border-amber transition-colors cursor-pointer" onchange="saveSettings()">
|
| 274 |
<optgroup label='Openrouter'>
|
|
|
|
| 275 |
<option value='openrouter/hunter-alpha'>Hunter Alpha </option>
|
| 276 |
<option value='openrouter/healer-alpha'>Healer Alpha </option>
|
| 277 |
-
<option value='openrouter/free'>Free Models Router </option>
|
| 278 |
</optgroup>
|
| 279 |
<optgroup label='Nvidia'>
|
| 280 |
<option value='nvidia/nemotron-3-super-120b-a12b:free'>NVIDIA: Nemotron 3 Super (free) </option>
|
|
@@ -591,26 +659,25 @@ let chats = {}; // { id: { id, title, messages: [], createdAt } }
|
|
| 591 |
|
| 592 |
// ββ Init βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 593 |
function init() {
|
| 594 |
-
if (localStorage.getItem('
|
| 595 |
|
| 596 |
-
if (localStorage.getItem('
|
| 597 |
|
| 598 |
loadSettings();
|
| 599 |
loadChats();
|
| 600 |
updateModelBadge();
|
| 601 |
-
newChat();
|
| 602 |
}
|
| 603 |
|
| 604 |
function dismissPrivacyNotice() {
|
| 605 |
document.getElementById('privacy-modal').remove();
|
| 606 |
-
localStorage.setItem('
|
| 607 |
}
|
| 608 |
|
| 609 |
function toggleTheme() {
|
| 610 |
const isLight = document.body.classList.toggle('light');
|
| 611 |
document.getElementById('theme-icon-dark').classList.toggle('hidden', isLight);
|
| 612 |
document.getElementById('theme-icon-light').classList.toggle('hidden', !isLight);
|
| 613 |
-
localStorage.setItem('
|
| 614 |
document.getElementById('hljs-theme').href = isLight
|
| 615 |
? 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css'
|
| 616 |
: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css';
|
|
@@ -624,13 +691,13 @@ function saveSettings() {
|
|
| 624 |
temperature: document.getElementById('temperature').value,
|
| 625 |
maxTokens: document.getElementById('max-tokens').value,
|
| 626 |
};
|
| 627 |
-
localStorage.setItem('
|
| 628 |
updateModelBadge();
|
| 629 |
}
|
| 630 |
|
| 631 |
function loadSettings() {
|
| 632 |
try {
|
| 633 |
-
const s = JSON.parse(localStorage.getItem('
|
| 634 |
if (s.apiKey) document.getElementById('api-key').value = s.apiKey;
|
| 635 |
if (s.model) document.getElementById('model-select').value = s.model;
|
| 636 |
if (s.systemPrompt) document.getElementById('system-prompt').value = s.systemPrompt;
|
|
@@ -657,12 +724,12 @@ function toggleKeyVisibility() {
|
|
| 657 |
function genId() { return Date.now().toString(36) + Math.random().toString(36).slice(2, 6); }
|
| 658 |
|
| 659 |
function saveChats() {
|
| 660 |
-
localStorage.setItem('
|
| 661 |
}
|
| 662 |
|
| 663 |
function loadChats() {
|
| 664 |
try {
|
| 665 |
-
chats = JSON.parse(localStorage.getItem('
|
| 666 |
} catch(e) { chats = {}; }
|
| 667 |
renderConvList();
|
| 668 |
}
|
|
@@ -685,7 +752,9 @@ function switchChat(id) {
|
|
| 685 |
renderMessages();
|
| 686 |
renderConvList();
|
| 687 |
const chat = chats[id];
|
| 688 |
-
|
|
|
|
|
|
|
| 689 |
closeSidebar();
|
| 690 |
}
|
| 691 |
|
|
@@ -698,9 +767,11 @@ function deleteChat(id, e) {
|
|
| 698 |
}
|
| 699 |
|
| 700 |
function clearCurrentChat() {
|
| 701 |
-
|
| 702 |
-
|
| 703 |
-
|
|
|
|
|
|
|
| 704 |
document.getElementById('chat-title').textContent = 'New conversation';
|
| 705 |
saveChats();
|
| 706 |
renderConvList();
|
|
@@ -709,6 +780,8 @@ function clearCurrentChat() {
|
|
| 709 |
|
| 710 |
function renderConvList() {
|
| 711 |
const list = document.getElementById('conv-list');
|
|
|
|
|
|
|
| 712 |
const sorted = Object.values(chats).sort((a,b) => b.createdAt - a.createdAt);
|
| 713 |
if (sorted.length === 0) {
|
| 714 |
list.innerHTML = '<div class="text-fog/60 text-xs font-mono px-3 py-2">No conversations yet</div>';
|
|
@@ -731,9 +804,10 @@ const msgContentMap = {};
|
|
| 731 |
|
| 732 |
function renderMessages() {
|
| 733 |
const container = document.getElementById('messages');
|
| 734 |
-
const
|
|
|
|
| 735 |
|
| 736 |
-
if (msgs.length === 0) {
|
| 737 |
container.innerHTML = `<div id="empty-state" class="h-full flex flex-col items-center justify-center text-center py-16">
|
| 738 |
<div class="font-display text-5xl italic text-cream/20 mb-4 select-none">FlexChat</div>
|
| 739 |
<div class="text-fog text-sm font-mono max-w-xs">Enter your OpenRouter API key in the sidebar, choose a model, and start a conversation.</div>
|
|
@@ -776,7 +850,7 @@ function renderMessage(msg, idx) {
|
|
| 776 |
} else {
|
| 777 |
return `<div class="flex gap-3 items-start">
|
| 778 |
<div class="w-7 h-7 rounded-md bg-gradient-to-br from-amber to-ember flex items-center justify-center flex-shrink-0 mt-0.5 shadow-md">
|
| 779 |
-
<span class="text-ink text-xs font-display font-bold italic">
|
| 780 |
</div>
|
| 781 |
<div class="flex-1 min-w-0 group">
|
| 782 |
<div class="text-xs font-mono text-ghost mb-1.5">${escHtml(msg.model || 'assistant')}</div>
|
|
@@ -824,7 +898,10 @@ async function sendMessage() {
|
|
| 824 |
const input = document.getElementById('user-input');
|
| 825 |
const text = input.value.trim();
|
| 826 |
if (!text || isStreaming) return;
|
| 827 |
-
|
|
|
|
|
|
|
|
|
|
| 828 |
const apiKey = document.getElementById('api-key').value.trim();
|
| 829 |
if (!apiKey) { showError('Please enter your OpenRouter API key in the sidebar.'); return; }
|
| 830 |
|
|
@@ -869,7 +946,7 @@ async function sendMessage() {
|
|
| 869 |
placeholder.className = 'flex gap-3 items-start';
|
| 870 |
placeholder.innerHTML = `
|
| 871 |
<div class="w-7 h-7 rounded-md bg-gradient-to-br from-amber to-ember flex items-center justify-center flex-shrink-0 mt-0.5 shadow-md">
|
| 872 |
-
<span class="text-ink text-xs font-display font-bold italic">
|
| 873 |
</div>
|
| 874 |
<div class="flex-1 min-w-0">
|
| 875 |
<div class="text-xs font-mono text-ghost mb-1.5">${escHtml(model.split('/').pop())}</div>
|
|
@@ -1055,13 +1132,18 @@ function openSidebar() {
|
|
| 1055 |
function closeSidebar() {
|
| 1056 |
const s = document.getElementById('sidebar');
|
| 1057 |
const o = document.getElementById('overlay');
|
|
|
|
|
|
|
| 1058 |
s.classList.add('-translate-x-full');
|
| 1059 |
-
|
| 1060 |
-
|
|
|
|
|
|
|
|
|
|
| 1061 |
}
|
| 1062 |
|
| 1063 |
// ββ Boot βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1064 |
init();
|
| 1065 |
</script>
|
| 1066 |
</body>
|
| 1067 |
-
</html>
|
|
|
|
| 104 |
--pre-bg: #d8eaf8;
|
| 105 |
}
|
| 106 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
body:not(.light) {
|
| 108 |
background-image:
|
| 109 |
radial-gradient(1px 1px at 20% 30%, rgba(160,200,255,0.4) 0%, transparent 100%),
|
|
|
|
| 113 |
radial-gradient(1px 1px at 10% 85%, rgba(160,200,255,0.2) 0%, transparent 100%);
|
| 114 |
}
|
| 115 |
|
|
|
|
| 116 |
body:not(.light) #sidebar::before {
|
| 117 |
content: '';
|
| 118 |
position: absolute;
|
|
|
|
| 121 |
background: linear-gradient(90deg, transparent, var(--accent), transparent);
|
| 122 |
opacity: 0.5;
|
| 123 |
}
|
| 124 |
+
/*#sidebar { position: relative; }*/
|
| 125 |
|
| 126 |
.font-display { font-family: 'Syne', sans-serif !important; }
|
| 127 |
|
|
|
|
| 155 |
.dot-3 { animation: blink 1.2s 0.4s infinite; }
|
| 156 |
select { -webkit-appearance: none; appearance: none; }
|
| 157 |
textarea:focus, input:focus, select:focus { outline: none; box-shadow: 0 0 0 1.5px var(--accent), 0 0 12px color-mix(in srgb, var(--accent) 20%, transparent); }
|
| 158 |
+
|
| 159 |
+
/* Tool panel styles */
|
| 160 |
+
#tool-panel, #file-explorer-panel { transition: all 0.3s ease; }
|
| 161 |
+
.tool-item { transition: all 0.2s ease; }
|
| 162 |
+
.tool-item:hover { transform: translateX(4px); }
|
| 163 |
+
.tool-item.active { background: var(--accent); color: var(--ink); }
|
| 164 |
+
|
| 165 |
+
/* File Explorer Styles */
|
| 166 |
+
.file-tree-item { transition: all 0.15s ease; }
|
| 167 |
+
.file-tree-item:hover { background: var(--mist); }
|
| 168 |
+
.file-tree-item.active { background: var(--accent) !important; color: var(--ink); }
|
| 169 |
+
.file-tree-children { border-left: 1px solid var(--border); margin-left: 11px; padding-left: 8px; }
|
| 170 |
+
.file-explorer-editor { animation: slideIn 0.25s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
|
| 171 |
+
|
| 172 |
+
/* CRITICAL MOBILE FIX - Main container */
|
| 173 |
+
@media (max-width: 768px) {
|
| 174 |
+
/* Override flex behavior on mobile */
|
| 175 |
+
main {
|
| 176 |
+
flex: none !important;
|
| 177 |
+
width: 100% !important;
|
| 178 |
+
max-width: 100vw !important;
|
| 179 |
+
margin-left: 0 !important;
|
| 180 |
+
position: relative !important;
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
/* Overlay */
|
| 185 |
+
#overlay {
|
| 186 |
+
position: fixed !important;
|
| 187 |
+
inset: 0 !important;
|
| 188 |
+
background: rgba(0,0,0,0.6) !important;
|
| 189 |
+
display: none !important;
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
#overlay:not(.hidden) {
|
| 193 |
+
display: block !important;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
/* Fix message container */
|
| 197 |
+
#messages {
|
| 198 |
+
padding-left: 16px !important;
|
| 199 |
+
padding-right: 16px !important;
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
/* Fix input footer */
|
| 203 |
+
#input-footer {
|
| 204 |
+
padding-left: 16px !important;
|
| 205 |
+
padding-right: 16px !important;
|
| 206 |
+
padding-bottom: 5vh;
|
| 207 |
+
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
/* Remove any negative margins */
|
| 211 |
+
.flex-1 {
|
| 212 |
+
min-width: 0 !important;
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
/* File explorer panel on mobile */
|
| 216 |
+
#file-explorer-panel {
|
| 217 |
+
width: 100% !important;
|
| 218 |
+
max-width: 100vw !important;
|
| 219 |
+
}
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
/* Extra small screens */
|
| 223 |
+
@media (max-width: 480px) {
|
| 224 |
+
main {
|
| 225 |
+
width: 100vw !important;
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
#messages, #input-footer {
|
| 229 |
+
padding-left: 12px !important;
|
| 230 |
+
padding-right: 12px !important;
|
| 231 |
+
}
|
| 232 |
+
#input-footer {
|
| 233 |
+
padding-bottom: 5vh;
|
| 234 |
+
|
| 235 |
+
}
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
/* iOS Safari safe area */
|
| 239 |
+
@supports (-webkit-touch-callout: none) {
|
| 240 |
+
body {
|
| 241 |
+
padding-bottom: env(safe-area-inset-bottom);
|
| 242 |
+
}
|
| 243 |
+
#input-footer {
|
| 244 |
+
padding-bottom: 5vh;
|
| 245 |
+
}
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
/* Smooth scrolling on mobile */
|
| 249 |
+
#messages {
|
| 250 |
+
-webkit-overflow-scrolling: touch;
|
| 251 |
+
overscroll-behavior: contain;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
#tool-result-banner, #error-banner {
|
| 255 |
+
margin-left: 12px;
|
| 256 |
+
margin-right: 12px;
|
| 257 |
+
font-size: 11px;
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
</style>
|
| 261 |
</head>
|
| 262 |
<body class="h-screen flex overflow-hidden">
|
|
|
|
| 340 |
<div class="relative">
|
| 341 |
<select id="model-select" class="w-full bg-ash border border-mist rounded-md px-3 py-2 pr-7 text-xs font-mono text-cream focus:border-amber transition-colors cursor-pointer" onchange="saveSettings()">
|
| 342 |
<optgroup label='Openrouter'>
|
| 343 |
+
<option value='openrouter/free'>Free Models Router </option>
|
| 344 |
<option value='openrouter/hunter-alpha'>Hunter Alpha </option>
|
| 345 |
<option value='openrouter/healer-alpha'>Healer Alpha </option>
|
|
|
|
| 346 |
</optgroup>
|
| 347 |
<optgroup label='Nvidia'>
|
| 348 |
<option value='nvidia/nemotron-3-super-120b-a12b:free'>NVIDIA: Nemotron 3 Super (free) </option>
|
|
|
|
| 659 |
|
| 660 |
// ββ Init βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 661 |
function init() {
|
| 662 |
+
if (localStorage.getItem('flexchat_privacy_seen')) dismissPrivacyNotice();
|
| 663 |
|
| 664 |
+
if (localStorage.getItem('flexchat_theme') === 'light') toggleTheme(); // β add this
|
| 665 |
|
| 666 |
loadSettings();
|
| 667 |
loadChats();
|
| 668 |
updateModelBadge();
|
|
|
|
| 669 |
}
|
| 670 |
|
| 671 |
function dismissPrivacyNotice() {
|
| 672 |
document.getElementById('privacy-modal').remove();
|
| 673 |
+
localStorage.setItem('flexchat_privacy_seen', '1');
|
| 674 |
}
|
| 675 |
|
| 676 |
function toggleTheme() {
|
| 677 |
const isLight = document.body.classList.toggle('light');
|
| 678 |
document.getElementById('theme-icon-dark').classList.toggle('hidden', isLight);
|
| 679 |
document.getElementById('theme-icon-light').classList.toggle('hidden', !isLight);
|
| 680 |
+
localStorage.setItem('flexchat_theme', isLight ? 'light' : 'dark');
|
| 681 |
document.getElementById('hljs-theme').href = isLight
|
| 682 |
? 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css'
|
| 683 |
: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css';
|
|
|
|
| 691 |
temperature: document.getElementById('temperature').value,
|
| 692 |
maxTokens: document.getElementById('max-tokens').value,
|
| 693 |
};
|
| 694 |
+
localStorage.setItem('flexchat_settings', JSON.stringify(settings));
|
| 695 |
updateModelBadge();
|
| 696 |
}
|
| 697 |
|
| 698 |
function loadSettings() {
|
| 699 |
try {
|
| 700 |
+
const s = JSON.parse(localStorage.getItem('flexchat_settings') || '{}');
|
| 701 |
if (s.apiKey) document.getElementById('api-key').value = s.apiKey;
|
| 702 |
if (s.model) document.getElementById('model-select').value = s.model;
|
| 703 |
if (s.systemPrompt) document.getElementById('system-prompt').value = s.systemPrompt;
|
|
|
|
| 724 |
function genId() { return Date.now().toString(36) + Math.random().toString(36).slice(2, 6); }
|
| 725 |
|
| 726 |
function saveChats() {
|
| 727 |
+
localStorage.setItem('flexchat_chats', JSON.stringify(chats));
|
| 728 |
}
|
| 729 |
|
| 730 |
function loadChats() {
|
| 731 |
try {
|
| 732 |
+
chats = JSON.parse(localStorage.getItem('flexchat_chats') || '{}');
|
| 733 |
} catch(e) { chats = {}; }
|
| 734 |
renderConvList();
|
| 735 |
}
|
|
|
|
| 752 |
renderMessages();
|
| 753 |
renderConvList();
|
| 754 |
const chat = chats[id];
|
| 755 |
+
if (chat) {
|
| 756 |
+
document.getElementById('chat-title').textContent = chat.title;
|
| 757 |
+
}
|
| 758 |
closeSidebar();
|
| 759 |
}
|
| 760 |
|
|
|
|
| 767 |
}
|
| 768 |
|
| 769 |
function clearCurrentChat() {
|
| 770 |
+
if (!currentChatId) return;
|
| 771 |
+
const chat = chats[currentChatId];
|
| 772 |
+
if (!chat) return;
|
| 773 |
+
chat.messages = [];
|
| 774 |
+
chat.title = 'New conversation';
|
| 775 |
document.getElementById('chat-title').textContent = 'New conversation';
|
| 776 |
saveChats();
|
| 777 |
renderConvList();
|
|
|
|
| 780 |
|
| 781 |
function renderConvList() {
|
| 782 |
const list = document.getElementById('conv-list');
|
| 783 |
+
if (!list) return;
|
| 784 |
+
|
| 785 |
const sorted = Object.values(chats).sort((a,b) => b.createdAt - a.createdAt);
|
| 786 |
if (sorted.length === 0) {
|
| 787 |
list.innerHTML = '<div class="text-fog/60 text-xs font-mono px-3 py-2">No conversations yet</div>';
|
|
|
|
| 804 |
|
| 805 |
function renderMessages() {
|
| 806 |
const container = document.getElementById('messages');
|
| 807 |
+
const chat = chats[currentChatId];
|
| 808 |
+
const msgs = chat ? chat.messages : [];
|
| 809 |
|
| 810 |
+
if (!chat || msgs.length === 0) {
|
| 811 |
container.innerHTML = `<div id="empty-state" class="h-full flex flex-col items-center justify-center text-center py-16">
|
| 812 |
<div class="font-display text-5xl italic text-cream/20 mb-4 select-none">FlexChat</div>
|
| 813 |
<div class="text-fog text-sm font-mono max-w-xs">Enter your OpenRouter API key in the sidebar, choose a model, and start a conversation.</div>
|
|
|
|
| 850 |
} else {
|
| 851 |
return `<div class="flex gap-3 items-start">
|
| 852 |
<div class="w-7 h-7 rounded-md bg-gradient-to-br from-amber to-ember flex items-center justify-center flex-shrink-0 mt-0.5 shadow-md">
|
| 853 |
+
<span class="text-ink text-xs font-display font-bold italic">Bot</span>
|
| 854 |
</div>
|
| 855 |
<div class="flex-1 min-w-0 group">
|
| 856 |
<div class="text-xs font-mono text-ghost mb-1.5">${escHtml(msg.model || 'assistant')}</div>
|
|
|
|
| 898 |
const input = document.getElementById('user-input');
|
| 899 |
const text = input.value.trim();
|
| 900 |
if (!text || isStreaming) return;
|
| 901 |
+
|
| 902 |
+
if (!currentChatId || !chats[currentChatId]) {
|
| 903 |
+
newChat();
|
| 904 |
+
}
|
| 905 |
const apiKey = document.getElementById('api-key').value.trim();
|
| 906 |
if (!apiKey) { showError('Please enter your OpenRouter API key in the sidebar.'); return; }
|
| 907 |
|
|
|
|
| 946 |
placeholder.className = 'flex gap-3 items-start';
|
| 947 |
placeholder.innerHTML = `
|
| 948 |
<div class="w-7 h-7 rounded-md bg-gradient-to-br from-amber to-ember flex items-center justify-center flex-shrink-0 mt-0.5 shadow-md">
|
| 949 |
+
<span class="text-ink text-xs font-display font-bold italic">Bot</span>
|
| 950 |
</div>
|
| 951 |
<div class="flex-1 min-w-0">
|
| 952 |
<div class="text-xs font-mono text-ghost mb-1.5">${escHtml(model.split('/').pop())}</div>
|
|
|
|
| 1132 |
function closeSidebar() {
|
| 1133 |
const s = document.getElementById('sidebar');
|
| 1134 |
const o = document.getElementById('overlay');
|
| 1135 |
+
|
| 1136 |
+
s.removeAttribute('data-open');
|
| 1137 |
s.classList.add('-translate-x-full');
|
| 1138 |
+
|
| 1139 |
+
o.classList.add('hidden');
|
| 1140 |
+
setTimeout(() => {
|
| 1141 |
+
o.style.display = 'none';
|
| 1142 |
+
}, 300);
|
| 1143 |
}
|
| 1144 |
|
| 1145 |
// ββ Boot βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1146 |
init();
|
| 1147 |
</script>
|
| 1148 |
</body>
|
| 1149 |
+
</html>
|