LogitCode commited on
Commit
42ce795
Β·
verified Β·
1 Parent(s): be23704

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +168 -64
index.html CHANGED
@@ -104,57 +104,47 @@ tailwind.config = {
104
  --pre-bg: #e4ddd0;
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
- .prose-msg { line-height: 1.65; color: var(--prose); }
139
  .prose-msg p { margin: 0 0 0.6em; } .prose-msg p:last-child { margin-bottom: 0; }
140
- .prose-msg code { font-family: 'DM Mono', monospace; background: var(--code-bg); padding: 0.1em 0.35em; border-radius: 3px; font-size: 0.875em; color: var(--code-col); }
141
- .prose-msg pre { background: var(--pre-bg); border: 1px solid var(--border); border-radius: 6px; padding: 12px 14px; overflow-x: auto; margin: 0.6em 0; }
142
- .prose-msg pre code { background: transparent; padding: 0; color: var(--prose); font-size: 0.82em; }
143
- .prose-msg h1,.prose-msg h2,.prose-msg h3 { font-family: 'Fraunces', serif; color: var(--cream); margin: 0.8em 0 0.3em; font-weight: 700; }
144
  .prose-msg h1 { font-size: 1.25em; } .prose-msg h2 { font-size: 1.1em; } .prose-msg h3 { font-size: 1em; }
145
  .prose-msg ul, .prose-msg ol { padding-left: 1.25em; margin: 0.4em 0 0.6em; }
 
146
  .prose-msg li { margin: 0.2em 0; }
147
  .prose-msg blockquote { border-left: 2px solid var(--accent); padding-left: 0.75em; color: var(--ghost); margin: 0.5em 0; font-style: italic; }
148
  .prose-msg a { color: var(--accent); text-decoration: underline; }
149
  .prose-msg strong { color: var(--cream); font-weight: 500; }
150
  .prose-msg table { border-collapse: collapse; width: 100%; margin: 0.5em 0; font-size: 0.85em; }
151
- .prose-msg th { background: var(--mist); color: var(--cream); padding: 5px 8px; text-align: left; }
152
  .prose-msg td { border-top: 1px solid var(--border); padding: 5px 8px; }
153
 
154
  .prose-user { line-height: 1.65; color: var(--user-text); }
155
  .prose-user p { margin: 0 0 0.5em; } .prose-user p:last-child { margin-bottom: 0; }
156
- .prose-user code { background: rgba(0,0,0,0.12); padding: 0.1em 0.3em; border-radius: 3px; font-size: 0.875em; }
157
- .prose-user pre { background: rgba(0,0,0,0.15); border-radius: 5px; padding: 10px 12px; overflow-x: auto; margin: 0.5em 0; }
158
  .prose-user strong { font-weight: 500; }
159
 
160
  textarea { resize: none; overflow: hidden; }
@@ -164,9 +154,109 @@ tailwind.config = {
164
  .dot-2 { animation: blink 1.2s 0.2s infinite; }
165
  .dot-3 { animation: blink 1.2s 0.4s infinite; }
166
  select { -webkit-appearance: none; appearance: none; }
167
- textarea:focus, input:focus, select:focus { outline: none; box-shadow: 0 0 0 1.5px var(--accent); }
168
- #input-footer { padding-bottom: max(12px, env(safe-area-inset-bottom)); }
169
- @media (max-width: 768px) { main { padding-bottom: 5vh; } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  </style>
171
  </head>
172
  <body class="h-screen flex overflow-hidden">
@@ -250,9 +340,9 @@ tailwind.config = {
250
  <div class="relative">
251
  <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()">
252
  <optgroup label='Openrouter'>
 
253
  <option value='openrouter/hunter-alpha'>Hunter Alpha </option>
254
  <option value='openrouter/healer-alpha'>Healer Alpha </option>
255
- <option value='openrouter/free'>Free Models Router </option>
256
  </optgroup>
257
  <optgroup label='Nvidia'>
258
  <option value='nvidia/nemotron-3-super-120b-a12b:free'>NVIDIA: Nemotron 3 Super (free) </option>
@@ -569,26 +659,25 @@ let chats = {}; // { id: { id, title, messages: [], createdAt } }
569
 
570
  // ── Init ───────────────────────────────────────────────────────────────────
571
  function init() {
572
- if (localStorage.getItem('nexus_privacy_seen')) dismissPrivacyNotice();
573
 
574
- if (localStorage.getItem('nexus_theme') === 'light') toggleTheme(); // ← add this
575
 
576
  loadSettings();
577
  loadChats();
578
  updateModelBadge();
579
- newChat();
580
  }
581
 
582
  function dismissPrivacyNotice() {
583
  document.getElementById('privacy-modal').remove();
584
- localStorage.setItem('nexus_privacy_seen', '1');
585
  }
586
 
587
  function toggleTheme() {
588
  const isLight = document.body.classList.toggle('light');
589
  document.getElementById('theme-icon-dark').classList.toggle('hidden', isLight);
590
  document.getElementById('theme-icon-light').classList.toggle('hidden', !isLight);
591
- localStorage.setItem('nexus_theme', isLight ? 'light' : 'dark');
592
  document.getElementById('hljs-theme').href = isLight
593
  ? 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css'
594
  : 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css';
@@ -602,13 +691,13 @@ function saveSettings() {
602
  temperature: document.getElementById('temperature').value,
603
  maxTokens: document.getElementById('max-tokens').value,
604
  };
605
- localStorage.setItem('nexus_settings', JSON.stringify(settings));
606
  updateModelBadge();
607
  }
608
 
609
  function loadSettings() {
610
  try {
611
- const s = JSON.parse(localStorage.getItem('nexus_settings') || '{}');
612
  if (s.apiKey) document.getElementById('api-key').value = s.apiKey;
613
  if (s.model) document.getElementById('model-select').value = s.model;
614
  if (s.systemPrompt) document.getElementById('system-prompt').value = s.systemPrompt;
@@ -635,12 +724,12 @@ function toggleKeyVisibility() {
635
  function genId() { return Date.now().toString(36) + Math.random().toString(36).slice(2, 6); }
636
 
637
  function saveChats() {
638
- localStorage.setItem('nexus_chats', JSON.stringify(chats));
639
  }
640
 
641
  function loadChats() {
642
  try {
643
- chats = JSON.parse(localStorage.getItem('nexus_chats') || '{}');
644
  } catch(e) { chats = {}; }
645
  renderConvList();
646
  }
@@ -663,7 +752,9 @@ function switchChat(id) {
663
  renderMessages();
664
  renderConvList();
665
  const chat = chats[id];
666
- document.getElementById('chat-title').textContent = chat.title;
 
 
667
  closeSidebar();
668
  }
669
 
@@ -676,9 +767,11 @@ function deleteChat(id, e) {
676
  }
677
 
678
  function clearCurrentChat() {
679
- if (!currentChatId) return;
680
- chats[currentChatId].messages = [];
681
- chats[currentChatId].title = 'New conversation';
 
 
682
  document.getElementById('chat-title').textContent = 'New conversation';
683
  saveChats();
684
  renderConvList();
@@ -687,6 +780,8 @@ function clearCurrentChat() {
687
 
688
  function renderConvList() {
689
  const list = document.getElementById('conv-list');
 
 
690
  const sorted = Object.values(chats).sort((a,b) => b.createdAt - a.createdAt);
691
  if (sorted.length === 0) {
692
  list.innerHTML = '<div class="text-fog/60 text-xs font-mono px-3 py-2">No conversations yet</div>';
@@ -709,9 +804,10 @@ const msgContentMap = {};
709
 
710
  function renderMessages() {
711
  const container = document.getElementById('messages');
712
- const msgs = chats[currentChatId]?.messages || [];
 
713
 
714
- if (msgs.length === 0) {
715
  container.innerHTML = `<div id="empty-state" class="h-full flex flex-col items-center justify-center text-center py-16">
716
  <div class="font-display text-5xl italic text-cream/20 mb-4 select-none">FlexChat</div>
717
  <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>
@@ -754,7 +850,7 @@ function renderMessage(msg, idx) {
754
  } else {
755
  return `<div class="flex gap-3 items-start">
756
  <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">
757
- <span class="text-ink text-xs font-display font-bold italic">N</span>
758
  </div>
759
  <div class="flex-1 min-w-0 group">
760
  <div class="text-xs font-mono text-ghost mb-1.5">${escHtml(msg.model || 'assistant')}</div>
@@ -802,7 +898,10 @@ async function sendMessage() {
802
  const input = document.getElementById('user-input');
803
  const text = input.value.trim();
804
  if (!text || isStreaming) return;
805
-
 
 
 
806
  const apiKey = document.getElementById('api-key').value.trim();
807
  if (!apiKey) { showError('Please enter your OpenRouter API key in the sidebar.'); return; }
808
 
@@ -847,7 +946,7 @@ async function sendMessage() {
847
  placeholder.className = 'flex gap-3 items-start';
848
  placeholder.innerHTML = `
849
  <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">
850
- <span class="text-ink text-xs font-display font-bold italic">N</span>
851
  </div>
852
  <div class="flex-1 min-w-0">
853
  <div class="text-xs font-mono text-ghost mb-1.5">${escHtml(model.split('/').pop())}</div>
@@ -1033,13 +1132,18 @@ function openSidebar() {
1033
  function closeSidebar() {
1034
  const s = document.getElementById('sidebar');
1035
  const o = document.getElementById('overlay');
 
 
1036
  s.classList.add('-translate-x-full');
1037
- o.classList.add('opacity-0');
1038
- setTimeout(() => o.classList.add('hidden'), 300);
 
 
 
1039
  }
1040
 
1041
  // ── Boot ───────────────────────────────────────────────────────────────────
1042
  init();
1043
  </script>
1044
  </body>
1045
- </html>
 
104
  --pre-bg: #e4ddd0;
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%),
110
+ radial-gradient(1px 1px at 75% 15%, rgba(160,200,255,0.3) 0%, transparent 100%),
111
+ radial-gradient(1px 1px at 55% 70%, rgba(160,200,255,0.25) 0%, transparent 100%),
112
+ radial-gradient(1px 1px at 90% 55%, rgba(160,200,255,0.2) 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;
119
+ top: 0; left: 0; right: 0;
120
+ height: 1px;
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
+
128
+ .prose-msg { line-height: 1.7; color: var(--prose); }
 
 
 
 
 
 
 
 
 
 
129
  .prose-msg p { margin: 0 0 0.6em; } .prose-msg p:last-child { margin-bottom: 0; }
130
+ .prose-msg code { font-family: 'Intel One Mono', 'Fira Code', monospace; background: var(--code-bg); padding: 0.1em 0.35em; border-radius: 4px; font-size: 0.875em; color: var(--code-col); border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent); }
131
+ .prose-msg pre { background: var(--pre-bg); border: 1px solid var(--border); border-left: 2px solid var(--accent); border-radius: 6px; padding: 12px 14px; overflow-x: auto; margin: 0.6em 0; }
132
+ .prose-msg pre code { background: transparent; border: none; padding: 0; color: var(--prose); font-size: 0.82em; }
133
+ .prose-msg h1,.prose-msg h2,.prose-msg h3 { font-family: 'Syne', sans-serif; color: var(--cream); margin: 0.8em 0 0.3em; font-weight: 700; }
134
  .prose-msg h1 { font-size: 1.25em; } .prose-msg h2 { font-size: 1.1em; } .prose-msg h3 { font-size: 1em; }
135
  .prose-msg ul, .prose-msg ol { padding-left: 1.25em; margin: 0.4em 0 0.6em; }
136
+ .prose-msg li::marker { color: var(--accent); }
137
  .prose-msg li { margin: 0.2em 0; }
138
  .prose-msg blockquote { border-left: 2px solid var(--accent); padding-left: 0.75em; color: var(--ghost); margin: 0.5em 0; font-style: italic; }
139
  .prose-msg a { color: var(--accent); text-decoration: underline; }
140
  .prose-msg strong { color: var(--cream); font-weight: 500; }
141
  .prose-msg table { border-collapse: collapse; width: 100%; margin: 0.5em 0; font-size: 0.85em; }
142
+ .prose-msg th { background: var(--mist); color: var(--cream); padding: 5px 8px; text-align: left; border-bottom: 1px solid var(--accent); }
143
  .prose-msg td { border-top: 1px solid var(--border); padding: 5px 8px; }
144
 
145
  .prose-user { line-height: 1.65; color: var(--user-text); }
146
  .prose-user p { margin: 0 0 0.5em; } .prose-user p:last-child { margin-bottom: 0; }
147
+ .prose-user code { background: rgba(255,255,255,0.1); padding: 0.1em 0.3em; border-radius: 3px; font-size: 0.875em; }
 
148
  .prose-user strong { font-weight: 500; }
149
 
150
  textarea { resize: none; overflow: hidden; }
 
154
  .dot-2 { animation: blink 1.2s 0.2s infinite; }
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>