awacke1 commited on
Commit
20fc1c2
·
verified ·
1 Parent(s): d5eb46e

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +655 -0
app.py ADDED
@@ -0,0 +1,655 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+
3
+ import os
4
+ import re
5
+ import glob
6
+ import json
7
+ import base64
8
+ import zipfile
9
+ import random
10
+ import requests
11
+ import openai
12
+ from PIL import Image
13
+ from urllib.parse import quote
14
+
15
+ import streamlit as st
16
+ import streamlit.components.v1 as components
17
+
18
+ # For demonstration, we'll import from huggingface_hub
19
+ from huggingface_hub import InferenceClient
20
+
21
+ # -----------------------------------------------------
22
+ # 1) Ensure our default MarkdownCode.md and MermaidCode.md exist
23
+ # If not, create them and restart.
24
+ # -----------------------------------------------------
25
+ if not os.path.exists("MarkdownCode.md"):
26
+ with open("MarkdownCode.md", 'w', encoding='utf-8') as f:
27
+ f.write("# Default Markdown\nThis is a default Markdown file.")
28
+ st.experimental_rerun()
29
+
30
+ if not os.path.exists("MermaidCode.md"):
31
+ with open("MermaidCode.md", 'w', encoding='utf-8') as f:
32
+ f.write("""# Default Mermaid
33
+ flowchart LR
34
+ A[Default] --> B[Example]
35
+ click A "/?q=Default" _self
36
+ click B "/?q=Example" _self
37
+ """)
38
+ st.experimental_rerun()
39
+
40
+ # ----------------------------
41
+ # Placeholder data structures
42
+ # ----------------------------
43
+ PromptPrefix = "AI-Search: "
44
+ PromptPrefix2 = "AI-Refine: "
45
+ PromptPrefix3 = "AI-JS: "
46
+
47
+ roleplaying_glossary = {
48
+ "Core Rulebooks": {
49
+ "Dungeons and Dragons": ["Player's Handbook", "Dungeon Master's Guide", "Monster Manual"],
50
+ "GURPS": ["Basic Set Characters", "Basic Set Campaigns"]
51
+ },
52
+ "Campaigns & Adventures": {
53
+ "Pathfinder": ["Rise of the Runelords", "Curse of the Crimson Throne"]
54
+ }
55
+ }
56
+
57
+ transhuman_glossary = {
58
+ "Neural Interfaces": ["Cortex Jack", "Mind-Machine Fusion"],
59
+ "Cybernetics": ["Robotic Limbs", "Augmented Eyes"],
60
+ }
61
+
62
+ # ------------
63
+ # Stub Methods
64
+ # ------------
65
+ def process_text(text):
66
+ st.write(f"process_text called with: {text}")
67
+
68
+ def process_text2(text_input):
69
+ return f"[process_text2 placeholder] Received: {text_input}"
70
+
71
+ def search_arxiv(text):
72
+ st.write(f"search_arxiv called with: {text}")
73
+
74
+ def SpeechSynthesis(text):
75
+ st.write(f"SpeechSynthesis called with: {text}")
76
+
77
+ def process_image(image_file, prompt):
78
+ return f"[process_image placeholder] Processing {image_file} with prompt: {prompt}"
79
+
80
+ def process_video(video_file, seconds_per_frame):
81
+ st.write(f"[process_video placeholder] Video: {video_file}, seconds/frame: {seconds_per_frame}")
82
+
83
+ def search_glossary(content):
84
+ st.write(f"search_glossary called with: {content}")
85
+
86
+ API_URL = "https://huggingface-inference-endpoint-placeholder"
87
+ API_KEY = "hf_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
88
+
89
+ @st.cache_resource
90
+ def InferenceLLM(prompt):
91
+ return f"[InferenceLLM placeholder response to prompt: {prompt}]"
92
+
93
+
94
+ # --------------------------------------
95
+ # Display Entities & Glossary Functions
96
+ # --------------------------------------
97
+ @st.cache_resource
98
+ def display_glossary_entity(k):
99
+ search_urls = {
100
+ "🚀🌌ArXiv": lambda k: f"/?q={quote(k)}",
101
+ "🃏Analyst": lambda k: f"/?q={quote(k)}-{quote(PromptPrefix)}",
102
+ "📚PyCoder": lambda k: f"/?q={quote(k)}-{quote(PromptPrefix2)}",
103
+ "🔬JSCoder": lambda k: f"/?q={quote(k)}-{quote(PromptPrefix3)}",
104
+ "📖": lambda k: f"https://en.wikipedia.org/wiki/{quote(k)}",
105
+ "🔍": lambda k: f"https://www.google.com/search?q={quote(k)}",
106
+ "🔎": lambda k: f"https://www.bing.com/search?q={quote(k)}",
107
+ "🎥": lambda k: f"https://www.youtube.com/results?search_query={quote(k)}",
108
+ "🐦": lambda k: f"https://twitter.com/search?q={quote(k)}",
109
+ }
110
+ links_md = ' '.join([f"[{emoji}]({url(k)})" for emoji, url in search_urls.items()])
111
+ st.markdown(f"**{k}** <small>{links_md}</small>", unsafe_allow_html=True)
112
+
113
+
114
+ @st.cache_resource
115
+ def display_glossary_grid(roleplaying_glossary):
116
+ search_urls = {
117
+ "🚀🌌ArXiv": lambda k: f"/?q={quote(k)}",
118
+ "🃏Analyst": lambda k: f"/?q={quote(k)}-{quote(PromptPrefix)}",
119
+ "📚PyCoder": lambda k: f"/?q={quote(k)}-{quote(PromptPrefix2)}",
120
+ "🔬JSCoder": lambda k: f"/?q={quote(k)}-{quote(PromptPrefix3)}",
121
+ "📖": lambda k: f"https://en.wikipedia.org/wiki/{quote(k)}",
122
+ "🔍": lambda k: f"https://www.google.com/search?q={quote(k)}",
123
+ "🔎": lambda k: f"https://www.bing.com/search?q={quote(k)}",
124
+ "🎥": lambda k: f"https://www.youtube.com/results?search_query={quote(k)}",
125
+ "🐦": lambda k: f"https://twitter.com/search?q={quote(k)}",
126
+ }
127
+
128
+ for category, details in roleplaying_glossary.items():
129
+ st.write(f"### {category}")
130
+ cols = st.columns(len(details))
131
+ for idx, (game, terms) in enumerate(details.items()):
132
+ with cols[idx]:
133
+ st.markdown(f"#### {game}")
134
+ for term in terms:
135
+ links_md = ' '.join([f"[{emoji}]({url(term)})" for emoji, url in search_urls.items()])
136
+ st.markdown(f"**{term}** <small>{links_md}</small>", unsafe_allow_html=True)
137
+
138
+
139
+ # --------------------
140
+ # File-Handling Logic
141
+ # --------------------
142
+ def load_file(file_path):
143
+ try:
144
+ with open(file_path, "r", encoding='utf-8') as f:
145
+ return f.read()
146
+ except:
147
+ return ""
148
+
149
+ @st.cache_resource
150
+ def create_zip_of_files(files):
151
+ zip_name = "Arxiv-Paper-Search-QA-RAG-Streamlit-Gradio-AP.zip"
152
+ with zipfile.ZipFile(zip_name, 'w') as zipf:
153
+ for file in files:
154
+ zipf.write(file)
155
+ return zip_name
156
+
157
+ @st.cache_resource
158
+ def get_zip_download_link(zip_file):
159
+ with open(zip_file, 'rb') as f:
160
+ data = f.read()
161
+ b64 = base64.b64encode(data).decode()
162
+ href = f'<a href="data:application/zip;base64,{b64}" download="{zip_file}">Download All</a>'
163
+ return href
164
+
165
+ def get_table_download_link(file_path):
166
+ try:
167
+ with open(file_path, 'r', encoding='utf-8') as file:
168
+ data = file.read()
169
+ b64 = base64.b64encode(data.encode()).decode()
170
+ file_name = os.path.basename(file_path)
171
+ ext = os.path.splitext(file_name)[1]
172
+ mime_map = {
173
+ '.txt': 'text/plain',
174
+ '.py': 'text/plain',
175
+ '.xlsx': 'text/plain',
176
+ '.csv': 'text/plain',
177
+ '.htm': 'text/html',
178
+ '.md': 'text/markdown',
179
+ '.wav': 'audio/wav'
180
+ }
181
+ mime_type = mime_map.get(ext, 'application/octet-stream')
182
+ href = f'<a href="data:{mime_type};base64,{b64}" target="_blank" download="{file_name}">{file_name}</a>'
183
+ return href
184
+ except:
185
+ return ''
186
+
187
+ def get_file_size(file_path):
188
+ return os.path.getsize(file_path)
189
+
190
+ def compare_and_delete_files(files):
191
+ if not files:
192
+ st.warning("No files to compare.")
193
+ return
194
+ file_sizes = {}
195
+ for file in files:
196
+ size = os.path.getsize(file)
197
+ file_sizes.setdefault(size, []).append(file)
198
+ for size, paths in file_sizes.items():
199
+ if len(paths) > 1:
200
+ latest_file = max(paths, key=os.path.getmtime)
201
+ for file in paths:
202
+ if file != latest_file:
203
+ os.remove(file)
204
+ st.success(f"Deleted {file} as a duplicate.")
205
+ st.rerun()
206
+
207
+ def FileSidebar():
208
+ """
209
+ Renders the file sidebar with all the open/view/run/delete logic.
210
+ Excludes README.md from the file list.
211
+ """
212
+ all_files = glob.glob("*.md")
213
+ # Exclude README.md
214
+ all_files = [f for f in all_files if f != 'README.md']
215
+ # Filter out short-named files if desired
216
+ all_files = [file for file in all_files if len(os.path.splitext(file)[0]) >= 5]
217
+ all_files.sort(key=lambda x: (os.path.splitext(x)[1], x), reverse=True)
218
+
219
+ # Buttons for "Delete All" and "Download"
220
+ Files1, Files2 = st.sidebar.columns(2)
221
+ with Files1:
222
+ if st.button("🗑 Delete All"):
223
+ for file in all_files:
224
+ os.remove(file)
225
+ st.rerun()
226
+ with Files2:
227
+ if st.button("⬇️ Download"):
228
+ zip_file = create_zip_of_files(all_files)
229
+ st.sidebar.markdown(get_zip_download_link(zip_file), unsafe_allow_html=True)
230
+
231
+ file_contents = ''
232
+ file_name = ''
233
+ next_action = ''
234
+
235
+ # Each file row
236
+ for file in all_files:
237
+ col1, col2, col3, col4, col5 = st.sidebar.columns([1,6,1,1,1])
238
+ with col1:
239
+ if st.button("🌐", key="md_"+file):
240
+ file_contents = load_file(file)
241
+ file_name = file
242
+ next_action = 'md'
243
+ st.session_state['next_action'] = next_action
244
+ with col2:
245
+ st.markdown(get_table_download_link(file), unsafe_allow_html=True)
246
+ with col3:
247
+ if st.button("📂", key="open_"+file):
248
+ file_contents = load_file(file)
249
+ file_name = file
250
+ next_action = 'open'
251
+ st.session_state['lastfilename'] = file
252
+ st.session_state['filename'] = file
253
+ st.session_state['filetext'] = file_contents
254
+ st.session_state['next_action'] = next_action
255
+ with col4:
256
+ if st.button("▶️", key="read_"+file):
257
+ file_contents = load_file(file)
258
+ file_name = file
259
+ next_action = 'search'
260
+ st.session_state['next_action'] = next_action
261
+ with col5:
262
+ if st.button("🗑", key="delete_"+file):
263
+ os.remove(file)
264
+ st.rerun()
265
+
266
+ # Duplicate detection
267
+ file_sizes = [get_file_size(file) for file in all_files]
268
+ previous_size = None
269
+ st.sidebar.title("File Operations")
270
+ for file, size in zip(all_files, file_sizes):
271
+ duplicate_flag = "🚩" if size == previous_size else ""
272
+ with st.sidebar.expander(f"File: {file} {duplicate_flag}"):
273
+ st.text(f"Size: {size} bytes")
274
+ if st.button("View", key=f"view_{file}"):
275
+ try:
276
+ with open(file, "r", encoding='utf-8') as f:
277
+ file_content = f.read()
278
+ st.code(file_content, language="markdown")
279
+ except UnicodeDecodeError:
280
+ st.error("Failed to decode the file with UTF-8.")
281
+ if st.button("Delete", key=f"delete3_{file}"):
282
+ os.remove(file)
283
+ st.rerun()
284
+ previous_size = size
285
+
286
+ # If we have loaded something
287
+ if len(file_contents) > 0:
288
+ if next_action == 'open':
289
+ open1, open2 = st.columns([0.8, 0.2])
290
+ with open1:
291
+ file_name_input = st.text_input('File Name:', file_name, key='file_name_input')
292
+ file_content_area = st.text_area('File Contents:', file_contents, height=300, key='file_content_area')
293
+
294
+ if st.button('💾 Save File'):
295
+ with open(file_name_input, 'w', encoding='utf-8') as f:
296
+ f.write(file_content_area)
297
+ st.markdown(f'Saved {file_name_input} successfully.')
298
+
299
+ elif next_action == 'search':
300
+ file_content_area = st.text_area("File Contents:", file_contents, height=500)
301
+ user_prompt = PromptPrefix2 + file_contents
302
+ st.markdown(user_prompt)
303
+ if st.button('🔍Re-Code'):
304
+ search_arxiv(file_contents)
305
+
306
+ elif next_action == 'md':
307
+ st.markdown(file_contents)
308
+ SpeechSynthesis(file_contents)
309
+ if st.button('🔍Run'):
310
+ st.write("Running GPT logic placeholder...")
311
+
312
+
313
+ # ---------------------------
314
+ # Basic Scoring / Glossaries
315
+ # ---------------------------
316
+ score_dir = "scores"
317
+ os.makedirs(score_dir, exist_ok=True)
318
+
319
+ def generate_key(label, header, idx):
320
+ return f"{header}_{label}_{idx}_key"
321
+
322
+ def update_score(key, increment=1):
323
+ score_file = os.path.join(score_dir, f"{key}.json")
324
+ if os.path.exists(score_file):
325
+ with open(score_file, "r") as file:
326
+ score_data = json.load(file)
327
+ else:
328
+ score_data = {"clicks": 0, "score": 0}
329
+ score_data["clicks"] += increment
330
+ score_data["score"] += increment
331
+ with open(score_file, "w") as file:
332
+ json.dump(score_data, file)
333
+ return score_data["score"]
334
+
335
+ def load_score(key):
336
+ score_file = os.path.join(score_dir, f"{key}.json")
337
+ if os.path.exists(score_file):
338
+ with open(score_file, "r") as file:
339
+ score_data = json.load(file)
340
+ return score_data["score"]
341
+ return 0
342
+
343
+ def display_buttons_with_scores(num_columns_text):
344
+ game_emojis = {
345
+ "Dungeons and Dragons": "🐉",
346
+ "Call of Cthulhu": "🐙",
347
+ "GURPS": "🎲",
348
+ "Pathfinder": "🗺️",
349
+ "Kindred of the East": "🌅",
350
+ "Changeling": "🍃",
351
+ }
352
+
353
+ topic_emojis = {
354
+ "Core Rulebooks": "📚",
355
+ "Maps & Settings": "🗺️",
356
+ "Game Mechanics & Tools": "⚙️",
357
+ "Monsters & Adversaries": "👹",
358
+ "Campaigns & Adventures": "📜",
359
+ "Creatives & Assets": "🎨",
360
+ "Game Master Resources": "🛠️",
361
+ "Lore & Background": "📖",
362
+ "Character Development": "🧍",
363
+ "Homebrew Content": "🔧",
364
+ "General Topics": "🌍",
365
+ }
366
+
367
+ for category, games in roleplaying_glossary.items():
368
+ category_emoji = topic_emojis.get(category, "🔍")
369
+ st.markdown(f"## {category_emoji} {category}")
370
+ for game, terms in games.items():
371
+ game_emoji = game_emojis.get(game, "🎮")
372
+ for term in terms:
373
+ key = f"{category}_{game}_{term}".replace(' ', '_').lower()
374
+ score = load_score(key)
375
+ if st.button(f"{game_emoji} {category} {game} {term} {score}", key=key):
376
+ newscore = update_score(key.replace('?',''))
377
+ st.markdown(f"Scored **{category} - {game} - {term}** -> {newscore}")
378
+
379
+
380
+ # --------------------
381
+ # Image & Video Grids
382
+ # --------------------
383
+ def display_images_and_wikipedia_summaries(num_columns=4):
384
+ image_files = [f for f in os.listdir('.') if f.endswith('.png')]
385
+ if not image_files:
386
+ st.write("No PNG images found in the current directory.")
387
+ return
388
+
389
+ image_files_sorted = sorted(image_files, key=lambda x: len(x.split('.')[0]))
390
+ cols = st.columns(num_columns)
391
+ col_index = 0
392
+
393
+ for image_file in image_files_sorted:
394
+ with cols[col_index % num_columns]:
395
+ try:
396
+ image = Image.open(image_file)
397
+ st.image(image, use_column_width=True)
398
+ k = image_file.split('.')[0]
399
+ display_glossary_entity(k)
400
+ image_text_input = st.text_input(f"Prompt for {image_file}", key=f"image_prompt_{image_file}")
401
+ if len(image_text_input) > 0:
402
+ response = process_image(image_file, image_text_input)
403
+ st.markdown(response)
404
+ except:
405
+ st.write(f"Could not open {image_file}")
406
+ col_index += 1
407
+
408
+ def display_videos_and_links(num_columns=4):
409
+ video_files = [f for f in os.listdir('.') if f.endswith(('.mp4', '.webm'))]
410
+ if not video_files:
411
+ st.write("No MP4 or WEBM videos found in the current directory.")
412
+ return
413
+
414
+ video_files_sorted = sorted(video_files, key=lambda x: len(x.split('.')[0]))
415
+ cols = st.columns(num_columns)
416
+ col_index = 0
417
+
418
+ for video_file in video_files_sorted:
419
+ with cols[col_index % num_columns]:
420
+ k = video_file.split('.')[0]
421
+ st.video(video_file, format='video/mp4', start_time=0)
422
+ display_glossary_entity(k)
423
+ video_text_input = st.text_input(f"Video Prompt for {video_file}", key=f"video_prompt_{video_file}")
424
+ if video_text_input:
425
+ try:
426
+ seconds_per_frame = 10
427
+ process_video(video_file, seconds_per_frame)
428
+ except ValueError:
429
+ st.error("Invalid input for seconds per frame!")
430
+ col_index += 1
431
+
432
+
433
+ # -------------------------------------
434
+ # Query Param Helpers
435
+ # -------------------------------------
436
+ def get_all_query_params(key):
437
+ return st.query_params.get(key, [])
438
+
439
+ def clear_query_params():
440
+ st.query_params
441
+
442
+
443
+ def display_content_or_image(query):
444
+ for category, term_list in transhuman_glossary.items():
445
+ for term in term_list:
446
+ if query.lower() in term.lower():
447
+ st.subheader(f"Found in {category}:")
448
+ st.write(term)
449
+ return True
450
+ image_path = f"images/{query}.png"
451
+ if os.path.exists(image_path):
452
+ st.image(image_path, caption=f"Image for {query}")
453
+ return True
454
+ st.warning("No matching content or image found.")
455
+ return False
456
+
457
+
458
+ # ------------------------------------
459
+ # MERMAID DIAGRAM with Clickable Links
460
+ # ------------------------------------
461
+ def generate_mermaid_html(mermaid_code: str) -> str:
462
+ return f"""
463
+ <html>
464
+ <head>
465
+ <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
466
+ <style>
467
+ .centered-mermaid {{
468
+ display: flex;
469
+ justify-content: center;
470
+ margin: 20px auto;
471
+ }}
472
+ .mermaid {{
473
+ max-width: 800px;
474
+ }}
475
+ </style>
476
+ </head>
477
+ <body>
478
+ <div class="mermaid centered-mermaid">
479
+ {mermaid_code}
480
+ </div>
481
+ <script>
482
+ mermaid.initialize({{ startOnLoad: true }});
483
+ </script>
484
+ </body>
485
+ </html>
486
+ """
487
+
488
+ def append_model_param(url: str, model_selected: bool) -> str:
489
+ if not model_selected:
490
+ return url
491
+ delimiter = "&" if "?" in url else "?"
492
+ return f"{url}{delimiter}model=1"
493
+
494
+
495
+ def main():
496
+ st.set_page_config(page_title="Mermaid + Clickable Links Demo", layout="wide")
497
+
498
+ # 1) Parse query strings using st.query_params
499
+ query_params = st.query_params
500
+ current_q = query_params.get("q", [""])[0]
501
+ current_r = query_params.get("r", [""])[0]
502
+
503
+ st.sidebar.write("## Diagram Link Settings")
504
+ model_selected = st.sidebar.checkbox("Append ?model=1 to each link?")
505
+
506
+ # Load the code from files we created or updated
507
+ # If the user empties them, they remain blank until re-saved
508
+ markdown_default = load_file("MarkdownCode.md")
509
+ mermaid_default = load_file("MermaidCode.md")
510
+
511
+ # Rebuild the clickable diagram code if user wants model param
512
+ base_diagram = mermaid_default or ""
513
+ lines = base_diagram.strip().split("\n")
514
+ new_lines = []
515
+ for line in lines:
516
+ if "click " in line and '"/?' in line:
517
+ parts = re.split(r'click\s+\S+\s+"([^"]+)"\s+("_self")', line)
518
+ if len(parts) == 4:
519
+ url = parts[1]
520
+ updated_url = append_model_param(url, model_selected)
521
+ new_line = f"{parts[0]}\"{updated_url}\" {parts[2]}"
522
+ new_lines.append(new_line)
523
+ else:
524
+ new_lines.append(line)
525
+ else:
526
+ new_lines.append(line)
527
+ mermaid_code = "\n".join(new_lines)
528
+
529
+ st.title("Top-Centered Mermaid Diagram with Clickable Links 🏺")
530
+ diagram_html = generate_mermaid_html(mermaid_code)
531
+ components.html(diagram_html, height=400, scrolling=True)
532
+
533
+ # Show inbound ?q or ?r
534
+ if current_q:
535
+ st.markdown(f"**Detected Query**: `?q={current_q}`")
536
+ display_content_or_image(current_q)
537
+ if current_r:
538
+ st.markdown(f"**Detected Relationship**: `?r={current_r}`")
539
+
540
+ left_col, right_col = st.columns(2)
541
+
542
+ # --- Left: Markdown Editor
543
+ with left_col:
544
+ st.subheader("Markdown Side 📝")
545
+ # Load from session or from MarkdownCode.md
546
+ if "markdown_text" not in st.session_state:
547
+ st.session_state["markdown_text"] = markdown_default
548
+
549
+ markdown_text = st.text_area(
550
+ "Edit Markdown:",
551
+ value=st.session_state["markdown_text"],
552
+ height=300
553
+ )
554
+ st.session_state["markdown_text"] = markdown_text
555
+
556
+ # Button row
557
+ colA, colB, colC, colD = st.columns(4)
558
+ with colA:
559
+ if st.button("🔄 Refresh"):
560
+ st.write("**Markdown** content refreshed! 🍿")
561
+ with colB:
562
+ if st.button("❌ Clear"):
563
+ st.session_state["markdown_text"] = ""
564
+ st.experimental_rerun()
565
+ with colC:
566
+ if st.button("💾 File Save"):
567
+ with open("MarkdownCode.md", 'w', encoding='utf-8') as f:
568
+ f.write(markdown_text)
569
+ st.success("Saved to MarkdownCode.md")
570
+ with colD:
571
+ # "Save As" with a text_input
572
+ md_filename = st.text_input("Filename for Markdown:", value="MarkdownCode.md", key="md_filename_key")
573
+ if st.button("💾 Save As"):
574
+ with open(md_filename, 'w', encoding='utf-8') as f:
575
+ f.write(markdown_text)
576
+ st.success(f"Saved to {md_filename}")
577
+
578
+ st.markdown("---")
579
+ st.markdown("**Preview:**")
580
+ st.markdown(markdown_text)
581
+
582
+ # --- Right: Mermaid Editor
583
+ with right_col:
584
+ st.subheader("Mermaid Side 🧜‍♂️")
585
+ if "current_mermaid" not in st.session_state:
586
+ st.session_state["current_mermaid"] = mermaid_default
587
+
588
+ mermaid_input = st.text_area(
589
+ "Edit Mermaid Code:",
590
+ value=st.session_state["current_mermaid"],
591
+ height=300
592
+ )
593
+
594
+ colC, colD, colE, colF = st.columns(4)
595
+ with colC:
596
+ if st.button("🎨 Refresh"):
597
+ st.session_state["current_mermaid"] = mermaid_input
598
+ st.write("**Mermaid** diagram refreshed! 🌈")
599
+ st.experimental_rerun()
600
+ with colD:
601
+ if st.button("❌ Clear "):
602
+ st.session_state["current_mermaid"] = ""
603
+ st.experimental_rerun()
604
+ with colE:
605
+ if st.button("💾 File Save "):
606
+ with open("MermaidCode.md", 'w', encoding='utf-8') as f:
607
+ f.write(mermaid_input)
608
+ st.success("Saved to MermaidCode.md")
609
+ with colF:
610
+ # "Save As" with text_input
611
+ mermaid_filename = st.text_input("Filename for Mermaid:", value="MermaidCode.md", key="mermaid_filename_key")
612
+ if st.button("💾 Save As "):
613
+ with open(mermaid_filename, 'w', encoding='utf-8') as f:
614
+ f.write(mermaid_input)
615
+ st.success(f"Saved to {mermaid_filename}")
616
+
617
+ st.markdown("---")
618
+ st.markdown("**Mermaid Source:**")
619
+ st.code(mermaid_input, language="python", line_numbers=True)
620
+
621
+ st.markdown("---")
622
+ st.header("Media Galleries")
623
+
624
+ num_columns_images = st.slider("Choose Number of Image Columns", 1, 15, 5, key="num_columns_images")
625
+ display_images_and_wikipedia_summaries(num_columns_images)
626
+
627
+ num_columns_video = st.slider("Choose Number of Video Columns", 1, 15, 5, key="num_columns_video")
628
+ display_videos_and_links(num_columns_video)
629
+
630
+ showExtendedTextInterface = False
631
+ if showExtendedTextInterface:
632
+ display_glossary_grid(roleplaying_glossary)
633
+ num_columns_text = st.slider("Choose Number of Text Columns", 1, 15, 4, key="num_columns_text")
634
+ display_buttons_with_scores(num_columns_text)
635
+ st.markdown("Extended text interface is on...")
636
+
637
+ FileSidebar()
638
+
639
+ # Random Title at bottom
640
+ titles = [
641
+ "🧠🎭 Semantic Symphonies & Episodic Encores",
642
+ "🌌🎼 AI Rhythms of Memory Lane",
643
+ "🎭🎉 Cognitive Crescendos & Neural Harmonies",
644
+ "🧠🎺 Mnemonic Melodies & Synaptic Grooves",
645
+ "🎼🎸 Straight Outta Cognition",
646
+ "🥁🎻 Jazzy Jambalaya of AI Memories",
647
+ "🏰 Semantic Soul & Episodic Essence",
648
+ "🥁🎻 The Music Of AI's Mind"
649
+ ]
650
+ selected_title = random.choice(titles)
651
+ st.markdown(f"**{selected_title}**")
652
+
653
+
654
+ if __name__ == "__main__":
655
+ main()