#!/usr/bin/env python3 import os import re import glob import json import base64 import zipfile import random import requests import streamlit as st import streamlit.components.v1 as components import time # If you do model inference via huggingface_hub: # from huggingface_hub import InferenceClient ######################################################################################## # 1) GLOBAL CONFIG & PLACEHOLDERS ######################################################################################## BASE_URL = "https://huggingface.co/spaces/awacke1/MermaidMarkdownDiagramEditor" BASE_URL = "" PromptPrefix = "AI-Search: " PromptPrefix2 = "AI-Refine: " PromptPrefix3 = "AI-JS: " roleplaying_glossary = { "Core Rulebooks": { "Dungeons and Dragons": ["Player's Handbook", "Dungeon Master's Guide", "Monster Manual"], "GURPS": ["Basic Set Characters", "Basic Set Campaigns"] }, "Campaigns & Adventures": { "Pathfinder": ["Rise of the Runelords", "Curse of the Crimson Throne"] } } transhuman_glossary = { "Neural Interfaces": ["Cortex Jack", "Mind-Machine Fusion"], "Cybernetics": ["Robotic Limbs", "Augmented Eyes"], } def process_text(text): """🕵️ process_text: detective style—prints lines to Streamlit for debugging.""" st.write(f"process_text called with: {text}") def search_arxiv(text): """🔭 search_arxiv: pretend to search ArXiv, just prints debug.""" st.write(f"search_arxiv called with: {text}") def SpeechSynthesis(text): """🗣 Simple logging for text-to-speech placeholders.""" st.write(f"SpeechSynthesis called with: {text}") def process_image(image_file, prompt): """📷 Simple placeholder for image AI pipeline.""" return f"[process_image placeholder] {image_file} => {prompt}" def process_video(video_file, seconds_per_frame): """🎞 Simple placeholder for video AI pipeline.""" st.write(f"[process_video placeholder] {video_file}, {seconds_per_frame} sec/frame") API_URL = "https://huggingface-inference-endpoint-placeholder" API_KEY = "hf_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" @st.cache_resource def InferenceLLM(prompt): """🔮 Stub returning mock response for 'prompt'.""" return f"[InferenceLLM placeholder response to prompt: {prompt}]" ######################################################################################## # 2) GLOSSARY & FILE UTILITY ######################################################################################## @st.cache_resource def display_glossary_entity(k): """ Creates multiple link emojis for a single entity. Each link might point to /?q=..., /?q=..., or external sites. """ search_urls = { "🚀🌌ArXiv": lambda x: f"/?q={quote(x)}", "🃏Analyst": lambda x: f"/?q={quote(x)}-{quote(PromptPrefix)}", "📚PyCoder": lambda x: f"/?q={quote(x)}-{quote(PromptPrefix2)}", "🔬JSCoder": lambda x: f"/?q={quote(x)}-{quote(PromptPrefix3)}", "📖": lambda x: f"https://en.wikipedia.org/wiki/{quote(x)}", "🔍": lambda x: f"https://www.google.com/search?q={quote(x)}", "🔎": lambda x: f"https://www.bing.com/search?q={quote(x)}", "🎥": lambda x: f"https://www.youtube.com/results?search_query={quote(x)}", "🐦": lambda x: f"https://twitter.com/search?q={quote(x)}", } links_md = ' '.join([f"[{emoji}]({url(k)})" for emoji, url in search_urls.items()]) st.markdown(f"**{k}** {links_md}", unsafe_allow_html=True) def display_content_or_image(query): """ If 'query' is in transhuman_glossary or there's an image matching 'images/.png', show it. Otherwise warn. """ for category, term_list in transhuman_glossary.items(): for term in term_list: if query.lower() in term.lower(): st.subheader(f"Found in {category}:") st.write(term) return True image_path = f"images/{query}.png" if os.path.exists(image_path): st.image(image_path, caption=f"Image for {query}") return True st.warning("No matching content or image found.") return False def clear_query_params(): """Warn about clearing. Full clearing requires a redirect or st.experimental_set_query_params().""" st.warning("Define a redirect or link without query params if you want to truly clear them.") ######################################################################################## # 3) FILE-HANDLING (MD files, etc.) ######################################################################################## def load_file(file_path): """Load file contents as UTF-8 text, or return empty on error.""" try: with open(file_path, "r", encoding='utf-8') as f: return f.read() except: return "" @st.cache_resource def create_zip_of_files(files): """Combine multiple local .md files into a single .zip for user to download.""" zip_name = "Arxiv-Paper-Search-QA-RAG-Streamlit-Gradio-AP.zip" with zipfile.ZipFile(zip_name, 'w') as zipf: for file in files: zipf.write(file) return zip_name @st.cache_resource def get_zip_download_link(zip_file): """Return an link to download the given zip_file (base64-encoded).""" with open(zip_file, 'rb') as f: data = f.read() b64 = base64.b64encode(data).decode() return f'Download All' def get_table_download_link(file_path): """ Creates a download link for a single file from your snippet. Encodes it as base64 data. """ try: with open(file_path, 'r', encoding='utf-8') as file: data = file.read() b64 = base64.b64encode(data.encode()).decode() file_name = os.path.basename(file_path) ext = os.path.splitext(file_name)[1] mime_map = { '.txt': 'text/plain', '.py': 'text/plain', '.xlsx': 'text/plain', '.csv': 'text/plain', '.htm': 'text/html', '.md': 'text/markdown', '.wav': 'audio/wav' } mime_type = mime_map.get(ext, 'application/octet-stream') return f'{file_name}' except: return '' def get_file_size(file_path): """Get file size in bytes.""" return os.path.getsize(file_path) def FileSidebar(): """ Renders .md files, providing open/view/delete/run logic in the sidebar. """ all_files = glob.glob("*.md") # Exclude short-named or special files if needed: all_files = [f for f in all_files if len(os.path.splitext(f)[0]) >= 5] all_files.sort(key=lambda x: (os.path.splitext(x)[1], x), reverse=True) Files1, Files2 = st.sidebar.columns(2) with Files1: if st.button("🗑 Delete All"): for file in all_files: os.remove(file) st.rerun() with Files2: if st.button("⬇️ Download"): zip_file = create_zip_of_files(all_files) st.sidebar.markdown(get_zip_download_link(zip_file), unsafe_allow_html=True) file_contents = '' file_name = '' next_action = '' for file in all_files: col1, col2, col3, col4, col5 = st.sidebar.columns([1,6,1,1,1]) with col1: if st.button("🌐", key="md_"+file): file_contents = load_file(file) file_name = file next_action = 'md' st.session_state['next_action'] = next_action with col2: st.markdown(get_table_download_link(file), unsafe_allow_html=True) with col3: if st.button("📂", key="open_"+file): file_contents = load_file(file) file_name = file next_action = 'open' st.session_state['lastfilename'] = file st.session_state['filename'] = file st.session_state['filetext'] = file_contents st.session_state['next_action'] = next_action with col4: if st.button("▶️", key="read_"+file): file_contents = load_file(file) file_name = file next_action = 'search' st.session_state['next_action'] = next_action with col5: if st.button("🗑", key="delete_"+file): os.remove(file) st.rerun() if file_contents: if next_action == 'open': open1, open2 = st.columns([0.8, 0.2]) with open1: file_name_input = st.text_input('File Name:', file_name, key='file_name_input') file_content_area = st.text_area('File Contents:', file_contents, height=300, key='file_content_area') if st.button('💾 Save File'): with open(file_name_input, 'w', encoding='utf-8') as f: f.write(file_content_area) st.markdown(f'Saved {file_name_input} successfully.') elif next_action == 'search': file_content_area = st.text_area("File Contents:", file_contents, height=500) user_prompt = PromptPrefix2 + file_contents st.markdown(user_prompt) if st.button('🔍Re-Code'): search_arxiv(file_contents) elif next_action == 'md': st.markdown(file_contents) SpeechSynthesis(file_contents) if st.button("🔍Run"): st.write("Running GPT logic placeholder...") ######################################################################################## # 4) SCORING / GLOSSARIES ######################################################################################## score_dir = "scores" os.makedirs(score_dir, exist_ok=True) def generate_key(label, header, idx): return f"{header}_{label}_{idx}_key" def update_score(key, increment=1): """ Track a 'score' for each glossary item or term, saved in JSON per key. """ score_file = os.path.join(score_dir, f"{key}.json") if os.path.exists(score_file): with open(score_file, "r") as file: score_data = json.load(file) else: score_data = {"clicks": 0, "score": 0} score_data["clicks"] += increment score_data["score"] += increment with open(score_file, "w") as file: json.dump(score_data, file) return score_data["score"] def load_score(key): file_path = os.path.join(score_dir, f"{key}.json") if os.path.exists(file_path): with open(file_path, "r") as file: score_data = json.load(file) return score_data["score"] return 0 def display_buttons_with_scores(num_columns_text): """ Show glossary items as clickable buttons that increment a 'score'. """ game_emojis = { "Dungeons and Dragons": "🐉", "Call of Cthulhu": "🐙", "GURPS": "🎲", "Pathfinder": "🗺️", "Kindred of the East": "🌅", "Changeling": "🍃", } topic_emojis = { "Core Rulebooks": "📚", "Maps & Settings": "🗺️", "Game Mechanics & Tools": "⚙️", "Monsters & Adversaries": "👹", "Campaigns & Adventures": "📜", "Creatives & Assets": "🎨", "Game Master Resources": "🛠️", "Lore & Background": "📖", "Character Development": "🧍", "Homebrew Content": "🔧", "General Topics": "🌍", } for category, games in roleplaying_glossary.items(): category_emoji = topic_emojis.get(category, "🔍") st.markdown(f"## {category_emoji} {category}") for game, terms in games.items(): game_emoji = game_emojis.get(game, "🎮") for term in terms: key = f"{category}_{game}_{term}".replace(' ', '_').lower() score_val = load_score(key) if st.button(f"{game_emoji} {category} {game} {term} {score_val}", key=key): newscore = update_score(key.replace('?', '')) st.markdown(f"Scored **{category} - {game} - {term}** -> {newscore}") ######################################################################################## # 5) IMAGES & VIDEOS ######################################################################################## def display_images_and_wikipedia_summaries(num_columns=4): """Display .png images in a grid, referencing the name as a 'keyword'.""" image_files = [f for f in os.listdir('.') if f.endswith('.png')] if not image_files: st.write("No PNG images found in the current directory.") return image_files_sorted = sorted(image_files, key=lambda x: len(x.split('.')[0])) cols = st.columns(num_columns) col_index = 0 for image_file in image_files_sorted: with cols[col_index % num_columns]: try: image = Image.open(image_file) st.image(image, use_column_width=True) k = image_file.split('.')[0] display_glossary_entity(k) image_text_input = st.text_input(f"Prompt for {image_file}", key=f"image_prompt_{image_file}") if image_text_input: response = process_image(image_file, image_text_input) st.markdown(response) except: st.write(f"Could not open {image_file}") col_index += 1 def display_videos_and_links(num_columns=4): """Displays all .mp4/.webm in a grid, plus text input for prompts.""" video_files = [f for f in os.listdir('.') if f.endswith(('.mp4', '.webm'))] if not video_files: st.write("No MP4 or WEBM videos found in the current directory.") return video_files_sorted = sorted(video_files, key=lambda x: len(x.split('.')[0])) cols = st.columns(num_columns) col_index = 0 for video_file in video_files_sorted: with cols[col_index % num_columns]: k = video_file.split('.')[0] st.video(video_file, format='video/mp4', start_time=0) display_glossary_entity(k) video_text_input = st.text_input(f"Video Prompt for {video_file}", key=f"video_prompt_{video_file}") if video_text_input: try: seconds_per_frame = 10 process_video(video_file, seconds_per_frame) except ValueError: st.error("Invalid input for seconds per frame!") col_index += 1 ######################################################################################## # 6) MERMAID ######################################################################################## def generate_mermaid_html(mermaid_code: str) -> str: """ Returns HTML that centers the Mermaid diagram, loading from a CDN. """ return f"""
{mermaid_code}
""" def append_model_param(url: str, model_selected: bool) -> str: """ If user checks 'Append ?model=1', we append &model=1 or ?model=1 if not present. """ if not model_selected: return url delimiter = "&" if "?" in url else "?" return f"{url}{delimiter}model=1" def inject_base_url(url: str) -> str: """ If a link does not start with http, prepend your BASE_URL so it becomes an absolute link to huggingface.co/spaces/... """ if url.startswith("http"): return url return f"{BASE_URL}{url}" # We use 2-parameter click lines for Mermaid 11.4.1 compatibility: DEFAULT_MERMAID = r""" flowchart LR U((User 😎)) -- "Talk 🗣️" --> LLM[LLM Agent 🤖\nExtract Info] click U "?q=U" _self click LLM "?q=LLM%20Agent%20Extract%20Info" _blank LLM -- "Query 🔍" --> HS[Hybrid Search 🔎\nVector+NER+Lexical] click HS "?q=Hybrid%20Search%20Vector%20NER%20Lexical" _blank HS -- "Reason 🤔" --> RE[Reasoning Engine 🛠️\nNeuralNetwork+Medical] click RE "?q=R" _blank RE -- "Link 📡" --> KG((Knowledge Graph 📚\nOntology+GAR+RAG)) click KG "?q=K" _blank """ # New function to generate Mermaid diagram for each paper def generate_mermaid_code(paper): title = paper.split('|')[1].strip() concepts = paper.split('\n') mermaid_code = f"flowchart TD\n A[{title}]" for concept in concepts[1:]: # Skip the title if concept.strip(): mermaid_code += f" --> {concept.strip().replace('*', '').replace(',', '').replace(' ', '')}" return mermaid_code ######################################################################################## # 7) MAIN UI ######################################################################################## def main(): st.set_page_config(page_title="Mermaid + Two-Parameter Click + LetterMap", layout="wide") # Define a list of 10 slides (each with left and right pages), built from 40 paper entries. slides = [ { "left": """ ### 07 Sep 2023 | [Structured Chain-of-Thought Prompting for Code Generation](https://arxiv.org/abs/2305.06599) | [⬇️](https://arxiv.org/pdf/2305.06599) *Jia Li, Ge Li, Yongmin Li, Zhi Jin* ### 15 Nov 2023 | [Eliminating Reasoning via Inferring with Planning: A New Framework to Guide LLMs' Non-linear Thinking](https://arxiv.org/abs/2310.12342) | [⬇️](https://arxiv.org/pdf/2310.12342) *Yongqi Tong, Yifan Wang, Dawei Li, Sizhe Wang, Zi Lin, Simeng Han, Jingbo Shang* """, "right": """ ### 04 Jun 2023 | [Evaluating and Improving Tool-Augmented Computation-Intensive Math Reasoning](https://arxiv.org/abs/2306.02408) | [⬇️](https://arxiv.org/pdf/2306.02408) *Beichen Zhang, Kun Zhou, Xilin Wei, Wayne Xin Zhao, Jing Sha, Shijin Wang, Ji-Rong Wen* ### 23 Oct 2023 | [Program of Thoughts Prompting: Disentangling Computation from Reasoning for Numerical Reasoning Tasks](https://arxiv.org/abs/2211.12588) | [⬇️](https://arxiv.org/pdf/2211.12588) *Wenhu Chen, Xueguang Ma, Xinyi Wang, William W. Cohen* """ }, { "left": """ ### 04 Jan 2024 | [Text2MDT: Extracting Medical Decision Trees from Medical Texts](https://arxiv.org/abs/2401.02034) | [⬇️](https://arxiv.org/pdf/2401.02034) *Wei Zhu, Wenfeng Li, Xing Tian, Pengfei Wang, Xiaoling Wang, Jin Chen, Yuanbin Wu, Yuan Ni, Guotong Xie* ### 21 Dec 2023 | [Automating Human Tutor-Style Programming Feedback: Leveraging GPT-4 Tutor Model for Hint Generation and GPT-3.5 Student Model for Hint Validation](https://arxiv.org/abs/2310.03780) | [⬇️](https://arxiv.org/pdf/2310.03780) *Tung Phung, Victor-Alexandru Pădurean, Anjali Singh, Christopher Brooks, José Cambronero, Sumit Gulwani, Adish Singla, Gustavo Soares* """, "right": """ ### 04 Feb 2024 | [STEVE-1: A Generative Model for Text-to-Behavior in Minecraft](https://arxiv.org/abs/2306.00937) | [⬇️](https://arxiv.org/pdf/2306.00937) *Shalev Lifshitz, Keiran Paster, Harris Chan, Jimmy Ba, Sheila McIlraith* ### 20 May 2021 | [Data-Efficient Reinforcement Learning with Self-Predictive Representations](https://arxiv.org/abs/2007.05929) | [⬇️](https://arxiv.org/pdf/2007.05929) *Max Schwarzer, Ankesh Anand, Rishab Goel, R Devon Hjelm, Aaron Courville, Philip Bachman* """ }, { "left": """ ### 06 Jul 2022 | [Learning Invariant World State Representations with Predictive Coding](https://arxiv.org/abs/2207.02972) | [⬇️](https://arxiv.org/pdf/2207.02972) *Avi Ziskind, Sujeong Kim, and Giedrius T. Burachas* ### 10 Nov 2023 | [State2Explanation: Concept-Based Explanations to Benefit Agent Learning and User Understanding](https://arxiv.org/abs/2309.12482) | [⬇️](https://arxiv.org/pdf/2309.12482) *Devleena Das, Sonia Chernova, Been Kim* """, "right": """ ### 17 May 2023 | [LeTI: Learning to Generate from Textual Interactions](https://arxiv.org/abs/2305.10314) | [⬇️](https://arxiv.org/pdf/2305.10314) *Xingyao Wang, Hao Peng, Reyhaneh Jabbarvand, Heng Ji* ### 01 Dec 2022 | [A General Purpose Supervisory Signal for Embodied Agents](https://arxiv.org/abs/2212.01186) | [⬇️](https://arxiv.org/pdf/2212.01186) *Kunal Pratap Singh, Jordi Salvador, Luca Weihs, Aniruddha Kembhavi* """ }, { "left": """ ### 16 May 2023 | [RAMario: Experimental Approach to Reptile Algorithm -- Reinforcement Learning for Mario](https://arxiv.org/abs/2305.09655) | [⬇️](https://arxiv.org/pdf/2305.09655) *Sanyam Jain* ### 31 Mar 2023 | [Pair Programming with Large Language Models for Sampling and Estimation of Copulas](https://arxiv.org/abs/2303.18116) | [⬇️](https://arxiv.org/pdf/2303.18116) *Jan Górecki* """, "right": """ ### 28 Jun 2023 | [AssistGPT: A General Multi-modal Assistant that can Plan, Execute, Inspect, and Learn](https://arxiv.org/abs/2306.08640) | [⬇️](https://arxiv.org/pdf/2306.08640) *Difei Gao, Lei Ji, Luowei Zhou, Kevin Qinghong Lin, Joya Chen, Zihan Fan, Mike Zheng Shou* ### 07 Nov 2023 | [Selective Visual Representations Improve Convergence and Generalization for Embodied AI](https://arxiv.org/abs/2311.04193) | [⬇️](https://arxiv.org/pdf/2311.04193) *Ainaz Eftekhar, Kuo-Hao Zeng, Jiafei Duan, Ali Farhadi, Ani Kembhavi, Ranjay Krishna* """ }, { "left": """ ### 16 Feb 2023 | [Foundation Models for Natural Language Processing -- Pre-trained Language Models Integrating Media](https://arxiv.org/abs/2302.08575) | [⬇️](https://arxiv.org/pdf/2302.08575) *Gerhard Paaß and Sven Giesselbach* ### 21 Dec 2023 | [Automating Human Tutor-Style Programming Feedback: Leveraging GPT-4 Tutor Model for Hint Generation and GPT-3.5 Student Model for Hint Validation](https://arxiv.org/abs/2310.03780) | [⬇️](https://arxiv.org/pdf/2310.03780) *Tung Phung, Victor-Alexandru Pădurean, Anjali Singh, Christopher Brooks, José Cambronero, Sumit Gulwani, Adish Singla, Gustavo Soares* """, "right": """ ### 04 Feb 2024 | [STEVE-1: A Generative Model for Text-to-Behavior in Minecraft](https://arxiv.org/abs/2306.00937) | [⬇️](https://arxiv.org/pdf/2306.00937) *Shalev Lifshitz, Keiran Paster, Harris Chan, Jimmy Ba, Sheila McIlraith* ### 20 May 2021 | [Data-Efficient Reinforcement Learning with Self-Predictive Representations](https://arxiv.org/abs/2007.05929) | [⬇️](https://arxiv.org/pdf/2007.05929) *Max Schwarzer, Ankesh Anand, Rishab Goel, R Devon Hjelm, Aaron Courville, Philip Bachman* """ }, { "left": """ ### 06 Jul 2022 | [Learning Invariant World State Representations with Predictive Coding](https://arxiv.org/abs/2207.02972) | [⬇️](https://arxiv.org/pdf/2207.02972) *Avi Ziskind, Sujeong Kim, and Giedrius T. Burachas* ### 10 Nov 2023 | [State2Explanation: Concept-Based Explanations to Benefit Agent Learning and User Understanding](https://arxiv.org/abs/2309.12482) | [⬇️](https://arxiv.org/pdf/2309.12482) *Devleena Das, Sonia Chernova, Been Kim* """, "right": """ ### 17 May 2023 | [LeTI: Learning to Generate from Textual Interactions](https://arxiv.org/abs/2305.10314) | [⬇️](https://arxiv.org/pdf/2305.10314) *Xingyao Wang, Hao Peng, Reyhaneh Jabbarvand, Heng Ji* ### 01 Dec 2022 | [A General Purpose Supervisory Signal for Embodied Agents](https://arxiv.org/abs/2212.01186) | [⬇️](https://arxiv.org/pdf/2212.01186) *Kunal Pratap Singh, Jordi Salvador, Luca Weihs, Aniruddha Kembhavi* """ }, { "left": """ ### 16 May 2023 | [RAMario: Experimental Approach to Reptile Algorithm -- Reinforcement Learning for Mario](https://arxiv.org/abs/2305.09655) | [⬇️](https://arxiv.org/pdf/2305.09655) *Sanyam Jain* ### 31 Mar 2023 | [Pair Programming with Large Language Models for Sampling and Estimation of Copulas](https://arxiv.org/abs/2303.18116) | [⬇️](https://arxiv.org/pdf/2303.18116) *Jan Górecki* """, "right": """ ### 28 Jun 2023 | [AssistGPT: A General Multi-modal Assistant that can Plan, Execute, Inspect, and Learn](https://arxiv.org/abs/2306.08640) | [⬇️](https://arxiv.org/pdf/2306.08640) *Difei Gao, Lei Ji, Luowei Zhou, Kevin Qinghong Lin, Joya Chen, Zihan Fan, Mike Zheng Shou* ### 07 Nov 2023 | [Selective Visual Representations Improve Convergence and Generalization for Embodied AI](https://arxiv.org/abs/2311.04193) | [⬇️](https://arxiv.org/pdf/2311.04193) *Ainaz Eftekhar, Kuo-Hao Zeng, Jiafei Duan, Ali Farhadi, Ani Kembhavi, Ranjay Krishna* """ }, { "left": """ ### 16 Feb 2023 | [Foundation Models for Natural Language Processing -- Pre-trained Language Models Integrating Media](https://arxiv.org/abs/2302.08575) | [⬇️](https://arxiv.org/pdf/2302.08575) *Gerhard Paaß and Sven Giesselbach* ### 21 Dec 2023 | [Automating Human Tutor-Style Programming Feedback: Leveraging GPT-4 Tutor Model for Hint Generation and GPT-3.5 Student Model for Hint Validation](https://arxiv.org/abs/2310.03780) | [⬇️](https://arxiv.org/pdf/2310.03780) *Tung Phung, Victor-Alexandru Pădurean, Anjali Singh, Christopher Brooks, José Cambronero, Sumit Gulwani, Adish Singla, Gustavo Soares* """, "right": """ ### 04 Feb 2024 | [STEVE-1: A Generative Model for Text-to-Behavior in Minecraft](https://arxiv.org/abs/2306.00937) | [⬇️](https://arxiv.org/pdf/2306.00937) *Shalev Lifshitz, Keiran Paster, Harris Chan, Jimmy Ba, Sheila McIlraith* ### 20 May 2021 | [Data-Efficient Reinforcement Learning with Self-Predictive Representations](https://arxiv.org/abs/2007.05929) | [⬇️](https://arxiv.org/pdf/2007.05929) *Max Schwarzer, Ankesh Anand, Rishab Goel, R Devon Hjelm, Aaron Courville, Philip Bachman* """ } ] # Initialize slide index in session state if not already set if "slide_idx" not in st.session_state: st.session_state.slide_idx = 0 num_slides = len(slides) current_slide = slides[st.session_state.slide_idx] # Display slide header (e.g., "Slide 1 of 10") st.markdown(f"## Slide {st.session_state.slide_idx + 1} of {num_slides}") # Display left and right pages side by side col_left, col_right = st.columns(2) with col_left: st.markdown("### Left Page") for paper in current_slide["left"].split('\n\n'): if paper.strip(): st.markdown(paper, unsafe_allow_html=True) mermaid_diagram = generate_mermaid_code(paper) st.markdown(f"```mermaid\n{mermaid_diagram}\n```", unsafe_allow_html=True) with col_right: st.markdown("### Right Page") for paper in current_slide["right"].split('\n\n'): if paper.strip(): st.markdown(paper, unsafe_allow_html=True) mermaid_diagram = generate_mermaid_code(paper) st.markdown(f"```mermaid\n{mermaid_diagram}\n```", unsafe_allow_html=True) # Countdown timer (15 seconds) for auto-advancement for remaining in range(15, 0, -1): st.markdown(f"**Advancing in {remaining} seconds...**") time.sleep(1) # Advance to the next slide (wrap around at the end) st.session_state.slide_idx = (st.session_state.slide_idx + 1) % num_slides # Rerun the app to display the next slide st.rerun() if __name__ == "__main__": main()