This repository was archived by the owner on Apr 23, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathdocs.py
More file actions
186 lines (147 loc) · 5.93 KB
/
docs.py
File metadata and controls
186 lines (147 loc) · 5.93 KB
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
"""Template for documentation pages."""
from typing import Any
import flexdown
import mistletoe
from .blocks import *
def right_sidebar_item_highlight():
return r"""
function setupTableOfContentsHighlight() {
// Delay to ensure DOM is fully loaded
setTimeout(() => {
const tocLinks = document.querySelectorAll('#toc-navigation a');
const activeClasses = [
'text-primary-9',
'dark:text-primary-11',
'shadow-[1.5px_0_0_0_var(--primary-11)_inset]',
'dark:shadow-[1.5px_0_0_0_var(--primary-9)_inset]',
];
const defaultClasses = ['text-m-slate-7', 'dark:text-m-slate-6'];
function normalizeId(id) {
return id.toLowerCase().replace(/\s+/g, '-');
}
function setDefaultState(link) {
activeClasses.forEach(cls => link.classList.remove(cls));
defaultClasses.forEach(cls => link.classList.add(cls));
}
function setActiveState(link) {
defaultClasses.forEach(cls => link.classList.remove(cls));
activeClasses.forEach(cls => link.classList.add(cls));
}
function highlightTocLink() {
// Get the current hash from the URL
const currentHash = window.location.hash.substring(1);
// Reset all links
tocLinks.forEach(link => setDefaultState(link));
// If there's a hash, find and highlight the corresponding link
if (currentHash) {
const correspondingLink = Array.from(tocLinks).find(link => {
// Extract the ID from the link's href
const linkHash = new URL(link.href).hash.substring(1);
return normalizeId(linkHash) === normalizeId(currentHash);
});
if (correspondingLink) {
setActiveState(correspondingLink);
}
}
}
// Add click event listeners to TOC links to force highlight
tocLinks.forEach(link => {
link.addEventListener('click', (e) => {
// Remove active class from all links
tocLinks.forEach(otherLink => setDefaultState(otherLink));
// Add active class to clicked link
setActiveState(e.target);
});
});
// Intersection Observer for scroll-based highlighting
const observerOptions = {
root: null,
rootMargin: '-20% 0px -70% 0px',
threshold: 0
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const headerId = entry.target.id;
// Find corresponding TOC link
const correspondingLink = Array.from(tocLinks).find(link => {
const linkHash = new URL(link.href).hash.substring(1);
return normalizeId(linkHash) === normalizeId(headerId);
});
if (correspondingLink) {
// Reset all links
tocLinks.forEach(link => setDefaultState(link));
// Highlight current link
setActiveState(correspondingLink);
}
}
});
}, observerOptions);
// Observe headers
const headerSelectors = Array.from(tocLinks).map(link =>
new URL(link.href).hash.substring(1)
);
headerSelectors.forEach(selector => {
const header = document.getElementById(selector);
if (header) {
observer.observe(header);
}
});
// Initial highlighting
highlightTocLink();
// Handle hash changes
window.addEventListener('hashchange', highlightTocLink);
}, 100);
}
// Run the function when the page loads
setupTableOfContentsHighlight();
"""
def get_headings(comp: Any):
"""Get the strings from markdown component."""
if isinstance(comp, mistletoe.block_token.Heading):
heading_text = "".join(
token.content for token in comp.children if hasattr(token, "content")
)
return [(comp.level, heading_text)]
# Recursively get the strings from the children.
if not hasattr(comp, "children") or comp.children is None:
return []
headings = []
for child in comp.children:
headings.extend(get_headings(child))
return headings
def get_toc(source: Any, href: str, component_list: list | None = None):
from reflex_ui_shared.flexdown import xd
component_list = component_list or []
component_list = component_list[1:]
# Generate the TOC
# The environment used for execing and evaling code.
from reflex_ui_shared.constants import REFLEX_ASSETS_CDN
env = source.metadata
env["__xd"] = xd
env["REFLEX_ASSETS_CDN"] = REFLEX_ASSETS_CDN
# Get the content of the document.
doc_content = source.content
# Get the blocks in the source code.
# Note: we must use reflex-web's special flexdown instance xd here - it knows about all custom block types (like DemoBlock)
blocks = xd.get_blocks(doc_content, href)
content_pieces = []
for block in blocks:
if (
not isinstance(block, flexdown.blocks.MarkdownBlock)
or len(block.lines) == 0
or not block.lines[0].startswith("#")
):
continue
# Now we should have all the env entries we need
content = block.get_content(env)
content_pieces.append(content)
content = "\n".join(content_pieces)
doc = mistletoe.Document(content)
# Parse the markdown headers.
headings = get_headings(doc)
if len(component_list):
headings.append((1, "API Reference"))
for component_tuple in component_list:
headings.append((2, component_tuple[1]))
return headings, doc_content