(query: string)
| 764 | } |
| 765 | |
| 766 | function handleSearch(query: string): void { |
| 767 | const area = document.getElementById('contentArea'); |
| 768 | if (!area) return; |
| 769 | |
| 770 | if (!query.trim()) { |
| 771 | renderSection(activeSection); |
| 772 | return; |
| 773 | } |
| 774 | |
| 775 | const q = query.toLowerCase(); |
| 776 | const matches: Array<{ feature: RuntimeFeatureDefinition; catLabel: string }> = []; |
| 777 | |
| 778 | for (const cat of SETTINGS_CATEGORIES) { |
| 779 | for (const fid of cat.features) { |
| 780 | const feature = RUNTIME_FEATURES.find(f => f.id === fid); |
| 781 | if (!feature) continue; |
| 782 | const searchable = [ |
| 783 | feature.name, |
| 784 | feature.description, |
| 785 | ...getEffectiveSecrets(feature).map(k => HUMAN_LABELS[k] || k), |
| 786 | ].join(' ').toLowerCase(); |
| 787 | if (searchable.includes(q)) { |
| 788 | matches.push({ feature, catLabel: cat.label }); |
| 789 | } |
| 790 | } |
| 791 | } |
| 792 | |
| 793 | if (matches.length === 0) { |
| 794 | area.innerHTML = `<div class="settings-search-empty"><p>No features match "${escapeHtml(query)}"</p></div>`; |
| 795 | return; |
| 796 | } |
| 797 | |
| 798 | const cards = matches.map(({ feature, catLabel }) => { |
| 799 | const enabled = isFeatureEnabled(feature.id); |
| 800 | const available = isFeatureAvailable(feature.id); |
| 801 | const effectiveSecrets = getEffectiveSecrets(feature); |
| 802 | const allStaged = !available && effectiveSecrets.every( |
| 803 | k => getSecretState(k).valid || (settingsManager.hasPending(k) && settingsManager.getValidationState(k).validated !== false) |
| 804 | ); |
| 805 | const borderClass = available ? 'ready' : allStaged ? 'staged' : 'needs'; |
| 806 | const pillClass = available ? 'ok' : allStaged ? 'staged' : 'warn'; |
| 807 | const pillLabel = available ? 'Ready' : allStaged ? 'Staged' : 'Needs keys'; |
| 808 | const secretRows = effectiveSecrets.map(key => renderSecretInput(key, feature.id)).join(''); |
| 809 | |
| 810 | return ` |
| 811 | <div class="settings-feat ${borderClass} expanded" data-feature-id="${feature.id}"> |
| 812 | <div class="settings-feat-header" data-feat-toggle-expand="${feature.id}"> |
| 813 | <label class="settings-feat-toggle-label" data-click-stop> |
| 814 | <div class="settings-feat-switch"> |
| 815 | <input type="checkbox" data-toggle="${feature.id}" ${enabled ? 'checked' : ''} /> |
| 816 | <span class="settings-feat-slider"></span> |
| 817 | </div> |
| 818 | </label> |
| 819 | <div class="settings-feat-info"> |
| 820 | <span class="settings-feat-name">${highlightMatch(feature.name, query)}</span> |
| 821 | <span class="settings-feat-desc">${highlightMatch(feature.description, query)}</span> |
| 822 | </div> |
| 823 | <span class="settings-feat-pill ${pillClass}">${pillLabel}</span> |
no test coverage detected