Markdown Viewer --------------- :: import os import streamlit as st import markdown from bs4 import BeautifulSoup Print banner. :: st.set_page_config( page_title="MD-View", ) @st.cache_data def print_banner(): print(""" d s sb d ss d b d d sss d d b S S S S S ~o S S S S S S S S S S S b S S S S S S S S S S S sSSs S S S S sSSs S S S S S S P S S S S S S S S S S S S S S S S S S P P P ss\" \"ssS P P sSSss \"ss\"S """) return 1 print_banner() We can show `.q.md` files only. :: if st.sidebar.toggle("Questions only"): md_files = [f for f in os.listdir('.') if f.endswith('.q.md')] else: Find all files in the current directory that have a `.md` extension. These files might contain text generated by large language models. If a particular `.md` file is a response to a specific question, that question will be stored in a separate file with the same base name but a `.q.md` extension. Do not include any `.q.md` files in the final list of files. :: md_files = [f for f in os.listdir('.') if f.endswith('.md') and not f.endswith('.q.md')] Sort files based on their modification time :: md_files.sort(key=os.path.getmtime, reverse=True) Create radio buttons to select a file :: selected_file = st.sidebar.radio("Markdown file:", md_files) Adds a prefix to each line of a multi-line string. :: def prefix_lines(query, prefix): lines = query.splitlines() prefixed_lines = [prefix + line for line in lines] return "\n".join(prefixed_lines) Check if query file exists. :: query_file_path = selected_file[:-3] + ".q.md" if os.path.exists(query_file_path): with open(query_file_path, 'r', encoding='utf-8') as file: query = file.read() st.write(prefix_lines(query, "> ")) st.write("---"); Read the contents of selected file :: with open(selected_file, 'r', encoding='utf-8') as file: md_text = file.read() Display the contents of the file that has been selected. :: st.write(md_text) html_format = st.sidebar.radio("Output HTML:", options=["Tailwind", "Bootstrap"], horizontal=True) Parse HTML and add Tailwind CSS classes to improve styling. :: def enhance_html_with_tailwind(html_text, filename): soup = BeautifulSoup(html_text, 'html.parser') # Headings for level in range(1, 7): for tag in soup.find_all(f'h{level}'): size = { 1: 'text-4xl', 2: 'text-3xl', 3: 'text-2xl', 4: 'text-xl', 5: 'text-lg', 6: 'text-base', }[level] tag['class'] = tag.get('class', []) + [size, 'font-bold', 'mt-4', 'mb-2'] # Paragraphs for p in soup.find_all('p'): p['class'] = p.get('class', []) + ['mb-4', 'leading-relaxed'] # Links for a in soup.find_all('a'): a['class'] = a.get('class', []) + ['text-blue-600', 'hover:underline'] a['target'] = '_blank' # Images for img in soup.find_all('img'): img['class'] = img.get('class', []) + ['my-4', 'max-w-full', 'h-auto', 'rounded'] # Lists for ul in soup.find_all('ul'): ul['class'] = ul.get('class', []) + ['list-disc', 'ml-6', 'mb-4'] for ol in soup.find_all('ol'): ol['class'] = ol.get('class', []) + ['list-decimal', 'ml-6', 'mb-4'] # Code blocks for code in soup.find_all('code'): parent = code.parent # Inline code if parent.name != 'pre': code['class'] = code.get('class', []) + ['bg-gray-100', 'px-1', 'py-0.5', 'rounded'] for pre in soup.find_all('pre'): pre['class'] = pre.get('class', []) + ['bg-gray-900', 'text-gray-100', 'p-4', 'rounded', 'overflow-auto', 'mb-4'] # Blockquotes for bq in soup.find_all('blockquote'): bq['class'] = bq.get('class', []) + ['border-l-4', 'border-gray-300', 'pl-4', 'italic', 'mb-4'] # Wrap in basic HTML structure return f""" {filename} {str(soup)} """ Parse HTML and add Bootstrap CSS classes to improve styling. :: def enhance_html_with_bootstrap(html_text, filename): soup = BeautifulSoup(html_text, 'html.parser') # Headings for level in range(1, 7): for tag in soup.find_all(f'h{level}'): # Add Bootstrap display headings display = { 1: 'display-1', 2: 'display-2', 3: 'display-3', 4: 'display-4', 5: 'h1', 6: 'h2', }[level] tag['class'] = tag.get('class', []) + [display, 'mt-4', 'mb-3'] # Paragraphs for p in soup.find_all('p'): p['class'] = p.get('class', []) + ['mb-3'] # Links for a in soup.find_all('a'): a['class'] = a.get('class', []) + ['link-primary'] a['target'] = '_blank' # Images for img in soup.find_all('img'): img['class'] = img.get('class', []) + ['img-fluid', 'my-3', 'rounded'] # Lists for ul in soup.find_all('ul'): ul['class'] = ul.get('class', []) + ['list-unstyled', 'mb-3'] for ol in soup.find_all('ol'): ol['class'] = ol.get('class', []) + ['mb-3'] # Code blocks for code in soup.find_all('code'): parent = code.parent # Inline code if parent.name != 'pre': code['class'] = code.get('class', []) + ['bg-light', 'px-1', 'py-0', 'rounded'] for pre in soup.find_all('pre'): pre['class'] = pre.get('class', []) + ['bg-dark', 'text-light', 'p-3', 'rounded', 'mb-3', 'overflow-auto'] # Blockquotes for bq in soup.find_all('blockquote'): bq['class'] = bq.get('class', []) + ['blockquote', 'ps-3', 'border-start', 'mb-3'] return f""" {filename} {str(soup)} """ Save the markdown text as an HTML file. :: def save_html(md_text, filename): html_text = markdown.markdown(md_text, extensions=[ 'fenced_code', 'codehilite', 'tables', 'toc' ]) if html_format == "Bootstrap": html_text = enhance_html_with_bootstrap(html_text, filename) elif html_format == "Tailwind": html_text = enhance_html_with_tailwind(html_text, filename) filename += ".html" with open(filename, 'w', encoding='utf-8') as file: file.write(html_text) st.toast(f"HTML file saved") Add a button to save the markdown text as an HTML file :: if st.sidebar.button("Save HTML", use_container_width=True): save_html(md_text, selected_file[:-3])