awacke1 commited on
Commit
0389c02
ยท
verified ยท
1 Parent(s): 3d13591

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +696 -0
app.py ADDED
@@ -0,0 +1,696 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ # (You can omit if you're not using HF or adapt your own client)
20
+ from huggingface_hub import InferenceClient
21
+
22
+ # ----------------------------
23
+ # Placeholder data structures
24
+ # ----------------------------
25
+
26
+ # Example placeholders for prompt prefixes
27
+ PromptPrefix = "AI-Search: "
28
+ PromptPrefix2 = "AI-Refine: "
29
+ PromptPrefix3 = "AI-JS: "
30
+
31
+ # Minimal example of a roleplaying glossary
32
+ roleplaying_glossary = {
33
+ "Core Rulebooks": {
34
+ "Dungeons and Dragons": ["Player's Handbook", "Dungeon Master's Guide", "Monster Manual"],
35
+ "GURPS": ["Basic Set Characters", "Basic Set Campaigns"]
36
+ },
37
+ "Campaigns & Adventures": {
38
+ "Pathfinder": ["Rise of the Runelords", "Curse of the Crimson Throne"]
39
+ }
40
+ }
41
+
42
+ # Minimal example of a transhuman glossary
43
+ transhuman_glossary = {
44
+ "Neural Interfaces": ["Cortex Jack", "Mind-Machine Fusion"],
45
+ "Cybernetics": ["Robotic Limbs", "Augmented Eyes"],
46
+ }
47
+
48
+ # Just to demonstrate how your "search_arxiv" or "SpeechSynthesis" etc. might be placeholders
49
+ def process_text(text):
50
+ st.write(f"process_text called with: {text}")
51
+
52
+ def process_text2(text_input):
53
+ return f"[process_text2 placeholder] Received: {text_input}"
54
+
55
+ def search_arxiv(text):
56
+ st.write(f"search_arxiv called with: {text}")
57
+
58
+ def SpeechSynthesis(text):
59
+ st.write(f"SpeechSynthesis called with: {text}")
60
+
61
+ def process_image(image_file, prompt):
62
+ return f"[process_image placeholder] Processing {image_file} with prompt: {prompt}"
63
+
64
+ def process_video(video_file, seconds_per_frame):
65
+ st.write(f"[process_video placeholder] Video: {video_file}, seconds/frame: {seconds_per_frame}")
66
+
67
+ def search_glossary(content):
68
+ st.write(f"search_glossary called with: {content}")
69
+
70
+ # If you have HF Inference endpoint, set them here, else placeholders
71
+ API_URL = "https://huggingface-inference-endpoint-placeholder"
72
+ API_KEY = "hf_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
73
+
74
+ @st.cache_resource
75
+ def InferenceLLM(prompt):
76
+ return f"[InferenceLLM placeholder response to prompt: {prompt}]"
77
+
78
+
79
+ # --------------------------------------
80
+ # Display Entities & Glossary Functions
81
+ # --------------------------------------
82
+ @st.cache_resource
83
+ def display_glossary_entity(k):
84
+ """
85
+ Example of how you'd create multiple links for a glossary entity.
86
+ This was in your original snippet. We'll keep it short.
87
+ """
88
+ search_urls = {
89
+ "๐Ÿš€๐ŸŒŒArXiv": lambda k: f"/?q={quote(k)}",
90
+ "๐ŸƒAnalyst": lambda k: f"/?q={quote(k)}-{quote(PromptPrefix)}",
91
+ "๐Ÿ“šPyCoder": lambda k: f"/?q={quote(k)}-{quote(PromptPrefix2)}",
92
+ "๐Ÿ”ฌJSCoder": lambda k: f"/?q={quote(k)}-{quote(PromptPrefix3)}",
93
+ "๐Ÿ“–": lambda k: f"https://en.wikipedia.org/wiki/{quote(k)}",
94
+ "๐Ÿ”": lambda k: f"https://www.google.com/search?q={quote(k)}",
95
+ "๐Ÿ”Ž": lambda k: f"https://www.bing.com/search?q={quote(k)}",
96
+ "๐ŸŽฅ": lambda k: f"https://www.youtube.com/results?search_query={quote(k)}",
97
+ "๐Ÿฆ": lambda k: f"https://twitter.com/search?q={quote(k)}",
98
+ }
99
+ links_md = ' '.join([f"[{emoji}]({url(k)})" for emoji, url in search_urls.items()])
100
+ st.markdown(f"**{k}** <small>{links_md}</small>", unsafe_allow_html=True)
101
+
102
+
103
+ @st.cache_resource
104
+ def display_glossary_grid(roleplaying_glossary):
105
+ """
106
+ Displays a glossary in columns with multiple link emojis.
107
+ """
108
+ search_urls = {
109
+ "๐Ÿš€๐ŸŒŒArXiv": lambda k: f"/?q={quote(k)}",
110
+ "๐ŸƒAnalyst": lambda k: f"/?q={quote(k)}-{quote(PromptPrefix)}",
111
+ "๐Ÿ“šPyCoder": lambda k: f"/?q={quote(k)}-{quote(PromptPrefix2)}",
112
+ "๐Ÿ”ฌJSCoder": lambda k: f"/?q={quote(k)}-{quote(PromptPrefix3)}",
113
+ "๐Ÿ“–": lambda k: f"https://en.wikipedia.org/wiki/{quote(k)}",
114
+ "๐Ÿ”": lambda k: f"https://www.google.com/search?q={quote(k)}",
115
+ "๐Ÿ”Ž": lambda k: f"https://www.bing.com/search?q={quote(k)}",
116
+ "๐ŸŽฅ": lambda k: f"https://www.youtube.com/results?search_query={quote(k)}",
117
+ "๐Ÿฆ": lambda k: f"https://twitter.com/search?q={quote(k)}",
118
+ }
119
+
120
+ for category, details in roleplaying_glossary.items():
121
+ st.write(f"### {category}")
122
+ cols = st.columns(len(details))
123
+ for idx, (game, terms) in enumerate(details.items()):
124
+ with cols[idx]:
125
+ st.markdown(f"#### {game}")
126
+ for term in terms:
127
+ links_md = ' '.join([f"[{emoji}]({url(term)})" for emoji, url in search_urls.items()])
128
+ st.markdown(f"**{term}** <small>{links_md}</small>", unsafe_allow_html=True)
129
+
130
+
131
+ # --------------------
132
+ # File-Handling Logic
133
+ # --------------------
134
+ def load_file(file_path):
135
+ try:
136
+ with open(file_path, "r", encoding='utf-8') as f:
137
+ return f.read()
138
+ except:
139
+ return ""
140
+
141
+ @st.cache_resource
142
+ def create_zip_of_files(files):
143
+ zip_name = "Arxiv-Paper-Search-QA-RAG-Streamlit-Gradio-AP.zip"
144
+ with zipfile.ZipFile(zip_name, 'w') as zipf:
145
+ for file in files:
146
+ zipf.write(file)
147
+ return zip_name
148
+
149
+ @st.cache_resource
150
+ def get_zip_download_link(zip_file):
151
+ with open(zip_file, 'rb') as f:
152
+ data = f.read()
153
+ b64 = base64.b64encode(data).decode()
154
+ href = f'<a href="data:application/zip;base64,{b64}" download="{zip_file}">Download All</a>'
155
+ return href
156
+
157
+ def get_table_download_link(file_path):
158
+ """
159
+ Creates a download link for a single file from your snippet.
160
+ """
161
+ try:
162
+ with open(file_path, 'r', encoding='utf-8') as file:
163
+ data = file.read()
164
+ b64 = base64.b64encode(data.encode()).decode()
165
+ file_name = os.path.basename(file_path)
166
+ ext = os.path.splitext(file_name)[1]
167
+ mime_map = {
168
+ '.txt': 'text/plain',
169
+ '.py': 'text/plain',
170
+ '.xlsx': 'text/plain',
171
+ '.csv': 'text/plain',
172
+ '.htm': 'text/html',
173
+ '.md': 'text/markdown',
174
+ '.wav': 'audio/wav'
175
+ }
176
+ mime_type = mime_map.get(ext, 'application/octet-stream')
177
+ href = f'<a href="data:{mime_type};base64,{b64}" target="_blank" download="{file_name}">{file_name}</a>'
178
+ return href
179
+ except:
180
+ return ''
181
+
182
+ def get_file_size(file_path):
183
+ return os.path.getsize(file_path)
184
+
185
+ def compare_and_delete_files(files):
186
+ """
187
+ Compare file sizes. If duplicates exist, keep only the latest.
188
+ """
189
+ if not files:
190
+ st.warning("No files to compare.")
191
+ return
192
+ file_sizes = {}
193
+ for file in files:
194
+ size = os.path.getsize(file)
195
+ file_sizes.setdefault(size, []).append(file)
196
+ # Remove all but the latest file for each size
197
+ for size, paths in file_sizes.items():
198
+ if len(paths) > 1:
199
+ latest_file = max(paths, key=os.path.getmtime)
200
+ for file in paths:
201
+ if file != latest_file:
202
+ os.remove(file)
203
+ st.success(f"Deleted {file} as a duplicate.")
204
+ st.rerun()
205
+
206
+ def FileSidebar():
207
+ """
208
+ Renders the file sidebar with all the open/view/run/delete logic.
209
+ """
210
+ all_files = glob.glob("*.md")
211
+ # Example logic filtering filenames
212
+ all_files = [file for file in all_files if len(os.path.splitext(file)[0]) >= 5]
213
+ all_files.sort(key=lambda x: (os.path.splitext(x)[1], x), reverse=True)
214
+
215
+ # Buttons for "Delete All" and "Download"
216
+ Files1, Files2 = st.sidebar.columns(2)
217
+ with Files1:
218
+ if st.button("๐Ÿ—‘ Delete All"):
219
+ for file in all_files:
220
+ os.remove(file)
221
+ st.rerun()
222
+ with Files2:
223
+ if st.button("โฌ‡๏ธ Download"):
224
+ zip_file = create_zip_of_files(all_files)
225
+ st.sidebar.markdown(get_zip_download_link(zip_file), unsafe_allow_html=True)
226
+
227
+ file_contents = ''
228
+ file_name = ''
229
+ next_action = ''
230
+
231
+ # Each file row
232
+ for file in all_files:
233
+ col1, col2, col3, col4, col5 = st.sidebar.columns([1,6,1,1,1])
234
+ with col1:
235
+ # Show an emoji button to do "md"
236
+ if st.button("๐ŸŒ", key="md_"+file):
237
+ file_contents = load_file(file)
238
+ file_name = file
239
+ next_action = 'md'
240
+ st.session_state['next_action'] = next_action
241
+ with col2:
242
+ st.markdown(get_table_download_link(file), unsafe_allow_html=True)
243
+ with col3:
244
+ if st.button("๐Ÿ“‚", key="open_"+file):
245
+ file_contents = load_file(file)
246
+ file_name = file
247
+ next_action = 'open'
248
+ st.session_state['lastfilename'] = file
249
+ st.session_state['filename'] = file
250
+ st.session_state['filetext'] = file_contents
251
+ st.session_state['next_action'] = next_action
252
+ with col4:
253
+ if st.button("โ–ถ๏ธ", key="read_"+file):
254
+ file_contents = load_file(file)
255
+ file_name = file
256
+ next_action = 'search'
257
+ st.session_state['next_action'] = next_action
258
+ with col5:
259
+ if st.button("๐Ÿ—‘", key="delete_"+file):
260
+ os.remove(file)
261
+ file_name = file
262
+ st.rerun()
263
+ next_action = 'delete'
264
+ st.session_state['next_action'] = next_action
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', on_change=None)
292
+ file_content_area = st.text_area('File Contents:', file_contents, height=300, key='file_content_area')
293
+
294
+ # Minimal โ€œSaveโ€ stubs
295
+ if st.button('๐Ÿ’พ Save File'):
296
+ with open(file_name_input, 'w', encoding='utf-8') as f:
297
+ f.write(file_content_area)
298
+ st.markdown(f'Saved {file_name_input} successfully.')
299
+
300
+ elif next_action == 'search':
301
+ # Example usage
302
+ file_content_area = st.text_area("File Contents:", file_contents, height=500)
303
+ user_prompt = PromptPrefix2 + file_contents
304
+ st.markdown(user_prompt)
305
+ if st.button('๐Ÿ”Re-Code'):
306
+ search_arxiv(file_contents)
307
+
308
+ elif next_action == 'md':
309
+ st.markdown(file_contents)
310
+ SpeechSynthesis(file_contents)
311
+ if st.button('๐Ÿ”Run'):
312
+ st.write("Running GPT logic placeholder...")
313
+
314
+ # ---------------------------
315
+ # Basic Scoring / Glossaries
316
+ # ---------------------------
317
+ score_dir = "scores"
318
+ os.makedirs(score_dir, exist_ok=True)
319
+
320
+ def generate_key(label, header, idx):
321
+ return f"{header}_{label}_{idx}_key"
322
+
323
+ def update_score(key, increment=1):
324
+ score_file = os.path.join(score_dir, f"{key}.json")
325
+ if os.path.exists(score_file):
326
+ with open(score_file, "r") as file:
327
+ score_data = json.load(file)
328
+ else:
329
+ score_data = {"clicks": 0, "score": 0}
330
+ score_data["clicks"] += increment
331
+ score_data["score"] += increment
332
+ with open(score_file, "w") as file:
333
+ json.dump(score_data, file)
334
+ return score_data["score"]
335
+
336
+ def load_score(key):
337
+ score_file = os.path.join(score_dir, f"{key}.json")
338
+ if os.path.exists(score_file):
339
+ with open(score_file, "r") as file:
340
+ score_data = json.load(file)
341
+ return score_data["score"]
342
+ return 0
343
+
344
+ def display_buttons_with_scores(num_columns_text):
345
+ """
346
+ Show buttons that track a 'score' from your glossary data.
347
+ """
348
+ game_emojis = {
349
+ "Dungeons and Dragons": "๐Ÿ‰",
350
+ "Call of Cthulhu": "๐Ÿ™",
351
+ "GURPS": "๐ŸŽฒ",
352
+ "Pathfinder": "๐Ÿ—บ๏ธ",
353
+ "Kindred of the East": "๐ŸŒ…",
354
+ "Changeling": "๐Ÿƒ",
355
+ }
356
+
357
+ topic_emojis = {
358
+ "Core Rulebooks": "๐Ÿ“š",
359
+ "Maps & Settings": "๐Ÿ—บ๏ธ",
360
+ "Game Mechanics & Tools": "โš™๏ธ",
361
+ "Monsters & Adversaries": "๐Ÿ‘น",
362
+ "Campaigns & Adventures": "๐Ÿ“œ",
363
+ "Creatives & Assets": "๐ŸŽจ",
364
+ "Game Master Resources": "๐Ÿ› ๏ธ",
365
+ "Lore & Background": "๐Ÿ“–",
366
+ "Character Development": "๐Ÿง",
367
+ "Homebrew Content": "๐Ÿ”ง",
368
+ "General Topics": "๐ŸŒ",
369
+ }
370
+
371
+ for category, games in roleplaying_glossary.items():
372
+ category_emoji = topic_emojis.get(category, "๐Ÿ”")
373
+ st.markdown(f"## {category_emoji} {category}")
374
+ for game, terms in games.items():
375
+ game_emoji = game_emojis.get(game, "๐ŸŽฎ")
376
+ for term in terms:
377
+ key = f"{category}_{game}_{term}".replace(' ', '_').lower()
378
+ score = load_score(key)
379
+ if st.button(f"{game_emoji} {category} {game} {term} {score}", key=key):
380
+ newscore = update_score(key.replace('?',''))
381
+ st.markdown(f"Scored **{category} - {game} - {term}** -> {newscore}")
382
+
383
+ # --------------------
384
+ # Image & Video Grids
385
+ # --------------------
386
+ def display_images_and_wikipedia_summaries(num_columns=4):
387
+ """
388
+ Display all .png images in the current directory in a grid, referencing the name as a 'keyword'.
389
+ """
390
+ image_files = [f for f in os.listdir('.') if f.endswith('.png')]
391
+ if not image_files:
392
+ st.write("No PNG images found in the current directory.")
393
+ return
394
+
395
+ # Sort by length of filename, just as an example
396
+ image_files_sorted = sorted(image_files, key=lambda x: len(x.split('.')[0]))
397
+ cols = st.columns(num_columns)
398
+ col_index = 0
399
+
400
+ for image_file in image_files_sorted:
401
+ with cols[col_index % num_columns]:
402
+ try:
403
+ image = Image.open(image_file)
404
+ st.image(image, use_column_width=True)
405
+ k = image_file.split('.')[0]
406
+ display_glossary_entity(k)
407
+ # Provide a text input for user interactions
408
+ image_text_input = st.text_input(f"Prompt for {image_file}", key=f"image_prompt_{image_file}")
409
+ if len(image_text_input) > 0:
410
+ response = process_image(image_file, image_text_input)
411
+ st.markdown(response)
412
+ except:
413
+ st.write(f"Could not open {image_file}")
414
+ col_index += 1
415
+
416
+ def display_videos_and_links(num_columns=4):
417
+ """
418
+ Displays all .mp4 or .webm videos found in the current directory in a grid.
419
+ """
420
+ video_files = [f for f in os.listdir('.') if f.endswith(('.mp4', '.webm'))]
421
+ if not video_files:
422
+ st.write("No MP4 or WEBM videos found in the current directory.")
423
+ return
424
+
425
+ video_files_sorted = sorted(video_files, key=lambda x: len(x.split('.')[0]))
426
+ cols = st.columns(num_columns)
427
+ col_index = 0
428
+
429
+ for video_file in video_files_sorted:
430
+ with cols[col_index % num_columns]:
431
+ k = video_file.split('.')[0]
432
+ st.video(video_file, format='video/mp4', start_time=0)
433
+ display_glossary_entity(k)
434
+ # Provide a text input
435
+ video_text_input = st.text_input(f"Video Prompt for {video_file}", key=f"video_prompt_{video_file}")
436
+ if video_text_input:
437
+ try:
438
+ # Hard-coded example
439
+ seconds_per_frame = 10
440
+ process_video(video_file, seconds_per_frame)
441
+ except ValueError:
442
+ st.error("Invalid input for seconds per frame!")
443
+ col_index += 1
444
+
445
+
446
+ # -------------------------------------
447
+ # Query Param Helpers from your snippet
448
+ # -------------------------------------
449
+ def get_all_query_params(key):
450
+ return st.query_params().get(key, [])
451
+
452
+ def clear_query_params():
453
+ st.query_params()
454
+
455
+ def display_content_or_image(query):
456
+ """
457
+ If a query matches something in transhuman_glossary or
458
+ a local image, show it. Otherwise warn no match.
459
+ """
460
+ for category, term_list in transhuman_glossary.items():
461
+ for term in term_list:
462
+ if query.lower() in term.lower():
463
+ st.subheader(f"Found in {category}:")
464
+ st.write(term)
465
+ return True
466
+ image_path = f"images/{query}.png"
467
+ if os.path.exists(image_path):
468
+ st.image(image_path, caption=f"Image for {query}")
469
+ return True
470
+ st.warning("No matching content or image found.")
471
+ return False
472
+
473
+
474
+ # ------------------------------------
475
+ # MERMAID DIAGRAM with Clickable Links
476
+ # ------------------------------------
477
+ def generate_mermaid_html(mermaid_code: str) -> str:
478
+ """
479
+ Returns HTML embedding a Mermaid diagram. We embed the code
480
+ in <div class="mermaid"> and center it with CSS.
481
+ """
482
+ return f"""
483
+ <html>
484
+ <head>
485
+ <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
486
+ <style>
487
+ .centered-mermaid {{
488
+ display: flex;
489
+ justify-content: center;
490
+ margin: 20px auto;
491
+ }}
492
+ .mermaid {{
493
+ /* Let the diagram scale or otherwise style as you wish */
494
+ max-width: 800px;
495
+ }}
496
+ </style>
497
+ </head>
498
+ <body>
499
+ <div class="mermaid centered-mermaid">
500
+ {mermaid_code}
501
+ </div>
502
+ <script>
503
+ mermaid.initialize({{ startOnLoad: true }});
504
+ </script>
505
+ </body>
506
+ </html>
507
+ """
508
+
509
+ def append_model_param(url: str, model_selected: bool) -> str:
510
+ """
511
+ If 'Model' checkbox is selected, we append '&model=1' or '?model=1' to the URL.
512
+ We'll handle whether the URL already has a '?' or not.
513
+ """
514
+ if not model_selected:
515
+ return url
516
+ delimiter = "&" if "?" in url else "?"
517
+ return f"{url}{delimiter}model=1"
518
+
519
+
520
+ # For demonstration, we add clickable nodes & edges:
521
+ # click <nodeId> "<URL>" "_self"
522
+ # If you want edges to be clickable, you can label them as well,
523
+ # but Mermaid typically only has a 'click' property for nodes.
524
+ DEFAULT_MERMAID = """
525
+ flowchart LR
526
+ %% Notice we have "click LLM ..." lines:
527
+ U((User ๐Ÿ˜Ž)) -- "Talk ๐Ÿ—ฃ๏ธ" --> LLM[LLM Agent ๐Ÿค–\\nExtract Info]
528
+ click U "/?q=User%20๐Ÿ˜Ž" _self
529
+ click LLM "/?q=LLM%20Agent%20Extract%20Info" _self
530
+
531
+ LLM -- "Query ๐Ÿ”" --> HS[Hybrid Search ๐Ÿ”Ž\\nVector+NER+Lexical]
532
+ click HS "/?q=Hybrid%20Search%20Vector+NER+Lexical" _self
533
+
534
+ HS -- "Reason ๐Ÿค”" --> RE[Reasoning Engine ๐Ÿ› ๏ธ\\nNeuralNetwork+Medical]
535
+ click RE "/?q=Reasoning%20Engine%20NeuralNetwork+Medical" _self
536
+
537
+ RE -- "Link ๐Ÿ“ก" --> KG((Knowledge Graph ๐Ÿ“š\\nOntology+GAR+RAG))
538
+ click KG "/?q=Knowledge%20Graph%20Ontology+GAR+RAG" _self
539
+
540
+ %% If you want an "edge click" to pass ?r= something,
541
+ %% Mermaid doesn't have direct 'click' for edges,
542
+ %% but you can define them as nodes or use linkStyle trick, etc.
543
+ """
544
+
545
+
546
+ # ---------------------------
547
+ # Streamlit Main App
548
+ # ---------------------------
549
+ def main():
550
+ st.set_page_config(page_title="Mermaid + Clickable Links Demo", layout="wide")
551
+
552
+ # 1) Parse query strings on page load
553
+ query_params = st.experimental_get_query_params()
554
+ current_q = query_params.get("q", [""])[0] # If present, first string
555
+ current_r = query_params.get("r", [""])[0]
556
+
557
+ # 2) Let user pick if they want to add the "model=1" param to clickable links
558
+ st.sidebar.write("## Diagram Link Settings")
559
+ model_selected = st.sidebar.checkbox("Append ?model=1 to each link?")
560
+
561
+ # 3) Generate a dynamic Mermaid code, appending model param if user wants
562
+ # We'll do a simple string replace to incorporate the model param
563
+ # For a robust approach, parse each URL carefully, then reassemble.
564
+ base_diagram = DEFAULT_MERMAID
565
+ lines = base_diagram.strip().split("\n")
566
+ new_lines = []
567
+ for line in lines:
568
+ if "click " in line and '"/?' in line:
569
+ # e.g. click LLM "/?q=LLM%20Agent" _self
570
+ # let's isolate the URL part
571
+ parts = re.split(r'click\s+\S+\s+"([^"]+)"\s+("_self")', line)
572
+ if len(parts) == 4:
573
+ # parts[0] = 'click LLM '
574
+ # parts[1] = '/?q=LLM%20Agent%20Extract%20Info'
575
+ # parts[2] = ' _self'
576
+ # parts[3] = '' (trailing possibly)
577
+ url = parts[1]
578
+ updated_url = append_model_param(url, model_selected)
579
+ # Recombine
580
+ new_line = f"{parts[0]}\"{updated_url}\" {parts[2]}"
581
+ new_lines.append(new_line)
582
+ else:
583
+ new_lines.append(line)
584
+ else:
585
+ new_lines.append(line)
586
+ mermaid_code = "\n".join(new_lines)
587
+
588
+ # 4) Render the top-centered Mermaid diagram
589
+ st.title("Top-Centered Mermaid Diagram with Clickable Links ๐Ÿบ")
590
+ diagram_html = generate_mermaid_html(mermaid_code)
591
+ components.html(diagram_html, height=400, scrolling=True)
592
+
593
+ # 5) Show what the inbound ?q / ?r was
594
+ if current_q:
595
+ st.markdown(f"**Detected Query**: `?q={current_q}`")
596
+ display_content_or_image(current_q)
597
+ if current_r:
598
+ st.markdown(f"**Detected Relationship**: `?r={current_r}`")
599
+
600
+ # 6) Editor Columns: Markdown & Mermaid
601
+ left_col, right_col = st.columns(2)
602
+
603
+ # --- Left: Markdown Editor
604
+ with left_col:
605
+ st.subheader("Markdown Side ๐Ÿ“")
606
+ if "markdown_text" not in st.session_state:
607
+ st.session_state["markdown_text"] = "## Hello!\nType some *Markdown* here.\n"
608
+ # Text area
609
+ markdown_text = st.text_area(
610
+ "Edit Markdown:",
611
+ value=st.session_state["markdown_text"],
612
+ height=300
613
+ )
614
+ st.session_state["markdown_text"] = markdown_text
615
+
616
+ # Button row
617
+ colA, colB = st.columns(2)
618
+ with colA:
619
+ if st.button("๐Ÿ”„ Refresh Markdown"):
620
+ st.write("**Markdown** content refreshed! ๐Ÿฟ")
621
+ with colB:
622
+ if st.button("โŒ Clear Markdown"):
623
+ st.session_state["markdown_text"] = ""
624
+ st.experimental_rerun()
625
+
626
+ # Display
627
+ st.markdown("---")
628
+ st.markdown("**Preview:**")
629
+ st.markdown(markdown_text)
630
+
631
+ # --- Right: Mermaid Editor
632
+ with right_col:
633
+ st.subheader("Mermaid Side ๐Ÿงœโ€โ™‚๏ธ")
634
+
635
+ if "current_mermaid" not in st.session_state:
636
+ st.session_state["current_mermaid"] = mermaid_code
637
+
638
+ mermaid_input = st.text_area(
639
+ "Edit Mermaid Code:",
640
+ value=st.session_state["current_mermaid"],
641
+ height=300
642
+ )
643
+ colC, colD = st.columns(2)
644
+ with colC:
645
+ if st.button("๐ŸŽจ Refresh Diagram"):
646
+ # Rebuild the diagram
647
+ st.session_state["current_mermaid"] = mermaid_input
648
+ st.write("**Mermaid** diagram refreshed! ๐ŸŒˆ")
649
+ st.experimental_rerun()
650
+ with colD:
651
+ if st.button("โŒ Clear Mermaid"):
652
+ st.session_state["current_mermaid"] = ""
653
+ st.experimental_rerun()
654
+
655
+ st.markdown("---")
656
+ st.markdown("**Mermaid Source:**")
657
+ st.code(mermaid_input, language="python", line_numbers=True)
658
+
659
+ # 7) Show Sliders & image/video galleries
660
+ st.markdown("---")
661
+ st.header("Media Galleries")
662
+
663
+ num_columns_images = st.slider("Choose Number of Image Columns", 1, 15, 5, key="num_columns_images")
664
+ display_images_and_wikipedia_summaries(num_columns_images)
665
+
666
+ num_columns_video = st.slider("Choose Number of Video Columns", 1, 15, 5, key="num_columns_video")
667
+ display_videos_and_links(num_columns_video)
668
+
669
+ # 8) Optional "Extended" UI
670
+ showExtendedTextInterface = False
671
+ if showExtendedTextInterface:
672
+ display_glossary_grid(roleplaying_glossary)
673
+ num_columns_text = st.slider("Choose Number of Text Columns", 1, 15, 4, key="num_columns_text")
674
+ display_buttons_with_scores(num_columns_text)
675
+ st.markdown("Extended text interface is on...")
676
+
677
+ # 9) Render the file sidebar
678
+ FileSidebar()
679
+
680
+ # 10) Random Title at bottom
681
+ titles = [
682
+ "๐Ÿง ๐ŸŽญ Semantic Symphonies & Episodic Encores",
683
+ "๐ŸŒŒ๐ŸŽผ AI Rhythms of Memory Lane",
684
+ "๐ŸŽญ๐ŸŽ‰ Cognitive Crescendos & Neural Harmonies",
685
+ "๐Ÿง ๐ŸŽบ Mnemonic Melodies & Synaptic Grooves",
686
+ "๐ŸŽผ๐ŸŽธ Straight Outta Cognition",
687
+ "๐Ÿฅ๐ŸŽป Jazzy Jambalaya of AI Memories",
688
+ "๐Ÿฐ Semantic Soul & Episodic Essence",
689
+ "๐Ÿฅ๐ŸŽป The Music Of AI's Mind"
690
+ ]
691
+ selected_title = random.choice(titles)
692
+ st.markdown(f"**{selected_title}**")
693
+
694
+
695
+ if __name__ == "__main__":
696
+ main()