kash-shell 0.3.15__py3-none-any.whl → 0.3.17__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/render_as_html.py +3 -1
- kash/actions/core/tabbed_webpage_generate.py +3 -1
- kash/commands/base/files_command.py +2 -2
- kash/config/colors.py +97 -15
- kash/file_storage/file_store.py +71 -53
- kash/file_storage/item_file_format.py +0 -2
- kash/local_server/local_server_routes.py +1 -1
- kash/model/actions_model.py +5 -1
- kash/model/items_model.py +39 -38
- kash/shell/completions/shell_completions.py +1 -1
- kash/utils/file_utils/file_formats_model.py +6 -4
- kash/web_gen/simple_webpage.py +4 -1
- kash/web_gen/tabbed_webpage.py +8 -3
- kash/web_gen/templates/base_styles.css.jinja +110 -14
- kash/web_gen/templates/base_webpage.html.jinja +155 -1
- kash/web_gen/templates/item_view.html.jinja +4 -2
- kash/web_gen/templates/simple_webpage.html.jinja +544 -16
- kash/web_gen/templates/tabbed_webpage.html.jinja +4 -2
- kash/xontrib/kash_extension.py +3 -2
- {kash_shell-0.3.15.dist-info → kash_shell-0.3.17.dist-info}/METADATA +1 -1
- {kash_shell-0.3.15.dist-info → kash_shell-0.3.17.dist-info}/RECORD +24 -24
- {kash_shell-0.3.15.dist-info → kash_shell-0.3.17.dist-info}/WHEEL +0 -0
- {kash_shell-0.3.15.dist-info → kash_shell-0.3.17.dist-info}/entry_points.txt +0 -0
- {kash_shell-0.3.15.dist-info → kash_shell-0.3.17.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,24 +1,552 @@
|
|
|
1
1
|
{% extends "base_webpage.html.jinja" %}
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
{% block custom_styles %}
|
|
4
|
+
{{ super() }}
|
|
5
|
+
<style>
|
|
6
|
+
/* Override Tailwind's bg-white in dark mode */
|
|
7
|
+
[data-theme="dark"] .bg-white {
|
|
8
|
+
background-color: var(--color-bg-alt-solid) !important;
|
|
9
|
+
}
|
|
10
|
+
.long-text {
|
|
11
|
+
transition: background 0.4s ease-in-out, color 0.4s ease-in-out;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* Ensure long-text containers respect theme */
|
|
15
|
+
[data-theme="dark"] .long-text {
|
|
16
|
+
background-color: var(--color-bg-alt-solid);
|
|
17
|
+
color: var(--color-text);
|
|
18
|
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 -2px 6px -1px rgba(0, 0, 0, 0.2);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Table of Contents Styles */
|
|
22
|
+
:root {
|
|
23
|
+
--toc-width: max(21vw, 15rem);
|
|
24
|
+
--toc-breakpoint: {{ toc_breakpoint | default(1200) }}px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@media (min-width: 1536px) {
|
|
28
|
+
:root {
|
|
29
|
+
--toc-width: min(21vw, 30rem);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Desktop: Always use grid layout, adjust TOC column width */
|
|
34
|
+
@media (min-width: {{ toc_breakpoint | default(1200) }}px) {
|
|
35
|
+
.content-with-toc {
|
|
36
|
+
display: grid;
|
|
37
|
+
grid-template-columns: calc(var(--toc-width) + 4rem) 1fr;
|
|
38
|
+
max-width: none;
|
|
39
|
+
min-height: 100vh;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.content-with-toc.has-toc {
|
|
43
|
+
grid-template-columns: calc(var(--toc-width) + 4rem) 1fr;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Content goes in the second column (right side) */
|
|
47
|
+
.content-with-toc .long-text {
|
|
48
|
+
max-width: 48rem;
|
|
49
|
+
margin: 0 auto;
|
|
50
|
+
order: 2;
|
|
51
|
+
grid-column: 2;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* TOC goes in the first column (left side) */
|
|
55
|
+
.toc-container {
|
|
56
|
+
order: 1;
|
|
57
|
+
grid-column: 1;
|
|
58
|
+
align-self: start;
|
|
59
|
+
width: var(--toc-width);
|
|
60
|
+
position: sticky;
|
|
61
|
+
top: 2rem;
|
|
62
|
+
max-height: calc(100vh - 4rem);
|
|
63
|
+
overflow-y: auto;
|
|
64
|
+
padding: 1rem 0.7rem;
|
|
65
|
+
margin: 0 0 0 2rem;
|
|
66
|
+
border: 1px solid var(--color-border-hint);
|
|
67
|
+
opacity: 0;
|
|
68
|
+
transform: translateX(-100%);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.content-with-toc.has-toc .toc-container {
|
|
72
|
+
transform: translateX(0);
|
|
73
|
+
opacity: 1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Hide mobile toggle on desktop */
|
|
77
|
+
.toc-toggle {
|
|
78
|
+
display: none !important;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* More minimal TOC scrollbar */
|
|
82
|
+
.toc-container::-webkit-scrollbar {
|
|
83
|
+
width: 2px;
|
|
84
|
+
}
|
|
85
|
+
.toc-container::-webkit-scrollbar-track {
|
|
86
|
+
background: transparent; /* Invisible track */
|
|
87
|
+
}
|
|
88
|
+
.toc-container::-webkit-scrollbar-thumb {
|
|
89
|
+
background: var(--color-hint-gentle);
|
|
90
|
+
border-radius: 2px;
|
|
91
|
+
opacity: 0.1;
|
|
92
|
+
}
|
|
93
|
+
.toc-container::-webkit-scrollbar-thumb:hover {
|
|
94
|
+
opacity: 0.2;
|
|
95
|
+
}
|
|
96
|
+
.toc-container {
|
|
97
|
+
/* For Firefox */
|
|
98
|
+
scrollbar-width: thin;
|
|
99
|
+
scrollbar-color: var(--color-hint-gentle) transparent;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* TOC Styling */
|
|
104
|
+
.toc {
|
|
105
|
+
font-family: var(--font-sans);
|
|
106
|
+
color: var(--color-tertiary);
|
|
107
|
+
font-variant-numeric: tabular-nums;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.toc-title {
|
|
111
|
+
font-size: var(--font-size-small);
|
|
112
|
+
font-weight: 550;
|
|
113
|
+
text-transform: uppercase;
|
|
114
|
+
letter-spacing: 0.025em;
|
|
115
|
+
margin: 0 0 0.5rem 0.3rem;
|
|
116
|
+
padding-bottom: 0.5rem;
|
|
117
|
+
border-bottom: 1px solid var(--color-border-hint);
|
|
118
|
+
transition: color 0.4s ease-in-out, border-bottom-color 0.4s ease-in-out;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.toc-list {
|
|
122
|
+
list-style: none;
|
|
123
|
+
margin: 0;
|
|
124
|
+
padding: 0;
|
|
125
|
+
font-size: var(--font-size-small);
|
|
126
|
+
line-height: 1.2;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.toc-list li {
|
|
130
|
+
margin: 0;
|
|
131
|
+
padding: 0 0.3rem 0 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.toc-list li::before {
|
|
135
|
+
display: none; /* Remove custom bullet points */
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.toc-link {
|
|
139
|
+
display: block;
|
|
140
|
+
color: var(--color-tertiary);
|
|
141
|
+
text-decoration: none;
|
|
142
|
+
padding: 0.2rem 0;
|
|
143
|
+
transition: background 0.4s ease-in-out, all 0.15s ease-in-out;
|
|
144
|
+
border-left: 2px solid transparent;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.toc-link:hover {
|
|
148
|
+
color: var(--color-secondary);
|
|
149
|
+
background-color: var(--color-hover-bg);
|
|
150
|
+
text-decoration: none;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.toc-link.active {
|
|
154
|
+
color: var(--color-secondary);
|
|
155
|
+
border-left: 2px solid var(--color-primary);
|
|
156
|
+
background-color: var(--color-hover-bg);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Hanging indent and styles for each TOC heading */
|
|
160
|
+
.toc-h1 {
|
|
161
|
+
padding-left: 1.3rem;
|
|
162
|
+
text-indent: -1em;
|
|
163
|
+
font-weight: 550;
|
|
164
|
+
}
|
|
165
|
+
.toc-h2 {
|
|
166
|
+
padding-left: 2.0rem;
|
|
167
|
+
text-indent: -1em;
|
|
168
|
+
font-weight: 550;
|
|
169
|
+
}
|
|
170
|
+
.toc-h3 {
|
|
171
|
+
padding-left: 2.7rem;
|
|
172
|
+
text-indent: -1em;
|
|
173
|
+
font-weight: 380;
|
|
174
|
+
}
|
|
175
|
+
.toc-h4 {
|
|
176
|
+
padding-left: 3.4rem;
|
|
177
|
+
text-indent: -1em;
|
|
178
|
+
font-weight: 380;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* Prevent body scroll when TOC is open */
|
|
182
|
+
body.toc-open {
|
|
183
|
+
overflow: hidden;
|
|
184
|
+
position: fixed;
|
|
185
|
+
width: 100%;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Mobile TOC Layout */
|
|
189
|
+
@media (max-width: {{ toc_breakpoint | default(1200) - 1 }}px) {
|
|
190
|
+
/* Specific positioning and z-index for TOC toggle */
|
|
191
|
+
.toc-toggle {
|
|
192
|
+
left: 1rem;
|
|
193
|
+
z-index: 101;
|
|
194
|
+
opacity: 0; /* Hidden by default */
|
|
195
|
+
visibility: hidden; /* Start hidden for FOUC prevention on mobile */
|
|
196
|
+
transition: opacity 0.3s ease-in-out, visibility 0.3s ease-in-out;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* Show TOC toggle when user has scrolled past top */
|
|
200
|
+
.toc-toggle.show-toggle {
|
|
201
|
+
opacity: 1;
|
|
202
|
+
visibility: visible;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* Show backdrop when visible */
|
|
206
|
+
.toc-backdrop.visible {
|
|
207
|
+
opacity: 1;
|
|
208
|
+
visibility: visible;
|
|
209
|
+
pointer-events: auto;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Mobile TOC state: always rendered but hidden by default */
|
|
213
|
+
.toc-container {
|
|
214
|
+
display: block !important; /* Override base rule and any JS inline styles */
|
|
215
|
+
position: fixed;
|
|
216
|
+
top: 4rem;
|
|
217
|
+
left: 1rem;
|
|
218
|
+
width: calc(100vw - 2rem);
|
|
219
|
+
max-height: calc(100vh - 5rem);
|
|
220
|
+
/* Keep background and darker text on mobile since it's primary UI */
|
|
221
|
+
color: var(--color-text);
|
|
222
|
+
background: var(--color-bg-alt-solid);
|
|
223
|
+
border: 1px solid var(--color-border-hint);
|
|
224
|
+
padding: 1rem 0.7rem;
|
|
225
|
+
z-index: 200;
|
|
226
|
+
|
|
227
|
+
/* Ensure TOC itself is scrollable */
|
|
228
|
+
overflow-y: auto;
|
|
229
|
+
-webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */
|
|
230
|
+
overscroll-behavior: contain; /* Prevent scroll chaining */
|
|
231
|
+
|
|
232
|
+
/* Initial hidden state for mobile FOUC and animation */
|
|
233
|
+
opacity: 0;
|
|
234
|
+
transform: translateY(-0.5rem);
|
|
235
|
+
visibility: hidden;
|
|
236
|
+
pointer-events: none; /* Prevent interaction when hidden */
|
|
237
|
+
|
|
238
|
+
transition: opacity 0.3s ease-in-out,
|
|
239
|
+
transform 0.3s ease-in-out,
|
|
240
|
+
visibility 0.3s ease-in-out,
|
|
241
|
+
pointer-events 0.3s ease-in-out,
|
|
242
|
+
background-color 0.4s ease-in-out,
|
|
243
|
+
border-color 0.4s ease-in-out,
|
|
244
|
+
box-shadow 0.4s ease-in-out;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* Darker text on mobile */
|
|
248
|
+
.toc {
|
|
249
|
+
color: var(--color-secondary);
|
|
250
|
+
}
|
|
251
|
+
.toc-link {
|
|
252
|
+
color: var(--color-secondary);
|
|
253
|
+
}
|
|
254
|
+
.toc-link:hover {
|
|
255
|
+
color: var(--color-text);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.toc-link.active {
|
|
259
|
+
color: var(--color-text);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.toc-container.mobile-visible {
|
|
263
|
+
/* Visible state */
|
|
264
|
+
opacity: 1;
|
|
265
|
+
transform: translateY(0);
|
|
266
|
+
visibility: visible;
|
|
267
|
+
pointer-events: auto; /* Re-enable interaction */
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
</style>
|
|
271
|
+
{% endblock custom_styles %}
|
|
272
|
+
|
|
4
273
|
{% block main_content %}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
<div>
|
|
13
|
-
{% block page_content %}
|
|
14
|
-
{% if thumbnail_url %}
|
|
15
|
-
<img class="thumbnail" src="{{ thumbnail_url }}" alt="{{ title }}" />
|
|
274
|
+
<!-- simple_webpage begin main_content block -->
|
|
275
|
+
<div class="content-with-toc" id="content-container">
|
|
276
|
+
<div class="long-text container max-w-3xl mx-auto bg-white py-4 px-6 md:px-16" id="main-content">
|
|
277
|
+
{% block page_title %}
|
|
278
|
+
{% if title and add_title_h1 %}
|
|
279
|
+
<h1 class="text-center text-4xl mt-6 mb-6">{{ title }}</h1>
|
|
16
280
|
{% endif %}
|
|
17
|
-
|
|
18
|
-
|
|
281
|
+
{% endblock page_title %}
|
|
282
|
+
|
|
283
|
+
<div>
|
|
284
|
+
{% block page_content %}
|
|
285
|
+
{% if thumbnail_url %}
|
|
286
|
+
<img class="thumbnail" src="{{ thumbnail_url }}" alt="{{ title }}" />
|
|
287
|
+
{% endif %}
|
|
288
|
+
<div class="content">
|
|
289
|
+
{{ content_html | safe }}
|
|
290
|
+
</div>
|
|
291
|
+
{% endblock page_content %}
|
|
19
292
|
</div>
|
|
20
|
-
{% endblock page_content %}
|
|
21
293
|
</div>
|
|
294
|
+
|
|
295
|
+
<!-- Mobile TOC toggle -->
|
|
296
|
+
<button class="button fixed-button toc-toggle" id="toc-toggle" aria-label="Toggle table of contents" style="display: none;">
|
|
297
|
+
<i data-feather="list"></i>
|
|
298
|
+
</button>
|
|
299
|
+
|
|
300
|
+
<!-- Mobile TOC Backdrop -->
|
|
301
|
+
<div class="toc-backdrop" id="toc-backdrop"></div>
|
|
302
|
+
|
|
303
|
+
<!-- TOC Container -->
|
|
304
|
+
<aside class="toc-container" id="toc-container" aria-label="Table of contents">
|
|
305
|
+
<div class="toc">
|
|
306
|
+
<h2 class="toc-title">Contents</h2>
|
|
307
|
+
<ul class="toc-list" id="toc-list">
|
|
308
|
+
<!-- TOC items will be populated by JavaScript -->
|
|
309
|
+
</ul>
|
|
310
|
+
</div>
|
|
311
|
+
</aside>
|
|
22
312
|
</div>
|
|
313
|
+
<!-- simple_webpage end main_content block -->
|
|
23
314
|
{% endblock main_content %}
|
|
24
|
-
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
{% block scripts_extra %}
|
|
318
|
+
{{ super() }}
|
|
319
|
+
<!-- simple_webpage begin scripts_extra block -->
|
|
320
|
+
<script>
|
|
321
|
+
// Table of Contents functionality
|
|
322
|
+
function initTOC() {
|
|
323
|
+
const tocContainer = document.getElementById('toc-container');
|
|
324
|
+
const tocList = document.getElementById('toc-list');
|
|
325
|
+
const tocToggle = document.getElementById('toc-toggle');
|
|
326
|
+
const contentContainer = document.getElementById('content-container');
|
|
327
|
+
const mainContent = document.getElementById('main-content');
|
|
328
|
+
|
|
329
|
+
if (!tocContainer || !tocList || !mainContent) {
|
|
330
|
+
console.debug("TOC not initialized: missing elements");
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const tocBreakpoint = parseInt(
|
|
335
|
+
getComputedStyle(document.documentElement)
|
|
336
|
+
.getPropertyValue('--toc-breakpoint')
|
|
337
|
+
.replace('px', '')
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
// Find all headings in the main content
|
|
341
|
+
const headings = mainContent.querySelectorAll('{{ toc_headings | default("h1, h2, h3") }}');
|
|
342
|
+
// Only show TOC if we have toc_min_headings (default 10) or more headings
|
|
343
|
+
const tocThreshold = {{ toc_min_headings | default(10) }};
|
|
344
|
+
|
|
345
|
+
if (headings.length < tocThreshold) {
|
|
346
|
+
// TOC is disabled
|
|
347
|
+
if (tocToggle) {
|
|
348
|
+
tocToggle.style.display = 'none';
|
|
349
|
+
}
|
|
350
|
+
console.debug("TOC hidden: not enough headings");
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// TOC is enabled
|
|
355
|
+
contentContainer.classList.add('has-toc'); // This triggers grid layout
|
|
356
|
+
mainContent.classList.add('with-toc');
|
|
357
|
+
document.body.classList.add('page-has-toc');
|
|
358
|
+
|
|
359
|
+
if (tocToggle) {
|
|
360
|
+
tocToggle.style.display = 'flex';
|
|
361
|
+
// Ensure feather icon is rendered after making visible
|
|
362
|
+
if (typeof feather !== 'undefined') {
|
|
363
|
+
feather.replace();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Generate TOC items
|
|
368
|
+
tocList.innerHTML = '';
|
|
369
|
+
|
|
370
|
+
// If there is only one h1, skip it as it is the title of the page.
|
|
371
|
+
let filteredHeadings = Array.from(headings);
|
|
372
|
+
if (headings.length > 0) {
|
|
373
|
+
const firstHeading = headings[0];
|
|
374
|
+
const h1Count = filteredHeadings.filter(h => h.tagName.toLowerCase() === 'h1').length;
|
|
375
|
+
|
|
376
|
+
if (firstHeading.tagName.toLowerCase() === 'h1' && h1Count === 1) {
|
|
377
|
+
filteredHeadings = filteredHeadings.slice(1);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
filteredHeadings.forEach((heading, index) => {
|
|
382
|
+
// Ensure heading has an ID
|
|
383
|
+
if (!heading.id) {
|
|
384
|
+
const text = heading.textContent.trim().toLowerCase()
|
|
385
|
+
.replace(/[^\w\s-]/g, '')
|
|
386
|
+
.replace(/\s+/g, '-')
|
|
387
|
+
.replace(/-+/g, '-')
|
|
388
|
+
.replace(/^-|-$/g, '');
|
|
389
|
+
heading.id = text || `heading-${index}`;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const level = heading.tagName.toLowerCase();
|
|
393
|
+
const text = heading.textContent.trim();
|
|
394
|
+
|
|
395
|
+
const li = document.createElement('li');
|
|
396
|
+
const a = document.createElement('a');
|
|
397
|
+
a.href = `#${heading.id}`;
|
|
398
|
+
a.textContent = text;
|
|
399
|
+
a.className = `toc-link toc-${level}`;
|
|
400
|
+
|
|
401
|
+
li.appendChild(a);
|
|
402
|
+
tocList.appendChild(li);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// Mobile TOC toggle functionality
|
|
406
|
+
if (tocToggle) {
|
|
407
|
+
const tocBackdrop = document.getElementById('toc-backdrop');
|
|
408
|
+
let scrollPosition = 0;
|
|
409
|
+
|
|
410
|
+
const openTOC = () => {
|
|
411
|
+
// Save current scroll position
|
|
412
|
+
scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
|
|
413
|
+
|
|
414
|
+
// Add classes to show TOC and prevent body scroll
|
|
415
|
+
tocContainer.classList.add('mobile-visible');
|
|
416
|
+
if (tocBackdrop) tocBackdrop.classList.add('visible');
|
|
417
|
+
document.body.classList.add('toc-open');
|
|
418
|
+
|
|
419
|
+
// Set body position to maintain scroll position while fixed
|
|
420
|
+
document.body.style.top = `-${scrollPosition}px`;
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
const closeTOC = () => {
|
|
424
|
+
// Remove classes
|
|
425
|
+
tocContainer.classList.remove('mobile-visible');
|
|
426
|
+
if (tocBackdrop) tocBackdrop.classList.remove('visible');
|
|
427
|
+
document.body.classList.remove('toc-open');
|
|
428
|
+
|
|
429
|
+
// Restore body position and scroll
|
|
430
|
+
document.body.style.top = '';
|
|
431
|
+
window.scrollTo(0, scrollPosition);
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
tocToggle.addEventListener('click', () => {
|
|
435
|
+
if (tocContainer.classList.contains('mobile-visible')) {
|
|
436
|
+
closeTOC();
|
|
437
|
+
} else {
|
|
438
|
+
openTOC();
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// Close TOC when clicking backdrop
|
|
443
|
+
if (tocBackdrop) {
|
|
444
|
+
tocBackdrop.addEventListener('click', closeTOC);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Update the existing click handler to use closeTOC
|
|
448
|
+
document.addEventListener('click', (e) => {
|
|
449
|
+
if (window.innerWidth < tocBreakpoint &&
|
|
450
|
+
tocContainer.classList.contains('mobile-visible') &&
|
|
451
|
+
!tocContainer.contains(e.target) &&
|
|
452
|
+
!tocToggle.contains(e.target)) {
|
|
453
|
+
closeTOC();
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
// Prevent touch events from propagating through TOC
|
|
458
|
+
tocContainer.addEventListener('touchmove', (e) => {
|
|
459
|
+
e.stopPropagation();
|
|
460
|
+
}, { passive: false });
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Add smooth scrolling and active state management
|
|
464
|
+
const tocLinks = tocList.querySelectorAll('.toc-link');
|
|
465
|
+
tocLinks.forEach(link => {
|
|
466
|
+
link.addEventListener('click', (e) => {
|
|
467
|
+
e.preventDefault();
|
|
468
|
+
const targetId = link.getAttribute('href').substring(1);
|
|
469
|
+
const target = document.getElementById(targetId);
|
|
470
|
+
|
|
471
|
+
if (target) {
|
|
472
|
+
// Close TOC first on mobile
|
|
473
|
+
if (window.innerWidth < tocBreakpoint) {
|
|
474
|
+
tocContainer.classList.remove('mobile-visible');
|
|
475
|
+
document.getElementById('toc-backdrop')?.classList.remove('visible');
|
|
476
|
+
document.body.classList.remove('toc-open');
|
|
477
|
+
document.body.style.top = '';
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
target.scrollIntoView({
|
|
481
|
+
behavior: 'smooth',
|
|
482
|
+
block: 'start'
|
|
483
|
+
});
|
|
484
|
+
tocLinks.forEach(l => l.classList.remove('active'));
|
|
485
|
+
link.classList.add('active');
|
|
486
|
+
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// Helper function to check if TOC toggle should be visible
|
|
492
|
+
const updateTocToggleVisibility = () => {
|
|
493
|
+
if (tocToggle && tocLinks.length > 0) {
|
|
494
|
+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
495
|
+
const activeLink = tocList.querySelector('.toc-link.active');
|
|
496
|
+
const firstTocLink = tocLinks[0];
|
|
497
|
+
|
|
498
|
+
// Only show toggle if:
|
|
499
|
+
// 1. We've scrolled down at least 100px from the top, AND
|
|
500
|
+
// 2. We're past the first section (activeLink exists and is not the first)
|
|
501
|
+
const hasScrolled = scrollTop > 100;
|
|
502
|
+
const isPastFirstSection = activeLink && activeLink !== firstTocLink;
|
|
503
|
+
const showToggle = hasScrolled && isPastFirstSection;
|
|
504
|
+
|
|
505
|
+
tocToggle.classList.toggle('show-toggle', showToggle);
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
// Intersection Observer for active state
|
|
510
|
+
const observerOptions = {
|
|
511
|
+
rootMargin: '-20% 0% -70% 0%',
|
|
512
|
+
threshold: 0
|
|
513
|
+
};
|
|
514
|
+
const observer = new IntersectionObserver((entries) => {
|
|
515
|
+
entries.forEach(entry => {
|
|
516
|
+
if (entry.isIntersecting) {
|
|
517
|
+
tocLinks.forEach(link => link.classList.remove('active'));
|
|
518
|
+
const activeLink = tocList.querySelector(`a[href="#${entry.target.id}"]`);
|
|
519
|
+
if (activeLink) {
|
|
520
|
+
activeLink.classList.add('active');
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
// Update toggle visibility after intersection changes
|
|
526
|
+
updateTocToggleVisibility();
|
|
527
|
+
}, observerOptions);
|
|
528
|
+
|
|
529
|
+
filteredHeadings.forEach(heading => observer.observe(heading));
|
|
530
|
+
|
|
531
|
+
// Update toggle visibility on scroll
|
|
532
|
+
let scrollTimeout;
|
|
533
|
+
window.addEventListener('scroll', () => {
|
|
534
|
+
// Throttle scroll events for performance
|
|
535
|
+
clearTimeout(scrollTimeout);
|
|
536
|
+
scrollTimeout = setTimeout(updateTocToggleVisibility, 16); // ~60fps
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// Initial check
|
|
540
|
+
updateTocToggleVisibility();
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Initialize immediately, no setTimeout
|
|
544
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
545
|
+
initTOC();
|
|
546
|
+
if (typeof feather !== 'undefined') {
|
|
547
|
+
feather.replace();
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
</script>
|
|
551
|
+
<!-- simple_webpage end scripts_extra block -->
|
|
552
|
+
{% endblock scripts_extra %}
|
|
@@ -40,8 +40,9 @@
|
|
|
40
40
|
{% endblock page_content %}
|
|
41
41
|
<!-- tabbed_webpage end page_content block -->
|
|
42
42
|
|
|
43
|
-
<!-- tabbed_webpage begin scripts_extra block -->
|
|
44
43
|
{% block scripts_extra %}
|
|
44
|
+
<!-- tabbed_webpage begin scripts_extra block -->
|
|
45
|
+
<script>
|
|
45
46
|
function showTab(tabId, element) {
|
|
46
47
|
document.querySelectorAll(".tab-pane").forEach((tab) => {
|
|
47
48
|
tab.classList.add("hidden");
|
|
@@ -54,5 +55,6 @@
|
|
|
54
55
|
element.classList.add("tab-button-active");
|
|
55
56
|
element.classList.remove("tab-button-inactive");
|
|
56
57
|
}
|
|
58
|
+
</script>
|
|
59
|
+
<!-- tabbed_webpage end scripts_extra block -->
|
|
57
60
|
{% endblock scripts_extra %}
|
|
58
|
-
<!-- tabbed_webpage end scripts_extra block -->
|
kash/xontrib/kash_extension.py
CHANGED
|
@@ -12,7 +12,6 @@ Can run from the custom kash shell (main.py) or from a regular xonsh shell.
|
|
|
12
12
|
import kash.exec.command_registry
|
|
13
13
|
import kash.xonsh_custom.load_into_xonsh
|
|
14
14
|
import kash.xonsh_custom.xonsh_env
|
|
15
|
-
from kash.config.logger import get_logger
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
# We add action loading here directly in the xontrib so we expose `load` and
|
|
@@ -55,6 +54,8 @@ kash.xonsh_custom.xonsh_env.set_alias("load", load)
|
|
|
55
54
|
try:
|
|
56
55
|
kash.xonsh_custom.load_into_xonsh.load_into_xonsh()
|
|
57
56
|
except Exception as e:
|
|
57
|
+
from kash.config.logger import get_logger
|
|
58
|
+
|
|
58
59
|
log = get_logger(__name__)
|
|
59
|
-
log.error("Could not initialize kash: %s", e)
|
|
60
|
+
log.error("Could not initialize kash: %s", e, exc_info=True)
|
|
60
61
|
raise
|