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

Update public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +80 -26
public/index.html CHANGED
@@ -16,13 +16,11 @@
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; }
@@ -30,11 +28,9 @@
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>
@@ -56,22 +52,40 @@
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>
@@ -87,9 +101,7 @@
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">
@@ -111,7 +123,6 @@
111
  </main>
112
 
113
  <script>
114
- // Configure Marked with highlight.js for styling block code
115
  marked.setOptions({
116
  highlight: function(code, lang) {
117
  const language = hljs.getLanguage(lang) ? lang : 'plaintext';
@@ -123,7 +134,50 @@
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');
@@ -131,7 +185,6 @@
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';
@@ -155,7 +208,7 @@
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() {
@@ -165,6 +218,7 @@
165
  currentChatId = null;
166
  document.getElementById('chat-window').innerHTML = '';
167
  document.getElementById('current-chat-title').innerText = 'Select or create a chat';
 
168
  document.getElementById('token-counter').innerText = '0';
169
  loadSidebar();
170
  }
@@ -177,15 +231,16 @@
177
  const chat = await res.json();
178
 
179
  document.getElementById('current-chat-title').innerText = chat.title;
 
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
 
@@ -209,14 +264,12 @@
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!';
@@ -234,6 +287,10 @@
234
  const chat = await res.json();
235
  renderMessages(chat.messages);
236
  document.getElementById('token-counter').innerText = chat.totalTokens.toLocaleString();
 
 
 
 
237
  if (!chat.isGenerating) clearInterval(pollingInterval);
238
  }, 1500);
239
  }
@@ -283,9 +340,8 @@
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');
@@ -346,8 +402,6 @@
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
  }
 
16
  }
17
  </script>
18
  <style>
 
19
  ::-webkit-scrollbar { width: 6px; height: 6px; }
20
  ::-webkit-scrollbar-track { background: transparent; }
21
  ::-webkit-scrollbar-thumb { background: #374151; border-radius: 4px; }
22
  ::-webkit-scrollbar-thumb:hover { background: #4b5563; }
23
 
 
24
  .markdown-body { font-size: 0.95rem; line-height: 1.6; }
25
  .markdown-body p { margin-bottom: 1rem; }
26
  .markdown-body p:last-child { margin-bottom: 0; }
 
28
  .markdown-body pre { background: #1e1e1e; padding: 2.5rem 1rem 1rem 1rem; border-radius: 0.5rem; position: relative; overflow-x: auto; margin-bottom: 1rem; }
29
  .markdown-body pre code { font-size: 0.85rem; }
30
 
 
31
  .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; }
32
  .copy-btn:hover { background: #6b7280; }
33
 
 
34
  .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; }
35
  </style>
36
  </head>
 
52
  </button>
53
  </div>
54
  </div>
55
+ <div id="chat-list" class="flex-1 overflow-y-auto p-3 space-y-2"></div>
 
 
56
  </aside>
57
 
58
  <!-- Main Chat Area -->
59
  <main class="flex-1 flex flex-col h-full w-full bg-gray-950 min-w-0">
60
  <!-- Header -->
61
  <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">
62
+
63
+ <div class="flex items-center gap-3 overflow-hidden group w-full max-w-[50%]">
64
+ <button onclick="toggleSidebar()" class="lg:hidden p-1 -ml-1 text-gray-400 hover:text-white shrink-0">
65
  <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>
66
  </button>
67
+
68
+ <!-- Title Display Mode -->
69
+ <div id="title-display" class="flex items-center gap-2 overflow-hidden w-full">
70
+ <h2 id="current-chat-title" class="font-semibold text-gray-100 truncate text-sm lg:text-base">Select or create a chat</h2>
71
+ <button onclick="enableTitleEdit()" id="edit-title-btn" class="hidden text-gray-500 hover:text-blue-400 p-1 opacity-0 group-hover:opacity-100 transition-opacity shrink-0" title="Rename Chat">
72
+ <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="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"></path></svg>
73
+ </button>
74
+ </div>
75
+
76
+ <!-- Title Edit Mode -->
77
+ <div id="title-edit" class="hidden items-center gap-2 w-full">
78
+ <input type="text" id="title-input" class="bg-gray-800 text-sm lg:text-base text-gray-100 border border-blue-500 rounded px-2 py-1 outline-none w-full" />
79
+ <button onclick="saveTitle()" class="text-blue-400 hover:text-blue-300 p-1 shrink-0 bg-gray-800 rounded">
80
+ <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="M5 13l4 4L19 7"></path></svg>
81
+ </button>
82
+ <button onclick="cancelTitleEdit()" class="text-gray-400 hover:text-red-400 p-1 shrink-0 bg-gray-800 rounded">
83
+ <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="M6 18L18 6M6 6l12 12"></path></svg>
84
+ </button>
85
+ </div>
86
  </div>
87
+
88
+ <div class="flex items-center gap-2 lg:gap-4 shrink-0 pl-2">
89
  <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">
90
  Tokens: <span id="token-counter">0</span>
91
  </span>
 
101
  </header>
102
 
103
  <!-- Messages -->
104
+ <div id="chat-window" class="flex-1 overflow-y-auto p-4 lg:p-6 space-y-6 scroll-smooth"></div>
 
 
105
 
106
  <!-- Input Area -->
107
  <div class="p-3 lg:p-4 bg-gray-900 border-t border-gray-800 shrink-0">
 
123
  </main>
124
 
125
  <script>
 
126
  marked.setOptions({
127
  highlight: function(code, lang) {
128
  const language = hljs.getLanguage(lang) ? lang : 'plaintext';
 
134
  let attachedImages =[];
135
  let pollingInterval = null;
136
 
137
+ // --- TITLE EDITING LOGIC ---
138
+ function enableTitleEdit() {
139
+ if (!currentChatId) return;
140
+ const currentTitle = document.getElementById('current-chat-title').innerText;
141
+ document.getElementById('title-display').classList.add('hidden');
142
+ document.getElementById('title-edit').classList.remove('hidden');
143
+ document.getElementById('title-edit').classList.add('flex');
144
+
145
+ const input = document.getElementById('title-input');
146
+ input.value = currentTitle;
147
+ input.focus();
148
+ input.select();
149
+ }
150
+
151
+ function cancelTitleEdit() {
152
+ document.getElementById('title-edit').classList.add('hidden');
153
+ document.getElementById('title-edit').classList.remove('flex');
154
+ document.getElementById('title-display').classList.remove('hidden');
155
+ }
156
+
157
+ async function saveTitle() {
158
+ if (!currentChatId) return cancelTitleEdit();
159
+ const newTitle = document.getElementById('title-input').value.trim();
160
+ if (!newTitle) return cancelTitleEdit();
161
+
162
+ document.getElementById('current-chat-title').innerText = newTitle;
163
+ cancelTitleEdit();
164
+
165
+ await fetch(`/api/chats/${currentChatId}/title`, {
166
+ method: 'PUT',
167
+ headers: { 'Content-Type': 'application/json' },
168
+ body: JSON.stringify({ title: newTitle })
169
+ });
170
+ loadSidebar(); // Refresh sidebar to show new name
171
+ }
172
+
173
+ // Handle Enter and Escape keys for title input
174
+ document.getElementById('title-input').addEventListener('keydown', (e) => {
175
+ if (e.key === 'Enter') saveTitle();
176
+ if (e.key === 'Escape') cancelTitleEdit();
177
+ });
178
+
179
+
180
+ // --- UI CORE LOGIC ---
181
  function toggleSidebar() {
182
  const sidebar = document.getElementById('sidebar');
183
  const overlay = document.getElementById('mobile-overlay');
 
185
  overlay.classList.toggle('hidden');
186
  }
187
 
 
188
  const textarea = document.getElementById('message-input');
189
  textarea.addEventListener('input', function() {
190
  this.style.height = 'auto';
 
208
  const chat = await res.json();
209
  selectChat(chat.id);
210
  loadSidebar();
211
+ if(window.innerWidth < 1024) toggleSidebar();
212
  }
213
 
214
  async function deleteCurrentChat() {
 
218
  currentChatId = null;
219
  document.getElementById('chat-window').innerHTML = '';
220
  document.getElementById('current-chat-title').innerText = 'Select or create a chat';
221
+ document.getElementById('edit-title-btn').classList.add('hidden');
222
  document.getElementById('token-counter').innerText = '0';
223
  loadSidebar();
224
  }
 
231
  const chat = await res.json();
232
 
233
  document.getElementById('current-chat-title').innerText = chat.title;
234
+ document.getElementById('edit-title-btn').classList.remove('hidden'); // Show edit icon for active chat
235
  document.getElementById('token-counter').innerText = chat.totalTokens.toLocaleString();
236
 
237
+ cancelTitleEdit(); // Ensure we reset title UI state if switching
238
  renderMessages(chat.messages);
239
  loadSidebar();
240
 
241
  if(window.innerWidth < 1024 && !document.getElementById('sidebar').classList.contains('-translate-x-full')) {
242
+ toggleSidebar();
243
  }
 
244
  if (chat.isGenerating) pollGeneratingChat(id);
245
  }
246
 
 
264
  `;
265
  }).join('');
266
 
 
267
  document.querySelectorAll('.markdown-body pre').forEach(pre => {
268
  if (!pre.querySelector('.copy-btn')) {
269
  const btn = document.createElement('button');
270
  btn.className = 'copy-btn';
271
  btn.innerText = 'Copy';
272
  btn.onclick = () => {
 
273
  const codeText = pre.querySelector('code')?.innerText || pre.innerText.replace('Copy', '');
274
  navigator.clipboard.writeText(codeText.trim());
275
  btn.innerText = 'Copied!';
 
287
  const chat = await res.json();
288
  renderMessages(chat.messages);
289
  document.getElementById('token-counter').innerText = chat.totalTokens.toLocaleString();
290
+ if (chat.title !== document.getElementById('current-chat-title').innerText && document.getElementById('title-display').classList.contains('hidden') === false) {
291
+ document.getElementById('current-chat-title').innerText = chat.title;
292
+ loadSidebar();
293
+ }
294
  if (!chat.isGenerating) clearInterval(pollingInterval);
295
  }, 1500);
296
  }
 
340
  `;
341
  container.scrollTop = container.scrollHeight;
342
 
 
343
  input.value = '';
344
+ input.style.height = 'auto';
345
  attachedImages =[];
346
  document.getElementById('image-preview-container').innerHTML = '';
347
  document.getElementById('image-preview-container').classList.add('hidden');
 
402
 
403
  document.getElementById('message-input').addEventListener('keydown', (e) => {
404
  if (e.key === 'Enter' && !e.shiftKey) {
 
 
405
  e.preventDefault();
406
  sendMessage();
407
  }