From 203a7feaf08e383495a36e826a7e23c6c6da10c7 Mon Sep 17 00:00:00 2001 From: Gil Desmarais Date: Sun, 16 Aug 2026 15:31:39 +0200 Subject: [PATCH 1/3] feat: filter feed directory by topic/language and export OPML Surface catalog directory.topics and channel.language in the feed directory UI so visitors can AND-filter and export the visible set. --- Gemfile | 5 +- Gemfile.lock | 85 ++----- bin/data-update | 3 +- src/components/FeedDirectory.astro | 136 ++++++++++- src/components/feed-directory.js | 124 ++++++++-- src/data/configs.json | 352 +++++++++++++++++++++++++++-- src/data/loadConfigs.ts | 3 + 7 files changed, 609 insertions(+), 99 deletions(-) diff --git a/Gemfile b/Gemfile index 56e0f1b4..34bac3c7 100644 --- a/Gemfile +++ b/Gemfile @@ -2,5 +2,6 @@ source 'https://rubygems.org' -gem 'html2rss', git: 'https://github.com/html2rss/html2rss.git' -gem 'html2rss-configs', git: 'https://github.com/html2rss/html2rss-configs.git' +# Path gems until directory.topics ships in released html2rss / html2rss-configs. +gem 'html2rss', path: '../html2rss' +gem 'html2rss-configs', path: '../html2rss-configs' diff --git a/Gemfile.lock b/Gemfile.lock index 0970b92e..b236ef63 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,13 +1,11 @@ -GIT - remote: https://github.com/html2rss/html2rss-configs.git - revision: 7f40e65325f33383a8bdabfc5261f8cff33a3a9e +PATH + remote: ../html2rss-configs specs: html2rss-configs (0.2.0) html2rss -GIT - remote: https://github.com/html2rss/html2rss.git - revision: 644c7f06ccd70f8ed231a26ffc9d8d3279a2617b +PATH + remote: ../html2rss specs: html2rss (0.25.0) addressable (~> 2.7) @@ -17,15 +15,18 @@ GIT faraday-follow_redirects faraday-gzip (~> 3) kramdown + mcp (~> 1.0) mime-types (> 3.0) nokogiri (>= 1.10, < 2.0) - puppeteer-ruby + rack (~> 3.0) + rackup (~> 2.0) regexp_parser reverse_markdown (~> 3.0) rss sanitize thor tzinfo + webrick (~> 1.9) zeitwerk GEM @@ -33,34 +34,9 @@ GEM specs: addressable (2.9.0) public_suffix (>= 2.0.2, < 8.0) - async (2.44.1) - console (~> 1.29) - fiber-annotation - io-event (~> 1.11) - async-http (0.99.0) - async (>= 2.35.1) - async-pool (~> 0.11) - io-endpoint (~> 0.14) - io-stream (~> 0.14) - protocol-http (~> 0.66) - protocol-http1 (~> 0.39) - protocol-http2 (~> 0.26) - protocol-url (~> 0.2) - async-pool (0.11.2) - async (>= 2.0) - async-websocket (0.30.1) - async-http (~> 0.76) - protocol-http (~> 0.34) - protocol-rack (~> 0.7) - protocol-websocket (~> 0.17) - base64 (0.3.0) bigdecimal (4.1.2) brotli (0.8.0) concurrent-ruby (1.3.8) - console (1.37.0) - fiber-annotation - fiber-local (~> 1.1) - json crass (1.0.7) dry-configurable (1.4.0) dry-core (~> 1.0) @@ -108,18 +84,18 @@ GEM zlib (~> 3.0) faraday-net_http (3.4.4) net-http (~> 0.5) - fiber-annotation (0.2.0) - fiber-local (1.1.0) - fiber-storage - fiber-storage (1.0.1) - io-endpoint (0.17.2) - io-event (1.19.5) - io-stream (0.14.0) - openssl (>= 3.3) + hana (1.3.7) json (2.21.2) + json_schemer (2.5.0) + bigdecimal + hana (~> 1.3) + regexp_parser (~> 2.0) + simpleidn (~> 0.2) kramdown (2.5.2) rexml (>= 3.4.4) logger (1.7.0) + mcp (1.2.0) + json_schemer (>= 2.4) mime-types (3.7.0) logger mime-types-data (~> 3.2025, >= 3.2025.0507) @@ -130,31 +106,11 @@ GEM racc (~> 1.4) nokogiri (1.19.4-x86_64-linux-gnu) racc (~> 1.4) - openssl (4.0.2) - protocol-hpack (1.5.1) - protocol-http (0.70.0) - protocol-http1 (0.40.2) - protocol-http (~> 0.68) - protocol-http2 (0.26.2) - protocol-hpack (~> 1.4) - protocol-http (~> 0.62) - protocol-rack (0.22.1) - io-stream (>= 0.10) - protocol-http (~> 0.58) - rack (>= 1.0) - protocol-url (0.18.0) - protocol-websocket (0.21.1) - protocol-http (~> 0.2) public_suffix (7.0.5) - puppeteer-ruby (0.53.0) - async (>= 2.35.1, < 3.0) - async-http (>= 0.60, < 1.0) - async-websocket (>= 0.27, < 1.0) - base64 - mime-types (>= 3.0) - urlpattern (~> 0.1.1) racc (1.8.1) rack (3.2.7) + rackup (2.3.1) + rack (>= 3) regexp_parser (2.12.0) reverse_markdown (3.0.2) nokogiri @@ -164,16 +120,17 @@ GEM sanitize (7.0.0) crass (~> 1.0.2) nokogiri (>= 1.16.8) + simpleidn (0.2.3) thor (1.5.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) uri (1.1.1) - urlpattern (0.1.1-arm64-darwin) - urlpattern (0.1.1-x86_64-linux) + webrick (1.9.2) zeitwerk (2.8.3) zlib (3.2.3) PLATFORMS + arm64-darwin-23 arm64-darwin-24 arm64-darwin-25 x86_64-linux diff --git a/bin/data-update b/bin/data-update index 8c8e35eb..618e09c6 100755 --- a/bin/data-update +++ b/bin/data-update @@ -52,7 +52,8 @@ output = file_names.map do |file_name| 'valid_channel_url' => valid_url(config['channel']['url']), 'url_parameters' => string_formatting_references(config['channel']['url']), 'default_parameters' => default_parameters, - 'channel' => config['channel'] + 'channel' => config['channel'], + 'directory' => config['directory'] } end diff --git a/src/components/FeedDirectory.astro b/src/components/FeedDirectory.astro index 25af5943..33e6c655 100644 --- a/src/components/FeedDirectory.astro +++ b/src/components/FeedDirectory.astro @@ -11,6 +11,18 @@ function formatDefaultParameters(defaultParameters: Record = {}) .join(", "); } +const availableTopics = [ + ...new Set(configs.flatMap((config) => config.directory?.topics || [])), +].sort((a, b) => a.localeCompare(b)); + +const availableLanguages = [ + ...new Set( + configs + .map((config) => config.channel?.language) + .filter((language): language is string => Boolean(language)) + ), +].sort((a, b) => a.localeCompare(b)); + const staticFeedUrls = configs.map((config) => ({ ...config, staticFeedUrl: "#", @@ -19,6 +31,8 @@ const staticFeedUrls = configs.map((config) => ({ ?.replace(/^https?:\/\//, "") .replace(/^www\./, "") .replace(/\/$/, ""), + topics: config.directory?.topics || [], + language: config.channel?.language || "", })); --- @@ -47,6 +61,35 @@ const staticFeedUrls = configs.map((config) => ({

+
+
+

Topics

+
+ { + availableTopics.map((topic) => ( + + )) + } +
+
+ +
+
+ + +
+ + +
+
+
Using instance: @@ -114,7 +157,9 @@ const staticFeedUrls = configs.map((config) => ({ class="feed-row" data-domain={config.domain} data-name={config.name} - data-searchable={`${config.domain}/${config.name} ${config.channel?.url || ""}`} + data-topics={config.topics.join(" ")} + data-language={config.language} + data-searchable={`${config.domain}/${config.name} ${config.channel?.url || ""} ${config.topics.join(" ")} ${config.language}`} >
@@ -272,6 +317,7 @@ const staticFeedUrls = configs.map((config) => ({ } .search-block, + .filter-block, .instance-block, .empty-state, .feed-row, @@ -279,7 +325,8 @@ const staticFeedUrls = configs.map((config) => ({ padding: var(--fd-padding); } - .search-block { + .search-block, + .filter-block { display: grid; gap: var(--fd-gap); border-bottom: 1px solid var(--fd-border); @@ -325,6 +372,81 @@ const staticFeedUrls = configs.map((config) => ({ outline: none; } + .filter-group { + display: grid; + gap: 0.375rem; + } + + .filter-label { + margin: 0; + color: var(--fd-muted); + font-size: var(--sl-text-xs); + } + + .topic-chips { + display: flex; + flex-wrap: wrap; + gap: 0.375rem; + } + + .topic-chip { + appearance: none; + padding: 0.25rem 0.625rem; + border: 1px solid var(--fd-border); + border-radius: calc(var(--fd-radius) - 0.125rem); + background: transparent; + color: var(--fd-muted); + font: inherit; + font-size: var(--sl-text-xs); + cursor: pointer; + } + + .topic-chip[aria-pressed="true"] { + border-color: var(--sl-color-accent); + background: var(--sl-color-accent-low); + color: var(--sl-color-white); + } + + .filter-actions { + display: flex; + flex-wrap: wrap; + gap: var(--fd-gap); + align-items: end; + justify-content: space-between; + } + + .language-filter { + display: grid; + gap: 0.375rem; + min-width: 10rem; + } + + .language-select { + min-width: 0; + padding: 0.5rem 0.75rem; + border: 1px solid var(--fd-border); + border-radius: calc(var(--fd-radius) - 0.125rem); + background: var(--fd-surface-alt); + color: var(--sl-color-white); + font: inherit; + font-size: var(--sl-text-sm); + } + + .export-opml { + appearance: none; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0.5rem 0.75rem; + border: 1px solid var(--fd-border); + border-radius: calc(var(--fd-radius) - 0.125rem); + background: transparent; + color: var(--sl-color-white); + font: inherit; + font-size: var(--sl-text-sm); + cursor: pointer; + } + .result-summary, .instance-feedback, .empty-title, @@ -551,6 +673,11 @@ const staticFeedUrls = configs.map((config) => ({ .instance-apply:focus-visible, .done-button:hover, .done-button:focus-visible, + .topic-chip:hover, + .topic-chip:focus-visible, + .export-opml:hover, + .export-opml:focus-visible, + .language-select:focus-visible, .instance-input:focus-visible, .form-input:focus-visible { border-color: var(--sl-color-accent); @@ -651,5 +778,10 @@ const staticFeedUrls = configs.map((config) => ({ .feed-meta { justify-content: flex-start; } + + .filter-actions { + flex-direction: column; + align-items: stretch; + } } diff --git a/src/components/feed-directory.js b/src/components/feed-directory.js index d9e24f45..e76511aa 100644 --- a/src/components/feed-directory.js +++ b/src/components/feed-directory.js @@ -159,11 +159,17 @@ function createUpdateFeedUrlsFunction() { }; } -function updateSearchState(feedItems, query) { +function updateSearchState(feedItems, query, selectedTopics = [], selectedLanguage = '') { let visibleCount = 0; feedItems.forEach((item) => { const searchableText = item.dataset.searchable?.toLowerCase() || ''; - const matches = fuzzyMatch(searchableText, query); + const matchesSearch = fuzzyMatch(searchableText, query); + const itemTopics = (item.dataset.topics || '').split(/\s+/).filter(Boolean); + const matchesTopics = + selectedTopics.length === 0 || selectedTopics.some((topic) => itemTopics.includes(topic)); + const itemLanguage = item.dataset.language || ''; + const matchesLanguage = !selectedLanguage || itemLanguage === selectedLanguage; + const matches = matchesSearch && matchesTopics && matchesLanguage; item.hidden = !matches; if (matches) visibleCount++; }); @@ -173,14 +179,15 @@ function updateSearchState(feedItems, query) { const emptyState = document.querySelector('[data-empty-state]'); const emptyCopy = document.querySelector('[data-empty-copy]'); const feedList = document.querySelector('[data-feed-list]'); + const hasActiveFilters = + Boolean(query.trim()) || selectedTopics.length > 0 || Boolean(selectedLanguage); if (resultCount) { resultCount.textContent = String(visibleCount); } if (resultLabel) { - const hasQuery = Boolean(query.trim()); - if (hasQuery) { + if (hasActiveFilters) { resultLabel.textContent = visibleCount === 1 ? 'matching feed' : 'matching feeds'; } else { resultLabel.textContent = visibleCount === 1 ? 'ready-to-use feed' : 'ready-to-use feeds'; @@ -192,25 +199,115 @@ function updateSearchState(feedItems, query) { emptyState.hidden = !hasNoResults; feedList.hidden = hasNoResults; if (hasNoResults) { - emptyCopy.textContent = query - ? `No configurations found for "${query}". Try another domain or feed name, check the community wiki, or contribute a new configuration.` + emptyCopy.textContent = hasActiveFilters + ? 'No configurations match the current search and filters. Try clearing a topic or language filter.' : 'Try a different domain or feed name, or contribute a new configuration.'; } } } -function setupSearch(searchInput, feedItems) { - if (!searchInput) return; +function getSelectedTopics() { + return Array.from(document.querySelectorAll('[data-topic-filter][aria-pressed="true"]')).map( + (button) => button.dataset.topicFilter + ); +} - const applySearch = debounce((query) => { - updateSearchState(feedItems, query.toLowerCase()); +function getSelectedLanguage() { + const languageFilter = document.querySelector('[data-language-filter]'); + return languageFilter?.value || ''; +} + +function setupFilters(searchInput, feedItems) { + const applyFilters = debounce(() => { + updateSearchState( + feedItems, + (searchInput?.value || '').toLowerCase(), + getSelectedTopics(), + getSelectedLanguage() + ); }, 120); - searchInput.addEventListener('input', (event) => { - applySearch(event.target.value); + if (searchInput) { + searchInput.addEventListener('input', applyFilters); + } + + document.querySelectorAll('[data-topic-filter]').forEach((button) => { + button.addEventListener('click', () => { + const pressed = button.getAttribute('aria-pressed') === 'true'; + button.setAttribute('aria-pressed', String(!pressed)); + applyFilters(); + }); }); - updateSearchState(feedItems, searchInput.value.toLowerCase()); + const languageFilter = document.querySelector('[data-language-filter]'); + if (languageFilter) { + languageFilter.addEventListener('change', applyFilters); + } + + applyFilters(); +} + +function escapeXml(value) { + return String(value) + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('"', '"') + .replaceAll("'", '''); +} + +function buildOpmlDocument(visibleItems) { + const outlines = visibleItems + .map((item) => { + const feedLink = item.querySelector('[data-feed-url]'); + const title = + item.querySelector('.feed-title')?.textContent?.trim() || + `${item.dataset.domain}/${item.dataset.name}`; + const xmlUrl = feedLink?.href; + if (!xmlUrl || xmlUrl === '#' || xmlUrl.endsWith('/#')) return null; + + const htmlUrl = item.querySelector('.meta-link[title]')?.getAttribute('href') || ''; + const htmlAttr = htmlUrl ? ` htmlUrl="${escapeXml(htmlUrl)}"` : ''; + return ` `; + }) + .filter(Boolean) + .join('\n'); + + return ` + + + html2rss feeds + + +${outlines} + + +`; +} + +function setupOpmlExport(feedItems) { + const exportButton = document.querySelector('[data-export-opml]'); + if (!exportButton) return; + + exportButton.addEventListener('click', () => { + const visibleItems = feedItems.filter((item) => !item.hidden); + if (visibleItems.length === 0) return; + + const opml = buildOpmlDocument(visibleItems); + const blob = new Blob([opml], { type: 'text/x-opml+xml' }); + const objectUrl = URL.createObjectURL(blob); + const anchor = document.createElement('a'); + anchor.href = objectUrl; + anchor.download = 'html2rss-feeds.opml'; + document.body.appendChild(anchor); + anchor.click(); + anchor.remove(); + URL.revokeObjectURL(objectUrl); + }); +} + +function setupSearch(searchInput, feedItems) { + setupFilters(searchInput, feedItems); } function setupInstanceEditor( @@ -363,6 +460,7 @@ function initializeFeedDirectory() { } setupSearch(searchInput, feedItems); + setupOpmlExport(feedItems); setupInstanceEditor( defaultInstanceUrl, () => currentInstanceUrl, diff --git a/src/data/configs.json b/src/data/configs.json index f5e4aa25..60b17a98 100644 --- a/src/data/configs.json +++ b/src/data/configs.json @@ -10,6 +10,11 @@ "time_zone": "Europe/Berlin", "ttl": 720, "language": "de" + }, + "directory": { + "topics": [ + "civic" + ] } }, { @@ -27,6 +32,11 @@ "language": "en", "ttl": 120, "time_zone": "UTC" + }, + "directory": { + "topics": [ + "news" + ] } }, { @@ -40,6 +50,12 @@ "language": "en", "time_zone": "UTC", "ttl": 360 + }, + "directory": { + "topics": [ + "tech", + "product" + ] } }, { @@ -53,9 +69,15 @@ "id": "b006wkfp" }, "channel": { + "language": "en", "url": "https://www.bbc.co.uk/programmes/%s/episodes/player", "time_zone": "UTC", "ttl": 720 + }, + "directory": { + "topics": [ + "entertainment" + ] } }, { @@ -69,6 +91,11 @@ "language": "es", "ttl": 360, "time_zone": "UTC" + }, + "directory": { + "topics": [ + "news" + ] } }, { @@ -82,10 +109,16 @@ "blog": "-Defense-en-ligne-" }, "channel": { + "language": "en", "url": "https://blog.mondediplo.net/%s", "time_zone": "Europe/Paris", "title": "blog.mondediplo.net: %s", "ttl": 120 + }, + "directory": { + "topics": [ + "news" + ] } }, { @@ -99,6 +132,12 @@ "time_zone": "Europe/London", "ttl": 720, "language": "en" + }, + "directory": { + "topics": [ + "news", + "travel" + ] } }, { @@ -108,10 +147,16 @@ "url_parameters": {}, "default_parameters": {}, "channel": { + "language": "en", "url": "https://webapp.cinemascore.com/guest/surveys", "ttl": 720, "json": true, "time_zone": "America/Los_Angeles" + }, + "directory": { + "topics": [ + "entertainment" + ] } }, { @@ -121,9 +166,16 @@ "url_parameters": {}, "default_parameters": {}, "channel": { - "url": "https://www.cleanenergywire.org/news-archive", + "language": "en", + "url": "https://www.cleanenergywire.org/news", "time_zone": "Europe/Berlin", "ttl": 360 + }, + "directory": { + "topics": [ + "energy", + "environment" + ] } }, { @@ -131,18 +183,22 @@ "name": "section_sub", "valid_channel_url": false, "url_parameters": { - "section": "String", - "sub": "String" + "section": "String" }, "default_parameters": { - "section": "news", - "sub": "tech" + "section": "tech" }, "channel": { - "url": "https://www.cnet.com/%
s/%s/", + "url": "https://www.cnet.com/%
s/", "language": "en", "ttl": 360, "time_zone": "UTC" + }, + "directory": { + "topics": [ + "tech", + "news" + ] } }, { @@ -157,6 +213,11 @@ "time_zone": "Europe/Berlin", "ttl": 360, "language": "de" + }, + "directory": { + "topics": [ + "tech" + ] } }, { @@ -166,9 +227,15 @@ "url_parameters": {}, "default_parameters": {}, "channel": { + "language": "en", "url": "https://cutle.fish/", "ttl": 360, "time_zone": "UTC" + }, + "directory": { + "topics": [ + "tech" + ] } }, { @@ -182,6 +249,12 @@ "language": "en", "time_zone": "UTC", "ttl": 360 + }, + "directory": { + "topics": [ + "science", + "research" + ] } }, { @@ -196,6 +269,11 @@ "time_zone": "Europe/Berlin", "ttl": 360, "language": "de" + }, + "directory": { + "topics": [ + "finance" + ] } }, { @@ -205,10 +283,17 @@ "url_parameters": {}, "default_parameters": {}, "channel": { + "language": "en", "json": true, "url": "https://developer.apple.com/tutorials/data/documentation/Technotes.json", "ttl": 360, "time_zone": "UTC" + }, + "directory": { + "topics": [ + "tech", + "product" + ] } }, { @@ -222,6 +307,11 @@ "time_zone": "Europe/Berlin", "ttl": 1440, "language": "de" + }, + "directory": { + "topics": [ + "civic" + ] } }, { @@ -231,10 +321,16 @@ "url_parameters": {}, "default_parameters": {}, "channel": { - "url": "https://www.dsw-info.de/presse", + "url": "https://www.dsw-info.de/presse/pressemitteilungen-2026/", "time_zone": "Europe/Berlin", "ttl": 720, "language": "de" + }, + "directory": { + "topics": [ + "civic", + "consumer" + ] } }, { @@ -248,6 +344,12 @@ "language": "en", "time_zone": "UTC", "ttl": 360 + }, + "directory": { + "topics": [ + "tech", + "product" + ] } }, { @@ -257,9 +359,15 @@ "url_parameters": {}, "default_parameters": {}, "channel": { + "language": "en", "url": "https://www.espn.com/f1/", "time_zone": "UTC", "ttl": 60 + }, + "directory": { + "topics": [ + "sports" + ] } }, { @@ -269,9 +377,15 @@ "url_parameters": {}, "default_parameters": {}, "channel": { + "language": "en", "url": "https://www.fia.com/documents/championships/fia-formula-one-world-championship-14/season/season-2026-2072", "ttl": 360, "time_zone": "UTC" + }, + "directory": { + "topics": [ + "sports" + ] } }, { @@ -281,9 +395,15 @@ "url_parameters": {}, "default_parameters": {}, "channel": { - "url": "https://www.formula1.com/en/latest/all.html", + "language": "en", + "url": "https://www.formula1.com/en/latest", "time_zone": "UTC", "ttl": 120 + }, + "directory": { + "topics": [ + "sports" + ] } }, { @@ -296,13 +416,20 @@ }, "default_parameters": { "username": "nuxt", - "repository": "nuxt.js" + "repository": "nuxt" }, "channel": { + "language": "en", "url": "https://github.com/%s/%s/releases", "time_zone": "UTC", "ttl": 720, "description": "Releases of %s/%s on github.com." + }, + "directory": { + "topics": [ + "tech", + "product" + ] } }, { @@ -316,6 +443,12 @@ "language": "en", "time_zone": "UTC", "ttl": 360 + }, + "directory": { + "topics": [ + "tech", + "product" + ] } }, { @@ -329,6 +462,12 @@ "language": "en", "time_zone": "UTC", "ttl": 360 + }, + "directory": { + "topics": [ + "tech", + "product" + ] } }, { @@ -338,9 +477,15 @@ "url_parameters": {}, "default_parameters": {}, "channel": { + "language": "en", "url": "https://iaapa.org/news-funworld", "time_zone": "UTC", "ttl": 720 + }, + "directory": { + "topics": [ + "entertainment" + ] } }, { @@ -354,9 +499,15 @@ "user_id": "ur7019649" }, "channel": { + "language": "en", "url": "https://www.imdb.com/user/%s/ratings", "time_zone": "UTC", "ttl": 1440 + }, + "directory": { + "topics": [ + "entertainment" + ] } }, { @@ -370,6 +521,11 @@ "language": "de-DE", "ttl": 360, "time_zone": "UTC" + }, + "directory": { + "topics": [ + "jobs" + ] } }, { @@ -383,6 +539,11 @@ "time_zone": "Europe/Berlin", "ttl": 1440, "language": "de" + }, + "directory": { + "topics": [ + "entertainment" + ] } }, { @@ -396,6 +557,12 @@ "language": "en", "time_zone": "US/Pacific", "ttl": 3600 + }, + "directory": { + "topics": [ + "tech", + "product" + ] } }, { @@ -409,6 +576,12 @@ "language": "en", "time_zone": "UTC", "ttl": 360 + }, + "directory": { + "topics": [ + "security", + "tech" + ] } }, { @@ -422,6 +595,11 @@ "language": "en", "ttl": 360, "time_zone": "UTC" + }, + "directory": { + "topics": [ + "news" + ] } }, { @@ -435,6 +613,11 @@ "language": "en-GB", "ttl": 360, "time_zone": "UTC" + }, + "directory": { + "topics": [ + "entertainment" + ] } }, { @@ -448,6 +631,12 @@ "language": "en", "time_zone": "UTC", "ttl": 360 + }, + "directory": { + "topics": [ + "tech", + "product" + ] } }, { @@ -461,6 +650,12 @@ "language": "de", "ttl": 360, "time_zone": "Europe/Berlin" + }, + "directory": { + "topics": [ + "consumer", + "civic" + ] } }, { @@ -470,9 +665,15 @@ "url_parameters": {}, "default_parameters": {}, "channel": { + "language": "en", "url": "https://phys.org/weekly-news/", "time_zone": "Europe/London", "ttl": 1440 + }, + "directory": { + "topics": [ + "science" + ] } }, { @@ -483,10 +684,15 @@ "default_parameters": {}, "channel": { "title": "rbb24.de: meistgeklickt", - "url": "https://rbb24.de/", + "url": "https://www.rbb24.de/das-beste/", "time_zone": "Europe/Berlin", "ttl": 30, "language": "de" + }, + "directory": { + "topics": [ + "news" + ] } }, { @@ -496,10 +702,15 @@ "url_parameters": {}, "default_parameters": {}, "channel": { - "url": "https://www.robinwood.de/was-gibt-es-neues/aktuelles", + "url": "https://www.robinwood.de/was-gibt-es-neues", "time_zone": "Europe/Berlin", "ttl": 720, "language": "de" + }, + "directory": { + "topics": [ + "environment" + ] } }, { @@ -509,10 +720,16 @@ "url_parameters": {}, "default_parameters": {}, "channel": { + "language": "en", "url": "https://s3.amazonaws.com/popular-movies/movies.json", "time_zone": "UTC", "ttl": 1440, "json": true + }, + "directory": { + "topics": [ + "entertainment" + ] } }, { @@ -522,10 +739,15 @@ "url_parameters": {}, "default_parameters": {}, "channel": { - "url": "https://sebastianvettel.de/news/", + "url": "https://sebastianvettel.de/", "language": "de-DE", "ttl": 8640, "time_zone": "Europe/Berlin" + }, + "directory": { + "topics": [ + "sports" + ] } }, { @@ -535,9 +757,16 @@ "url_parameters": {}, "default_parameters": {}, "channel": { + "language": "en", "url": "https://www.shopify.com/blog/latest", "time_zone": "UTC", "ttl": 360 + }, + "directory": { + "topics": [ + "tech", + "product" + ] } }, { @@ -547,9 +776,15 @@ "url_parameters": {}, "default_parameters": {}, "channel": { + "language": "en", "url": "https://softwareleadweekly.com/issues", "time_zone": "UTC", "ttl": 720 + }, + "directory": { + "topics": [ + "tech" + ] } }, { @@ -559,9 +794,15 @@ "url_parameters": {}, "default_parameters": {}, "channel": { + "language": "en", "url": "https://solarthermalworld.org/news", "time_zone": "UTC", "ttl": 180 + }, + "directory": { + "topics": [ + "energy" + ] } }, { @@ -576,6 +817,11 @@ "time_zone": "Europe/Berlin", "ttl": 60, "language": "de" + }, + "directory": { + "topics": [ + "science" + ] } }, { @@ -593,6 +839,11 @@ "time_zone": "Europe/Berlin", "ttl": 180, "language": "de" + }, + "directory": { + "topics": [ + "news" + ] } }, { @@ -606,6 +857,11 @@ "language": "en", "time_zone": "UTC", "ttl": 360 + }, + "directory": { + "topics": [ + "entertainment" + ] } }, { @@ -615,10 +871,16 @@ "url_parameters": {}, "default_parameters": {}, "channel": { + "language": "en", "title": "stackoverflow.com: Hot Network Questions", "url": "https://stackoverflow.com/questions", "time_zone": "America/New_York", "ttl": 30 + }, + "directory": { + "topics": [ + "tech" + ] } }, { @@ -628,10 +890,15 @@ "url_parameters": {}, "default_parameters": {}, "channel": { - "url": "https://www.steuerzahler.de/news", + "url": "https://www.steuerzahler.de/news/", "time_zone": "Europe/Berlin", "ttl": 720, "language": "de" + }, + "directory": { + "topics": [ + "civic" + ] } }, { @@ -645,6 +912,11 @@ "language": "en", "ttl": 360, "time_zone": "America/New_York" + }, + "directory": { + "topics": [ + "news" + ] } }, { @@ -654,10 +926,16 @@ "url_parameters": {}, "default_parameters": {}, "channel": { - "url": "https://support.apple.com/en-gb/HT201222", + "url": "https://support.apple.com/en-gb/100100", "language": "en", "ttl": 360, "time_zone": "UTC" + }, + "directory": { + "topics": [ + "tech", + "security" + ] } }, { @@ -667,9 +945,16 @@ "url_parameters": {}, "default_parameters": {}, "channel": { - "url": "https://support.apple.com/exchange_repair", + "language": "en", + "url": "https://support.apple.com/service-programs", "time_zone": "America/Los_Angeles", "ttl": 720 + }, + "directory": { + "topics": [ + "tech", + "security" + ] } }, { @@ -683,6 +968,12 @@ "time_zone": "Europe/Lisbon", "ttl": 720, "language": "de" + }, + "directory": { + "topics": [ + "news", + "travel" + ] } }, { @@ -696,6 +987,11 @@ "language": "de", "ttl": 360, "time_zone": "Europe/Berlin" + }, + "directory": { + "topics": [ + "consumer" + ] } }, { @@ -705,10 +1001,16 @@ "url_parameters": {}, "default_parameters": {}, "channel": { + "language": "en", "title": "theguardian.com: International most popular", "url": "https://www.theguardian.com/international", "time_zone": "Europe/London", "ttl": 60 + }, + "directory": { + "topics": [ + "news" + ] } }, { @@ -718,10 +1020,16 @@ "url_parameters": {}, "default_parameters": {}, "channel": { - "url": "https://www.thoughtworks.com/insights", + "url": "https://www.thoughtworks.com/insights/blog", "language": "en", "ttl": 360, "time_zone": "UTC" + }, + "directory": { + "topics": [ + "tech", + "research" + ] } }, { @@ -735,6 +1043,11 @@ "time_zone": "Europe/Berlin", "ttl": 720, "language": "de" + }, + "directory": { + "topics": [ + "travel" + ] } }, { @@ -752,6 +1065,11 @@ "language": "de", "ttl": 360, "time_zone": "Europe/Berlin" + }, + "directory": { + "topics": [ + "jobs" + ] } } -] +] \ No newline at end of file diff --git a/src/data/loadConfigs.ts b/src/data/loadConfigs.ts index 272735a4..a49f9bf6 100644 --- a/src/data/loadConfigs.ts +++ b/src/data/loadConfigs.ts @@ -15,6 +15,9 @@ export interface Config { title?: string; json?: boolean; }; + directory?: { + topics?: string[]; + }; } export function loadConfigs(): Config[] { From d8c7acb7f3d43db6cfb49a7fac0022f3e66e3d18 Mon Sep 17 00:00:00 2001 From: Gil Desmarais Date: Sun, 16 Aug 2026 15:46:06 +0200 Subject: [PATCH 2/3] chore(deps): point html2rss gems at feature branches for CI Replace local path pins with git branch refs so CI can resolve unreleased directory.topics from the core and configs PRs. --- Gemfile | 5 ++--- Gemfile.lock | 10 ++++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Gemfile b/Gemfile index 34bac3c7..56e0f1b4 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,5 @@ source 'https://rubygems.org' -# Path gems until directory.topics ships in released html2rss / html2rss-configs. -gem 'html2rss', path: '../html2rss' -gem 'html2rss-configs', path: '../html2rss-configs' +gem 'html2rss', git: 'https://github.com/html2rss/html2rss.git' +gem 'html2rss-configs', git: 'https://github.com/html2rss/html2rss-configs.git' diff --git a/Gemfile.lock b/Gemfile.lock index b236ef63..e89e020a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,11 +1,13 @@ -PATH - remote: ../html2rss-configs +GIT + remote: https://github.com/html2rss/html2rss-configs.git + revision: d9e3dc55b38514eb9cd4a17cb0285d9ef73bc9d5 specs: html2rss-configs (0.2.0) html2rss -PATH - remote: ../html2rss +GIT + remote: https://github.com/html2rss/html2rss.git + revision: aa974733c6e88a5c934f445516d9752dfa316067 specs: html2rss (0.25.0) addressable (~> 2.7) From 0f6a4005b0ecb8d76f3ba0c14f4f67ebaa9e53b0 Mon Sep 17 00:00:00 2001 From: Gil Desmarais Date: Sun, 16 Aug 2026 16:03:46 +0200 Subject: [PATCH 3/3] style: lintfix --- src/components/FeedDirectory.astro | 19 ++- src/components/feed-directory.js | 3 +- src/data/configs.json | 246 +++++++---------------------- 3 files changed, 69 insertions(+), 199 deletions(-) diff --git a/src/components/FeedDirectory.astro b/src/components/FeedDirectory.astro index 33e6c655..fcb4ff1a 100644 --- a/src/components/FeedDirectory.astro +++ b/src/components/FeedDirectory.astro @@ -11,15 +11,15 @@ function formatDefaultParameters(defaultParameters: Record = {}) .join(", "); } -const availableTopics = [ - ...new Set(configs.flatMap((config) => config.directory?.topics || [])), -].sort((a, b) => a.localeCompare(b)); +const availableTopics = [...new Set(configs.flatMap((config) => config.directory?.topics || []))].sort( + (a, b) => a.localeCompare(b), +); const availableLanguages = [ ...new Set( configs .map((config) => config.channel?.language) - .filter((language): language is string => Boolean(language)) + .filter((language): language is string => Boolean(language)), ), ].sort((a, b) => a.localeCompare(b)); @@ -78,15 +78,18 @@ const staticFeedUrls = configs.map((config) => ({
- {availableLanguages.map((language) => )}
- +
diff --git a/src/components/feed-directory.js b/src/components/feed-directory.js index e76511aa..91dad026 100644 --- a/src/components/feed-directory.js +++ b/src/components/feed-directory.js @@ -179,8 +179,7 @@ function updateSearchState(feedItems, query, selectedTopics = [], selectedLangua const emptyState = document.querySelector('[data-empty-state]'); const emptyCopy = document.querySelector('[data-empty-copy]'); const feedList = document.querySelector('[data-feed-list]'); - const hasActiveFilters = - Boolean(query.trim()) || selectedTopics.length > 0 || Boolean(selectedLanguage); + const hasActiveFilters = Boolean(query.trim()) || selectedTopics.length > 0 || Boolean(selectedLanguage); if (resultCount) { resultCount.textContent = String(visibleCount); diff --git a/src/data/configs.json b/src/data/configs.json index 60b17a98..f20764f2 100644 --- a/src/data/configs.json +++ b/src/data/configs.json @@ -12,9 +12,7 @@ "language": "de" }, "directory": { - "topics": [ - "civic" - ] + "topics": ["civic"] } }, { @@ -34,9 +32,7 @@ "time_zone": "UTC" }, "directory": { - "topics": [ - "news" - ] + "topics": ["news"] } }, { @@ -52,10 +48,7 @@ "ttl": 360 }, "directory": { - "topics": [ - "tech", - "product" - ] + "topics": ["tech", "product"] } }, { @@ -75,9 +68,7 @@ "ttl": 720 }, "directory": { - "topics": [ - "entertainment" - ] + "topics": ["entertainment"] } }, { @@ -93,9 +84,7 @@ "time_zone": "UTC" }, "directory": { - "topics": [ - "news" - ] + "topics": ["news"] } }, { @@ -116,9 +105,7 @@ "ttl": 120 }, "directory": { - "topics": [ - "news" - ] + "topics": ["news"] } }, { @@ -134,10 +121,7 @@ "language": "en" }, "directory": { - "topics": [ - "news", - "travel" - ] + "topics": ["news", "travel"] } }, { @@ -154,9 +138,7 @@ "time_zone": "America/Los_Angeles" }, "directory": { - "topics": [ - "entertainment" - ] + "topics": ["entertainment"] } }, { @@ -172,10 +154,7 @@ "ttl": 360 }, "directory": { - "topics": [ - "energy", - "environment" - ] + "topics": ["energy", "environment"] } }, { @@ -195,10 +174,7 @@ "time_zone": "UTC" }, "directory": { - "topics": [ - "tech", - "news" - ] + "topics": ["tech", "news"] } }, { @@ -215,9 +191,7 @@ "language": "de" }, "directory": { - "topics": [ - "tech" - ] + "topics": ["tech"] } }, { @@ -233,9 +207,7 @@ "time_zone": "UTC" }, "directory": { - "topics": [ - "tech" - ] + "topics": ["tech"] } }, { @@ -251,10 +223,7 @@ "ttl": 360 }, "directory": { - "topics": [ - "science", - "research" - ] + "topics": ["science", "research"] } }, { @@ -271,9 +240,7 @@ "language": "de" }, "directory": { - "topics": [ - "finance" - ] + "topics": ["finance"] } }, { @@ -290,10 +257,7 @@ "time_zone": "UTC" }, "directory": { - "topics": [ - "tech", - "product" - ] + "topics": ["tech", "product"] } }, { @@ -309,9 +273,7 @@ "language": "de" }, "directory": { - "topics": [ - "civic" - ] + "topics": ["civic"] } }, { @@ -327,10 +289,7 @@ "language": "de" }, "directory": { - "topics": [ - "civic", - "consumer" - ] + "topics": ["civic", "consumer"] } }, { @@ -346,10 +305,7 @@ "ttl": 360 }, "directory": { - "topics": [ - "tech", - "product" - ] + "topics": ["tech", "product"] } }, { @@ -365,9 +321,7 @@ "ttl": 60 }, "directory": { - "topics": [ - "sports" - ] + "topics": ["sports"] } }, { @@ -383,9 +337,7 @@ "time_zone": "UTC" }, "directory": { - "topics": [ - "sports" - ] + "topics": ["sports"] } }, { @@ -401,9 +353,7 @@ "ttl": 120 }, "directory": { - "topics": [ - "sports" - ] + "topics": ["sports"] } }, { @@ -426,10 +376,7 @@ "description": "Releases of %s/%s on github.com." }, "directory": { - "topics": [ - "tech", - "product" - ] + "topics": ["tech", "product"] } }, { @@ -445,10 +392,7 @@ "ttl": 360 }, "directory": { - "topics": [ - "tech", - "product" - ] + "topics": ["tech", "product"] } }, { @@ -464,10 +408,7 @@ "ttl": 360 }, "directory": { - "topics": [ - "tech", - "product" - ] + "topics": ["tech", "product"] } }, { @@ -483,9 +424,7 @@ "ttl": 720 }, "directory": { - "topics": [ - "entertainment" - ] + "topics": ["entertainment"] } }, { @@ -505,9 +444,7 @@ "ttl": 1440 }, "directory": { - "topics": [ - "entertainment" - ] + "topics": ["entertainment"] } }, { @@ -523,9 +460,7 @@ "time_zone": "UTC" }, "directory": { - "topics": [ - "jobs" - ] + "topics": ["jobs"] } }, { @@ -541,9 +476,7 @@ "language": "de" }, "directory": { - "topics": [ - "entertainment" - ] + "topics": ["entertainment"] } }, { @@ -559,10 +492,7 @@ "ttl": 3600 }, "directory": { - "topics": [ - "tech", - "product" - ] + "topics": ["tech", "product"] } }, { @@ -578,10 +508,7 @@ "ttl": 360 }, "directory": { - "topics": [ - "security", - "tech" - ] + "topics": ["security", "tech"] } }, { @@ -597,9 +524,7 @@ "time_zone": "UTC" }, "directory": { - "topics": [ - "news" - ] + "topics": ["news"] } }, { @@ -615,9 +540,7 @@ "time_zone": "UTC" }, "directory": { - "topics": [ - "entertainment" - ] + "topics": ["entertainment"] } }, { @@ -633,10 +556,7 @@ "ttl": 360 }, "directory": { - "topics": [ - "tech", - "product" - ] + "topics": ["tech", "product"] } }, { @@ -652,10 +572,7 @@ "time_zone": "Europe/Berlin" }, "directory": { - "topics": [ - "consumer", - "civic" - ] + "topics": ["consumer", "civic"] } }, { @@ -671,9 +588,7 @@ "ttl": 1440 }, "directory": { - "topics": [ - "science" - ] + "topics": ["science"] } }, { @@ -690,9 +605,7 @@ "language": "de" }, "directory": { - "topics": [ - "news" - ] + "topics": ["news"] } }, { @@ -708,9 +621,7 @@ "language": "de" }, "directory": { - "topics": [ - "environment" - ] + "topics": ["environment"] } }, { @@ -727,9 +638,7 @@ "json": true }, "directory": { - "topics": [ - "entertainment" - ] + "topics": ["entertainment"] } }, { @@ -745,9 +654,7 @@ "time_zone": "Europe/Berlin" }, "directory": { - "topics": [ - "sports" - ] + "topics": ["sports"] } }, { @@ -763,10 +670,7 @@ "ttl": 360 }, "directory": { - "topics": [ - "tech", - "product" - ] + "topics": ["tech", "product"] } }, { @@ -782,9 +686,7 @@ "ttl": 720 }, "directory": { - "topics": [ - "tech" - ] + "topics": ["tech"] } }, { @@ -800,9 +702,7 @@ "ttl": 180 }, "directory": { - "topics": [ - "energy" - ] + "topics": ["energy"] } }, { @@ -819,9 +719,7 @@ "language": "de" }, "directory": { - "topics": [ - "science" - ] + "topics": ["science"] } }, { @@ -841,9 +739,7 @@ "language": "de" }, "directory": { - "topics": [ - "news" - ] + "topics": ["news"] } }, { @@ -859,9 +755,7 @@ "ttl": 360 }, "directory": { - "topics": [ - "entertainment" - ] + "topics": ["entertainment"] } }, { @@ -878,9 +772,7 @@ "ttl": 30 }, "directory": { - "topics": [ - "tech" - ] + "topics": ["tech"] } }, { @@ -896,9 +788,7 @@ "language": "de" }, "directory": { - "topics": [ - "civic" - ] + "topics": ["civic"] } }, { @@ -914,9 +804,7 @@ "time_zone": "America/New_York" }, "directory": { - "topics": [ - "news" - ] + "topics": ["news"] } }, { @@ -932,10 +820,7 @@ "time_zone": "UTC" }, "directory": { - "topics": [ - "tech", - "security" - ] + "topics": ["tech", "security"] } }, { @@ -951,10 +836,7 @@ "ttl": 720 }, "directory": { - "topics": [ - "tech", - "security" - ] + "topics": ["tech", "security"] } }, { @@ -970,10 +852,7 @@ "language": "de" }, "directory": { - "topics": [ - "news", - "travel" - ] + "topics": ["news", "travel"] } }, { @@ -989,9 +868,7 @@ "time_zone": "Europe/Berlin" }, "directory": { - "topics": [ - "consumer" - ] + "topics": ["consumer"] } }, { @@ -1008,9 +885,7 @@ "ttl": 60 }, "directory": { - "topics": [ - "news" - ] + "topics": ["news"] } }, { @@ -1026,10 +901,7 @@ "time_zone": "UTC" }, "directory": { - "topics": [ - "tech", - "research" - ] + "topics": ["tech", "research"] } }, { @@ -1045,9 +917,7 @@ "language": "de" }, "directory": { - "topics": [ - "travel" - ] + "topics": ["travel"] } }, { @@ -1067,9 +937,7 @@ "time_zone": "Europe/Berlin" }, "directory": { - "topics": [ - "jobs" - ] + "topics": ["jobs"] } } -] \ No newline at end of file +]