kash-shell 0.3.16__py3-none-any.whl → 0.3.18__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kash/actions/core/minify_html.py +41 -0
- kash/commands/base/files_command.py +2 -2
- kash/commands/base/show_command.py +11 -1
- kash/config/colors.py +20 -8
- kash/docs/markdown/topics/a1_what_is_kash.md +52 -23
- kash/docs/markdown/topics/a2_installation.md +17 -30
- kash/docs/markdown/topics/a3_getting_started.md +5 -19
- kash/exec/action_exec.py +1 -1
- kash/exec/fetch_url_metadata.py +9 -0
- kash/exec/precondition_registry.py +3 -3
- kash/file_storage/file_store.py +18 -1
- kash/llm_utils/llm_features.py +5 -1
- kash/llm_utils/llms.py +18 -8
- kash/media_base/media_cache.py +48 -24
- kash/media_base/media_services.py +63 -14
- kash/media_base/services/local_file_media.py +9 -1
- kash/model/actions_model.py +2 -2
- kash/model/items_model.py +4 -5
- kash/model/media_model.py +9 -1
- kash/model/params_model.py +9 -3
- kash/utils/common/function_inspect.py +97 -1
- kash/utils/common/testing.py +58 -0
- kash/utils/common/url_slice.py +329 -0
- kash/utils/file_utils/file_formats.py +1 -1
- kash/utils/text_handling/markdown_utils.py +424 -16
- kash/web_gen/templates/base_styles.css.jinja +204 -25
- kash/web_gen/templates/base_webpage.html.jinja +48 -26
- kash/web_gen/templates/components/toc_scripts.js.jinja +319 -0
- kash/web_gen/templates/components/toc_styles.css.jinja +284 -0
- kash/web_gen/templates/components/tooltip_scripts.js.jinja +730 -0
- kash/web_gen/templates/components/tooltip_styles.css.jinja +482 -0
- kash/web_gen/templates/content_styles.css.jinja +13 -8
- kash/web_gen/templates/simple_webpage.html.jinja +59 -21
- kash/web_gen/templates/tabbed_webpage.html.jinja +4 -2
- kash/workspaces/workspaces.py +10 -1
- {kash_shell-0.3.16.dist-info → kash_shell-0.3.18.dist-info}/METADATA +75 -72
- {kash_shell-0.3.16.dist-info → kash_shell-0.3.18.dist-info}/RECORD +40 -33
- {kash_shell-0.3.16.dist-info → kash_shell-0.3.18.dist-info}/WHEEL +0 -0
- {kash_shell-0.3.16.dist-info → kash_shell-0.3.18.dist-info}/entry_points.txt +0 -0
- {kash_shell-0.3.16.dist-info → kash_shell-0.3.18.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
// Table of Contents functionality
|
|
2
|
+
function initTOC() {
|
|
3
|
+
const tocContainer = document.getElementById('toc-container');
|
|
4
|
+
const tocList = document.getElementById('toc-list');
|
|
5
|
+
const tocToggle = document.getElementById('toc-toggle');
|
|
6
|
+
const contentContainer = document.getElementById('content-container');
|
|
7
|
+
const mainContent = document.getElementById('main-content');
|
|
8
|
+
|
|
9
|
+
if (!tocContainer || !tocList || !mainContent) {
|
|
10
|
+
console.debug("TOC not initialized: missing elements");
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const tocBreakpoint = parseInt(
|
|
15
|
+
getComputedStyle(document.documentElement)
|
|
16
|
+
.getPropertyValue('--toc-breakpoint')
|
|
17
|
+
.replace('px', '')
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// Find all headings in the main content
|
|
21
|
+
const headings = mainContent.querySelectorAll('{{ toc_headings | default("h1, h2, h3") }}');
|
|
22
|
+
// Only show TOC if we have toc_min_headings (default 10) or more headings
|
|
23
|
+
const tocThreshold = {{ toc_min_headings | default(10) }};
|
|
24
|
+
|
|
25
|
+
if (headings.length < tocThreshold) {
|
|
26
|
+
// TOC is disabled
|
|
27
|
+
contentContainer.classList.remove('content-with-toc');
|
|
28
|
+
if (tocToggle) {
|
|
29
|
+
tocToggle.style.display = 'none';
|
|
30
|
+
}
|
|
31
|
+
console.debug("TOC hidden: not enough headings");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// TOC is enabled
|
|
36
|
+
contentContainer.classList.add('has-toc'); // This triggers grid layout
|
|
37
|
+
mainContent.classList.add('with-toc');
|
|
38
|
+
document.body.classList.add('page-has-toc');
|
|
39
|
+
|
|
40
|
+
if (tocToggle) {
|
|
41
|
+
tocToggle.style.display = 'flex';
|
|
42
|
+
// Ensure feather icon is rendered after making visible
|
|
43
|
+
if (typeof feather !== 'undefined') {
|
|
44
|
+
feather.replace();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Generate TOC items
|
|
49
|
+
tocList.innerHTML = '';
|
|
50
|
+
|
|
51
|
+
// If there is only one h1, skip it as it is the title of the page.
|
|
52
|
+
let filteredHeadings = Array.from(headings);
|
|
53
|
+
if (headings.length > 0) {
|
|
54
|
+
const firstHeading = headings[0];
|
|
55
|
+
const h1Count = filteredHeadings.filter(h => h.tagName.toLowerCase() === 'h1').length;
|
|
56
|
+
|
|
57
|
+
if (firstHeading.tagName.toLowerCase() === 'h1' && h1Count === 1) {
|
|
58
|
+
filteredHeadings = filteredHeadings.slice(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
filteredHeadings.forEach((heading, index) => {
|
|
63
|
+
// Ensure heading has an ID
|
|
64
|
+
if (!heading.id) {
|
|
65
|
+
const text = heading.textContent.trim().toLowerCase()
|
|
66
|
+
.replace(/[^\w\s-]/g, '')
|
|
67
|
+
.replace(/\s+/g, '-')
|
|
68
|
+
.replace(/-+/g, '-')
|
|
69
|
+
.replace(/^-|-$/g, '');
|
|
70
|
+
heading.id = text || `heading-${index}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const level = heading.tagName.toLowerCase();
|
|
74
|
+
const text = heading.textContent.trim();
|
|
75
|
+
|
|
76
|
+
const li = document.createElement('li');
|
|
77
|
+
const a = document.createElement('a');
|
|
78
|
+
a.href = `#${heading.id}`;
|
|
79
|
+
a.textContent = text;
|
|
80
|
+
a.className = `toc-link toc-${level}`;
|
|
81
|
+
|
|
82
|
+
li.appendChild(a);
|
|
83
|
+
tocList.appendChild(li);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Mobile TOC toggle functionality
|
|
87
|
+
if (tocToggle) {
|
|
88
|
+
const tocBackdrop = document.getElementById('toc-backdrop');
|
|
89
|
+
let scrollPosition = 0;
|
|
90
|
+
|
|
91
|
+
// Calculate scrollbar width
|
|
92
|
+
const getScrollbarWidth = () => {
|
|
93
|
+
// Create a temporary div with scrollbar
|
|
94
|
+
const outer = document.createElement('div');
|
|
95
|
+
outer.style.visibility = 'hidden';
|
|
96
|
+
outer.style.overflow = 'scroll';
|
|
97
|
+
outer.style.msOverflowStyle = 'scrollbar';
|
|
98
|
+
document.body.appendChild(outer);
|
|
99
|
+
|
|
100
|
+
// Add inner div
|
|
101
|
+
const inner = document.createElement('div');
|
|
102
|
+
outer.appendChild(inner);
|
|
103
|
+
|
|
104
|
+
// Calculate scrollbar width
|
|
105
|
+
const scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
|
|
106
|
+
|
|
107
|
+
// Clean up
|
|
108
|
+
outer.parentNode.removeChild(outer);
|
|
109
|
+
|
|
110
|
+
return scrollbarWidth;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const openTOC = () => {
|
|
114
|
+
// Save current scroll position
|
|
115
|
+
scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
|
|
116
|
+
|
|
117
|
+
// Calculate scrollbar width and add padding to prevent shift
|
|
118
|
+
const scrollbarWidth = getScrollbarWidth();
|
|
119
|
+
const hasVerticalScrollbar = document.documentElement.scrollHeight > window.innerHeight;
|
|
120
|
+
|
|
121
|
+
// Add classes to show TOC and prevent body scroll
|
|
122
|
+
tocContainer.classList.add('mobile-visible');
|
|
123
|
+
if (tocBackdrop) tocBackdrop.classList.add('visible');
|
|
124
|
+
document.body.classList.add('toc-open');
|
|
125
|
+
|
|
126
|
+
// Set body position to maintain scroll position while fixed
|
|
127
|
+
document.body.style.top = `-${scrollPosition}px`;
|
|
128
|
+
|
|
129
|
+
// Add padding to compensate for scrollbar removal (only if there was a scrollbar)
|
|
130
|
+
if (hasVerticalScrollbar && scrollbarWidth > 0) {
|
|
131
|
+
document.body.style.paddingRight = `${scrollbarWidth}px`;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const closeTOC = () => {
|
|
136
|
+
// Remove classes
|
|
137
|
+
tocContainer.classList.remove('mobile-visible');
|
|
138
|
+
if (tocBackdrop) tocBackdrop.classList.remove('visible');
|
|
139
|
+
document.body.classList.remove('toc-open');
|
|
140
|
+
|
|
141
|
+
// Restore body position and scroll
|
|
142
|
+
document.body.style.top = '';
|
|
143
|
+
document.body.style.paddingRight = '';
|
|
144
|
+
window.scrollTo(0, scrollPosition);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
tocToggle.addEventListener('click', () => {
|
|
148
|
+
if (tocContainer.classList.contains('mobile-visible')) {
|
|
149
|
+
closeTOC();
|
|
150
|
+
} else {
|
|
151
|
+
openTOC();
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Close TOC when clicking backdrop
|
|
156
|
+
if (tocBackdrop) {
|
|
157
|
+
tocBackdrop.addEventListener('click', closeTOC);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Update the existing click handler to use closeTOC
|
|
161
|
+
document.addEventListener('click', (e) => {
|
|
162
|
+
if (window.innerWidth < tocBreakpoint &&
|
|
163
|
+
tocContainer.classList.contains('mobile-visible') &&
|
|
164
|
+
!tocContainer.contains(e.target) &&
|
|
165
|
+
!tocToggle.contains(e.target)) {
|
|
166
|
+
closeTOC();
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Prevent touch events from propagating through TOC
|
|
171
|
+
tocContainer.addEventListener('touchmove', (e) => {
|
|
172
|
+
e.stopPropagation();
|
|
173
|
+
}, { passive: false });
|
|
174
|
+
|
|
175
|
+
// Additional scroll prevention for iOS and other edge cases
|
|
176
|
+
let touchStartY = 0;
|
|
177
|
+
|
|
178
|
+
// Track touch start position
|
|
179
|
+
tocContainer.addEventListener('touchstart', (e) => {
|
|
180
|
+
touchStartY = e.touches[0].clientY;
|
|
181
|
+
}, { passive: true });
|
|
182
|
+
|
|
183
|
+
// Prevent overscroll on TOC container
|
|
184
|
+
tocContainer.addEventListener('touchmove', (e) => {
|
|
185
|
+
const touchY = e.touches[0].clientY;
|
|
186
|
+
const scrollTop = tocContainer.scrollTop;
|
|
187
|
+
const scrollHeight = tocContainer.scrollHeight;
|
|
188
|
+
const height = tocContainer.clientHeight;
|
|
189
|
+
const isScrollingUp = touchY > touchStartY;
|
|
190
|
+
const isScrollingDown = touchY < touchStartY;
|
|
191
|
+
|
|
192
|
+
// Prevent scroll when at boundaries
|
|
193
|
+
if ((isScrollingUp && scrollTop <= 0) ||
|
|
194
|
+
(isScrollingDown && scrollTop + height >= scrollHeight)) {
|
|
195
|
+
e.preventDefault();
|
|
196
|
+
}
|
|
197
|
+
}, { passive: false });
|
|
198
|
+
|
|
199
|
+
// Prevent any scrolling on the backdrop
|
|
200
|
+
if (tocBackdrop) {
|
|
201
|
+
tocBackdrop.addEventListener('touchmove', (e) => {
|
|
202
|
+
e.preventDefault();
|
|
203
|
+
}, { passive: false });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Add smooth scrolling and active state management
|
|
208
|
+
const tocLinks = tocList.querySelectorAll('.toc-link');
|
|
209
|
+
tocLinks.forEach(link => {
|
|
210
|
+
link.addEventListener('click', (e) => {
|
|
211
|
+
e.preventDefault();
|
|
212
|
+
const targetId = link.getAttribute('href').substring(1);
|
|
213
|
+
const target = document.getElementById(targetId);
|
|
214
|
+
|
|
215
|
+
if (target) {
|
|
216
|
+
// Close TOC first on mobile
|
|
217
|
+
if (window.innerWidth < tocBreakpoint) {
|
|
218
|
+
tocContainer.classList.remove('mobile-visible');
|
|
219
|
+
document.getElementById('toc-backdrop')?.classList.remove('visible');
|
|
220
|
+
document.body.classList.remove('toc-open');
|
|
221
|
+
document.body.style.top = '';
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
target.scrollIntoView({
|
|
225
|
+
behavior: 'smooth',
|
|
226
|
+
block: 'start'
|
|
227
|
+
});
|
|
228
|
+
tocLinks.forEach(l => l.classList.remove('active'));
|
|
229
|
+
link.classList.add('active');
|
|
230
|
+
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Add click handler for Contents title link
|
|
236
|
+
const tocTitleLink = document.getElementById('toc-title-link');
|
|
237
|
+
if (tocTitleLink) {
|
|
238
|
+
tocTitleLink.addEventListener('click', (e) => {
|
|
239
|
+
e.preventDefault();
|
|
240
|
+
scrollToTop();
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Scroll to top function
|
|
245
|
+
function scrollToTop() {
|
|
246
|
+
// Close TOC first on mobile
|
|
247
|
+
if (window.innerWidth < tocBreakpoint) {
|
|
248
|
+
tocContainer.classList.remove('mobile-visible');
|
|
249
|
+
document.getElementById('toc-backdrop')?.classList.remove('visible');
|
|
250
|
+
document.body.classList.remove('toc-open');
|
|
251
|
+
document.body.style.top = '';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Scroll to top
|
|
255
|
+
window.scrollTo({
|
|
256
|
+
top: 0,
|
|
257
|
+
behavior: 'smooth'
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Helper function to check if TOC toggle should be visible
|
|
262
|
+
const updateTocToggleVisibility = () => {
|
|
263
|
+
if (tocToggle && tocLinks.length > 0) {
|
|
264
|
+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
265
|
+
const activeLink = tocList.querySelector('.toc-link.active');
|
|
266
|
+
const firstTocLink = tocLinks[0];
|
|
267
|
+
|
|
268
|
+
// Only show toggle if:
|
|
269
|
+
// 1. We've scrolled down at least 100px from the top, AND
|
|
270
|
+
// 2. We're past the first section (activeLink exists and is not the first)
|
|
271
|
+
const hasScrolled = scrollTop > 100;
|
|
272
|
+
const isPastFirstSection = activeLink && activeLink !== firstTocLink;
|
|
273
|
+
const showToggle = hasScrolled && isPastFirstSection;
|
|
274
|
+
|
|
275
|
+
tocToggle.classList.toggle('show-toggle', showToggle);
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// Intersection Observer for active state
|
|
280
|
+
const observerOptions = {
|
|
281
|
+
rootMargin: '-20% 0% -70% 0%',
|
|
282
|
+
threshold: 0
|
|
283
|
+
};
|
|
284
|
+
const observer = new IntersectionObserver((entries) => {
|
|
285
|
+
entries.forEach(entry => {
|
|
286
|
+
if (entry.isIntersecting) {
|
|
287
|
+
tocLinks.forEach(link => link.classList.remove('active'));
|
|
288
|
+
const activeLink = tocList.querySelector(`a[href="#${entry.target.id}"]`);
|
|
289
|
+
if (activeLink) {
|
|
290
|
+
activeLink.classList.add('active');
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Update toggle visibility after intersection changes
|
|
296
|
+
updateTocToggleVisibility();
|
|
297
|
+
}, observerOptions);
|
|
298
|
+
|
|
299
|
+
filteredHeadings.forEach(heading => observer.observe(heading));
|
|
300
|
+
|
|
301
|
+
// Update toggle visibility on scroll
|
|
302
|
+
let scrollTimeout;
|
|
303
|
+
window.addEventListener('scroll', () => {
|
|
304
|
+
// Throttle scroll events for performance
|
|
305
|
+
clearTimeout(scrollTimeout);
|
|
306
|
+
scrollTimeout = setTimeout(updateTocToggleVisibility, 16); // ~60fps
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Initial check
|
|
310
|
+
updateTocToggleVisibility();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Initialize immediately, no setTimeout
|
|
314
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
315
|
+
initTOC();
|
|
316
|
+
if (typeof feather !== 'undefined') {
|
|
317
|
+
feather.replace();
|
|
318
|
+
}
|
|
319
|
+
});
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/* Table of Contents Styles */
|
|
2
|
+
:root {
|
|
3
|
+
--toc-width: max(21vw, 15rem);
|
|
4
|
+
--toc-breakpoint: {{ toc_breakpoint | default(1200) }}px;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
@media (min-width: 1536px) {
|
|
8
|
+
:root {
|
|
9
|
+
--toc-width: min(21vw, 30rem);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* Desktop: Always use grid layout, adjust TOC column width */
|
|
14
|
+
@media (min-width: {{ toc_breakpoint | default(1200) }}px) {
|
|
15
|
+
.content-with-toc {
|
|
16
|
+
display: grid;
|
|
17
|
+
grid-template-columns: calc(var(--toc-width) + 4rem) 1fr;
|
|
18
|
+
max-width: none;
|
|
19
|
+
min-height: 100vh;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.content-with-toc.has-toc {
|
|
23
|
+
grid-template-columns: calc(var(--toc-width) + 4rem) 1fr;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Content goes in the second column (right side) */
|
|
27
|
+
.content-with-toc .long-text {
|
|
28
|
+
max-width: 48rem;
|
|
29
|
+
margin-left: max(1rem, calc((100vw - 48rem) / 2 - var(--toc-width)));
|
|
30
|
+
margin-right: auto;
|
|
31
|
+
order: 2;
|
|
32
|
+
grid-column: 2;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* TOC goes in the first column (left side) */
|
|
36
|
+
.toc-container {
|
|
37
|
+
order: 1;
|
|
38
|
+
grid-column: 1;
|
|
39
|
+
align-self: start;
|
|
40
|
+
width: var(--toc-width);
|
|
41
|
+
position: sticky;
|
|
42
|
+
top: 2rem;
|
|
43
|
+
max-height: calc(100vh - 4rem);
|
|
44
|
+
overflow-y: auto;
|
|
45
|
+
padding: 0.5rem 0.7rem 1rem 1.2rem;
|
|
46
|
+
margin: 0 0 0 2rem;
|
|
47
|
+
border: 1px solid var(--color-border-hint);
|
|
48
|
+
opacity: 0;
|
|
49
|
+
transform: translateX(-100%);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.content-with-toc.has-toc .toc-container {
|
|
53
|
+
transform: translateX(0);
|
|
54
|
+
opacity: 1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Hide mobile toggle on desktop */
|
|
58
|
+
.toc-toggle {
|
|
59
|
+
display: none !important;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* More minimal TOC scrollbar */
|
|
63
|
+
.toc-container::-webkit-scrollbar {
|
|
64
|
+
width: 2px;
|
|
65
|
+
}
|
|
66
|
+
.toc-container::-webkit-scrollbar-track {
|
|
67
|
+
background: transparent; /* Invisible track */
|
|
68
|
+
}
|
|
69
|
+
.toc-container::-webkit-scrollbar-thumb {
|
|
70
|
+
background: var(--color-hint-gentle);
|
|
71
|
+
border-radius: 2px;
|
|
72
|
+
opacity: 0.1;
|
|
73
|
+
}
|
|
74
|
+
.toc-container::-webkit-scrollbar-thumb:hover {
|
|
75
|
+
opacity: 0.2;
|
|
76
|
+
}
|
|
77
|
+
.toc-container {
|
|
78
|
+
/* For Firefox */
|
|
79
|
+
scrollbar-width: thin;
|
|
80
|
+
scrollbar-color: var(--color-hint-gentle) transparent;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* TOC Styling */
|
|
85
|
+
.toc {
|
|
86
|
+
font-family: var(--font-sans);
|
|
87
|
+
color: var(--color-tertiary);
|
|
88
|
+
font-variant-numeric: tabular-nums;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.toc-title {
|
|
92
|
+
font-size: var(--font-size-small);
|
|
93
|
+
font-weight: 550;
|
|
94
|
+
text-transform: uppercase;
|
|
95
|
+
letter-spacing: 0.025em;
|
|
96
|
+
padding-left: 0.3rem;
|
|
97
|
+
border-bottom: 1px solid var(--color-border-hint);
|
|
98
|
+
border-left: none !important; /* Override toc-link border */
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.toc-list {
|
|
102
|
+
list-style: none;
|
|
103
|
+
margin: 0.5rem 0 0 0;
|
|
104
|
+
padding: 0;
|
|
105
|
+
font-size: var(--font-size-small);
|
|
106
|
+
line-height: 1.2;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.toc-list li {
|
|
110
|
+
margin: 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.toc-list li::before {
|
|
114
|
+
display: none; /* Remove custom bullet points */
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.toc-link {
|
|
118
|
+
display: block;
|
|
119
|
+
color: var(--color-tertiary);
|
|
120
|
+
text-decoration: none;
|
|
121
|
+
padding-top: 0.2rem;
|
|
122
|
+
padding-bottom: 0.2rem;
|
|
123
|
+
transition: background 0.4s ease-in-out, all 0.15s ease-in-out;
|
|
124
|
+
border-left: 2px solid transparent;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.toc-link:hover {
|
|
128
|
+
color: var(--color-secondary);
|
|
129
|
+
background-color: var(--color-hover-bg);
|
|
130
|
+
text-decoration: none;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.toc-link.active {
|
|
134
|
+
color: var(--color-secondary);
|
|
135
|
+
border-left: 2px solid var(--color-primary);
|
|
136
|
+
background-color: var(--color-bg-selected);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* Hanging indent and styles for each TOC heading */
|
|
140
|
+
.toc-h1 {
|
|
141
|
+
padding-left: 1.3rem;
|
|
142
|
+
text-indent: -1em;
|
|
143
|
+
font-weight: 550;
|
|
144
|
+
letter-spacing: 0.007em;
|
|
145
|
+
}
|
|
146
|
+
.toc-h2 {
|
|
147
|
+
padding-left: 2.0rem;
|
|
148
|
+
text-indent: -1em;
|
|
149
|
+
font-weight: 550;
|
|
150
|
+
letter-spacing: 0.007em;
|
|
151
|
+
}
|
|
152
|
+
.toc-h3 {
|
|
153
|
+
padding-left: 2.7rem;
|
|
154
|
+
text-indent: -1em;
|
|
155
|
+
font-weight: 370;
|
|
156
|
+
}
|
|
157
|
+
.toc-h4 {
|
|
158
|
+
padding-left: 3.4rem;
|
|
159
|
+
text-indent: -1em;
|
|
160
|
+
font-weight: 370;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* Prevent body scroll when TOC is open */
|
|
164
|
+
body.toc-open {
|
|
165
|
+
overflow: hidden;
|
|
166
|
+
position: fixed;
|
|
167
|
+
width: 100%;
|
|
168
|
+
/* Prevent iOS bounce scrolling on body */
|
|
169
|
+
touch-action: none;
|
|
170
|
+
-webkit-overflow-scrolling: auto;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Mobile TOC Layout */
|
|
174
|
+
@media (max-width: {{ toc_breakpoint | default(1200) - 1 }}px) {
|
|
175
|
+
/* TOC backdrop - semi-transparent overlay */
|
|
176
|
+
.toc-backdrop {
|
|
177
|
+
position: fixed;
|
|
178
|
+
top: 0;
|
|
179
|
+
left: 0;
|
|
180
|
+
width: 100vw;
|
|
181
|
+
height: 100vh;
|
|
182
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
183
|
+
z-index: 199; /* Below TOC container (200) but above everything else */
|
|
184
|
+
|
|
185
|
+
/* Hidden by default */
|
|
186
|
+
opacity: 0;
|
|
187
|
+
visibility: hidden;
|
|
188
|
+
pointer-events: none;
|
|
189
|
+
|
|
190
|
+
/* Smooth transition */
|
|
191
|
+
transition: opacity 0.3s ease-in-out, visibility 0.3s ease-in-out;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* Specific positioning and z-index for TOC toggle */
|
|
195
|
+
.toc-toggle {
|
|
196
|
+
left: 1rem;
|
|
197
|
+
z-index: 101;
|
|
198
|
+
opacity: 0; /* Hidden by default */
|
|
199
|
+
visibility: hidden; /* Start hidden for FOUC prevention on mobile */
|
|
200
|
+
transition: opacity 0.3s ease-in-out,
|
|
201
|
+
visibility 0.3s ease-in-out,
|
|
202
|
+
background-color 0.4s ease-in-out,
|
|
203
|
+
color 0.4s ease-in-out;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/* Show TOC toggle when user has scrolled past top */
|
|
207
|
+
.toc-toggle.show-toggle {
|
|
208
|
+
opacity: 1;
|
|
209
|
+
visibility: visible;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Show backdrop when visible */
|
|
213
|
+
.toc-backdrop.visible {
|
|
214
|
+
opacity: 1;
|
|
215
|
+
visibility: visible;
|
|
216
|
+
pointer-events: auto;
|
|
217
|
+
/* Prevent any scrolling or touch interaction on backdrop */
|
|
218
|
+
touch-action: none;
|
|
219
|
+
-webkit-overflow-scrolling: auto;
|
|
220
|
+
overflow: hidden;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* Mobile TOC state: always rendered but hidden by default */
|
|
224
|
+
.toc-container {
|
|
225
|
+
display: block !important; /* Override base rule and any JS inline styles */
|
|
226
|
+
position: fixed;
|
|
227
|
+
top: 4rem;
|
|
228
|
+
left: 1rem;
|
|
229
|
+
width: calc(100vw - 2rem);
|
|
230
|
+
max-height: calc(100vh - 5rem);
|
|
231
|
+
/* Keep background and darker text on mobile since it's primary UI */
|
|
232
|
+
color: var(--color-text);
|
|
233
|
+
background: var(--color-bg-alt-solid);
|
|
234
|
+
border: 1px solid var(--color-border-hint);
|
|
235
|
+
padding: 1rem 0.7rem;
|
|
236
|
+
z-index: 200;
|
|
237
|
+
|
|
238
|
+
/* Ensure TOC itself is scrollable */
|
|
239
|
+
overflow-y: auto;
|
|
240
|
+
overflow-x: hidden; /* Prevent horizontal scroll */
|
|
241
|
+
-webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */
|
|
242
|
+
overscroll-behavior: contain; /* Prevent scroll chaining */
|
|
243
|
+
|
|
244
|
+
/* Ensure touch scrolling works properly */
|
|
245
|
+
touch-action: pan-y;
|
|
246
|
+
|
|
247
|
+
/* Initial hidden state for mobile FOUC and animation */
|
|
248
|
+
opacity: 0;
|
|
249
|
+
transform: translateY(-0.5rem);
|
|
250
|
+
visibility: hidden;
|
|
251
|
+
pointer-events: none; /* Prevent interaction when hidden */
|
|
252
|
+
|
|
253
|
+
transition: opacity 0.3s ease-in-out,
|
|
254
|
+
transform 0.3s ease-in-out,
|
|
255
|
+
visibility 0.3s ease-in-out,
|
|
256
|
+
pointer-events 0.3s ease-in-out,
|
|
257
|
+
background-color 0.4s ease-in-out,
|
|
258
|
+
border-color 0.4s ease-in-out,
|
|
259
|
+
box-shadow 0.4s ease-in-out;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* Darker text on mobile */
|
|
263
|
+
.toc {
|
|
264
|
+
color: var(--color-secondary);
|
|
265
|
+
}
|
|
266
|
+
.toc-link {
|
|
267
|
+
color: var(--color-secondary);
|
|
268
|
+
}
|
|
269
|
+
.toc-link:hover {
|
|
270
|
+
color: var(--color-text);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.toc-link.active {
|
|
274
|
+
color: var(--color-text);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.toc-container.mobile-visible {
|
|
278
|
+
/* Visible state */
|
|
279
|
+
opacity: 1;
|
|
280
|
+
transform: translateY(0);
|
|
281
|
+
visibility: visible;
|
|
282
|
+
pointer-events: auto; /* Re-enable interaction */
|
|
283
|
+
}
|
|
284
|
+
}
|