feat(corpus): category filter, passage expand, drill enhancements, URL hash state

- Search: category filter pills scope results to a legal domain
- Search: full chunk text returned; click to expand inline beyond 600-char excerpt
- Drill panel: total count label ("Showing X of Y"), sort dropdown, title filter (300ms debounce)
- URL hash: preserves query/mode/lang/category/drill state for bookmarking

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-18 15:47:56 +02:00
parent ffcf887428
commit 2e2dfd7310
3 changed files with 208 additions and 42 deletions
+15 -1
View File
@@ -12,8 +12,15 @@ try {
$category = isset($_GET['category']) && $_GET['category'] !== '' ? trim((string)$_GET['category']) : null;
$sourceName = isset($_GET['source_name']) && $_GET['source_name'] !== '' ? trim((string)$_GET['source_name']) : null;
$titleFilter = isset($_GET['title']) && $_GET['title'] !== '' ? trim((string)$_GET['title']) : null;
$offset = max(0, (int)($_GET['offset'] ?? 0));
$limit = max(1, min(50, (int)($_GET['limit'] ?? 20)));
$orderBy = match($_GET['sort'] ?? 'newest') {
'oldest' => 'd.updated_at ASC',
'alpha' => 'd.title ASC',
'chunks' => 'chunk_count DESC',
default => 'd.updated_at DESC',
};
// Build WHERE clause
$where = ["d.corpus_id = 1", "d.status = 'ready'"];
@@ -24,6 +31,11 @@ try {
$params[] = $category;
}
if ($titleFilter !== null) {
$where[] = 'd.title LIKE ?';
$params[] = '%' . str_replace(['%', '_'], ['\\%', '\\_'], $titleFilter) . '%';
}
if ($sourceName !== null) {
// Filter by source via a JOIN to corpus_sources on category match
// or by matching the scraper's URL pattern in source_url
@@ -61,7 +73,7 @@ try {
LEFT JOIN chunks c ON c.document_id = d.id
WHERE $whereStr
GROUP BY d.id
ORDER BY d.updated_at DESC
ORDER BY $orderBy
LIMIT $limit OFFSET $offset"
);
$dataStmt->execute($params);
@@ -82,6 +94,8 @@ try {
'filter' => [
'category' => $category,
'source_name' => $sourceName,
'title' => $titleFilter,
'sort' => $_GET['sort'] ?? 'newest',
],
]);
} catch (Throwable $e) {