Claude e204c70e00 Fix source deletion, add content clearing, multi-category/editable sources, News category, wider layout, and a YouTube source module
- Fix "Body cannot be empty" error on DELETE by making the JSON content-type
  parser tolerate empty bodies, and by only sending Content-Type from the
  frontend when a request actually has one.
- Deleting a source now cascades: raw content items and any article composed
  entirely from that source are removed too, plus their media.
- Add admin endpoints/UI to clear all articles, all media, or a single
  source's content without deleting the source, so things can be repopulated
  fresh.
- Sources can now be assigned multiple categories via checkboxes (instead of
  free text) and edited in place, not just added/deleted.
- Add a "News" default category (seeded fresh, backfilled on existing DBs) so
  general news sources have a real home instead of the pseudo-category "Top
  stories", which is just the homepage's all-categories chronological view.
- Widen the site's content column 15% (1080px -> 1242px).
- Add YouTube as its own source type/ingestion module: pulls a channel's
  public Atom feed, and each video always publishes directly as its own
  article (title, embedded video, publish date, description) rather than
  going through the cross-source clustering/synthesis pipeline.
2026-07-21 18:09:20 +00:00
2026-07-21 12:51:31 -04:00
2026-07-17 10:33:20 -04:00

Homefeed — frontend, real backend, and mock backend

frontend/        SvelteKit app — the actual site (homepage, category pages, article view, admin panel)
backend/         The real backend — Node.js/TypeScript, SQLite, RSS/API ingestion, Ollama-backed synthesis
mock-backend/    Tiny Express server serving dummy articles — useful for pure frontend UI work without Ollama running

Both backend/ and mock-backend/ implement the identical /api/feed, /api/article/:id, /api/tags, /api/events, /api/admin/* contract — the frontend doesn't know or care which one it's talking to. Switch between them by changing VITE_BACKEND_URL in frontend/.env.

Running the real backend

cd backend
cp .env.example .env   # set ADMIN_PASSWORD at minimum
npm install
npm run dev

See backend/README.md for what's fully implemented vs. stubbed (Telegram adapter, image-selection heuristic vs. vision model, etc.), and how it behaves when Ollama isn't reachable.

Running the mock backend instead (frontend-only work, no Ollama needed)

Two terminals:

# Terminal 1 — mock backend (http://localhost:4000)
cd mock-backend
npm install
npm start

# Terminal 2 — frontend (http://localhost:5173)
cd frontend
npm install
npm run dev

Open http://localhost:5173.

What's implemented

  • Homepage (/) — hero story, Local section, Business and Tech rails
  • Category pages (/category/local, /category/world, etc.) — full listing per category, local maps to the Philadelphia geo filter
  • Article page (/article/:id) — merge badge, hero image with single-source attribution, body, video slot, tag chips, thread continuation banners (both directions — "newer coverage" / "earlier coverage"), sources footer
  • Article cards — show source count (⇄ N sources), single-source attribution, or a video indicator, matching the design decided earlier
  • Light/dark theme toggle — slider in the masthead, top right, left of the settings cog. Dark is a genuine slate palette (not an inverted light theme). Persists via localStorage, respects system preference on first load, no flash-of-wrong-theme (set before hydration in app.html).
  • Admin panel (/admin/settings) — six tabs, all wired to the mock backend's /api/admin/* routes:
    • Merge — strictness slider, poll interval, hold-before-publish, follow-up thresholds, category priority (reorderable), tag dedup threshold, tag expiry
    • Sources — list, add, enable/disable, delete RSS/API/Telegram feeds
    • Models — AI service status, per-task model selection (embedding/image/synthesis), fetched from the mock's simulated Ollama catalog
    • Retention — published-article and raw-item age presets, storage cap with FIFO note and usage bar
    • Tracked events — list, create, toggle active/paused, delete
    • Connections — the asymmetric pair: frontend→backend URL (saved to this browser via localStorage, not a backend setting) and backend→AI-service host/port (a real backend setting, saved via /api/admin/settings)

Mock data

mock-backend/data.js has ~10 dummy articles covering: a 4-source merge with a follow-up (art-1art-2, same threadId), single-source articles across Business/Tech, and a Local section with a mix of merged, single-source, and video items — enough variety to sanity-check every card/badge state in the design.

Timestamps are generated relative to Date.now() (see hoursAgo() in data.js) rather than hardcoded, so "time ago" labels stay sensible no matter when you run this.

Connecting to the real backend later

The frontend never hardcodes localhost:4000 — see frontend/src/lib/config.ts. It reads VITE_BACKEND_URL (set in frontend/.env) or a value saved via setBackendUrl(). Pointing this project at the real backend instead of the mock is a one-line change, not a rewrite — swap the URL in .env and everything else keeps working, since both servers implement the same /api/feed, /api/article/:id, /api/tags, /api/events contract from homefeed-data-schema.md.

S
Description
No description provided
Readme 767 KiB
Languages
TypeScript 64.4%
Svelte 31.9%
JavaScript 3.3%
CSS 0.3%
HTML 0.1%