kash-shell 0.3.17__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 (37) hide show
  1. kash/actions/core/minify_html.py +41 -0
  2. kash/commands/base/show_command.py +11 -1
  3. kash/config/colors.py +6 -2
  4. kash/docs/markdown/topics/a1_what_is_kash.md +52 -23
  5. kash/docs/markdown/topics/a2_installation.md +17 -30
  6. kash/docs/markdown/topics/a3_getting_started.md +5 -19
  7. kash/exec/action_exec.py +1 -1
  8. kash/exec/fetch_url_metadata.py +9 -0
  9. kash/exec/precondition_registry.py +3 -3
  10. kash/file_storage/file_store.py +18 -1
  11. kash/llm_utils/llm_features.py +5 -1
  12. kash/llm_utils/llms.py +18 -8
  13. kash/media_base/media_cache.py +48 -24
  14. kash/media_base/media_services.py +63 -14
  15. kash/media_base/services/local_file_media.py +9 -1
  16. kash/model/items_model.py +4 -5
  17. kash/model/media_model.py +9 -1
  18. kash/model/params_model.py +9 -3
  19. kash/utils/common/function_inspect.py +97 -1
  20. kash/utils/common/testing.py +58 -0
  21. kash/utils/common/url_slice.py +329 -0
  22. kash/utils/file_utils/file_formats.py +1 -1
  23. kash/utils/text_handling/markdown_utils.py +424 -16
  24. kash/web_gen/templates/base_styles.css.jinja +137 -15
  25. kash/web_gen/templates/base_webpage.html.jinja +13 -17
  26. kash/web_gen/templates/components/toc_scripts.js.jinja +319 -0
  27. kash/web_gen/templates/components/toc_styles.css.jinja +284 -0
  28. kash/web_gen/templates/components/tooltip_scripts.js.jinja +730 -0
  29. kash/web_gen/templates/components/tooltip_styles.css.jinja +482 -0
  30. kash/web_gen/templates/content_styles.css.jinja +13 -8
  31. kash/web_gen/templates/simple_webpage.html.jinja +15 -481
  32. kash/workspaces/workspaces.py +10 -1
  33. {kash_shell-0.3.17.dist-info → kash_shell-0.3.18.dist-info}/METADATA +75 -72
  34. {kash_shell-0.3.17.dist-info → kash_shell-0.3.18.dist-info}/RECORD +37 -30
  35. {kash_shell-0.3.17.dist-info → kash_shell-0.3.18.dist-info}/WHEEL +0 -0
  36. {kash_shell-0.3.17.dist-info → kash_shell-0.3.18.dist-info}/entry_points.txt +0 -0
  37. {kash_shell-0.3.17.dist-info → kash_shell-0.3.18.dist-info}/licenses/LICENSE +0 -0
@@ -5,7 +5,15 @@
5
5
  <style>
6
6
  /* Override Tailwind's bg-white in dark mode */
7
7
  [data-theme="dark"] .bg-white {
8
- background-color: var(--color-bg-alt-solid) !important;
8
+ background-color: var(--color-bg-alt-solid);
9
+ }
10
+
11
+ /* Fallback responsive padding for compatibility */
12
+ @media (min-width: 768px) {
13
+ .content-with-toc .long-text {
14
+ padding-left: 4rem;
15
+ padding-right: 4rem;
16
+ }
9
17
  }
10
18
  .long-text {
11
19
  transition: background 0.4s ease-in-out, color 0.4s ease-in-out;
@@ -18,255 +26,8 @@
18
26
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 -2px 6px -1px rgba(0, 0, 0, 0.2);
19
27
  }
20
28
 
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
- }
29
+ {% include "components/toc_styles.css.jinja" %}
30
+ {% include "components/tooltip_styles.css.jinja" %}
270
31
  </style>
271
32
  {% endblock custom_styles %}
272
33
 
@@ -293,7 +54,7 @@ body.toc-open {
293
54
  </div>
294
55
 
295
56
  <!-- Mobile TOC toggle -->
296
- <button class="button fixed-button toc-toggle" id="toc-toggle" aria-label="Toggle table of contents" style="display: none;">
57
+ <button class="button fixed-button floating-button toc-toggle" id="toc-toggle" aria-label="Toggle table of contents" style="display: none;">
297
58
  <i data-feather="list"></i>
298
59
  </button>
299
60
 
@@ -303,7 +64,7 @@ body.toc-open {
303
64
  <!-- TOC Container -->
304
65
  <aside class="toc-container" id="toc-container" aria-label="Table of contents">
305
66
  <div class="toc">
306
- <h2 class="toc-title">Contents</h2>
67
+ <a href="#" class="toc-link toc-title" id="toc-title-link">Contents</a>
307
68
  <ul class="toc-list" id="toc-list">
308
69
  <!-- TOC items will be populated by JavaScript -->
309
70
  </ul>
@@ -318,235 +79,8 @@ body.toc-open {
318
79
  {{ super() }}
319
80
  <!-- simple_webpage begin scripts_extra block -->
320
81
  <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
- });
82
+ {% include "components/toc_scripts.js.jinja" %}
83
+ {% include "components/tooltip_scripts.js.jinja" %}
550
84
  </script>
551
85
  <!-- simple_webpage end scripts_extra block -->
552
86
  {% endblock scripts_extra %}
@@ -19,7 +19,11 @@ from kash.model.params_model import GLOBAL_PARAMS, RawParamValues
19
19
  from kash.shell.output.shell_output import PrintHooks, cprint
20
20
  from kash.utils.errors import FileNotFound, InvalidInput, InvalidState
21
21
  from kash.utils.file_utils.ignore_files import IgnoreFilter, is_ignored_default
22
- from kash.workspaces.workspace_dirs import check_strict_workspace_name, is_global_ws_dir, is_ws_dir
22
+ from kash.workspaces.workspace_dirs import (
23
+ check_strict_workspace_name,
24
+ is_global_ws_dir,
25
+ is_ws_dir,
26
+ )
23
27
  from kash.workspaces.workspace_registry import WorkspaceInfo, get_ws_registry
24
28
 
25
29
  if TYPE_CHECKING:
@@ -49,6 +53,11 @@ class Workspace(ABC):
49
53
  def base_dir(self) -> Path:
50
54
  """The base directory for this workspace."""
51
55
 
56
+ @property
57
+ @abstractmethod
58
+ def assets_dir(self) -> Path:
59
+ """The directory for this workspace's assets."""
60
+
52
61
 
53
62
  def resolve_ws(name: str | Path) -> WorkspaceInfo:
54
63
  """