feat(search): tag-based page search surface + per-page tags & collections

Add the /search surface (Pages / Chapters / Mangas tabs) backed by
per-user page tags and per-page collections: schema (migration 0023),
backend endpoints for page tags/collections and tagged-page aggregations
(with the OCR text-search param reserved at 501), plus the frontend API
clients, library Page-tags tab, collection page sections, page context
menu / AddTagsSheet, and reader long-press wiring. Includes the
continuous-reader navigation fixes (?page=N handling, chapter-reset
timing, back-button pops history) and tag-normalization hardening
accumulated on the branch.

Bump version 0.60.2 -> 0.62.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-13 15:51:38 +02:00
parent 9910a0a995
commit 6c901e64c9
50 changed files with 6971 additions and 132 deletions

View File

@@ -33,6 +33,22 @@ pub struct CollectionSummary {
pub sample_covers: Vec<String>,
}
/// Row returned by `GET /collections/:id/pages`. Joins through
/// `chapters` and `mangas` so the collection detail view can render a
/// thumbnail + breadcrumb without per-row follow-up fetches.
#[derive(Debug, Clone, Serialize, FromRow)]
pub struct CollectionPageItem {
pub page_id: Uuid,
pub chapter_id: Uuid,
pub manga_id: Uuid,
pub page_number: i32,
pub chapter_number: i32,
pub chapter_title: Option<String>,
pub manga_title: String,
pub storage_key: String,
pub added_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct NewCollection {
pub name: String,

View File

@@ -7,6 +7,7 @@ pub mod collection;
pub mod genre;
pub mod manga;
pub mod page;
pub mod page_tag;
pub mod patch;
pub mod read_progress;
pub mod session;
@@ -21,10 +22,14 @@ pub use api_token::ApiToken;
pub use author::{Author, AuthorRef, AuthorWithCount};
pub use bookmark::{Bookmark, BookmarkSummary};
pub use chapter::Chapter;
pub use collection::{Collection, CollectionSummary};
pub use collection::{Collection, CollectionPageItem, CollectionSummary};
pub use genre::{Genre, GenreRef};
pub use manga::{Manga, MangaCard, MangaDetail};
pub use page::Page;
pub use page_tag::{
NewPageTag, PageTagSummary, TaggedChapterAggregate, TaggedMangaAggregate,
TaggedPageItem,
};
pub use patch::Patch;
pub use read_progress::{ReadProgress, ReadProgressForManga, ReadProgressSummary};
pub use session::Session;

View File

@@ -0,0 +1,63 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use uuid::Uuid;
#[derive(Debug, Clone, Deserialize)]
pub struct NewPageTag {
pub tag: String,
}
/// Returned by `GET /v1/me/page-tags`. Joins through chapters and
/// mangas so the library Page-tags tab can render the breadcrumb
/// without follow-up requests.
#[derive(Debug, Clone, Serialize, FromRow)]
pub struct TaggedPageItem {
pub tag: String,
pub page_id: Uuid,
pub chapter_id: Uuid,
pub manga_id: Uuid,
pub page_number: i32,
pub chapter_number: i32,
pub chapter_title: Option<String>,
pub manga_title: String,
pub storage_key: String,
pub tagged_at: DateTime<Utc>,
}
/// Distinct-tags histogram row. Used both for autocomplete in the
/// "Add tag" sheet and for the chip cloud in the library Page-tags
/// tab.
#[derive(Debug, Clone, Serialize, FromRow)]
pub struct PageTagSummary {
pub tag: String,
pub count: i64,
}
/// One chapter (with breadcrumb) ranked by how many of its pages the
/// caller has tagged with a given tag. Returned by the `/search`
/// page's Chapters tab via `GET /v1/me/page-tags/chapters`.
#[derive(Debug, Clone, Serialize, FromRow)]
pub struct TaggedChapterAggregate {
pub chapter_id: Uuid,
pub manga_id: Uuid,
pub manga_title: String,
pub chapter_number: i32,
pub chapter_title: Option<String>,
pub match_count: i64,
/// Up to 3 storage keys of matching pages in this chapter,
/// page-number ascending. Powers the thumbnail strip in the row.
pub sample_storage_keys: Vec<String>,
}
/// One manga ranked by how many of its pages (across all chapters)
/// the caller has tagged with a given tag. Returned by the `/search`
/// page's Mangas tab.
#[derive(Debug, Clone, Serialize, FromRow)]
pub struct TaggedMangaAggregate {
pub manga_id: Uuid,
pub manga_title: String,
pub manga_cover_image_path: Option<String>,
pub match_count: i64,
pub sample_storage_keys: Vec<String>,
}