cyclecad 3.6.0 → 3.8.0

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.
@@ -0,0 +1,805 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>cycleCAD Help Viewer</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ :root {
15
+ --bg-primary: #1e1e1e;
16
+ --bg-secondary: #252526;
17
+ --bg-tertiary: #2d2d30;
18
+ --text-primary: #e0e0e0;
19
+ --text-secondary: #a0a0a0;
20
+ --accent-blue: #0284C7;
21
+ --accent-green: #10b981;
22
+ --accent-red: #ef4444;
23
+ --border-color: #3e3e42;
24
+ }
25
+
26
+ html, body {
27
+ width: 100%;
28
+ height: 100%;
29
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
30
+ background-color: var(--bg-primary);
31
+ color: var(--text-primary);
32
+ }
33
+
34
+ body {
35
+ display: flex;
36
+ flex-direction: column;
37
+ }
38
+
39
+ .header {
40
+ background-color: var(--bg-secondary);
41
+ border-bottom: 1px solid var(--border-color);
42
+ padding: 1rem;
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: space-between;
46
+ flex-wrap: wrap;
47
+ gap: 1rem;
48
+ }
49
+
50
+ .header h1 {
51
+ font-size: 1.5rem;
52
+ color: var(--accent-blue);
53
+ flex: 0 0 auto;
54
+ }
55
+
56
+ .search-box {
57
+ flex: 1;
58
+ min-width: 300px;
59
+ display: flex;
60
+ gap: 0.5rem;
61
+ }
62
+
63
+ .search-box input {
64
+ flex: 1;
65
+ padding: 0.75rem;
66
+ background-color: var(--bg-tertiary);
67
+ border: 1px solid var(--border-color);
68
+ color: var(--text-primary);
69
+ border-radius: 4px;
70
+ font-size: 0.95rem;
71
+ }
72
+
73
+ .search-box input::placeholder {
74
+ color: var(--text-secondary);
75
+ }
76
+
77
+ .search-box input:focus {
78
+ outline: none;
79
+ border-color: var(--accent-blue);
80
+ box-shadow: 0 0 0 2px rgba(2, 132, 199, 0.2);
81
+ }
82
+
83
+ .clear-btn {
84
+ padding: 0.75rem 1rem;
85
+ background-color: var(--bg-tertiary);
86
+ border: 1px solid var(--border-color);
87
+ color: var(--text-secondary);
88
+ border-radius: 4px;
89
+ cursor: pointer;
90
+ transition: all 0.2s;
91
+ }
92
+
93
+ .clear-btn:hover {
94
+ background-color: var(--accent-red);
95
+ color: white;
96
+ }
97
+
98
+ .container {
99
+ display: flex;
100
+ flex: 1;
101
+ overflow: hidden;
102
+ }
103
+
104
+ .sidebar {
105
+ width: 240px;
106
+ background-color: var(--bg-secondary);
107
+ border-right: 1px solid var(--border-color);
108
+ display: flex;
109
+ flex-direction: column;
110
+ overflow: hidden;
111
+ }
112
+
113
+ .sidebar-title {
114
+ padding: 0.75rem 1rem;
115
+ font-size: 0.85rem;
116
+ font-weight: 600;
117
+ color: var(--text-secondary);
118
+ text-transform: uppercase;
119
+ border-bottom: 1px solid var(--border-color);
120
+ }
121
+
122
+ .module-list {
123
+ flex: 1;
124
+ overflow-y: auto;
125
+ padding: 0.5rem;
126
+ }
127
+
128
+ .module-filter {
129
+ padding: 0.5rem;
130
+ background-color: var(--bg-tertiary);
131
+ border: 1px solid var(--border-color);
132
+ margin: 0.25rem;
133
+ border-radius: 4px;
134
+ cursor: pointer;
135
+ font-size: 0.9rem;
136
+ transition: all 0.2s;
137
+ }
138
+
139
+ .module-filter:hover {
140
+ background-color: var(--border-color);
141
+ }
142
+
143
+ .module-filter.active {
144
+ background-color: var(--accent-blue);
145
+ color: white;
146
+ }
147
+
148
+ .content-area {
149
+ flex: 1;
150
+ display: flex;
151
+ flex-direction: column;
152
+ overflow: hidden;
153
+ }
154
+
155
+ .results-header {
156
+ padding: 1rem;
157
+ background-color: var(--bg-secondary);
158
+ border-bottom: 1px solid var(--border-color);
159
+ font-size: 0.9rem;
160
+ color: var(--text-secondary);
161
+ }
162
+
163
+ .results-list {
164
+ flex: 1;
165
+ overflow-y: auto;
166
+ padding: 1rem;
167
+ }
168
+
169
+ .result-item {
170
+ background-color: var(--bg-secondary);
171
+ border: 1px solid var(--border-color);
172
+ border-radius: 6px;
173
+ margin-bottom: 1rem;
174
+ overflow: hidden;
175
+ cursor: pointer;
176
+ transition: all 0.2s;
177
+ }
178
+
179
+ .result-item:hover {
180
+ border-color: var(--accent-blue);
181
+ background-color: var(--bg-tertiary);
182
+ }
183
+
184
+ .result-header {
185
+ padding: 1rem;
186
+ display: flex;
187
+ align-items: center;
188
+ justify-content: space-between;
189
+ gap: 1rem;
190
+ }
191
+
192
+ .result-title-group {
193
+ flex: 1;
194
+ }
195
+
196
+ .result-title {
197
+ font-size: 1.05rem;
198
+ font-weight: 500;
199
+ color: var(--accent-blue);
200
+ margin-bottom: 0.25rem;
201
+ }
202
+
203
+ .result-module {
204
+ display: inline-block;
205
+ padding: 0.25rem 0.75rem;
206
+ background-color: var(--bg-tertiary);
207
+ border-radius: 12px;
208
+ font-size: 0.75rem;
209
+ color: var(--text-secondary);
210
+ margin-right: 0.5rem;
211
+ }
212
+
213
+ .result-module.Text-to-CAD {
214
+ background-color: rgba(2, 132, 199, 0.2);
215
+ color: #0284C7;
216
+ }
217
+
218
+ .result-module.Photo-to-CAD {
219
+ background-color: rgba(16, 185, 129, 0.2);
220
+ color: #10b981;
221
+ }
222
+
223
+ .result-module.Manufacturability {
224
+ background-color: rgba(239, 68, 68, 0.2);
225
+ color: #ef4444;
226
+ }
227
+
228
+ .result-module.Generative {
229
+ background-color: rgba(168, 85, 247, 0.2);
230
+ color: #a855f7;
231
+ }
232
+
233
+ .result-module.Multi {
234
+ background-color: rgba(249, 115, 22, 0.2);
235
+ color: #f97316;
236
+ }
237
+
238
+ .result-module.Smart {
239
+ background-color: rgba(59, 130, 246, 0.2);
240
+ color: #3b82f6;
241
+ }
242
+
243
+ .result-preview {
244
+ font-size: 0.9rem;
245
+ color: var(--text-secondary);
246
+ line-height: 1.4;
247
+ }
248
+
249
+ .expand-toggle {
250
+ width: 24px;
251
+ height: 24px;
252
+ display: flex;
253
+ align-items: center;
254
+ justify-content: center;
255
+ background-color: var(--bg-tertiary);
256
+ border-radius: 4px;
257
+ color: var(--text-secondary);
258
+ flex-shrink: 0;
259
+ }
260
+
261
+ .result-item.expanded .expand-toggle {
262
+ color: var(--accent-blue);
263
+ }
264
+
265
+ .result-detail {
266
+ display: none;
267
+ padding: 0 1rem 1rem;
268
+ border-top: 1px solid var(--border-color);
269
+ max-height: 500px;
270
+ overflow-y: auto;
271
+ }
272
+
273
+ .result-item.expanded .result-detail {
274
+ display: block;
275
+ }
276
+
277
+ .detail-section {
278
+ margin-bottom: 1rem;
279
+ }
280
+
281
+ .detail-label {
282
+ font-size: 0.8rem;
283
+ color: var(--text-secondary);
284
+ text-transform: uppercase;
285
+ font-weight: 600;
286
+ margin-bottom: 0.5rem;
287
+ }
288
+
289
+ .detail-description {
290
+ font-size: 0.95rem;
291
+ line-height: 1.5;
292
+ color: var(--text-primary);
293
+ margin-bottom: 1rem;
294
+ }
295
+
296
+ .detail-syntax {
297
+ background-color: var(--bg-tertiary);
298
+ padding: 0.75rem;
299
+ border-left: 3px solid var(--accent-blue);
300
+ font-family: 'Monaco', 'Menlo', monospace;
301
+ font-size: 0.85rem;
302
+ border-radius: 2px;
303
+ margin-bottom: 1rem;
304
+ overflow-x: auto;
305
+ }
306
+
307
+ .detail-examples {
308
+ margin-bottom: 1rem;
309
+ }
310
+
311
+ .detail-examples .detail-label {
312
+ margin-bottom: 0.5rem;
313
+ }
314
+
315
+ .example-item {
316
+ background-color: var(--bg-tertiary);
317
+ padding: 0.5rem 0.75rem;
318
+ margin-bottom: 0.5rem;
319
+ border-radius: 3px;
320
+ font-family: 'Monaco', 'Menlo', monospace;
321
+ font-size: 0.85rem;
322
+ display: flex;
323
+ justify-content: space-between;
324
+ align-items: center;
325
+ gap: 1rem;
326
+ }
327
+
328
+ .example-text {
329
+ flex: 1;
330
+ color: var(--text-secondary);
331
+ }
332
+
333
+ .copy-btn {
334
+ padding: 0.35rem 0.7rem;
335
+ background-color: var(--accent-blue);
336
+ color: white;
337
+ border: none;
338
+ border-radius: 3px;
339
+ cursor: pointer;
340
+ font-size: 0.75rem;
341
+ flex-shrink: 0;
342
+ transition: all 0.2s;
343
+ }
344
+
345
+ .copy-btn:hover {
346
+ background-color: #0975b4;
347
+ }
348
+
349
+ .copy-btn.copied {
350
+ background-color: var(--accent-green);
351
+ }
352
+
353
+ .detail-tips {
354
+ background-color: rgba(16, 185, 129, 0.15);
355
+ border-left: 3px solid var(--accent-green);
356
+ padding: 0.75rem;
357
+ border-radius: 2px;
358
+ margin-bottom: 1rem;
359
+ }
360
+
361
+ .tips-label {
362
+ color: var(--accent-green);
363
+ font-weight: 600;
364
+ margin-bottom: 0.5rem;
365
+ }
366
+
367
+ .tips-text {
368
+ font-size: 0.9rem;
369
+ color: var(--text-secondary);
370
+ line-height: 1.5;
371
+ }
372
+
373
+ .related-items {
374
+ display: flex;
375
+ gap: 0.5rem;
376
+ flex-wrap: wrap;
377
+ }
378
+
379
+ .related-badge {
380
+ padding: 0.35rem 0.7rem;
381
+ background-color: var(--border-color);
382
+ border-radius: 12px;
383
+ font-size: 0.8rem;
384
+ cursor: pointer;
385
+ transition: all 0.2s;
386
+ color: var(--text-secondary);
387
+ }
388
+
389
+ .related-badge:hover {
390
+ background-color: var(--accent-blue);
391
+ color: white;
392
+ }
393
+
394
+ .empty-state {
395
+ display: flex;
396
+ flex-direction: column;
397
+ align-items: center;
398
+ justify-content: center;
399
+ height: 100%;
400
+ color: var(--text-secondary);
401
+ }
402
+
403
+ .empty-state-icon {
404
+ font-size: 3rem;
405
+ margin-bottom: 1rem;
406
+ opacity: 0.5;
407
+ }
408
+
409
+ .empty-state-text {
410
+ font-size: 1.05rem;
411
+ margin-bottom: 0.5rem;
412
+ }
413
+
414
+ .empty-state-hint {
415
+ font-size: 0.9rem;
416
+ }
417
+
418
+ .recently-viewed {
419
+ padding: 1rem;
420
+ background-color: var(--bg-secondary);
421
+ border-bottom: 1px solid var(--border-color);
422
+ }
423
+
424
+ .recently-viewed-title {
425
+ font-size: 0.8rem;
426
+ color: var(--text-secondary);
427
+ text-transform: uppercase;
428
+ font-weight: 600;
429
+ margin-bottom: 0.5rem;
430
+ }
431
+
432
+ .recently-viewed-items {
433
+ display: flex;
434
+ gap: 0.5rem;
435
+ flex-wrap: wrap;
436
+ }
437
+
438
+ .recent-item {
439
+ padding: 0.4rem 0.8rem;
440
+ background-color: var(--bg-tertiary);
441
+ border: 1px solid var(--border-color);
442
+ border-radius: 4px;
443
+ font-size: 0.85rem;
444
+ cursor: pointer;
445
+ transition: all 0.2s;
446
+ }
447
+
448
+ .recent-item:hover {
449
+ border-color: var(--accent-blue);
450
+ background-color: var(--border-color);
451
+ }
452
+
453
+ @media print {
454
+ .header, .sidebar, .expand-toggle, .copy-btn, .clear-btn {
455
+ display: none;
456
+ }
457
+
458
+ .results-list {
459
+ overflow: visible;
460
+ }
461
+
462
+ .result-detail {
463
+ display: block !important;
464
+ max-height: none;
465
+ }
466
+
467
+ .result-item {
468
+ page-break-inside: avoid;
469
+ margin-bottom: 2rem;
470
+ }
471
+ }
472
+
473
+ @media (max-width: 768px) {
474
+ .sidebar {
475
+ display: none;
476
+ }
477
+
478
+ .header {
479
+ flex-direction: column;
480
+ align-items: stretch;
481
+ }
482
+
483
+ .search-box {
484
+ min-width: 100%;
485
+ }
486
+ }
487
+
488
+ /* Scrollbar styling */
489
+ ::-webkit-scrollbar {
490
+ width: 10px;
491
+ height: 10px;
492
+ }
493
+
494
+ ::-webkit-scrollbar-track {
495
+ background: var(--bg-secondary);
496
+ }
497
+
498
+ ::-webkit-scrollbar-thumb {
499
+ background: var(--border-color);
500
+ border-radius: 5px;
501
+ }
502
+
503
+ ::-webkit-scrollbar-thumb:hover {
504
+ background: var(--text-secondary);
505
+ }
506
+ </style>
507
+ </head>
508
+ <body>
509
+ <div class="header">
510
+ <h1>cycleCAD Help</h1>
511
+ <div class="search-box">
512
+ <input type="text" id="searchInput" placeholder="Search help... (e.g., 'cylinder', 'fillet', 'FDM')" autocomplete="off">
513
+ <button class="clear-btn" id="clearBtn">Clear</button>
514
+ </div>
515
+ </div>
516
+
517
+ <div class="container">
518
+ <div class="sidebar">
519
+ <div class="sidebar-title">Modules</div>
520
+ <div class="module-list" id="moduleList"></div>
521
+ <div class="recently-viewed">
522
+ <div class="recently-viewed-title">Recently Viewed</div>
523
+ <div class="recently-viewed-items" id="recentlyViewed"></div>
524
+ </div>
525
+ </div>
526
+
527
+ <div class="content-area">
528
+ <div class="results-header" id="resultsHeader"></div>
529
+ <div class="results-list" id="resultsList"></div>
530
+ </div>
531
+ </div>
532
+
533
+ <script>
534
+ // Help database
535
+ let helpData = [];
536
+
537
+ // Recently viewed (in-memory only)
538
+ let recentlyViewed = [];
539
+
540
+ // Current filters
541
+ let currentModule = null;
542
+ let currentSearch = '';
543
+
544
+ // Load help data
545
+ async function loadHelpData() {
546
+ try {
547
+ const response = await fetch('./js/killer-features-help.json');
548
+ helpData = await response.json();
549
+ renderModuleFilters();
550
+ renderResults();
551
+ } catch (error) {
552
+ console.error('Failed to load help data:', error);
553
+ document.getElementById('resultsList').innerHTML = `
554
+ <div class="empty-state">
555
+ <div class="empty-state-icon">!</div>
556
+ <div class="empty-state-text">Failed to load help database</div>
557
+ <div class="empty-state-hint">Make sure killer-features-help.json is in the js folder</div>
558
+ </div>
559
+ `;
560
+ }
561
+ }
562
+
563
+ // Get unique modules
564
+ function getModules() {
565
+ const modules = [...new Set(helpData.map(item => item.module))];
566
+ return modules.sort();
567
+ }
568
+
569
+ // Render module filters
570
+ function renderModuleFilters() {
571
+ const moduleList = document.getElementById('moduleList');
572
+ moduleList.innerHTML = '';
573
+
574
+ getModules().forEach(module => {
575
+ const btn = document.createElement('button');
576
+ btn.className = 'module-filter';
577
+ btn.textContent = module;
578
+ btn.addEventListener('click', () => {
579
+ currentModule = currentModule === module ? null : module;
580
+ renderModuleFilters();
581
+ renderResults();
582
+ });
583
+ if (currentModule === module) {
584
+ btn.classList.add('active');
585
+ }
586
+ moduleList.appendChild(btn);
587
+ });
588
+ }
589
+
590
+ // Fuzzy search
591
+ function fuzzyMatch(query, text) {
592
+ const queryLower = query.toLowerCase();
593
+ const textLower = text.toLowerCase();
594
+
595
+ if (textLower.includes(queryLower)) return true;
596
+
597
+ let queryIdx = 0;
598
+ for (let i = 0; i < textLower.length && queryIdx < queryLower.length; i++) {
599
+ if (textLower[i] === queryLower[queryIdx]) {
600
+ queryIdx++;
601
+ }
602
+ }
603
+ return queryIdx === queryLower.length;
604
+ }
605
+
606
+ // Filter and sort results
607
+ function getFilteredResults() {
608
+ let results = helpData;
609
+
610
+ // Filter by module
611
+ if (currentModule) {
612
+ results = results.filter(item => item.module === currentModule);
613
+ }
614
+
615
+ // Filter by search
616
+ if (currentSearch.trim()) {
617
+ results = results.filter(item => {
618
+ const query = currentSearch.toLowerCase();
619
+ return fuzzyMatch(query, item.title) ||
620
+ fuzzyMatch(query, item.description) ||
621
+ item.keywords.some(kw => fuzzyMatch(query, kw));
622
+ });
623
+ }
624
+
625
+ return results;
626
+ }
627
+
628
+ // Render results
629
+ function renderResults() {
630
+ const results = getFilteredResults();
631
+ const resultsList = document.getElementById('resultsList');
632
+ const resultsHeader = document.getElementById('resultsHeader');
633
+
634
+ // Update header
635
+ resultsHeader.textContent = `${results.length} result${results.length !== 1 ? 's' : ''} ${currentModule ? `in ${currentModule}` : ''}`;
636
+
637
+ // Render results
638
+ if (results.length === 0) {
639
+ resultsList.innerHTML = `
640
+ <div class="empty-state">
641
+ <div class="empty-state-icon">O_o</div>
642
+ <div class="empty-state-text">No results found</div>
643
+ <div class="empty-state-hint">Try searching for something else or clear filters</div>
644
+ </div>
645
+ `;
646
+ return;
647
+ }
648
+
649
+ resultsList.innerHTML = results.map(item => `
650
+ <div class="result-item" data-id="${item.id}">
651
+ <div class="result-header">
652
+ <div class="result-title-group">
653
+ <div class="result-title">${item.title}</div>
654
+ <div>
655
+ <span class="result-module ${item.module.replace(/ /g, '-')}">${item.module}</span>
656
+ <span class="result-module">${item.category}</span>
657
+ </div>
658
+ <div class="result-preview">${item.description}</div>
659
+ </div>
660
+ <div class="expand-toggle">▼</div>
661
+ </div>
662
+ <div class="result-detail">
663
+ ${item.syntax !== 'N/A' ? `
664
+ <div class="detail-section">
665
+ <div class="detail-label">Syntax</div>
666
+ <div class="detail-syntax">${item.syntax}</div>
667
+ </div>
668
+ ` : ''}
669
+
670
+ ${item.examples && item.examples.length > 0 ? `
671
+ <div class="detail-section">
672
+ <div class="detail-label">Examples</div>
673
+ <div class="detail-examples">
674
+ ${item.examples.map(ex => `
675
+ <div class="example-item">
676
+ <span class="example-text">${ex}</span>
677
+ <button class="copy-btn" onclick="copyToClipboard('${ex.replace(/'/g, "\\'")}', this)">Copy</button>
678
+ </div>
679
+ `).join('')}
680
+ </div>
681
+ </div>
682
+ ` : ''}
683
+
684
+ ${Object.keys(item.parameters).length > 0 ? `
685
+ <div class="detail-section">
686
+ <div class="detail-label">Parameters</div>
687
+ ${Object.entries(item.parameters).map(([key, desc]) => `
688
+ <div style="margin-bottom: 0.5rem;">
689
+ <strong>${key}:</strong> ${desc}
690
+ </div>
691
+ `).join('')}
692
+ </div>
693
+ ` : ''}
694
+
695
+ ${item.tips ? `
696
+ <div class="detail-tips">
697
+ <div class="tips-label">Tips</div>
698
+ <div class="tips-text">${item.tips}</div>
699
+ </div>
700
+ ` : ''}
701
+
702
+ ${item.related && item.related.length > 0 ? `
703
+ <div class="detail-section">
704
+ <div class="detail-label">Related Topics</div>
705
+ <div class="related-items">
706
+ ${item.related.map(id => {
707
+ const related = helpData.find(h => h.id === id);
708
+ return related ? `
709
+ <span class="related-badge" onclick="searchForId('${id}')">${related.title}</span>
710
+ ` : '';
711
+ }).join('')}
712
+ </div>
713
+ </div>
714
+ ` : ''}
715
+ </div>
716
+ </div>
717
+ `).join('');
718
+
719
+ // Add expand/collapse handlers
720
+ document.querySelectorAll('.result-item').forEach(item => {
721
+ item.addEventListener('click', (e) => {
722
+ if (e.target.closest('.copy-btn')) return;
723
+ item.classList.toggle('expanded');
724
+ addToRecentlyViewed(item.dataset.id);
725
+ });
726
+ });
727
+ }
728
+
729
+ // Copy to clipboard
730
+ function copyToClipboard(text, button) {
731
+ navigator.clipboard.writeText(text).then(() => {
732
+ button.classList.add('copied');
733
+ button.textContent = 'Copied!';
734
+ setTimeout(() => {
735
+ button.classList.remove('copied');
736
+ button.textContent = 'Copy';
737
+ }, 2000);
738
+ });
739
+ }
740
+
741
+ // Add to recently viewed
742
+ function addToRecentlyViewed(id) {
743
+ const item = helpData.find(h => h.id === id);
744
+ if (!item) return;
745
+
746
+ recentlyViewed = recentlyViewed.filter(h => h.id !== id);
747
+ recentlyViewed.unshift(item);
748
+ if (recentlyViewed.length > 10) {
749
+ recentlyViewed.pop();
750
+ }
751
+
752
+ renderRecentlyViewed();
753
+ }
754
+
755
+ // Render recently viewed
756
+ function renderRecentlyViewed() {
757
+ const container = document.getElementById('recentlyViewed');
758
+ container.innerHTML = recentlyViewed.map(item => `
759
+ <span class="recent-item" onclick="searchForId('${item.id}')">${item.title}</span>
760
+ `).join('');
761
+ }
762
+
763
+ // Search for ID
764
+ function searchForId(id) {
765
+ const item = helpData.find(h => h.id === id);
766
+ if (!item) return;
767
+ document.getElementById('searchInput').value = item.title;
768
+ currentSearch = item.title;
769
+ currentModule = null;
770
+ renderModuleFilters();
771
+ renderResults();
772
+ document.getElementById('resultsList').scrollTop = 0;
773
+ }
774
+
775
+ // Search handler
776
+ document.getElementById('searchInput').addEventListener('input', (e) => {
777
+ currentSearch = e.target.value;
778
+ renderResults();
779
+ });
780
+
781
+ // Clear button
782
+ document.getElementById('clearBtn').addEventListener('click', () => {
783
+ document.getElementById('searchInput').value = '';
784
+ currentSearch = '';
785
+ currentModule = null;
786
+ renderModuleFilters();
787
+ renderResults();
788
+ });
789
+
790
+ // Keyboard navigation
791
+ document.addEventListener('keydown', (e) => {
792
+ if (e.key === 'Escape') {
793
+ document.getElementById('searchInput').value = '';
794
+ currentSearch = '';
795
+ currentModule = null;
796
+ renderModuleFilters();
797
+ renderResults();
798
+ }
799
+ });
800
+
801
+ // Initialize
802
+ loadHelpData();
803
+ </script>
804
+ </body>
805
+ </html>