article-row had no layout structure at all — title link, score badge,
and the dismiss button were plain inline siblings in a block <li>, so a
long title pushed the button flush against (or past) the row's edge.
Group title+score into a header row and make the row itself a flex
column so summary/topics/button always stack on their own line below,
regardless of title length.
Also stopped rendering the summary <p> and topics div at all when
there's no content (many RSS entries have no description), instead of
emitting empty elements — spacing was uneven because those still
reserved a line's worth of height. Left this as an explicit Rust-side
branch rather than CSS :empty: an empty <p> still contains a
zero-length text node, and whether :empty matches that is
inconsistent across engines, whereas the topics div (an empty `for`
loop) has zero children and would be a safe :empty candidate — but
kept both on the same explicit mechanism for consistency.
Swapped the dismiss button from a raw <button> to the dx Button
component, matching the rest of the row and the project's preference
for the component library over raw elements.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqTJmazHBQK878vjQ4JnnF
Default view stays the all-feeds joined article list, now with a sidebar
listing every subscribed feed so a reader can pin down to one feed's
articles. Filtering happens server-side (list_ranked_articles now takes
an optional feed_id).
Pulled in the sidebar/badge/scroll_area (plus their sheet/skeleton/
tooltip/separator dependencies) components via `dx components add`
instead of hand-rolling nav/tag/scroll markup, matching this project's
existing pattern of using the Dioxus component library over raw
elements (see button/input).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqTJmazHBQK878vjQ4JnnF
Adds a URL input + Subscribe button (dx-components Input/Button) to the
UI, wired to a new subscribe_feed server function that upserts the feed
row and does an immediate first fetch so the reader isn't empty until
the next scheduled poll. feedsignal_feeds::fetch_feed now also returns
the feed's title, used to replace the URL placeholder once fetched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJJQb2DWZZwQ1yPiaAQQoY
Rust workspace with core (topic-affinity learning engine + relevance
scoring), db (sqlite/sqlx schema + repo), feeds (RSS/Atom fetch), llm
(rig + local Ollama embeddings/completion), and web (axum + Dioxus
fullstack UI, no separate JS stack). Two-stage relevance filtering
(embedding shortlist -> LLM judgment) and an engagement/surprise-based
topic affinity engine with daily decay. All crates compile and core's
affinity engine has passing unit tests; server and wasm client targets
of feedsignal-web both check clean.