LogitCode commited on
Commit
9dbfa4b
Β·
verified Β·
1 Parent(s): 5b7452c

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +170 -35
index.html CHANGED
@@ -130,31 +130,52 @@ tailwind.config = {
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 +185,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 +371,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 +690,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 +722,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 +755,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 +783,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 +798,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 +811,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 +835,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 +881,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 +929,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 +977,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 +1163,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>
 
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
+ body:not(.light) {
139
+ background-image:
140
+ radial-gradient(1px 1px at 20% 30%, rgba(160,200,255,0.4) 0%, transparent 100%),
141
+ radial-gradient(1px 1px at 75% 15%, rgba(160,200,255,0.3) 0%, transparent 100%),
142
+ radial-gradient(1px 1px at 55% 70%, rgba(160,200,255,0.25) 0%, transparent 100%),
143
+ radial-gradient(1px 1px at 90% 55%, rgba(160,200,255,0.2) 0%, transparent 100%),
144
+ radial-gradient(1px 1px at 10% 85%, rgba(160,200,255,0.2) 0%, transparent 100%);
145
+ }
146
+
147
+ body:not(.light) #sidebar::before {
148
+ content: '';
149
+ position: absolute;
150
+ top: 0; left: 0; right: 0;
151
+ height: 1px;
152
+ background: linear-gradient(90deg, transparent, var(--accent), transparent);
153
+ opacity: 0.5;
154
+ }
155
+ /*#sidebar { position: relative; }*/
156
+
157
+ .font-display { font-family: 'Syne', sans-serif !important; }
158
+
159
+ .prose-msg { line-height: 1.7; color: var(--prose); }
160
  .prose-msg p { margin: 0 0 0.6em; } .prose-msg p:last-child { margin-bottom: 0; }
161
+ .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); }
162
+ .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; }
163
+ .prose-msg pre code { background: transparent; border: none; padding: 0; color: var(--prose); font-size: 0.82em; }
164
+ .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; }
165
  .prose-msg h1 { font-size: 1.25em; } .prose-msg h2 { font-size: 1.1em; } .prose-msg h3 { font-size: 1em; }
166
  .prose-msg ul, .prose-msg ol { padding-left: 1.25em; margin: 0.4em 0 0.6em; }
167
+ .prose-msg li::marker { color: var(--accent); }
168
  .prose-msg li { margin: 0.2em 0; }
169
  .prose-msg blockquote { border-left: 2px solid var(--accent); padding-left: 0.75em; color: var(--ghost); margin: 0.5em 0; font-style: italic; }
170
  .prose-msg a { color: var(--accent); text-decoration: underline; }
171
  .prose-msg strong { color: var(--cream); font-weight: 500; }
172
  .prose-msg table { border-collapse: collapse; width: 100%; margin: 0.5em 0; font-size: 0.85em; }
173
+ .prose-msg th { background: var(--mist); color: var(--cream); padding: 5px 8px; text-align: left; border-bottom: 1px solid var(--accent); }
174
  .prose-msg td { border-top: 1px solid var(--border); padding: 5px 8px; }
175
 
176
  .prose-user { line-height: 1.65; color: var(--user-text); }
177
  .prose-user p { margin: 0 0 0.5em; } .prose-user p:last-child { margin-bottom: 0; }
178
+ .prose-user code { background: rgba(255,255,255,0.1); padding: 0.1em 0.3em; border-radius: 3px; font-size: 0.875em; }
 
179
  .prose-user strong { font-weight: 500; }
180
 
181
  textarea { resize: none; overflow: hidden; }
 
185
  .dot-2 { animation: blink 1.2s 0.2s infinite; }
186
  .dot-3 { animation: blink 1.2s 0.4s infinite; }
187
  select { -webkit-appearance: none; appearance: none; }
188
+ 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); }
189
+
190
+ /* Tool panel styles */
191
+ #tool-panel, #file-explorer-panel { transition: all 0.3s ease; }
192
+ .tool-item { transition: all 0.2s ease; }
193
+ .tool-item:hover { transform: translateX(4px); }
194
+ .tool-item.active { background: var(--accent); color: var(--ink); }
195
+
196
+ /* File Explorer Styles */
197
+ .file-tree-item { transition: all 0.15s ease; }
198
+ .file-tree-item:hover { background: var(--mist); }
199
+ .file-tree-item.active { background: var(--accent) !important; color: var(--ink); }
200
+ .file-tree-children { border-left: 1px solid var(--border); margin-left: 11px; padding-left: 8px; }
201
+ .file-explorer-editor { animation: slideIn 0.25s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
202
+
203
+ /* CRITICAL MOBILE FIX - Main container */
204
+ @media (max-width: 768px) {
205
+ /* Override flex behavior on mobile */
206
+ main {
207
+ flex: none !important;
208
+ width: 100% !important;
209
+ max-width: 100vw !important;
210
+ margin-left: 0 !important;
211
+ position: relative !important;
212
+ }
213
+
214
+
215
+ /* Overlay */
216
+ #overlay {
217
+ position: fixed !important;
218
+ inset: 0 !important;
219
+ background: rgba(0,0,0,0.6) !important;
220
+ display: none !important;
221
+ }
222
+
223
+ #overlay:not(.hidden) {
224
+ display: block !important;
225
+ }
226
+
227
+ /* Fix message container */
228
+ #messages {
229
+ padding-left: 16px !important;
230
+ padding-right: 16px !important;
231
+ }
232
+
233
+ /* Fix input footer */
234
+ #input-footer {
235
+ padding-left: 16px !important;
236
+ padding-right: 16px !important;
237
+ padding-bottom: 5vh;
238
+
239
+ }
240
+
241
+ /* Remove any negative margins */
242
+ .flex-1 {
243
+ min-width: 0 !important;
244
+ }
245
+
246
+ /* File explorer panel on mobile */
247
+ #file-explorer-panel {
248
+ width: 100% !important;
249
+ max-width: 100vw !important;
250
+ }
251
+ }
252
+
253
+ /* Extra small screens */
254
+ @media (max-width: 480px) {
255
+ main {
256
+ width: 100vw !important;
257
+ }
258
+
259
+ #messages, #input-footer {
260
+ padding-left: 12px !important;
261
+ padding-right: 12px !important;
262
+ }
263
+ #input-footer {
264
+ padding-bottom: 5vh;
265
+
266
+ }
267
+ }
268
+
269
+ /* iOS Safari safe area */
270
+ @supports (-webkit-touch-callout: none) {
271
+ body {
272
+ padding-bottom: env(safe-area-inset-bottom);
273
+ }
274
+ #input-footer {
275
+ padding-bottom: 5vh;
276
+ }
277
+ }
278
+
279
+ /* Smooth scrolling on mobile */
280
+ #messages {
281
+ -webkit-overflow-scrolling: touch;
282
+ overscroll-behavior: contain;
283
+ }
284
+
285
+ #tool-result-banner, #error-banner {
286
+ margin-left: 12px;
287
+ margin-right: 12px;
288
+ font-size: 11px;
289
+ }
290
+
291
  </style>
292
  </head>
293
  <body class="h-screen flex overflow-hidden">
 
371
  <div class="relative">
372
  <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()">
373
  <optgroup label='Openrouter'>
374
+ <option value='openrouter/free'>Free Models Router </option>
375
  <option value='openrouter/hunter-alpha'>Hunter Alpha </option>
376
  <option value='openrouter/healer-alpha'>Healer Alpha </option>
 
377
  </optgroup>
378
  <optgroup label='Nvidia'>
379
  <option value='nvidia/nemotron-3-super-120b-a12b:free'>NVIDIA: Nemotron 3 Super (free) </option>
 
690
 
691
  // ── Init ───────────────────────────────────────────────────────────────────
692
  function init() {
693
+ if (localStorage.getItem('flexchat_privacy_seen')) dismissPrivacyNotice();
694
 
695
+ if (localStorage.getItem('flexchat_theme') === 'light') toggleTheme(); // ← add this
696
 
697
  loadSettings();
698
  loadChats();
699
  updateModelBadge();
 
700
  }
701
 
702
  function dismissPrivacyNotice() {
703
  document.getElementById('privacy-modal').remove();
704
+ localStorage.setItem('flexchat_privacy_seen', '1');
705
  }
706
 
707
  function toggleTheme() {
708
  const isLight = document.body.classList.toggle('light');
709
  document.getElementById('theme-icon-dark').classList.toggle('hidden', isLight);
710
  document.getElementById('theme-icon-light').classList.toggle('hidden', !isLight);
711
+ localStorage.setItem('flexchat_theme', isLight ? 'light' : 'dark');
712
  document.getElementById('hljs-theme').href = isLight
713
  ? 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css'
714
  : 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css';
 
722
  temperature: document.getElementById('temperature').value,
723
  maxTokens: document.getElementById('max-tokens').value,
724
  };
725
+ localStorage.setItem('flexchat_settings', JSON.stringify(settings));
726
  updateModelBadge();
727
  }
728
 
729
  function loadSettings() {
730
  try {
731
+ const s = JSON.parse(localStorage.getItem('flexchat_settings') || '{}');
732
  if (s.apiKey) document.getElementById('api-key').value = s.apiKey;
733
  if (s.model) document.getElementById('model-select').value = s.model;
734
  if (s.systemPrompt) document.getElementById('system-prompt').value = s.systemPrompt;
 
755
  function genId() { return Date.now().toString(36) + Math.random().toString(36).slice(2, 6); }
756
 
757
  function saveChats() {
758
+ localStorage.setItem('flexchat_chats', JSON.stringify(chats));
759
  }
760
 
761
  function loadChats() {
762
  try {
763
+ chats = JSON.parse(localStorage.getItem('flexchat_chats') || '{}');
764
  } catch(e) { chats = {}; }
765
  renderConvList();
766
  }
 
783
  renderMessages();
784
  renderConvList();
785
  const chat = chats[id];
786
+ if (chat) {
787
+ document.getElementById('chat-title').textContent = chat.title;
788
+ }
789
  closeSidebar();
790
  }
791
 
 
798
  }
799
 
800
  function clearCurrentChat() {
801
+ if (!currentChatId) return;
802
+ const chat = chats[currentChatId];
803
+ if (!chat) return;
804
+ chat.messages = [];
805
+ chat.title = 'New conversation';
806
  document.getElementById('chat-title').textContent = 'New conversation';
807
  saveChats();
808
  renderConvList();
 
811
 
812
  function renderConvList() {
813
  const list = document.getElementById('conv-list');
814
+ if (!list) return;
815
+
816
  const sorted = Object.values(chats).sort((a,b) => b.createdAt - a.createdAt);
817
  if (sorted.length === 0) {
818
  list.innerHTML = '<div class="text-fog/60 text-xs font-mono px-3 py-2">No conversations yet</div>';
 
835
 
836
  function renderMessages() {
837
  const container = document.getElementById('messages');
838
+ const chat = chats[currentChatId];
839
+ const msgs = chat ? chat.messages : [];
840
 
841
+ if (!chat || msgs.length === 0) {
842
  container.innerHTML = `<div id="empty-state" class="h-full flex flex-col items-center justify-center text-center py-16">
843
  <div class="font-display text-5xl italic text-cream/20 mb-4 select-none">FlexChat</div>
844
  <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>
 
881
  } else {
882
  return `<div class="flex gap-3 items-start">
883
  <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">
884
+ <span class="text-ink text-xs font-display font-bold italic">Bot</span>
885
  </div>
886
  <div class="flex-1 min-w-0 group">
887
  <div class="text-xs font-mono text-ghost mb-1.5">${escHtml(msg.model || 'assistant')}</div>
 
929
  const input = document.getElementById('user-input');
930
  const text = input.value.trim();
931
  if (!text || isStreaming) return;
932
+
933
+ if (!currentChatId || !chats[currentChatId]) {
934
+ newChat();
935
+ }
936
  const apiKey = document.getElementById('api-key').value.trim();
937
  if (!apiKey) { showError('Please enter your OpenRouter API key in the sidebar.'); return; }
938
 
 
977
  placeholder.className = 'flex gap-3 items-start';
978
  placeholder.innerHTML = `
979
  <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">
980
+ <span class="text-ink text-xs font-display font-bold italic">Bot</span>
981
  </div>
982
  <div class="flex-1 min-w-0">
983
  <div class="text-xs font-mono text-ghost mb-1.5">${escHtml(model.split('/').pop())}</div>
 
1163
  function closeSidebar() {
1164
  const s = document.getElementById('sidebar');
1165
  const o = document.getElementById('overlay');
1166
+
1167
+ s.removeAttribute('data-open');
1168
  s.classList.add('-translate-x-full');
1169
+
1170
+ o.classList.add('hidden');
1171
+ setTimeout(() => {
1172
+ o.style.display = 'none';
1173
+ }, 300);
1174
  }
1175
 
1176
  // ── Boot ───────────────────────────────────────────────────────────────────
1177
  init();
1178
  </script>
1179
  </body>
1180
+ </html>