forked from stanford-oval/storm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
stoc.py
131 lines (112 loc) · 4.48 KB
/
stoc.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"""https://github.com/arnaudmiribel/stoc"""
import re
import streamlit as st
import unidecode
DISABLE_LINK_CSS = """
<style>
a.toc {
color: inherit;
text-decoration: none; /* no underline */
}
</style>"""
class stoc:
def __init__(self):
self.toc_items = list()
def h1(self, text: str, write: bool = True):
if write:
st.write(f"# {text}")
self.toc_items.append(("h1", text))
def h2(self, text: str, write: bool = True):
if write:
st.write(f"## {text}")
self.toc_items.append(("h2", text))
def h3(self, text: str, write: bool = True):
if write:
st.write(f"### {text}")
self.toc_items.append(("h3", text))
def toc(self, expander):
st.write(DISABLE_LINK_CSS, unsafe_allow_html=True)
# st.sidebar.caption("Table of contents")
if expander is None:
expander = st.sidebar.expander("**Table of contents**", expanded=True)
with expander:
with st.container(height=600, border=False):
markdown_toc = ""
for title_size, title in self.toc_items:
h = int(title_size.replace("h", ""))
markdown_toc += (
" " * 2 * h
+ "- "
+ f'<a href="#{normalize(title)}" class="toc"> {title}</a> \n'
)
# st.sidebar.write(markdown_toc, unsafe_allow_html=True)
st.write(markdown_toc, unsafe_allow_html=True)
@classmethod
def get_toc(cls, markdown_text: str, topic=""):
def increase_heading_depth_and_add_top_heading(markdown_text, new_top_heading):
lines = markdown_text.splitlines()
# Increase the depth of each heading by adding an extra '#'
increased_depth_lines = ['#' + line if line.startswith('#') else line for line in lines]
# Add the new top-level heading at the beginning
increased_depth_lines.insert(0, f"# {new_top_heading}")
# Re-join the modified lines back into a single string
modified_text = '\n'.join(increased_depth_lines)
return modified_text
if topic:
markdown_text = increase_heading_depth_and_add_top_heading(markdown_text, topic)
toc = []
for line in markdown_text.splitlines():
if line.startswith('#'):
# Remove the '#' characters and strip leading/trailing spaces
heading_text = line.lstrip('#').strip()
# Create slug (lowercase, spaces to hyphens, remove non-alphanumeric characters)
slug = re.sub(r'[^a-zA-Z0-9\s-]', '', heading_text).lower().replace(' ', '-')
# Determine heading level for indentation
level = line.count('#') - 1
# Add to the table of contents
toc.append(' ' * level + f'- [{heading_text}](#{slug})')
return '\n'.join(toc)
@classmethod
def from_markdown(cls, text: str, expander=None):
self = cls()
for line in text.splitlines():
if line.startswith("###"):
self.h3(line[3:], write=False)
elif line.startswith("##"):
self.h2(line[2:], write=False)
elif line.startswith("#"):
self.h1(line[1:], write=False)
# customize markdown font size
custom_css = """
<style>
/* Adjust the font size for headings */
h1 { font-size: 28px; }
h2 { font-size: 24px; }
h3 { font-size: 22px; }
h4 { font-size: 20px; }
h5 { font-size: 18px; }
/* Adjust the font size for normal text */
p { font-size: 18px; }
</style>
"""
st.markdown(custom_css, unsafe_allow_html=True)
st.write(text)
self.toc(expander=expander)
def normalize(s):
"""
Normalize titles as valid HTML ids for anchors
>>> normalize("it's a test to spot how Things happ3n héhé")
"it-s-a-test-to-spot-how-things-happ3n-h-h"
"""
# Replace accents with "-"
s_wo_accents = unidecode.unidecode(s)
accents = [s for s in s if s not in s_wo_accents]
for accent in accents:
s = s.replace(accent, "-")
# Lowercase
s = s.lower()
# Keep only alphanum and remove "-" suffix if existing
normalized = (
"".join([char if char.isalnum() else "-" for char in s]).strip("-").lower()
)
return normalized