From 89035b231d1dcc584ead4361d1858e45617d1335 Mon Sep 17 00:00:00 2001 From: Gil Desmarais Date: Wed, 19 Aug 2026 18:17:35 +0200 Subject: [PATCH 1/2] feat(feed-directory): load catalog from instance API --- .rubocop.yml | 4 - AGENTS.md | 32 +- Gemfile | 3 +- Makefile | 8 +- README.md | 11 +- bin/data-update | 65 - package.json | 3 +- src/components/FeedDirectory.astro | 250 +-- src/components/catalogClient.js | 59 + src/components/feed-directory.js | 487 ++--- src/components/instanceUrl.js | 99 + src/content/docs/creating-custom-feeds.mdx | 7 +- src/content/docs/feed-directory/index.mdx | 6 +- .../guides/use-the-feed-directory.mdx | 6 +- src/data/configs.json | 1663 ----------------- src/data/loadConfigs.ts | 37 - 16 files changed, 495 insertions(+), 2245 deletions(-) delete mode 100755 bin/data-update create mode 100644 src/components/catalogClient.js create mode 100644 src/components/instanceUrl.js delete mode 100644 src/data/configs.json delete mode 100644 src/data/loadConfigs.ts diff --git a/.rubocop.yml b/.rubocop.yml index f7cf9277..30793ac8 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -2,7 +2,3 @@ AllCops: NewCops: enable TargetRubyVersion: 3.4 DisplayCopNames: true - -Style/DoubleNegation: - Exclude: - - bin/data-update diff --git a/AGENTS.md b/AGENTS.md index ae1250b8..e0381b04 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,14 +9,13 @@ What this repo owns: - docs content and navigation under `src/content/docs/` - docs-specific components and styling under `src/components/` -- feed-directory presentation and client behavior -- generated docs data consumed by the site (`src/data/configs.json`) +- feed-directory presentation and client behavior (`FeedDirectory.astro`, `feed-directory.js`) What this repo does not own: - runtime extractor behavior and CLI semantics (`html2rss/`) -- web API behavior and OpenAPI generation (`html2rss-web/`) -- feed YAML catalog definitions (`html2rss-configs/`) +- catalog metadata, YAML configs, or catalog serialization (`html2rss-configs/` β†’ `Html2rss::Configs::Catalog`) +- catalog HTTP API (`html2rss-web/` β†’ `GET /api/v1/configs`) When docs describe behavior from other repos, treat those repos as source-of-truth and update docs to match them. @@ -30,40 +29,41 @@ Before substantial edits, state cross-repo context in your notes: Common contracts: -- Feed directory data comes from `html2rss-configs` via `bin/data-update`. +- Feed Directory browse data comes from `{instance}/api/v1/configs` on a running `html2rss-web` instance (see OpenAPI in `html2rss-web`). +- Instance URL persistence: default public instance, `#!url=` hash deep link from the web app, and browser localStorage (see `feed-directory.js`). +- Deep link from `html2rss-web`: `https://html2rss.github.io/feed-directory/#!url={encodedInstanceUrl}` must keep working. +- Catalog metadata in YAML (`directory.title`, `directory.summary`, `directory.topics`) is authored in `html2rss-configs` only. - Ruby gem docs should match `html2rss` behavior and CLI output. - Web application docs should match `html2rss-web` behavior and published OpenAPI. If a cross-repo behavior changed but upstream is not updated yet, document the gap clearly instead of inventing new behavior. -## Generated Artifacts +## Feed Directory (agent maintenance) -Treat `src/data/configs.json` as generated. +- The browse UI is a **thin client**: fetch catalog JSON from the active instance, render rows client-side, build RSS links from each entry's `path`. +- Do not reintroduce `bin/data-update`, `src/data/configs.json`, or a `html2rss-configs` gem dependency in this repo. +- Wire shape v1 is defined in `html2rss-web` request specs and OpenAPI (`catalog_version`, `parameters.schema`, `parameters.defaults`). +- When the instance is unreachable or returns `404` with `catalog_disabled`, show an error state β€” no static fallback list. -- Do not hand-edit it. -- Regenerate with repo-native commands: - - `make update` - - or `bin/data-update` (after dependencies are installed) -- `bin/data-update` reads packaged configs (from `html2rss-configs`) and writes `src/data/configs.json`. +## Generated Artifacts -If a change only affects generated data, include the source change rationale in the PR description. +This repo has no generated catalog data. Do not add a packaged-config snapshot back into the docs build. ## Build, Test, and Dev Commands Run commands from `html2rss.github.io/`: -- `make setup` installs dependencies and refreshes generated data +- `make setup` installs npm dependencies - `make dev` runs Astro locally - `make build` builds production output - `make lint` checks formatting - `make lintfix` applies formatting fixes -- `make update` refreshes feed-directory data from packaged configs Preferred verification flow for docs/content changes: 1. Run targeted check(s) first (`make lint` or `make build`). 2. Run the broader check set before PR (`make lint` and `make build`). -3. If feed directory or config references changed, run `make update` and verify resulting diffs. +3. For feed-directory UI changes, spot-check against a running instance with catalog enabled (`GET /api/v1/configs` returns entries). ## Docs Authoring Rules diff --git a/Gemfile b/Gemfile index 0e904547..e5531aed 100644 --- a/Gemfile +++ b/Gemfile @@ -2,5 +2,4 @@ source 'https://rubygems.org' -gem 'html2rss', '~> 0.26' -gem 'html2rss-configs', git: 'https://github.com/html2rss/html2rss-configs.git' +# Ruby dependencies removed β€” feed directory loads live from html2rss-web API. diff --git a/Makefile b/Makefile index f809ba7c..401598b1 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,7 @@ -default: setup clean lintfix dev +default: setup lint dev setup: npm ci - bundle check || bundle - make update dev: npm run dev @@ -26,7 +24,3 @@ lintfix: clean: find . -type d -empty -delete find . -type f -empty -delete - -update: - bundle update html2rss-configs - bin/data-update diff --git a/README.md b/README.md index 773adb1a..8d0a40cc 100644 --- a/README.md +++ b/README.md @@ -8,35 +8,34 @@ website. ### Prerequisites - Node.js (for Astro) -- Ruby (for data updates) ### Quick Setup ```bash -# Install dependencies and refresh generated feed data +# Install dependencies make setup # Start the local Astro development server make dev ``` +Feed Directory browse data comes from a running `html2rss-web` instance (`GET /api/v1/configs`). For local feed-directory testing, run an instance (for example from `html2rss-web`) and point the directory UI at it. + ### πŸ’» Try in Browser You can develop html2rss directly in your browser using GitHub Codespaces: [Open in GitHub Codespaces](https://github.com/codespaces/new?repo=html2rss/html2rss.github.io) -The Codespace provides a cloud development environment with Node.js and Ruby pre-installed. Run `make setup` to install dependencies and get started! +The Codespace provides a cloud development environment with Node.js pre-installed. Run `make setup` to install dependencies and get started! ### Available Commands -- `make setup` - Install dependencies and refresh generated feed data +- `make setup` - Install npm dependencies - `make dev` - Start Astro development server - `make build` - Build for production - `make preview` - Preview production build -- `make build-full` - Update data and build for production - `make lintfix` - Fix code formatting -- `make update` - Update feed data ## 🌐 Community & Resources diff --git a/bin/data-update b/bin/data-update deleted file mode 100755 index 618e09c6..00000000 --- a/bin/data-update +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -require 'rubygems' -require 'bundler/setup' - -require 'fileutils' -require 'html2rss' -require 'html2rss/configs' -require 'uri' -require 'yaml' -require 'json' - -file_names = Html2rss::Configs.file_names.sort - -def extract_default_parameters(parameters) - return {} unless parameters.is_a?(Hash) - - parameters.each_with_object({}) do |(param_name, param_config), defaults| - defaults[param_name] = param_config['default'] if param_config.is_a?(Hash) && param_config['default'] - end -end - -def valid_url(url) - !!URI(url) -rescue StandardError - false -end - -def string_formatting_references(string) - string.to_s.scan(/%[{<](\w+)[>}](\w)?/).to_h.transform_values do |value| - case value - when 'i', 'd', 'u' - Numeric - else - String - end - end -end - -output = file_names.map do |file_name| - config = YAML.safe_load(File.open(file_name), symbolize_names: false) - - file_name_splits = file_name.split('/') - - # Extract default parameter values from the parameters section - default_parameters = extract_default_parameters(config['parameters']) - - { - 'domain' => file_name_splits[-2..-2].join, - 'name' => File.basename(file_name_splits[-1..].join, '.*'), - 'valid_channel_url' => valid_url(config['channel']['url']), - 'url_parameters' => string_formatting_references(config['channel']['url']), - 'default_parameters' => default_parameters, - 'channel' => config['channel'], - 'directory' => config['directory'] - } -end - -config_file = File.join(__dir__, '..', 'src/data/configs.json') -FileUtils.touch config_file - -json_content = JSON.pretty_generate(output) - -File.write(config_file, json_content) diff --git a/package.json b/package.json index 94a3c027..dbfe04f7 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,7 @@ "build": "astro build", "preview": "astro preview", "astro": "astro", - "update-data": "ruby bin/data-update", - "build:full": "npm run update-data && npm run build", + "build:full": "npm run build", "lint": "prettier --check .", "lintfix": "prettier --write ." }, diff --git a/src/components/FeedDirectory.astro b/src/components/FeedDirectory.astro index fcb4ff1a..679788db 100644 --- a/src/components/FeedDirectory.astro +++ b/src/components/FeedDirectory.astro @@ -1,42 +1,10 @@ --- -import { configs } from "../data/loadConfigs"; import { Icon } from "@astrojs/starlight/components"; - -const feedCount = configs.length; - -function formatDefaultParameters(defaultParameters: Record = {}) { - return Object.entries(defaultParameters) - .filter(([, value]) => value) - .map(([key, value]) => `${key}=${value}`) - .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: "#", - defaultSummary: formatDefaultParameters(config.default_parameters), - sourceSummary: config.channel?.url - ?.replace(/^https?:\/\//, "") - .replace(/^www\./, "") - .replace(/\/$/, ""), - topics: config.directory?.topics || [], - language: config.channel?.language || "", -})); ---
+ +
@@ -56,7 +24,7 @@ const staticFeedUrls = configs.map((config) => ({

Search across - {feedCount} + 0 ready-to-use feeds

@@ -64,15 +32,7 @@ const staticFeedUrls = configs.map((config) => ({

Topics

-
- { - availableTopics.map((topic) => ( - - )) - } -
+
@@ -85,11 +45,10 @@ const staticFeedUrls = configs.map((config) => ({ aria-label="Filter by language" > - {availableLanguages.map((language) => )}
- +
@@ -108,7 +67,7 @@ const staticFeedUrls = configs.map((config) => ({ -
+ -
- { - staticFeedUrls.map((config, index) => ( -
-
-
-

{config.sourceSummary || `${config.domain}/${config.name}`}

- - -
- -
- {config.defaultSummary ? ( -

Defaults: {config.defaultSummary}

- ) : ( -
-
- - {Object.keys(config.url_parameters || {}).length > 0 && ( - - )} -
- )) - } -
+
- +