Shirpi commited on
Commit
7ea7ab2
·
verified ·
1 Parent(s): 3828c80

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +93 -28
app.py CHANGED
@@ -142,27 +142,22 @@ HTML_TEMPLATE = """
142
  }
143
  * { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
144
 
145
- /* --- FIX 4: PREVENT HORIZONTAL SCROLL --- */
146
  body, html {
147
  margin: 0; padding: 0; height: 100dvh; width: 100%; max-width: 100%;
148
  background: var(--bg); color: var(--text); font-family: 'Outfit', sans-serif;
149
- overflow: hidden; /* Locks the page */
150
  font-size: 17px;
151
-
152
- /* --- FIX 2: DISABLE SELECT GLOBALLY --- */
153
  -webkit-user-select: none;
154
  -moz-user-select: none;
155
  -ms-user-select: none;
156
  user-select: none;
157
  }
158
 
159
- /* Allow typing in inputs only */
160
  textarea, input {
161
  -webkit-user-select: text !important;
162
  user-select: text !important;
163
  }
164
 
165
- /* Make sure content is NOT selectable via long press */
166
  .user-content, .ai-content, code, pre, p, h1, h2, span, div {
167
  -webkit-user-select: none !important;
168
  user-select: none !important;
@@ -173,7 +168,7 @@ HTML_TEMPLATE = """
173
  height: 100dvh;
174
  width: 100%;
175
  position: relative;
176
- overflow-x: hidden; /* Extra safety */
177
  }
178
 
179
  header {
@@ -221,11 +216,24 @@ HTML_TEMPLATE = """
221
  .history-item:last-child { margin-bottom: 0; }
222
  .history-item:active { background: #222; color: #fff; border-color: #444; }
223
 
224
- .h-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 200px; }
225
  .h-actions { display: none; gap: 15px; }
226
  .history-item.active-mode .h-actions { display: flex; }
227
  .h-icon { font-size: 16px; color: #fff; padding: 5px; }
228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  .brand-section { text-align: center; margin-top: 20px; padding-bottom: env(safe-area-inset-bottom); }
230
  .brand-name { font-family: 'Outfit', sans-serif; font-weight: 600; font-size: 12px; color: var(--dim); letter-spacing: 2px; margin-bottom: 10px; opacity: 0.6; }
231
  .logout-btn { color: #ef4444; cursor: pointer; font-size: 15px; font-weight: 600; padding: 10px; }
@@ -277,7 +285,6 @@ HTML_TEMPLATE = """
277
  .ai-content strong { color: #fff; font-weight: 700; }
278
  .ai-content h1, .ai-content h2 { margin-top: 20px; color: #fff; font-weight: 700; }
279
 
280
- /* FIX 3: STYLE FOR IMAGES TO BE CLICKABLE */
281
  .ai-content img, .user-content img { cursor: pointer; transition: 0.2s; }
282
  .ai-content img:active, .user-content img:active { transform: scale(0.98); }
283
 
@@ -314,7 +321,6 @@ HTML_TEMPLATE = """
314
  #login-overlay { position: fixed; inset: 0; background: #000; z-index: 2000; display: flex; align-items: center; justify-content: center; }
315
  .login-box { width: 90%; max-width: 350px; text-align: center; padding: 40px; border: 1px solid var(--border); border-radius: 20px; background: #0a0a0a; }
316
 
317
- /* --- FIX 3: LIGHTBOX (IMAGE PREVIEW) CSS --- */
318
  #image-modal {
319
  position: fixed; top: 0; left: 0; width: 100%; height: 100%;
320
  background: rgba(0,0,0,0.9); z-index: 3000;
@@ -391,7 +397,6 @@ HTML_TEMPLATE = """
391
  let currentAttachment = { type: null, data: null, name: null };
392
  let longPressTimer;
393
 
394
- // --- HELPER FOR INTRO TEXT ---
395
  function getIntroHtml(name) {
396
  return `<div class="msg ai-msg"><div class="ai-content"><h1>Hi ${name},</h1><p>Ready to master your studies today?</p></div></div>`;
397
  }
@@ -422,7 +427,6 @@ HTML_TEMPLATE = """
422
  if(!currentChatId) {
423
  const box = document.getElementById("chat-box");
424
  if(box.innerHTML === "") {
425
- // FIX 1: USE FUNCTION FOR CONSISTENT INTRO
426
  box.innerHTML = getIntroHtml(currentUser);
427
  }
428
  }
@@ -541,8 +545,7 @@ HTML_TEMPLATE = """
541
  function editMessage(oldText) { document.getElementById('input').value = oldText; document.getElementById('input').focus(); }
542
  function regenerate(text) { document.getElementById('input').value = text; send(); }
543
 
544
- // --- FIX 3: LIGHTBOX LOGIC ---
545
- // Open Image Logic
546
  document.getElementById('chat-box').addEventListener('click', function(e) {
547
  if(e.target.tagName === 'IMG') {
548
  const modal = document.getElementById('image-modal');
@@ -558,20 +561,57 @@ HTML_TEMPLATE = """
558
  setTimeout(() => modal.style.display = 'none', 300);
559
  }
560
 
561
- // --- HISTORY LOGIC ---
562
- function handleHistoryTouchStart(e, cid, title) {
563
  longPressTimer = setTimeout(() => {
564
  e.target.closest('.history-item').classList.add('active-mode');
565
  }, 600);
566
  }
567
  function handleHistoryTouchEnd(e) { clearTimeout(longPressTimer); }
568
 
569
- async function renameChat(cid, oldTitle) {
570
- const newTitle = prompt("Rename:", oldTitle);
571
- if(newTitle) { await fetch('/rename_chat', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({username:currentUser, chat_id:cid, title:newTitle})}); loadHistory(); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
572
  }
 
573
  async function deleteChat(cid) {
574
- if(confirm("Delete?")) { await fetch('/delete_chat', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({username:currentUser, chat_id:cid})}); if(currentChatId === cid) newChat(); loadHistory(); }
 
 
 
 
 
 
575
  }
576
 
577
  async function loadHistory() {
@@ -583,11 +623,11 @@ HTML_TEMPLATE = """
583
  Object.keys(data.chats).reverse().forEach(cid => {
584
  const title = data.chats[cid].title || "New Chat";
585
  list.innerHTML += `
586
- <div class="history-item" onclick="loadChat('${cid}')" oncontextmenu="return false;"
587
- ontouchstart="handleHistoryTouchStart(event, '${cid}', '${title}')" ontouchend="handleHistoryTouchEnd(event)">
588
  <span class="h-title">${title}</span>
589
  <div class="h-actions">
590
- <i class="fas fa-pen h-icon" onclick="event.stopPropagation(); renameChat('${cid}', '${title}')"></i>
591
  <i class="fas fa-trash h-icon" onclick="event.stopPropagation(); deleteChat('${cid}')"></i>
592
  </div>
593
  </div>`;
@@ -615,7 +655,6 @@ HTML_TEMPLATE = """
615
  function toggleSidebar() { document.getElementById('sidebar').classList.toggle('open'); }
616
  async function newChat() {
617
  currentChatId = null;
618
- // FIX 1: SET INTRO TEXT FOR EVERY NEW CHAT
619
  document.getElementById('chat-box').innerHTML = getIntroHtml(currentUser);
620
 
621
  const r = await fetch('/new_chat', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({username:currentUser})});
@@ -642,6 +681,31 @@ HTML_TEMPLATE = """
642
  box.scrollTop = box.scrollHeight;
643
  }
644
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
645
  checkLogin();
646
  </script>
647
  </body>
@@ -715,7 +779,7 @@ def chat():
715
  def manifest():
716
  data = {
717
  "name": "Student's AI",
718
- "short_name": "Student's AI",
719
  "start_url": "/",
720
  "display": "standalone",
721
  "orientation": "portrait",
@@ -723,12 +787,12 @@ def manifest():
723
  "theme_color": "#09090b",
724
  "icons": [
725
  {
726
- "src": "https://huggingface.co/spaces/Shirpi/Student-s_AI/resolve/main/1000177401.png",
727
  "sizes": "192x192",
728
  "type": "image/png"
729
  },
730
  {
731
- "src": "https://huggingface.co/spaces/Shirpi/Student-s_AI/resolve/main/1000177401.png",
732
  "sizes": "512x512",
733
  "type": "image/png"
734
  }
@@ -737,4 +801,5 @@ def manifest():
737
  return Response(json.dumps(data), mimetype='application/json')
738
 
739
  if __name__ == '__main__':
740
- app.run(host='0.0.0.0', port=7860)
 
 
142
  }
143
  * { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
144
 
 
145
  body, html {
146
  margin: 0; padding: 0; height: 100dvh; width: 100%; max-width: 100%;
147
  background: var(--bg); color: var(--text); font-family: 'Outfit', sans-serif;
148
+ overflow: hidden;
149
  font-size: 17px;
 
 
150
  -webkit-user-select: none;
151
  -moz-user-select: none;
152
  -ms-user-select: none;
153
  user-select: none;
154
  }
155
 
 
156
  textarea, input {
157
  -webkit-user-select: text !important;
158
  user-select: text !important;
159
  }
160
 
 
161
  .user-content, .ai-content, code, pre, p, h1, h2, span, div {
162
  -webkit-user-select: none !important;
163
  user-select: none !important;
 
168
  height: 100dvh;
169
  width: 100%;
170
  position: relative;
171
+ overflow-x: hidden;
172
  }
173
 
174
  header {
 
216
  .history-item:last-child { margin-bottom: 0; }
217
  .history-item:active { background: #222; color: #fff; border-color: #444; }
218
 
219
+ .h-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 200px; flex: 1; margin-right: 10px; }
220
  .h-actions { display: none; gap: 15px; }
221
  .history-item.active-mode .h-actions { display: flex; }
222
  .h-icon { font-size: 16px; color: #fff; padding: 5px; }
223
 
224
+ /* --- NEW STYLE FOR INLINE RENAME INPUT --- */
225
+ .rename-input {
226
+ background: transparent;
227
+ border: none;
228
+ border-bottom: 1px solid #fff;
229
+ color: #fff;
230
+ font-family: 'Outfit', sans-serif;
231
+ font-size: 15px;
232
+ width: 100%;
233
+ outline: none;
234
+ padding: 0;
235
+ }
236
+
237
  .brand-section { text-align: center; margin-top: 20px; padding-bottom: env(safe-area-inset-bottom); }
238
  .brand-name { font-family: 'Outfit', sans-serif; font-weight: 600; font-size: 12px; color: var(--dim); letter-spacing: 2px; margin-bottom: 10px; opacity: 0.6; }
239
  .logout-btn { color: #ef4444; cursor: pointer; font-size: 15px; font-weight: 600; padding: 10px; }
 
285
  .ai-content strong { color: #fff; font-weight: 700; }
286
  .ai-content h1, .ai-content h2 { margin-top: 20px; color: #fff; font-weight: 700; }
287
 
 
288
  .ai-content img, .user-content img { cursor: pointer; transition: 0.2s; }
289
  .ai-content img:active, .user-content img:active { transform: scale(0.98); }
290
 
 
321
  #login-overlay { position: fixed; inset: 0; background: #000; z-index: 2000; display: flex; align-items: center; justify-content: center; }
322
  .login-box { width: 90%; max-width: 350px; text-align: center; padding: 40px; border: 1px solid var(--border); border-radius: 20px; background: #0a0a0a; }
323
 
 
324
  #image-modal {
325
  position: fixed; top: 0; left: 0; width: 100%; height: 100%;
326
  background: rgba(0,0,0,0.9); z-index: 3000;
 
397
  let currentAttachment = { type: null, data: null, name: null };
398
  let longPressTimer;
399
 
 
400
  function getIntroHtml(name) {
401
  return `<div class="msg ai-msg"><div class="ai-content"><h1>Hi ${name},</h1><p>Ready to master your studies today?</p></div></div>`;
402
  }
 
427
  if(!currentChatId) {
428
  const box = document.getElementById("chat-box");
429
  if(box.innerHTML === "") {
 
430
  box.innerHTML = getIntroHtml(currentUser);
431
  }
432
  }
 
545
  function editMessage(oldText) { document.getElementById('input').value = oldText; document.getElementById('input').focus(); }
546
  function regenerate(text) { document.getElementById('input').value = text; send(); }
547
 
548
+ // --- LIGHTBOX ---
 
549
  document.getElementById('chat-box').addEventListener('click', function(e) {
550
  if(e.target.tagName === 'IMG') {
551
  const modal = document.getElementById('image-modal');
 
561
  setTimeout(() => modal.style.display = 'none', 300);
562
  }
563
 
564
+ // --- HISTORY & INLINE RENAME LOGIC ---
565
+ function handleHistoryTouchStart(e, cid) {
566
  longPressTimer = setTimeout(() => {
567
  e.target.closest('.history-item').classList.add('active-mode');
568
  }, 600);
569
  }
570
  function handleHistoryTouchEnd(e) { clearTimeout(longPressTimer); }
571
 
572
+ function startRename(cid, currentTitle) {
573
+ const item = document.getElementById('chat-' + cid);
574
+ const titleSpan = item.querySelector('.h-title');
575
+
576
+ // Create input
577
+ const input = document.createElement('input');
578
+ input.type = 'text';
579
+ input.value = currentTitle;
580
+ input.className = 'rename-input';
581
+
582
+ // Handle save on Enter or Blur
583
+ async function save() {
584
+ const newTitle = input.value.trim();
585
+ if(newTitle && newTitle !== currentTitle) {
586
+ await fetch('/rename_chat', {
587
+ method:'POST', headers:{'Content-Type':'application/json'},
588
+ body:JSON.stringify({username:currentUser, chat_id:cid, title:newTitle})
589
+ });
590
+ loadHistory();
591
+ } else {
592
+ // Revert if empty or same
593
+ loadHistory();
594
+ }
595
+ }
596
+
597
+ input.addEventListener('blur', save);
598
+ input.addEventListener('keydown', (e) => {
599
+ if(e.key === 'Enter') { input.blur(); }
600
+ });
601
+
602
+ // Replace span with input
603
+ titleSpan.replaceWith(input);
604
+ input.focus();
605
  }
606
+
607
  async function deleteChat(cid) {
608
+ // FIX: No confirmation, just delete immediately
609
+ await fetch('/delete_chat', {
610
+ method:'POST', headers:{'Content-Type':'application/json'},
611
+ body:JSON.stringify({username:currentUser, chat_id:cid})
612
+ });
613
+ if(currentChatId === cid) newChat();
614
+ loadHistory();
615
  }
616
 
617
  async function loadHistory() {
 
623
  Object.keys(data.chats).reverse().forEach(cid => {
624
  const title = data.chats[cid].title || "New Chat";
625
  list.innerHTML += `
626
+ <div class="history-item" id="chat-${cid}" onclick="loadChat('${cid}')" oncontextmenu="return false;"
627
+ ontouchstart="handleHistoryTouchStart(event, '${cid}')" ontouchend="handleHistoryTouchEnd(event)">
628
  <span class="h-title">${title}</span>
629
  <div class="h-actions">
630
+ <i class="fas fa-pen h-icon" onclick="event.stopPropagation(); startRename('${cid}', '${title}')"></i>
631
  <i class="fas fa-trash h-icon" onclick="event.stopPropagation(); deleteChat('${cid}')"></i>
632
  </div>
633
  </div>`;
 
655
  function toggleSidebar() { document.getElementById('sidebar').classList.toggle('open'); }
656
  async function newChat() {
657
  currentChatId = null;
 
658
  document.getElementById('chat-box').innerHTML = getIntroHtml(currentUser);
659
 
660
  const r = await fetch('/new_chat', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({username:currentUser})});
 
681
  box.scrollTop = box.scrollHeight;
682
  }
683
 
684
+ @app.route('/manifest.json')
685
+ def manifest():
686
+ data = {
687
+ "name": "Student's AI",
688
+ "short_name": "StudentAI",
689
+ "start_url": "/",
690
+ "display": "standalone",
691
+ "orientation": "portrait",
692
+ "background_color": "#09090b",
693
+ "theme_color": "#09090b",
694
+ "icons": [
695
+ {
696
+ "src": "https://cdn-icons-png.flaticon.com/512/4712/4712035.png",
697
+ "sizes": "192x192",
698
+ "type": "image/png"
699
+ },
700
+ {
701
+ "src": "https://cdn-icons-png.flaticon.com/512/4712/4712035.png",
702
+ "sizes": "512x512",
703
+ "type": "image/png"
704
+ }
705
+ ]
706
+ }
707
+ return Response(json.dumps(data), mimetype='application/json')
708
+
709
  checkLogin();
710
  </script>
711
  </body>
 
779
  def manifest():
780
  data = {
781
  "name": "Student's AI",
782
+ "short_name": "StudentAI",
783
  "start_url": "/",
784
  "display": "standalone",
785
  "orientation": "portrait",
 
787
  "theme_color": "#09090b",
788
  "icons": [
789
  {
790
+ "src": "https://cdn-icons-png.flaticon.com/512/4712/4712035.png",
791
  "sizes": "192x192",
792
  "type": "image/png"
793
  },
794
  {
795
+ "src": "https://cdn-icons-png.flaticon.com/512/4712/4712035.png",
796
  "sizes": "512x512",
797
  "type": "image/png"
798
  }
 
801
  return Response(json.dumps(data), mimetype='application/json')
802
 
803
  if __name__ == '__main__':
804
+ app.run(host='0.0.0.0', port=7860)
805
+