Merges the work landed on dev since the last main release.
Browse: sticky tabs + per-tab search bar (server-side, scope-aware)
Tab strip (Posts/Artists/Tags) + a shared search field rolled into one sticky block pinned under the 64px TopNav, so neither scrolls away.
Posts gains server-side text search (it had none): PostFeedService.scroll()/around() + /api/posts accept q (ILIKE over post_title OR description), applied inside the artist/platform WHERE so search stays scoped to the active filter. Scope shown as clearable chips; term is deep-linkable (/browse?tab=posts&q=…).
Artists/Tags search consolidated into the sticky bar; their inner search boxes removed, platform/kind filters retained.
Posts empty state distinguishes "no matches" from "no posts yet".
Series: operator-set sparse page numbering
Replaces the auto-renumber model (which had overwritten hand-set numbers) with operator-set numbers: gaps allowed, fill without moving neighbors, type-on-card editing, one placeholder block per gap, pending-tray "place from page N".
Fix
Round the kebab backing on series cards (was a translucent square behind the round ⋮).
CI green on dev (run 959). No new migrations in the Browse work; series numbering shipped its migration in an earlier dev push.
Merges the work landed on dev since the last main release.
## Browse: sticky tabs + per-tab search bar (server-side, scope-aware)
- Tab strip (Posts/Artists/Tags) + a shared search field rolled into one sticky block pinned under the 64px TopNav, so neither scrolls away.
- **Posts** gains server-side text search (it had none): `PostFeedService.scroll()/around()` + `/api/posts` accept `q` (ILIKE over `post_title` OR `description`), applied inside the artist/platform WHERE so search stays scoped to the active filter. Scope shown as clearable chips; term is deep-linkable (`/browse?tab=posts&q=…`).
- **Artists/Tags** search consolidated into the sticky bar; their inner search boxes removed, platform/kind filters retained.
- Posts empty state distinguishes "no matches" from "no posts yet".
## Series: operator-set sparse page numbering
- Replaces the auto-renumber model (which had overwritten hand-set numbers) with operator-set numbers: gaps allowed, fill without moving neighbors, type-on-card editing, one placeholder block per gap, pending-tray "place from page N".
## Fix
- Round the kebab backing on series cards (was a translucent square behind the round ⋮).
CI green on dev (run 959). No new migrations in the Browse work; series numbering shipped its migration in an earlier dev push.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Replaces the auto-renumbered 1..N position key with operator-OWNED page
numbers: sparse, gaps allowed, editable, never auto-renumbered. Order follows
the numbers; unnumbered pages sort to the tail. This is the fix for the model
that clobbered hand-set numbers on the flatten — numbers are now data, not a
derived sequence.
- series_service: drop the renumber-on-reorder/remove; order by page_number
NULLS LAST; new set_page_number(image_id, n|None); list_pages returns `gaps`
(one entry per missing-number run) + each pending group's parsed `start_page`;
set_cover renumbers below the current min; place_pending(image_ids, start_page)
numbers placed pages sequentially from the start (drop junk first → numbers
line up); add_post stamps the parsed start on staged pages.
- api/tags: POST /series/<id>/pages/number (set one page's number); /pending/
place takes start_page; removed /reorder.
- frontend: per-card editable number input; one gap block per gap with
drop-on-edge to assign the adjacent number (middle → type); append drop zone;
pending tray gets a "from page N" field + "Place from page N".
- tests reworked: sparse numbers + gaps, place-from-start, set-page-number route.
No migration; nothing destructive.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
add_post now stamps the post's parsed START (constant) on every staged
pending page so the group start survives junk removal; list_pages
surfaces it as start_page. Update the stale per-page [9,10,11] assertion
to check grp["start_page"] == 9.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The tinted backing was set on the square .fc-kebab wrapper span while the
button is round, so a translucent square showed behind the round ⋮.
border-radius:50% makes the backing a circle matching the button.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Browse tab nav scrolled away (operator didn't know it existed) and
Posts had no search. Roll the tab strip + a shared search field into one
sticky block pinned under the 64px TopNav.
- Posts gains server-side text search: PostFeedService.scroll()/around()
+ /api/posts accept q (ILIKE over post_title OR description), applied
INSIDE the artist/platform WHERE so search stays scoped to the active
filter. Scope shown as clearable chips next to the search field.
- Artists/Tags search consolidates into the sticky bar: their inner
search boxes are removed; they react to route.query.q (q is deep-
linkable, e.g. /browse?tab=posts&q=foo). Platform/kind filters stay.
- Posts empty state now distinguishes 'no matches' from 'no posts yet'.
Tests: posts q-search matches title|description and stays artist-scoped
(service); q passthrough (api).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Merges the work landed on dev since the last main release.
Browse: sticky tabs + per-tab search bar (server-side, scope-aware)
PostFeedService.scroll()/around()+/api/postsacceptq(ILIKE overpost_titleORdescription), applied inside the artist/platform WHERE so search stays scoped to the active filter. Scope shown as clearable chips; term is deep-linkable (/browse?tab=posts&q=…).Series: operator-set sparse page numbering
Fix
CI green on dev (run 959). No new migrations in the Browse work; series numbering shipped its migration in an earlier dev push.
🤖 Generated with Claude Code