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.
Files changed (40) hide show
  1. kash/actions/core/minify_html.py +41 -0
  2. kash/commands/base/files_command.py +2 -2
  3. kash/commands/base/show_command.py +11 -1
  4. kash/config/colors.py +20 -8
  5. kash/docs/markdown/topics/a1_what_is_kash.md +52 -23
  6. kash/docs/markdown/topics/a2_installation.md +17 -30
  7. kash/docs/markdown/topics/a3_getting_started.md +5 -19
  8. kash/exec/action_exec.py +1 -1
  9. kash/exec/fetch_url_metadata.py +9 -0
  10. kash/exec/precondition_registry.py +3 -3
  11. kash/file_storage/file_store.py +18 -1
  12. kash/llm_utils/llm_features.py +5 -1
  13. kash/llm_utils/llms.py +18 -8
  14. kash/media_base/media_cache.py +48 -24
  15. kash/media_base/media_services.py +63 -14
  16. kash/media_base/services/local_file_media.py +9 -1
  17. kash/model/actions_model.py +2 -2
  18. kash/model/items_model.py +4 -5
  19. kash/model/media_model.py +9 -1
  20. kash/model/params_model.py +9 -3
  21. kash/utils/common/function_inspect.py +97 -1
  22. kash/utils/common/testing.py +58 -0
  23. kash/utils/common/url_slice.py +329 -0
  24. kash/utils/file_utils/file_formats.py +1 -1
  25. kash/utils/text_handling/markdown_utils.py +424 -16
  26. kash/web_gen/templates/base_styles.css.jinja +204 -25
  27. kash/web_gen/templates/base_webpage.html.jinja +48 -26
  28. kash/web_gen/templates/components/toc_scripts.js.jinja +319 -0
  29. kash/web_gen/templates/components/toc_styles.css.jinja +284 -0
  30. kash/web_gen/templates/components/tooltip_scripts.js.jinja +730 -0
  31. kash/web_gen/templates/components/tooltip_styles.css.jinja +482 -0
  32. kash/web_gen/templates/content_styles.css.jinja +13 -8
  33. kash/web_gen/templates/simple_webpage.html.jinja +59 -21
  34. kash/web_gen/templates/tabbed_webpage.html.jinja +4 -2
  35. kash/workspaces/workspaces.py +10 -1
  36. {kash_shell-0.3.16.dist-info → kash_shell-0.3.18.dist-info}/METADATA +75 -72
  37. {kash_shell-0.3.16.dist-info → kash_shell-0.3.18.dist-info}/RECORD +40 -33
  38. {kash_shell-0.3.16.dist-info → kash_shell-0.3.18.dist-info}/WHEEL +0 -0
  39. {kash_shell-0.3.16.dist-info → kash_shell-0.3.18.dist-info}/entry_points.txt +0 -0
  40. {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
+ }