feat: honor OCR text and tag scope in search Chapters/Mangas views

The Chapters/Mangas aggregation views accept an OCR ?text= filter and the
page search intersects tags, but the client dropped both: any text query
collapsed every view into a flat page search that discarded the selected tag
and grouping. Add text to AggregateOptions, thread it into
listTaggedChapters/listTaggedMangas, and rework the search loader so a
tag-scoped view runs the aggregation (optionally text-filtered) and the page
search passes the selected tag.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-11 14:58:48 +02:00
parent 1ed1a134ea
commit 69a9309c54
4 changed files with 43 additions and 11 deletions

View File

@@ -165,6 +165,16 @@ describe('page_tags api client', () => {
expect(url).toMatch(/\/v1\/me\/page-tags\/mangas\?tag=funny$/);
});
it('aggregation calls forward the OCR text filter when provided', async () => {
fetchSpy.mockResolvedValueOnce(
ok({ items: [], page: { limit: 50, offset: 0, total: 0 } })
);
await listTaggedChapters({ tag: 'funny', text: 'hello world' });
const url = fetchSpy.mock.calls[0][0] as string;
expect(url).toContain('tag=funny');
expect(url).toContain('text=hello+world');
});
it('searchPages serializes tags (CSV), text and content-warning filters', async () => {
fetchSpy.mockResolvedValueOnce(
ok({ items: [], page: { limit: 50, offset: 0, total: 0 } })

View File

@@ -179,6 +179,9 @@ export type AggregateOptions = {
order?: 'desc' | 'asc';
limit?: number;
offset?: number;
/** OCR full-text filter: when set, only pages whose analysis search_doc
* matches are aggregated (and `match_count` reflects that). */
text?: string;
};
function aggregateQs(opts: AggregateOptions): string {
@@ -187,6 +190,7 @@ function aggregateQs(opts: AggregateOptions): string {
if (opts.order != null) params.set('order', opts.order);
if (opts.limit != null) params.set('limit', String(opts.limit));
if (opts.offset != null) params.set('offset', String(opts.offset));
if (opts.text != null && opts.text !== '') params.set('text', opts.text);
return params.toString();
}