Add queryloop smart tag: query-driven post/page card listings

Add queryloop smart tag: query-driven post/page card listings

#35 in Riparion/riparion-cms — merged 2026-06-02

Summary

A new [[component:queryloop query="…"]] smart-tag embed that parses an author-written pseudo-SQL query into a typed boolean AST and renders the matching posts or pages as paginated summary cards.

Example:

[[component:queryloop query="select posts where category=foo and (tag=a or tag=b) order_by_desc=published_date take=6"]]

Grammar (full boolean expression; precedence not > and > or):

select posts|pages [where <bool expr over category=/tag=>]
  [order_by[_desc]=published_date|created_date|title] [take=N]

What's included

  • Parser/AST (src/embeds/queryloop.rs): tokenizer + recursive-descent parser with and/or/not, parentheses, and precedence. Non-gated (cfg(any(server, web)) like mdx.rs) so the wasm editor validates inline; QueryLoop component holds a per-instance page signal + PaginationBar (multiple loops in one body paginate independently).
  • DB builders (queryloop_posts_db / queryloop_pages_db): compose SQL only from whitelisted fragments and bind every literal as $N (the facet_clause discipline — no author string ever reaches SQL text); return (items, total) via a COUNT companion for the pager.
  • Server fn #[post("/api/queryloop")]: re-parses as the authoritative security boundary, dispatches, attaches responsive srcsets.
  • Wire types QueryLoopItems/QueryLoopResult + PageCard/From<Page>; new PageCardView (links pages by path) and widget re-exports.
  • Registered in EmbedBlock + the SMART_TAGS slash menu.
  • CSS fix: the hand-rolled .prose * rules now honor not-prose, so embed cards (and any embed with links/headings/images) no longer inherit article typography — fixes the accent-colored card title/author links reported in review.

Decisions

  • [[component:…]] grammar (no core mdx.rs change), full boolean AST, paged prev/next — all per request.
  • v1 scope cuts (documented in code): page cards skip image srcset renditions; not category=x excludes posts with no category.

Tests / checks

  • 8 parser unit tests + 3 DB-builder tests (server::tests::queryloop). Extended test_pool with the users card columns + pages table.
  • Passes cargo fmt --check, clippy -D warnings on server,sqlite + server,postgres (--all-targets) and the wasm32 client, and cargo test --features server,sqlite.
  • Manual E2E steps captured in TODO_VERIFICATION.md (gitignored).

🤖 Generated with Claude Code

Last updated 2026-06-03