-
-

+
+
{item.title}
@@ -139,6 +158,7 @@ const slugToTitle = Object.fromEntries(items.map((i) => [i.slug, i.title]))
const categorySelect = catalogEl?.querySelector('#category-select') as HTMLSelectElement | null
const authSelect = catalogEl?.querySelector('#auth-select') as HTMLSelectElement | null
const noResults = catalogEl?.querySelector('.no-results') as HTMLElement | null
+ const resultCount = catalogEl?.querySelector('.result-count') as HTMLElement | null
const toolResultsEl = catalogEl?.querySelector('.tool-results') as HTMLElement | null
const toolResultsList = toolResultsEl?.querySelector('.tool-results-list') as HTMLElement | null
@@ -182,13 +202,33 @@ const slugToTitle = Object.fromEntries(items.map((i) => [i.slug, i.title]))
})
section.style.display = sectionVisible > 0 ? '' : 'none'
+
+ // Keep the per-category count badge in sync with what's actually visible.
+ const countEl = section.querySelector
('.category-heading .count')
+ if (countEl) {
+ const total = countEl.dataset.total ?? String(sectionVisible)
+ countEl.textContent = searchQuery ? `${sectionVisible} of ${total}` : total
+ }
+
totalVisible += sectionVisible
})
+ updateResultCount(totalVisible)
// noResults visibility is decided after tool results are also considered (see updateNoResults)
return totalVisible
}
+ // Show a running total only while the view is filtered; stay quiet at rest.
+ function updateResultCount(connectorVisible: number): void {
+ if (!resultCount) return
+ if (searchQuery || activeCategory !== 'all' || activeAuth !== 'all') {
+ const noun = connectorVisible === 1 ? 'connector' : 'connectors'
+ resultCount.textContent = `Showing ${connectorVisible} ${noun}`
+ } else {
+ resultCount.textContent = ''
+ }
+ }
+
async function loadToolIndex(): Promise<
Array<{ slug: string; name: string; description: string }>
> {
@@ -303,10 +343,6 @@ const slugToTitle = Object.fromEntries(items.map((i) => [i.slug, i.title]))
return str.length > n ? str.slice(0, n - 1) + '…' : str
}
- function escapeHtml(str: string) {
- return str.replace(/&/g, '&').replace(//g, '>')
- }
-
function updateNoResults(connectorVisible: number, hasToolMatches: boolean) {
if (!noResults) return
const anyVisible = connectorVisible > 0 || hasToolMatches
@@ -314,7 +350,6 @@ const slugToTitle = Object.fromEntries(items.map((i) => [i.slug, i.title]))
}
// Main search handler (connectors + tools)
- let searchDebounce: number | null = null
searchInput?.addEventListener('input', async (e) => {
searchQuery = (e.target as HTMLInputElement).value.toLowerCase().trim()
@@ -349,9 +384,12 @@ const slugToTitle = Object.fromEntries(items.map((i) => [i.slug, i.title]))
updateNoResults(connectorVisible, !!hasTools)
})
+ // Graceful logo fallback: if a provider icon fails to load, hide the broken
+ // image and reveal the connector's initial letter (rendered via CSS ::after).
catalogEl?.querySelectorAll('.provider-logo img').forEach((img) => {
img.addEventListener('error', () => {
img.style.display = 'none'
+ img.closest('.provider-logo')?.classList.add('logo-fallback')
})
})
@@ -362,20 +400,39 @@ const slugToTitle = Object.fromEntries(items.map((i) => [i.slug, i.title]))