Update app.py
Browse files
app.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
import markdown
|
| 3 |
import os
|
|
|
|
| 4 |
|
| 5 |
# Title of the app
|
| 6 |
st.title("Markdown Viewer")
|
|
@@ -14,16 +15,104 @@ def handle_file_upload(uploaded_file):
|
|
| 14 |
return uploaded_file.read().decode("utf-8")
|
| 15 |
return None
|
| 16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
with st.sidebar.expander("Markdown Input", expanded=True):
|
| 18 |
markdown_input = st.text_area("Enter your markdown content here:", height=300)
|
| 19 |
uploaded_file = st.file_uploader("Upload a Markdown file:", type="md")
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
if uploaded_file:
|
| 22 |
markdown_input = handle_file_upload(uploaded_file)
|
| 23 |
|
|
|
|
| 24 |
if markdown_input:
|
| 25 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
html_content = markdown.markdown(markdown_input, extensions=['tables', 'fenced_code'])
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
| 28 |
else:
|
| 29 |
st.info("Please upload a markdown file or enter content in the sidebar.")
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
import markdown
|
| 3 |
import os
|
| 4 |
+
import re
|
| 5 |
|
| 6 |
# Title of the app
|
| 7 |
st.title("Markdown Viewer")
|
|
|
|
| 15 |
return uploaded_file.read().decode("utf-8")
|
| 16 |
return None
|
| 17 |
|
| 18 |
+
def generate_toc_with_links(markdown_text):
|
| 19 |
+
toc = []
|
| 20 |
+
for line in markdown_text.splitlines():
|
| 21 |
+
match = re.match(r'^(#{1,3})\s+(.*)', line)
|
| 22 |
+
if match:
|
| 23 |
+
level = len(match.group(1))
|
| 24 |
+
title = match.group(2).strip()
|
| 25 |
+
# Create a clean anchor ID - remove special chars and replace spaces with hyphens
|
| 26 |
+
anchor = re.sub(r'[^\w\s-]', '', title.lower())
|
| 27 |
+
anchor = re.sub(r'[\s-]+', '-', anchor).strip('-')
|
| 28 |
+
toc.append((level, title, anchor))
|
| 29 |
+
return toc
|
| 30 |
+
|
| 31 |
+
def render_toc_with_links(toc):
|
| 32 |
+
if not toc:
|
| 33 |
+
return "<p>No headings found in document.</p>"
|
| 34 |
+
|
| 35 |
+
toc_html = '<div class="toc">'
|
| 36 |
+
prev_level = 0
|
| 37 |
+
|
| 38 |
+
for level, title, anchor in toc:
|
| 39 |
+
# Handle proper nesting of lists
|
| 40 |
+
if level > prev_level:
|
| 41 |
+
for _ in range(level - prev_level):
|
| 42 |
+
toc_html += "<ul>"
|
| 43 |
+
elif level < prev_level:
|
| 44 |
+
for _ in range(prev_level - level):
|
| 45 |
+
toc_html += "</ul>"
|
| 46 |
+
|
| 47 |
+
indent = 20 * (level - 1)
|
| 48 |
+
toc_html += f'<li style="margin-left: {indent}px;"><a href="#{anchor}">{title}</a></li>'
|
| 49 |
+
prev_level = level
|
| 50 |
+
|
| 51 |
+
# Close any remaining open lists
|
| 52 |
+
for _ in range(prev_level):
|
| 53 |
+
toc_html += "</ul>"
|
| 54 |
+
|
| 55 |
+
toc_html += '</div>'
|
| 56 |
+
return toc_html
|
| 57 |
+
|
| 58 |
+
def modify_html_with_anchors(html_content, toc_items):
|
| 59 |
+
"""Add ID attributes to headers for anchor links"""
|
| 60 |
+
modified_html = html_content
|
| 61 |
+
for level, title, anchor in toc_items:
|
| 62 |
+
# Find the HTML heading and add an id attribute to it
|
| 63 |
+
heading_regex = f'<h{level}>({re.escape(title)})</h{level}>'
|
| 64 |
+
replacement = f'<h{level} id="{anchor}">\\1</h{level}>'
|
| 65 |
+
modified_html = re.sub(heading_regex, replacement, modified_html)
|
| 66 |
+
|
| 67 |
+
return modified_html
|
| 68 |
+
|
| 69 |
+
# Add CSS for TOC styling
|
| 70 |
+
st.markdown("""
|
| 71 |
+
<style>
|
| 72 |
+
.toc {
|
| 73 |
+
background-color: #f8f9fa;
|
| 74 |
+
border: 1px solid #ddd;
|
| 75 |
+
border-radius: 5px;
|
| 76 |
+
padding: 15px;
|
| 77 |
+
margin-bottom: 20px;
|
| 78 |
+
}
|
| 79 |
+
.toc ul {
|
| 80 |
+
padding-left: 10px;
|
| 81 |
+
margin-bottom: 0;
|
| 82 |
+
}
|
| 83 |
+
.toc li {
|
| 84 |
+
list-style-type: none;
|
| 85 |
+
margin-bottom: 5px;
|
| 86 |
+
}
|
| 87 |
+
</style>
|
| 88 |
+
""", unsafe_allow_html=True)
|
| 89 |
+
|
| 90 |
with st.sidebar.expander("Markdown Input", expanded=True):
|
| 91 |
markdown_input = st.text_area("Enter your markdown content here:", height=300)
|
| 92 |
uploaded_file = st.file_uploader("Upload a Markdown file:", type="md")
|
| 93 |
+
|
| 94 |
+
# Add a toggle for TOC display
|
| 95 |
+
show_toc = st.checkbox("Show Table of Contents", value=True)
|
| 96 |
|
| 97 |
if uploaded_file:
|
| 98 |
markdown_input = handle_file_upload(uploaded_file)
|
| 99 |
|
| 100 |
+
# Main content area
|
| 101 |
if markdown_input:
|
| 102 |
+
# Generate and display TOC
|
| 103 |
+
toc_items = generate_toc_with_links(markdown_input)
|
| 104 |
+
|
| 105 |
+
if show_toc and toc_items:
|
| 106 |
+
st.markdown("## Table of Contents", unsafe_allow_html=True)
|
| 107 |
+
toc_html = render_toc_with_links(toc_items)
|
| 108 |
+
st.markdown(toc_html, unsafe_allow_html=True)
|
| 109 |
+
st.markdown("---", unsafe_allow_html=True)
|
| 110 |
+
|
| 111 |
+
# Convert markdown to HTML and add anchor IDs
|
| 112 |
html_content = markdown.markdown(markdown_input, extensions=['tables', 'fenced_code'])
|
| 113 |
+
html_with_anchors = modify_html_with_anchors(html_content, toc_items)
|
| 114 |
+
|
| 115 |
+
# Display the markdown content
|
| 116 |
+
st.markdown(html_with_anchors, unsafe_allow_html=True)
|
| 117 |
else:
|
| 118 |
st.info("Please upload a markdown file or enter content in the sidebar.")
|