feat: add image search with Bing, DuckDuckGo, and Qwant engines
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 6s
Mirror to GitHub / mirror (push) Failing after 3s
Tests / test (push) Successful in 25s

Three new image search engines:
- bing_images: Bing Images via RSS endpoint
- ddg_images: DuckDuckGo Images via VQD API
- qwant_images: Qwant Images via v3 search API

Frontend:
- Image grid layout with responsive columns
- image_item template with thumbnail, title, and source metadata
- Hover animations and lazy loading
- Grid activates automatically when category=images

Backend:
- category=images routes to image engines via planner
- Image engines registered in factory and engine allowlist
- extractImgSrc helper for parsing thumbnail URLs from HTML
- IsImageSearch flag on PageData for template layout switching
This commit is contained in:
Franz Kafka 2026-03-22 16:49:24 +00:00
parent a316763aca
commit 2b072e4de3
11 changed files with 687 additions and 4 deletions

View file

@ -952,6 +952,100 @@ footer a:hover {
}
}
/* ============================================================
Image Results
============================================================ */
.image-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 1rem;
}
.image-result {
display: block;
border-radius: var(--radius-sm);
overflow: hidden;
background: var(--bg-secondary);
border: 1px solid var(--border);
transition: transform 0.15s ease, box-shadow 0.15s ease;
text-decoration: none;
color: inherit;
}
.image-result:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px var(--shadow);
}
.image-result:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.image-thumb {
aspect-ratio: 1;
overflow: hidden;
background: var(--bg-tertiary);
display: flex;
align-items: center;
justify-content: center;
}
.image-thumb img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
transition: transform 0.2s ease;
}
.image-result:hover .image-thumb img {
transform: scale(1.05);
}
.image-thumb.image-error img,
.image-thumb.image-error {
display: none;
}
.image-placeholder {
font-size: 2rem;
opacity: 0.3;
}
.image-meta {
padding: 0.5rem;
min-height: 2.5rem;
display: flex;
flex-direction: column;
gap: 0.15rem;
}
.image-title {
font-size: 0.8rem;
font-weight: 500;
color: var(--text-primary);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.image-source {
font-size: 0.7rem;
color: var(--text-secondary);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
@media (max-width: 480px) {
.image-grid {
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
gap: 0.5rem;
}
}
/* ============================================================
Infoboxes
============================================================ */