Phase 2: Refactor feed management from jQuery to HTMX
Migrates feed add/list/delete operations from jQuery AJAX to HTMX while preserving JSON API backward compatibility. Backend now detects HTMX requests via HX-Request header and returns HTML fragments; non-HTMX requests still receive JSON.
Backend Changes
-
New fragment renderer (
fragments_feed.go):renderFeedRow()- Single feed HTMLrenderFeedList()- Complete list including static Unread/Starred feedsrenderFeedError()- Error message fragments
-
Dual-mode handlers (
feed.go):func (h *Router) addFeed(w http.ResponseWriter, r *http.Request, p httprouter.Params) { // ... validation ... if isHTMXRequest(r) { feeds, _ := h.feedService.ListFeeds(userID) w.Header().Set("Content-Type", "text/html; charset=utf-8") w.WriteHeader(http.StatusCreated) _, _ = w.Write([]byte(renderFeedList(feeds))) return } // JSON response for non-HTMX w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(toAddFeedResponse(t)) } -
Form data support (
model.go): Handlers now accept bothapplication/jsonandapplication/x-www-form-urlencoded
Frontend Changes
-
HTMX form (
index.html):<form hx-post="/feeds" hx-target="#feeds-list" hx-swap="innerHTML"> <input type="text" name="url" required /> </form> -
Removed jQuery AJAX (
feeds.js): Replaced$.ajax()calls with HTMX for add/delete, vanillafetch()for reads -
Auth configuration (
main.js):htmx.on('htmx:configRequest', function(evt) { const token = getAuthToken(); if (token) evt.detail.headers['Authorization'] = 'Bearer ' + token; });
Known Issue
⚠️ HTMX auth header configuration timing issue causing 401 errors in tests. Event listener registers but may not fire before initial requests. Requires alternative approach (meta tags, pre-config, or extension).
Testing
Updated MainPage.js to submit form via Enter key instead of double-click pattern. Backend unit tests pass. UI tests blocked by auth issue above.
Original prompt
Phase 2: Refactor Feed Management with HTMX (No htmx-go)
Refactor the feed management portion of the application to eliminate all jQuery / jQuery-UI usage and migrate interactions to HTMX while preserving existing JSON API behavior for non-HTMX requests. This phase focuses ONLY on feed-related functionality and must maintain full test parity (Playwright feed tests) and code quality (lint passes). Do NOT integrate htmx-go; use the existing backend architecture (httprouter handlers, middleware chain) and manual detection of HTMX via the HX-Request header.
Scope
- Add feed form submission
- Feed listing display
- Refresh / fetch new items for a feed
- Empty feed states
- Feed deletion (if present)
- Updating unread counts / status indicators related to feeds
Constraints & Principles
- No introduction of new backend libraries for HTMX response handling.
- Detect HTMX requests with:
isHTMX := r.Header.Get("HX-Request") == "true". - For HTMX requests: return minimal HTML fragments (only what needs updating).
- For non-HTMX requests: preserve existing JSON responses (backward compatibility).
- Remove ALL jQuery and jQuery-UI code related to feeds.
- Avoid adding large custom JavaScript; prefer HTMX attributes. Any JS > 3 lines must live in
public/js/feeds.js. - Maintain existing CSS classes / IDs used by Playwright tests (or update tests if unavoidable, but prefer not to break selectors).
- Ensure all Playwright feed tests pass after changes.
- Run
make lintafter backend changes and fix all issues.
Backend Tasks
- In
internal/infra/http/api/feed.go(and any related handlers):- Wrap logic with HTMX detection.
- For HTMX requests return
Content-Type: text/html; charset=utf-8and write fragment HTML only. - Provide helper functions (can be added in a new file
internal/infra/http/api/fragments_feed.go) such as:renderFeedRow(f *feed.Feed) stringrenderFeedList(feeds []*feed.Feed) stringrenderFeedStatus(msg string, kind string) string(kind = success|error)
- Ensure error paths return small HTML fragments for HTMX (e.g., a
<div class="feed-error">...</div>). - Preserve JSON response logic if
!isHTMX.
- Support feed refresh endpoint to return only updated feed row fragment or status fragment when HTMX is used.
- Feed deletion: return a fragment that removes/updates the list (e.g., updated list or a status message). Consider using
hx-swapstrategies; if removal causes an empty list, return an empty state fragment. - Make sure handlers still respect middleware (auth, content type for JSON in non-HTMX mode). For HTMX responses override content-type manually.
- Keep existing models / response structs for JSON path untouched.
Frontend Tasks (public/ directory)
- Locate and remove jQuery-based feed scripts (DOM selection, AJAX calls, event binding) associated with feed management.
- Modify feed add form:
- Add
hx-post="/feeds". - Add
hx-target="#feeds-list"(or appropriate container ID). - Add
hx-swap="innerHTML"(or suitable strategy). - Add loading indicator: set up element with
id="feed-add-indicator"and includehx-indicator="#feed-add-indicator"on the form.
- Add
- Refresh buttons/links for feeds:
- Add
hx-put="/feeds/{id}/fetch"with dynamic ID. - Target only the feed row container:
hx-target="#feed-row-{id}". - Swap strategy:
hx-swap="outerHTML"to replace the entire row.
- Add
- Delete feed action:
- Use
hx-delete="/feeds/{id}". hx-target="#feeds-list"with server returning a new list or status fragment.
- Use
- Empty state handling:
- If list becomes empty, server returns an
<div class="feed-empty">No feeds added yet</div>fragment.
- If list becomes empty, server returns an
- Add/Update
public/js/feeds.jsONLY if logic > 3 lines is needed (e.g., small utility for progressive enhancement). Keep it minimal and documented.
HTML / Fragment Guidelines
- Fragments must be valid, minimal HTML (no full
<html>/<body>wrappers). - Each feed row should have stable identifiers: e.g.,
<div id="feed-row-{{.ID}}" class="feed-row">for targeting. - Avoid changing existing test selectors unless absolutely required.
Testing Requirements
- Run
npm run test:ui tests/ui/feeds.spec.js(ormake test-ui) after changes; MUST pass 100%. - If selectors change, update Page Object files (
tests/ui/pages/) consistently. - Manually verify: RSS & Atom feed addition, refresh, item display, empty feed scenario.
Linting & Quality
- Run
make lintafter backend modifications; resolve all issues. - Run
go mod tidyonly if imports were added/removed (expect no new module for HTMX server handling).
Deliverables
- Updated Go handlers with HTMX conditional fragment responses.
- New helper fragment rendering functions (if added).
- Updated HTML templates/forms using HTMX attributes.
- Removal of feed-related jQuery code.
- Optional
public/js/feeds.js(only if >3 lines of JS needed) with inline comments explaining necessity. - Pas...
This pull request was created as a result of the following prompt from Copilot chat.
Phase 2: Refactor Feed Management with HTMX (No htmx-go)
Refactor the feed management portion of the application to eliminate all jQuery / jQuery-UI usage and migrate interactions to HTMX while preserving existing JSON API behavior for non-HTMX requests. This phase focuses ONLY on feed-related functionality and must maintain full test parity (Playwright feed tests) and code quality (lint passes). Do NOT integrate
htmx-go; use the existing backend architecture (httprouter handlers, middleware chain) and manual detection of HTMX via theHX-Requestheader.Scope
- Add feed form submission
- Feed listing display
- Refresh / fetch new items for a feed
- Empty feed states
- Feed deletion (if present)
- Updating unread counts / status indicators related to feeds
Constraints & Principles
- No introduction of new backend libraries for HTMX response handling.
- Detect HTMX requests with:
isHTMX := r.Header.Get("HX-Request") == "true".- For HTMX requests: return minimal HTML fragments (only what needs updating).
- For non-HTMX requests: preserve existing JSON responses (backward compatibility).
- Remove ALL jQuery and jQuery-UI code related to feeds.
- Avoid adding large custom JavaScript; prefer HTMX attributes. Any JS > 3 lines must live in
public/js/feeds.js.- Maintain existing CSS classes / IDs used by Playwright tests (or update tests if unavoidable, but prefer not to break selectors).
- Ensure all Playwright feed tests pass after changes.
- Run
make lintafter backend changes and fix all issues.Backend Tasks
- In
internal/infra/http/api/feed.go(and any related handlers):
- Wrap logic with HTMX detection.
- For HTMX requests return
Content-Type: text/html; charset=utf-8and write fragment HTML only.- Provide helper functions (can be added in a new file
internal/infra/http/api/fragments_feed.go) such as:
renderFeedRow(f *feed.Feed) stringrenderFeedList(feeds []*feed.Feed) stringrenderFeedStatus(msg string, kind string) string(kind = success|error)- Ensure error paths return small HTML fragments for HTMX (e.g., a
<div class="feed-error">...</div>).- Preserve JSON response logic if
!isHTMX.- Support feed refresh endpoint to return only updated feed row fragment or status fragment when HTMX is used.
- Feed deletion: return a fragment that removes/updates the list (e.g., updated list or a status message). Consider using
hx-swapstrategies; if removal causes an empty list, return an empty state fragment.- Make sure handlers still respect middleware (auth, content type for JSON in non-HTMX mode). For HTMX responses override content-type manually.
- Keep existing models / response structs for JSON path untouched.
Frontend Tasks (public/ directory)
- Locate and remove jQuery-based feed scripts (DOM selection, AJAX calls, event binding) associated with feed management.
- Modify feed add form:
- Add
hx-post="/feeds".- Add
hx-target="#feeds-list"(or appropriate container ID).- Add
hx-swap="innerHTML"(or suitable strategy).- Add loading indicator: set up element with
id="feed-add-indicator"and includehx-indicator="#feed-add-indicator"on the form.- Refresh buttons/links for feeds:
- Add
hx-put="/feeds/{id}/fetch"with dynamic ID.- Target only the feed row container:
hx-target="#feed-row-{id}".- Swap strategy:
hx-swap="outerHTML"to replace the entire row.- Delete feed action:
- Use
hx-delete="/feeds/{id}".hx-target="#feeds-list"with server returning a new list or status fragment.- Empty state handling:
- If list becomes empty, server returns an
<div class="feed-empty">No feeds added yet</div>fragment.- Add/Update
public/js/feeds.jsONLY if logic > 3 lines is needed (e.g., small utility for progressive enhancement). Keep it minimal and documented.HTML / Fragment Guidelines
- Fragments must be valid, minimal HTML (no full
<html>/<body>wrappers).- Each feed row should have stable identifiers: e.g.,
<div id="feed-row-{{.ID}}" class="feed-row">for targeting.- Avoid changing existing test selectors unless absolutely required.
Testing Requirements
- Run
npm run test:ui tests/ui/feeds.spec.js(ormake test-ui) after changes; MUST pass 100%.- If selectors change, update Page Object files (
tests/ui/pages/) consistently.- Manually verify: RSS & Atom feed addition, refresh, item display, empty feed scenario.
Linting & Quality
- Run
make lintafter backend modifications; resolve all issues.- Run
go mod tidyonly if imports were added/removed (expect no new module for HTMX server handling).Deliverables
- Updated Go handlers with HTMX conditional fragment responses.
- New helper fragment rendering functions (if added).
- Updated HTML templates/forms using HTMX attributes.
- Removal of feed-related jQuery code.
- Optional
public/js/feeds.js(only if >3 lines of JS needed) with inline comments explaining necessity.- Passing Playwright feed tests.
- Lint-clean codebase for modified files.
Success Criteria
- All jQuery/jQuery-UI feed code removed.
- HTMX attributes drive feed interactions.
- HTML fragment responses implemented for HTMX paths; JSON preserved otherwise.
- All feed Playwright tests pass.
make lintproduces zero errors.- Minimal, organized JavaScript (none or small dedicated file).
- No regression in existing feed functionality.
Non-Goals (Phase 2)
- Do not refactor authentication, items, or UI components (covered in other phases).
- Do not introduce htmx-go or other response abstraction libraries.
- Do not alter database or domain logic.
Proceed to implement and open a pull request with these changes.
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.