awacke1 commited on
Commit
170551e
ยท
verified ยท
1 Parent(s): c0ef2f1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +47 -54
app.py CHANGED
@@ -127,7 +127,7 @@ def init_session_state():
127
  'autosend': True, 'autosearch': True, 'last_message': "", 'last_query': "",
128
  'mp3_files': {}, 'timer_start': time.time(), 'quote_index': 0,
129
  'quote_source': "famous", 'last_sent_transcript': "", 'old_val': None,
130
- 'last_refresh': time.time(), 'paper_metadata': {}, 'paste_image_base64': None
131
  }
132
  for k, v in defaults.items():
133
  if k not in st.session_state:
@@ -282,7 +282,6 @@ async def perform_claude_search(query, username, image=None):
282
  client = anthropic.Anthropic(api_key=anthropic_key)
283
  message_content = [{"type": "text", "text": query}]
284
  if image:
285
- # Convert PIL Image to base64
286
  buffered = io.BytesIO()
287
  image.save(buffered, format="PNG")
288
  img_base64 = base64.b64encode(buffered.getvalue()).decode('utf-8')
@@ -565,49 +564,43 @@ def create_zip_of_files(md_files, mp3_files, png_files, mp4_files, query):
565
 
566
  # Custom Paste Image Component
567
  def paste_image_component():
568
- # JavaScript to capture clipboard image and send it to Streamlit
569
- paste_script = """
570
- <script>
571
- function pasteImage() {
572
- navigator.clipboard.read().then(clipboardItems => {
573
- for (const item of clipboardItems) {
574
- if (item.types.includes('image/png') || item.types.includes('image/jpeg')) {
575
- item.getType('image/png').then(blob => {
576
- const reader = new FileReader();
577
- reader.onload = function(event) {
578
- document.getElementById('paste_input').value = event.target.result.split(',')[1];
579
- document.getElementById('paste_form').submit();
580
- };
581
- reader.readAsDataURL(blob);
582
- });
583
- break;
584
- }
585
- }
586
- }).catch(err => {
587
- console.error('Failed to read clipboard: ', err);
588
- document.getElementById('paste_input').value = 'ERROR: ' + err.message;
589
- document.getElementById('paste_form').submit();
590
- });
591
- }
592
- </script>
593
- <form id="paste_form" action="/" method="POST" style="display: none;">
594
- <input type="text" id="paste_input" name="paste_data" />
595
- </form>
596
- <button onclick="pasteImage()">Paste Image ๐Ÿ“‹</button>
597
- """
598
- paste_data = components.html(paste_script, height=50)
599
- if paste_data and 'paste_data' in paste_data:
600
- base64_str = paste_data['paste_data']
601
- if base64_str.startswith('ERROR:'):
602
- st.warning(f"Paste failed: {base64_str}")
603
- return None
604
- try:
605
- img_bytes = base64.b64decode(base64_str)
606
- img = Image.open(io.BytesIO(img_bytes))
607
- return img
608
- except Exception as e:
609
- st.warning(f"Error decoding pasted image: {e}")
610
- return None
611
  return None
612
 
613
  # ๐ŸŽฎ Main Interface
@@ -641,10 +634,10 @@ def main():
641
  st.rerun()
642
 
643
  tab_main = st.radio("Action:", ["๐ŸŽค Chat & Voice", "๐Ÿ” ArXiv", "๐Ÿ“š PDF to Audio"], horizontal=True, key="tab_main")
644
- useArxiv = st.checkbox("Search ArXiv", True, key="use_arxiv")
645
- useArxivAudio = st.checkbox("ArXiv Audio", False, key="use_arxiv_audio")
646
- st.checkbox("Autosend Chat", value=True, key="autosend")
647
- st.checkbox("Autosearch ArXiv", value=True, key="autosearch")
648
 
649
  # ๐ŸŽค Chat & Voice
650
  if tab_main == "๐ŸŽค Chat & Voice":
@@ -659,8 +652,8 @@ def main():
659
 
660
  # Custom Paste Image Button
661
  pasted_image = paste_image_component()
662
- if pasted_image is not None and st.session_state['paste_image_base64'] != pasted_image.tobytes():
663
- st.session_state['paste_image_base64'] = pasted_image.tobytes()
664
  voice = FUN_USERNAMES.get(st.session_state.username, "en-US-AriaNeural")
665
  st.image(pasted_image, caption="Pasted Image")
666
  filename = asyncio.run(save_pasted_image(pasted_image, st.session_state.username))
@@ -679,7 +672,7 @@ def main():
679
  if audio_file_arxiv:
680
  play_and_download_audio(audio_file_arxiv)
681
  st.session_state.pasted_image_data = None
682
- st.session_state['paste_image_base64'] = None
683
  st.session_state.timer_start = time.time()
684
  save_username(st.session_state.username)
685
  st.rerun()
@@ -731,7 +724,7 @@ def main():
731
  if q and q != st.session_state.last_query:
732
  st.session_state.last_query = q
733
  if st.session_state.autosearch or st.button("๐Ÿ” Run", key="arxiv_run"):
734
- result, papers = asyncio.run(perform_ai_lookup(q, useArxiv=useArxiv, useArxivAudio=useArxivAudio))
735
  st.markdown(f"### Query: {q}")
736
  for i, p in enumerate(papers, 1):
737
  expander_label = f"{p['title']} | [arXiv Link]({p['url']})"
@@ -874,7 +867,7 @@ def main():
874
  else:
875
  timer_placeholder.markdown(f"<p class='timer'>โณ Next refresh in: {remaining_time} seconds</p>", unsafe_allow_html=True)
876
 
877
- # Start WebSocket server in a separate thread
878
  if not st.session_state.server_running and not st.session_state.server_task:
879
  st.session_state.server_task = threading.Thread(target=start_websocket_server, daemon=True)
880
  st.session_state.server_task.start()
 
127
  'autosend': True, 'autosearch': True, 'last_message': "", 'last_query': "",
128
  'mp3_files': {}, 'timer_start': time.time(), 'quote_index': 0,
129
  'quote_source': "famous", 'last_sent_transcript': "", 'old_val': None,
130
+ 'last_refresh': time.time(), 'paper_metadata': {}, 'paste_image_base64': ""
131
  }
132
  for k, v in defaults.items():
133
  if k not in st.session_state:
 
282
  client = anthropic.Anthropic(api_key=anthropic_key)
283
  message_content = [{"type": "text", "text": query}]
284
  if image:
 
285
  buffered = io.BytesIO()
286
  image.save(buffered, format="PNG")
287
  img_base64 = base64.b64encode(buffered.getvalue()).decode('utf-8')
 
564
 
565
  # Custom Paste Image Component
566
  def paste_image_component():
567
+ with st.form(key="paste_form"):
568
+ # Hidden text input to receive clipboard data
569
+ paste_input = st.text_input("Paste Base64 Image Here (hidden)", value="", key="paste_input", label_visibility="collapsed")
570
+ # JavaScript to paste clipboard content into the input
571
+ st.markdown("""
572
+ <script>
573
+ function pasteClipboard() {
574
+ navigator.clipboard.readText().then(text => {
575
+ document.getElementById('paste_input').value = text;
576
+ document.getElementById('paste_form').requestSubmit();
577
+ }).catch(err => {
578
+ console.error('Failed to read clipboard: ', err);
579
+ document.getElementById('paste_input').value = 'ERROR: ' + err.message;
580
+ document.getElementById('paste_form').requestSubmit();
581
+ });
582
+ }
583
+ </script>
584
+ """, unsafe_allow_html=True)
585
+ # Button to trigger paste
586
+ paste_button = st.form_submit_button("Paste Image ๐Ÿ“‹", on_click=lambda: st.markdown("<script>pasteClipboard();</script>", unsafe_allow_html=True))
587
+
588
+ if paste_button and paste_input:
589
+ if paste_input.startswith('ERROR:'):
590
+ st.warning(f"Paste failed: {paste_input}")
591
+ return None
592
+ if paste_input.startswith('data:image'):
593
+ try:
594
+ base64_str = paste_input.split(',')[1]
595
+ img_bytes = base64.b64decode(base64_str)
596
+ img = Image.open(io.BytesIO(img_bytes))
597
+ return img
598
+ except Exception as e:
599
+ st.warning(f"Error decoding pasted image: {e}")
600
+ return None
601
+ else:
602
+ st.warning("Clipboard does not contain a valid image (expected base64 data:image)")
603
+ return None
 
 
 
 
 
 
604
  return None
605
 
606
  # ๐ŸŽฎ Main Interface
 
634
  st.rerun()
635
 
636
  tab_main = st.radio("Action:", ["๐ŸŽค Chat & Voice", "๐Ÿ” ArXiv", "๐Ÿ“š PDF to Audio"], horizontal=True, key="tab_main")
637
+ st.checkbox("Search ArXiv", value=st.session_state['use_arxiv'], key="use_arxiv")
638
+ st.checkbox("ArXiv Audio", value=st.session_state['use_arxiv_audio'], key="use_arxiv_audio")
639
+ st.checkbox("Autosend Chat", value=st.session_state['autosend'], key="autosend")
640
+ st.checkbox("Autosearch ArXiv", value=st.session_state['autosearch'], key="autosearch")
641
 
642
  # ๐ŸŽค Chat & Voice
643
  if tab_main == "๐ŸŽค Chat & Voice":
 
652
 
653
  # Custom Paste Image Button
654
  pasted_image = paste_image_component()
655
+ if pasted_image is not None and st.session_state['paste_image_base64'] != base64.b64encode(pasted_image.tobytes()).decode('utf-8'):
656
+ st.session_state['paste_image_base64'] = base64.b64encode(pasted_image.tobytes()).decode('utf-8')
657
  voice = FUN_USERNAMES.get(st.session_state.username, "en-US-AriaNeural")
658
  st.image(pasted_image, caption="Pasted Image")
659
  filename = asyncio.run(save_pasted_image(pasted_image, st.session_state.username))
 
672
  if audio_file_arxiv:
673
  play_and_download_audio(audio_file_arxiv)
674
  st.session_state.pasted_image_data = None
675
+ st.session_state['paste_image_base64'] = ""
676
  st.session_state.timer_start = time.time()
677
  save_username(st.session_state.username)
678
  st.rerun()
 
724
  if q and q != st.session_state.last_query:
725
  st.session_state.last_query = q
726
  if st.session_state.autosearch or st.button("๐Ÿ” Run", key="arxiv_run"):
727
+ result, papers = asyncio.run(perform_ai_lookup(q, useArxiv=st.session_state['use_arxiv'], useArxivAudio=st.session_state['use_arxiv_audio']))
728
  st.markdown(f"### Query: {q}")
729
  for i, p in enumerate(papers, 1):
730
  expander_label = f"{p['title']} | [arXiv Link]({p['url']})"
 
867
  else:
868
  timer_placeholder.markdown(f"<p class='timer'>โณ Next refresh in: {remaining_time} seconds</p>", unsafe_allow_html=True)
869
 
870
+ # Start WebSocket server in a thread
871
  if not st.session_state.server_running and not st.session_state.server_task:
872
  st.session_state.server_task = threading.Thread(target=start_websocket_server, daemon=True)
873
  st.session_state.server_task.start()