Pepguy commited on
Commit
04956a8
·
verified ·
1 Parent(s): bb18161

Update public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +130 -50
public/index.html CHANGED
@@ -2,7 +2,7 @@
2
  <html lang="en" class="dark">
3
  <head>
4
  <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>AI Developer Hub</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
@@ -16,54 +16,96 @@
16
  }
17
  </script>
18
  <style>
19
- .markdown-body pre { background: #1e1e1e; padding: 1rem; border-radius: 0.5rem; position: relative; }
20
- .copy-btn { position: absolute; top: 0.5rem; right: 0.5rem; background: #374151; color: white; border: none; padding: 0.25rem 0.5rem; border-radius: 0.25rem; cursor: pointer; font-size: 0.75rem; }
21
- .copy-btn:hover { background: #4b5563; }
22
- .reasoning-block { border-left: 3px solid #6366f1; padding-left: 1rem; margin-bottom: 1rem; color: #9ca3af; font-size: 0.9em; background: rgba(99, 102, 241, 0.05); padding: 0.5rem; border-radius: 0 0.5rem 0.5rem 0; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  </style>
24
  </head>
25
- <body class="bg-gray-950 text-gray-200 h-screen flex font-sans overflow-hidden">
 
 
 
26
 
27
  <!-- Sidebar -->
28
- <aside class="w-64 bg-gray-900 border-r border-gray-800 flex flex-col">
29
- <div class="p-4 border-b border-gray-800 flex justify-between items-center">
30
- <h1 class="font-bold text-lg text-white">Dev AI Hub</h1>
31
- <button onclick="createNewChat()" class="text-blue-400 hover:text-blue-300">+</button>
 
 
 
 
 
 
 
 
 
 
32
  </div>
33
- <div id="chat-list" class="flex-1 overflow-y-auto p-2 space-y-1"></div>
34
  </aside>
35
 
36
  <!-- Main Chat Area -->
37
- <main class="flex-1 flex flex-col h-full bg-gray-950">
38
  <!-- Header -->
39
- <header class="h-14 border-b border-gray-800 flex items-center px-6 justify-between bg-gray-900/50">
40
- <h2 id="current-chat-title" class="font-semibold text-gray-100">Select or create a chat</h2>
41
- <div class="flex items-center space-x-4">
42
- <span class="text-xs text-gray-500 bg-gray-800 px-2 py-1 rounded">Tokens: <span id="token-counter">0</span></span>
43
- <select id="model-select" class="bg-gray-800 text-sm rounded border border-gray-700 px-2 py-1 outline-none focus:border-blue-500">
44
- <option value="claude">Claude Sonnet 3.5</option>
 
 
 
 
 
 
 
45
  <option value="haiku">Claude Haiku</option>
46
  <option value="maverick">Llama 3 Maverick</option>
47
  </select>
48
- <button onclick="deleteCurrentChat()" class="text-red-500 text-sm hover:text-red-400">Delete Chat</button>
 
 
49
  </div>
50
  </header>
51
 
52
  <!-- Messages -->
53
- <div id="chat-window" class="flex-1 overflow-y-auto p-6 space-y-6"></div>
 
 
54
 
55
  <!-- Input Area -->
56
- <div class="p-4 bg-gray-900 border-t border-gray-800">
57
- <div id="image-preview-container" class="flex gap-2 mb-2 hidden"></div>
58
- <div class="flex items-end gap-2 bg-gray-800 rounded-xl border border-gray-700 p-2 focus-within:border-blue-500 transition-colors">
59
- <label class="cursor-pointer p-2 text-gray-400 hover:text-white transition">
60
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>
61
- <input type="file" id="image-input" accept="image/*" multiple class="hidden" onchange="handleImageSelect(event)">
62
- </label>
63
- <textarea id="message-input" rows="1" class="flex-1 bg-transparent resize-none outline-none text-sm p-2 max-h-32" placeholder="Ask anything..."></textarea>
64
- <button onclick="sendMessage()" id="send-btn" class="p-2 text-blue-400 hover:text-blue-300 disabled:opacity-50 transition">
65
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"></path></svg>
66
- </button>
 
 
 
67
  </div>
68
  </div>
69
  </main>
@@ -81,13 +123,28 @@
81
  let attachedImages =[];
82
  let pollingInterval = null;
83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  async function loadSidebar() {
85
  const res = await fetch('/api/chats');
86
  const chats = await res.json();
87
  const list = document.getElementById('chat-list');
88
  list.innerHTML = chats.map(c => `
89
- <div onclick="selectChat('${c.id}')" class="p-3 rounded-lg cursor-pointer hover:bg-gray-800 transition ${c.id === currentChatId ? 'bg-gray-800' : ''}">
90
- <div class="text-sm font-medium truncate">${c.title}</div>
91
  <div class="text-xs text-gray-500 mt-1">${c.totalTokens.toLocaleString()} tokens</div>
92
  </div>
93
  `).join('');
@@ -98,6 +155,7 @@
98
  const chat = await res.json();
99
  selectChat(chat.id);
100
  loadSidebar();
 
101
  }
102
 
103
  async function deleteCurrentChat() {
@@ -122,12 +180,13 @@
122
  document.getElementById('token-counter').innerText = chat.totalTokens.toLocaleString();
123
 
124
  renderMessages(chat.messages);
125
- loadSidebar(); // Updates highlighting
126
 
127
- // If a reload happens during generation, poll until done.
128
- if (chat.isGenerating) {
129
- pollGeneratingChat(id);
130
  }
 
 
131
  }
132
 
133
  function renderMessages(messages) {
@@ -135,29 +194,31 @@
135
  container.innerHTML = messages.map(m => {
136
  let html = '';
137
  if (m.reasoning) {
138
- html += `<div class="reasoning-block"><i>Thinking...</i><br/>${marked.parse(m.reasoning)}</div>`;
139
  }
140
  if (m.content) {
141
  html += DOMPurify.sanitize(marked.parse(m.content));
142
  }
143
 
144
  return `
145
- <div class="flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}">
146
- <div class="max-w-[80%] rounded-xl p-4 ${m.role === 'user' ? 'bg-blue-600 text-white' : 'bg-gray-850 border border-gray-800 markdown-body'}">
147
- ${m.role === 'user' ? m.content : html}
148
  </div>
149
  </div>
150
  `;
151
  }).join('');
152
 
153
- // Add copy buttons to code blocks
154
  document.querySelectorAll('.markdown-body pre').forEach(pre => {
155
  if (!pre.querySelector('.copy-btn')) {
156
  const btn = document.createElement('button');
157
  btn.className = 'copy-btn';
158
  btn.innerText = 'Copy';
159
  btn.onclick = () => {
160
- navigator.clipboard.writeText(pre.innerText.replace('Copy', '').trim());
 
 
161
  btn.innerText = 'Copied!';
162
  setTimeout(() => btn.innerText = 'Copy', 2000);
163
  };
@@ -186,7 +247,10 @@
186
  const reader = new FileReader();
187
  reader.onload = (e) => {
188
  attachedImages.push(e.target.result);
189
- container.innerHTML += `<img src="${e.target.result}" class="h-12 w-12 object-cover rounded border border-gray-600">`;
 
 
 
190
  };
191
  reader.readAsDataURL(file);
192
  });
@@ -205,12 +269,23 @@
205
 
206
  const container = document.getElementById('chat-window');
207
  container.innerHTML += `
208
- <div class="flex justify-end mb-6"><div class="max-w-[80%] rounded-xl p-4 bg-blue-600 text-white">${text}</div></div>
209
- <div class="flex justify-start mb-6" id="temp-ai-wrapper"><div class="max-w-[80%] rounded-xl p-4 bg-gray-850 border border-gray-800 markdown-body" id="temp-ai-msg"><span class="animate-pulse">Thinking...</span></div></div>
 
 
 
 
 
 
 
 
 
210
  `;
211
  container.scrollTop = container.scrollHeight;
212
 
 
213
  input.value = '';
 
214
  attachedImages =[];
215
  document.getElementById('image-preview-container').innerHTML = '';
216
  document.getElementById('image-preview-container').classList.add('hidden');
@@ -252,10 +327,10 @@
252
  }
253
 
254
  let tempHtml = "";
255
- if (aiReasoning) tempHtml += `<div class="reasoning-block"><i>Thinking...</i><br/>${marked.parse(aiReasoning)}</div>`;
256
  if (aiContent) tempHtml += DOMPurify.sanitize(marked.parse(aiContent));
257
 
258
- document.getElementById('temp-ai-msg').innerHTML = tempHtml || "<span class='animate-pulse'>Thinking...</span>";
259
  container.scrollTop = container.scrollHeight;
260
  }
261
 
@@ -263,14 +338,19 @@
263
 
264
  } catch (error) {
265
  console.error(error);
266
- document.getElementById('temp-ai-msg').innerHTML = "<span class='text-red-500'>Error connecting to API.</span>";
267
  } finally {
268
  document.getElementById('send-btn').disabled = false;
269
  }
270
  }
271
 
272
  document.getElementById('message-input').addEventListener('keydown', (e) => {
273
- if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); }
 
 
 
 
 
274
  });
275
 
276
  loadSidebar();
 
2
  <html lang="en" class="dark">
3
  <head>
4
  <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
  <title>AI Developer Hub</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
 
16
  }
17
  </script>
18
  <style>
19
+ /* Custom Scrollbar for better look */
20
+ ::-webkit-scrollbar { width: 6px; height: 6px; }
21
+ ::-webkit-scrollbar-track { background: transparent; }
22
+ ::-webkit-scrollbar-thumb { background: #374151; border-radius: 4px; }
23
+ ::-webkit-scrollbar-thumb:hover { background: #4b5563; }
24
+
25
+ /* Markdown Styles & Overrides */
26
+ .markdown-body { font-size: 0.95rem; line-height: 1.6; }
27
+ .markdown-body p { margin-bottom: 1rem; }
28
+ .markdown-body p:last-child { margin-bottom: 0; }
29
+ .markdown-body code:not(pre code) { background: #374151; padding: 0.2rem 0.4rem; border-radius: 0.25rem; font-size: 0.85em; }
30
+ .markdown-body pre { background: #1e1e1e; padding: 2.5rem 1rem 1rem 1rem; border-radius: 0.5rem; position: relative; overflow-x: auto; margin-bottom: 1rem; }
31
+ .markdown-body pre code { font-size: 0.85rem; }
32
+
33
+ /* Copy Button inside Code Blocks */
34
+ .copy-btn { position: absolute; top: 0.5rem; right: 0.5rem; background: #4b5563; color: #e5e7eb; border: none; padding: 0.25rem 0.75rem; border-radius: 0.375rem; cursor: pointer; font-size: 0.75rem; transition: background 0.2s; }
35
+ .copy-btn:hover { background: #6b7280; }
36
+
37
+ /* Reasoning Block */
38
+ .reasoning-block { border-left: 3px solid #6366f1; padding-left: 1rem; margin-bottom: 1rem; color: #9ca3af; font-size: 0.9em; background: rgba(99, 102, 241, 0.05); padding: 0.75rem; border-radius: 0 0.5rem 0.5rem 0; }
39
  </style>
40
  </head>
41
+ <body class="bg-gray-950 text-gray-200 h-[100dvh] flex font-sans overflow-hidden antialiased">
42
+
43
+ <!-- Mobile Overlay -->
44
+ <div id="mobile-overlay" class="fixed inset-0 bg-black/60 z-20 hidden lg:hidden transition-opacity" onclick="toggleSidebar()"></div>
45
 
46
  <!-- Sidebar -->
47
+ <aside id="sidebar" class="absolute lg:relative z-30 inset-y-0 left-0 w-72 lg:w-64 bg-gray-900 border-r border-gray-800 transform -translate-x-full lg:translate-x-0 transition-transform duration-300 ease-in-out flex flex-col shadow-2xl lg:shadow-none">
48
+ <div class="p-4 border-b border-gray-800 flex justify-between items-center bg-gray-900">
49
+ <h1 class="font-bold text-lg text-white tracking-wide">Dev AI Hub</h1>
50
+ <div class="flex items-center gap-2">
51
+ <button onclick="createNewChat()" class="p-2 text-blue-400 hover:text-blue-300 bg-gray-800 rounded-md transition" title="New Chat">
52
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path></svg>
53
+ </button>
54
+ <button onclick="toggleSidebar()" class="p-2 text-gray-400 hover:text-white lg:hidden">
55
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
56
+ </button>
57
+ </div>
58
+ </div>
59
+ <div id="chat-list" class="flex-1 overflow-y-auto p-3 space-y-2">
60
+ <!-- Chats loaded here -->
61
  </div>
 
62
  </aside>
63
 
64
  <!-- Main Chat Area -->
65
+ <main class="flex-1 flex flex-col h-full w-full bg-gray-950 min-w-0">
66
  <!-- Header -->
67
+ <header class="h-14 min-h-[3.5rem] border-b border-gray-800 flex items-center px-4 lg:px-6 justify-between bg-gray-900/80 backdrop-blur-sm z-10">
68
+ <div class="flex items-center gap-3 overflow-hidden">
69
+ <button onclick="toggleSidebar()" class="lg:hidden p-1 -ml-1 text-gray-400 hover:text-white">
70
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path></svg>
71
+ </button>
72
+ <h2 id="current-chat-title" class="font-semibold text-gray-100 truncate text-sm lg:text-base">Select or create a chat</h2>
73
+ </div>
74
+ <div class="flex items-center gap-2 lg:gap-4 shrink-0">
75
+ <span class="hidden sm:inline-block text-xs text-blue-300 bg-blue-900/30 border border-blue-800/50 px-2 py-1 rounded-md font-mono">
76
+ Tokens: <span id="token-counter">0</span>
77
+ </span>
78
+ <select id="model-select" class="bg-gray-800 text-xs lg:text-sm rounded-md border border-gray-700 px-2 py-1.5 outline-none focus:border-blue-500 transition-colors cursor-pointer text-gray-200">
79
+ <option value="claude">Claude Sonnet 4.6</option>
80
  <option value="haiku">Claude Haiku</option>
81
  <option value="maverick">Llama 3 Maverick</option>
82
  </select>
83
+ <button onclick="deleteCurrentChat()" class="text-gray-500 hover:text-red-400 transition-colors p-1" title="Delete Chat">
84
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>
85
+ </button>
86
  </div>
87
  </header>
88
 
89
  <!-- Messages -->
90
+ <div id="chat-window" class="flex-1 overflow-y-auto p-4 lg:p-6 space-y-6 scroll-smooth">
91
+ <!-- Messages populated here -->
92
+ </div>
93
 
94
  <!-- Input Area -->
95
+ <div class="p-3 lg:p-4 bg-gray-900 border-t border-gray-800 shrink-0">
96
+ <div class="max-w-4xl mx-auto">
97
+ <div id="image-preview-container" class="flex flex-wrap gap-2 mb-2 hidden px-1"></div>
98
+ <div class="flex items-end gap-2 bg-gray-850 rounded-xl border border-gray-700 p-2 focus-within:border-blue-500 focus-within:ring-1 focus-within:ring-blue-500 transition-all shadow-sm">
99
+ <label class="cursor-pointer p-2 text-gray-400 hover:text-blue-400 transition shrink-0 rounded-lg hover:bg-gray-800">
100
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>
101
+ <input type="file" id="image-input" accept="image/*" multiple class="hidden" onchange="handleImageSelect(event)">
102
+ </label>
103
+ <textarea id="message-input" rows="1" class="flex-1 bg-transparent resize-none outline-none text-[15px] p-2 max-h-32 min-h-[40px] text-gray-100 placeholder-gray-500" placeholder="Message the AI..."></textarea>
104
+ <button onclick="sendMessage()" id="send-btn" class="p-2 text-blue-400 hover:text-blue-300 disabled:opacity-50 disabled:hover:text-blue-400 transition shrink-0 rounded-lg hover:bg-gray-800">
105
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"></path></svg>
106
+ </button>
107
+ </div>
108
+ <div class="text-center mt-2 text-[10px] text-gray-600">Shift + Enter for new line</div>
109
  </div>
110
  </div>
111
  </main>
 
123
  let attachedImages =[];
124
  let pollingInterval = null;
125
 
126
+ // Mobile Sidebar Toggle
127
+ function toggleSidebar() {
128
+ const sidebar = document.getElementById('sidebar');
129
+ const overlay = document.getElementById('mobile-overlay');
130
+ sidebar.classList.toggle('-translate-x-full');
131
+ overlay.classList.toggle('hidden');
132
+ }
133
+
134
+ // Auto-resize Textarea
135
+ const textarea = document.getElementById('message-input');
136
+ textarea.addEventListener('input', function() {
137
+ this.style.height = 'auto';
138
+ this.style.height = (this.scrollHeight < 128 ? this.scrollHeight : 128) + 'px';
139
+ });
140
+
141
  async function loadSidebar() {
142
  const res = await fetch('/api/chats');
143
  const chats = await res.json();
144
  const list = document.getElementById('chat-list');
145
  list.innerHTML = chats.map(c => `
146
+ <div onclick="selectChat('${c.id}')" class="p-3 rounded-xl cursor-pointer transition border border-transparent ${c.id === currentChatId ? 'bg-gray-800 border-gray-700' : 'hover:bg-gray-800/50'}">
147
+ <div class="text-sm font-medium truncate text-gray-200">${c.title}</div>
148
  <div class="text-xs text-gray-500 mt-1">${c.totalTokens.toLocaleString()} tokens</div>
149
  </div>
150
  `).join('');
 
155
  const chat = await res.json();
156
  selectChat(chat.id);
157
  loadSidebar();
158
+ if(window.innerWidth < 1024) toggleSidebar(); // Close sidebar on mobile
159
  }
160
 
161
  async function deleteCurrentChat() {
 
180
  document.getElementById('token-counter').innerText = chat.totalTokens.toLocaleString();
181
 
182
  renderMessages(chat.messages);
183
+ loadSidebar();
184
 
185
+ if(window.innerWidth < 1024 && !document.getElementById('sidebar').classList.contains('-translate-x-full')) {
186
+ toggleSidebar(); // Close sidebar on mobile selection
 
187
  }
188
+
189
+ if (chat.isGenerating) pollGeneratingChat(id);
190
  }
191
 
192
  function renderMessages(messages) {
 
194
  container.innerHTML = messages.map(m => {
195
  let html = '';
196
  if (m.reasoning) {
197
+ html += `<div class="reasoning-block"><i>Thinking Process</i><br/>${marked.parse(m.reasoning)}</div>`;
198
  }
199
  if (m.content) {
200
  html += DOMPurify.sanitize(marked.parse(m.content));
201
  }
202
 
203
  return `
204
+ <div class="flex ${m.role === 'user' ? 'justify-end' : 'justify-start'} w-full">
205
+ <div class="max-w-[95%] lg:max-w-[85%] rounded-2xl p-4 shadow-sm ${m.role === 'user' ? 'bg-blue-600 text-white rounded-br-sm' : 'bg-gray-850 border border-gray-800 text-gray-200 markdown-body rounded-bl-sm'}">
206
+ ${m.role === 'user' ? `<div class="whitespace-pre-wrap">${m.content}</div>` : html}
207
  </div>
208
  </div>
209
  `;
210
  }).join('');
211
 
212
+ // Add copy buttons to code blocks dynamically
213
  document.querySelectorAll('.markdown-body pre').forEach(pre => {
214
  if (!pre.querySelector('.copy-btn')) {
215
  const btn = document.createElement('button');
216
  btn.className = 'copy-btn';
217
  btn.innerText = 'Copy';
218
  btn.onclick = () => {
219
+ // Extract text excluding the button itself
220
+ const codeText = pre.querySelector('code')?.innerText || pre.innerText.replace('Copy', '');
221
+ navigator.clipboard.writeText(codeText.trim());
222
  btn.innerText = 'Copied!';
223
  setTimeout(() => btn.innerText = 'Copy', 2000);
224
  };
 
247
  const reader = new FileReader();
248
  reader.onload = (e) => {
249
  attachedImages.push(e.target.result);
250
+ container.innerHTML += `
251
+ <div class="relative group">
252
+ <img src="${e.target.result}" class="h-14 w-14 object-cover rounded-lg border border-gray-600 shadow-sm">
253
+ </div>`;
254
  };
255
  reader.readAsDataURL(file);
256
  });
 
269
 
270
  const container = document.getElementById('chat-window');
271
  container.innerHTML += `
272
+ <div class="flex justify-end w-full">
273
+ <div class="max-w-[95%] lg:max-w-[85%] rounded-2xl rounded-br-sm p-4 bg-blue-600 text-white shadow-sm whitespace-pre-wrap">${text}</div>
274
+ </div>
275
+ <div class="flex justify-start w-full" id="temp-ai-wrapper">
276
+ <div class="max-w-[95%] lg:max-w-[85%] rounded-2xl rounded-bl-sm p-4 bg-gray-850 border border-gray-800 text-gray-200 markdown-body shadow-sm" id="temp-ai-msg">
277
+ <span class="flex items-center gap-2 text-gray-400">
278
+ <svg class="animate-spin h-4 w-4" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
279
+ Thinking...
280
+ </span>
281
+ </div>
282
+ </div>
283
  `;
284
  container.scrollTop = container.scrollHeight;
285
 
286
+ // Reset Input Area
287
  input.value = '';
288
+ input.style.height = 'auto'; // Reset height
289
  attachedImages =[];
290
  document.getElementById('image-preview-container').innerHTML = '';
291
  document.getElementById('image-preview-container').classList.add('hidden');
 
327
  }
328
 
329
  let tempHtml = "";
330
+ if (aiReasoning) tempHtml += `<div class="reasoning-block"><i>Thinking Process</i><br/>${marked.parse(aiReasoning)}</div>`;
331
  if (aiContent) tempHtml += DOMPurify.sanitize(marked.parse(aiContent));
332
 
333
+ document.getElementById('temp-ai-msg').innerHTML = tempHtml || "<span class='animate-pulse text-gray-400'>Thinking...</span>";
334
  container.scrollTop = container.scrollHeight;
335
  }
336
 
 
338
 
339
  } catch (error) {
340
  console.error(error);
341
+ document.getElementById('temp-ai-msg').innerHTML = "<span class='text-red-400'>Error connecting to API.</span>";
342
  } finally {
343
  document.getElementById('send-btn').disabled = false;
344
  }
345
  }
346
 
347
  document.getElementById('message-input').addEventListener('keydown', (e) => {
348
+ if (e.key === 'Enter' && !e.shiftKey) {
349
+ // Don't send if on mobile to allow enter for newlines natively, unless specifically required.
350
+ // For a dev tool, shift+enter is usually fine.
351
+ e.preventDefault();
352
+ sendMessage();
353
+ }
354
  });
355
 
356
  loadSidebar();