Commit graph

161 commits

Author SHA1 Message Date
6e45abb150 feat: add screenshot
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 25s
2026-03-23 23:53:03 +01:00
90ea4c9f56 fix(prefs): persist favicon choice and apply to HTML results
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 9s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 28s
- Save favicon cookie on POST /preferences; reflect selection in template
- Add getFaviconService helper; pass favicon service into FromResponse
- Compute ResultView.FaviconIconURL (none/google/duckduckgo/self proxy)
- Update result_item and video_item templates; add httpapi/views tests

Made-with: Cursor
2026-03-23 23:08:21 +01:00
518215f62e feat(ui): dark theme redesign, fix image search and defaults
- Inline CSS in base.html (Inter, dark mode, sticky search, tabs, results)
- Remove HTMX/JS from templates; pagination via GET links
- Atmospheric side gradients + grid; wider column on large viewports
- Parse ?category= for HTML tabs (fixes Images category routing)
- Include bing_images, ddg_images, qwant_images in local_ported defaults
- Default listen port 5355; update Docker, compose, flake, README
- Favicon img uses /favicon/ proxy; preferences without inline JS

Made-with: Cursor
2026-03-23 22:49:41 +01:00
Claude
bdc3dae4f5 fix: add dark mode for search result classes
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 24s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 20:31:28 +00:00
Claude
77f939016f fix: expand dark mode CSS coverage for all page elements
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 9s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 26s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 20:27:42 +00:00
Claude
29cda763eb fix: add class-based dark mode fallback styling
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 33s
Add .dark class on html element with direct element styling as
fallback for when CSS custom properties don't work.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 20:05:17 +00:00
Claude
0852707df0 fix: add :root[data-theme="dark"] for even higher CSS specificity
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 8s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 29s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 19:59:34 +00:00
Claude
2aa5f00192 fix: use !important on dark theme CSS variables
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 25s
Force dark theme variables to override :root values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 19:47:27 +00:00
Claude
39c1c1b9ea fix: use html[data-theme="dark"] for higher specificity
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 8s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 29s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 19:25:00 +00:00
Claude
bc4c2d468f fix: add [data-theme="dark"] CSS selector back
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 24s
The server-side theme cookie sets data-theme attribute, but CSS was
only using @media (prefers-color-scheme: dark). Need both selectors
so theme works via cookie AND via system preference.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 18:57:47 +00:00
Claude
fe0c7e8dc8 feat: add server-side theme cookie with dropdown selector (no JS)
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 27s
- Add theme POST handler that sets HttpOnly cookie
- Update preferences page to use <select> dropdown instead of JS buttons
- Theme cookie set on POST /preferences with theme parameter
- Theme read from cookie on all page renders
- No JavaScript required for theme selection

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 18:47:06 +00:00
Claude
056d2d1175 feat: use CSS prefers-color-scheme for dark mode (no JS)
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 27s
- Remove inline JS that sets data-theme from localStorage
- Use @media (prefers-color-scheme: dark) in CSS for automatic dark mode
- Remove JS-dependent theme toggle from preferences
- Theme now follows system preference automatically

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 18:34:18 +00:00
8f2fd671f1 fix: extract only hostname for favicon data-domain
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 27s
data-domain was set to the full result URL (https://en.wikipedia.org/...).
This caused /favicon/ to receive malformed domain strings.

Now extracts u.Hostname() in FromResponse and passes it as Domain
to result_item.html.
2026-03-23 14:56:51 +00:00
b57a041b6a perf: use Redis for favicon cache with 24h TTL
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 27s
Favicons are now cached in Valkey/Redis instead of an in-memory map:
- TTL: 24 hours (up from 1 hour in-memory)
- ETag derived from body SHA256 (no extra storage needed)
- Falls back to in-memory on cache miss when Valkey is unavailable
- GetBytes/SetBytes added to cache package for raw byte storage

In-memory faviconCache map, sync.RWMutex, and time-based expiry
logic removed from handlers.go.
2026-03-23 14:38:32 +00:00
352264509c feat: self-hosted favicon resolver via /favicon/<domain>
Adds a Kafka-hosted favicon proxy at /favicon/<domain>:
- Fetches favicon.ico from the target domain
- In-memory cache with 1-hour TTL and ETag support (304 Not Modified)
- Max 64KB per favicon to prevent memory abuse
- Privacy: user browser talks to Kafka, not Google/DuckDuckGo

New "Self (Kafka)" option in the favicon service selector.
Defaults to None. No third-party requests when self is chosen.
2026-03-23 14:35:19 +00:00
d0efcb0309 Merge branch 'main' of https://github.com/metamorphosis-dev/samsa
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 27s
2026-03-23 14:32:04 +00:00
665494304d perf(htmx): reduce swap payload via OOB swaps and hx-select
Before: every HTMX response returned the full results_inner template
(~400 lines of corrections, meta, pagination, back-to-top) even though
only the result list (#urls) changed between searches.

After:
- Corrections and results-meta use hx-swap-oob="true" — they update
  in-place in the DOM without duplication, no extra payload
- #urls div carries hx-select="#urls" hx-target="#urls" hx-swap="innerHTML"
  — only the result rows are extracted from the response and swapped
- Pagination forms replaced with paginate-btn buttons — JS calls
  htmx.ajax() directly with select:#urls so only result rows reload
- Header search form gains hx-get/hx-target/hx-select for partial updates
  on subsequent searches

Payload reduction per HTMX swap: ~60-70% (no more nav, meta, pagination,
back-to-top, htmx-indicator in the swap response body)
2026-03-23 14:31:21 +00:00
9e95ce7b53 perf: shared http.Transport with tuned connection pooling
Add internal/httpclient package as a singleton RoundTripper used by
all outbound engine requests (search, engines, autocomplete, upstream).

Key Transport settings:
- MaxIdleConnsPerHost = 20  (up from Go default of 2)
- MaxIdleConns = 100
- IdleConnTimeout = 90s
- DialContext timeout = 5s

Previously, the default transport limited each host to 2 idle connections,
forcing a new TCP+TLS handshake on every search for each engine. With
12 engines hitting the same upstream hosts in parallel, connections
were constantly recycled. Now warm connections are reused across all
goroutines and requests.
2026-03-23 14:26:26 +00:00
7ea50d3123 feat(ui): make favicons user-configurable, off by default
- Add favicon service preference: None (default), Google, DuckDuckGo
- result_item.html: remove hardcoded Google favicon src, defer to JS
- applyFavicon() reads data-domain attr and sets src or display:none
- Privacy-by-default: users must explicitly opt in to any favicon service
- Add favicon selector to both the settings panel and preferences page
2026-03-23 14:22:24 +00:00
540a127f7b
Update README.md
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 23s
2026-03-23 15:19:29 +01:00
aaac1f8f4b docs: fix ASCII architecture diagram alignment
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 8s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 23s
2026-03-23 14:17:41 +00:00
71b96598ed docs: refresh README — 11 engines, accurate clone URLs, API key clarity
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 22s
2026-03-23 14:07:49 +00:00
ba06582218 docs: add CONTRIBUTING guide for adding new engines
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 22s
Covers the full lifecycle: interface, SearchRequest/SearchResponse,
result building, graceful degradation, factory wiring, planner
registration, testing, and RSS parsing example.
2026-03-23 08:19:28 +00:00
1e81eea28e chore: remove stale design and implementation plan docs
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 22s
2026-03-23 08:10:15 +00:00
015f8b357a fix: rename remaining kafka references to samsa
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 26s
- OpenSearch description: 'Search results for "..." - kafka' → samsa
- Test error message: 'kafka trial' → samsa trial
2026-03-23 07:25:30 +00:00
8e9aae062b rename: kafka → samsa
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 11s
Mirror to GitHub / mirror (push) Failing after 5s
Tests / test (push) Successful in 42s
Full project rename from kafka to samsa (after Gregor Samsa, who
woke one morning from uneasy dreams to find himself transformed).

- Module: github.com/metamorphosis-dev/kafka → samsa
- Binary: cmd/kafka/ → cmd/samsa/
- CSS: kafka.css → samsa.css
- UI: all 'kafka' product names, titles, localStorage keys → samsa
- localStorage keys: kafka-theme → samsa-theme, kafka-engines → samsa-engines
- OpenSearch: ShortName, LongName, description, URLs updated
- AGPL headers: 'kafka' → 'samsa'
- Docs, configs, examples updated
- Cache key prefix: kafka: → samsa:
2026-03-22 23:44:55 +00:00
c91908a427 Merge commit 'df67492' 2026-03-22 23:41:36 +00:00
0030cf97ad feat: per-engine accent colors in search results
Each engine now has a distinctive color accent applied to its result
card (left border) and engine badge (colored left strip + text).

16 engines mapped to brand-appropriate colors:
Google blue, Bing teal, DDG orange-red, Brave red, Qwant blue,
Wikipedia dark, GitHub purple, Reddit orange-red, YouTube red,
Stack Overflow amber, arXiv crimson, Crossref navy blue.

Pure CSS via data-engine attribute — no JavaScript.
2026-03-22 22:59:32 +00:00
df67492602 feat: add Stack Overflow search engine
Uses the Stack Exchange API v3 (/search/advanced) to find questions
sorted by relevance. No API key required (300 req/day); optionally
configure via STACKOVERFLOW_KEY env var or [engines.stackoverflow].

Results include score, answer count, view count, and tags in the
snippet. Assigned to the 'it' category, triggered by the IT category
tab or explicit engine selection.

6 tests covering parsing, edge cases, and helpers.
2026-03-22 22:29:34 +00:00
e96040ef35 chore: remove React frontend, SPA server, and compiled binary
The project uses pure Go HTML templates + CSS. The React frontend
(frontend/), SPA handler (internal/spa/), and prebuilt binary (kafka)
were dead weight.

Also removes the frontend replacement plan/spec docs.
2026-03-22 22:18:19 +00:00
c97e6a6182 fix(frontend): improve pagination CSS for better centering and sizing
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 4s
Tests / test (push) Failing after 19s
- Match .page-current padding to button padding (0 0.75rem)
- Add box-sizing: border-box to buttons
- Add margin/padding reset to pagination forms
- Simplify page-current flex layout

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 23:14:48 +01:00
f92ec02dba fix: center page numbers in pagination
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 4s
Tests / test (push) Failing after 18s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 22:36:06 +01:00
a61a3a9c70 fix: improve pagination styling for active page and next button
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 3s
Tests / test (push) Failing after 18s
Match padding and sizing for active page number and next button
to match inactive page buttons.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 22:31:53 +01:00
d7ec0217c4 feat: add settings link to header
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 3s
Tests / test (push) Failing after 18s
Add gear icon link to preferences page in the header.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 22:29:53 +01:00
77a41834a5 feat: add preferences page with theme toggle
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) Failing after 18s
- Simple preferences page with engine selection
- Light/dark theme toggle with localStorage persistence
- Clean form layout without complex JS dependencies
- Add dark theme CSS variables

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 22:27:48 +01:00
030b4a8508 feat: make search bar sticky on results page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 22:24:02 +01:00
23dcdef26f fix: unescape HTML entities in result titles
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) Failing after 19s
Wikipedia returns HTML entities like &lt;span&gt; which were being
double-escaped by Go templates. Now using html.UnescapeString and
template.HTML to render properly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 22:19:07 +01:00
37420ae5a8 refactor(frontend): port search-zen-50 style to Go templates
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) Failing after 18s
Replace React SPA with simple Go templates using search-zen-50
visual style. No JavaScript required - pure HTML/CSS with clean
teal accent color scheme, monospace logo, and minimal design.

- Simplified base.html without HTMX or autocomplete JS
- Clean homepage with centered search box
- Results page with sticky header and category tabs
- Simplified CSS matching search-zen-50 aesthetics

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 22:05:56 +01:00
168cb78fab feat: add frontend source code
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) Failing after 16s
Add search-zen-50 React SPA source code to frontend/ directory.
Build artifacts (dist, node_modules, lock files) are gitignored.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 21:27:45 +01:00
6b418057ef feat(frontend): replace Go templates with React SPA
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) Failing after 16s
- Add internal/spa package for embedding React build
- Wire SPA handler in main.go for non-API routes
- Add gitignore entry for internal/spa/dist
- Add implementation plan

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 21:12:01 +01:00
5d14d291ca feat(main): wire SPA handler in main.go
Replace template-based handlers (h.Index, h.Preferences) with the new spa
handler. API routes (healthz, search, autocompleter, opensearch.xml) are
registered first as exact matches, followed by the SPA catchall handler
for all other routes. Remove unused views and io/fs imports.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 19:50:03 +01:00
8651183540 feat(spa): add SPA Go package with embedded dist FS
Creates internal/spa package that:
- Embeds React build output from cmd/kafka/dist/
- Provides HTTP handler for static file serving
- Falls back to index.html for SPA client-side routing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 19:40:34 +01:00
1543b16605 docs: add frontend replacement design spec
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 18:58:50 +01:00
00b2be9e79 fix(css): restore original layout, re-add only image grid styles
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 3s
Tests / test (push) Successful in 24s
Reverted CSS to the known-working state at 4b0cde9, then re-applied
only the image grid styles. The duplicate .results-layout block is
intentional — it was present in the working version too.
2026-03-22 17:35:35 +00:00
2f10f4e1e5 fix(css): remove duplicate .results-layout that broke 3-column grid
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 22s
The old 3-column layout block (referencing .left-sidebar/.right-sidebar
classes that don't exist in the HTML) was overriding the correct layout
defined earlier. Removed the stale duplicate.
2026-03-22 17:31:06 +00:00
a9ae69cad5 fix(security): allow HTMX CDN and inline scripts in CSP
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 8s
Mirror to GitHub / mirror (push) Failing after 3s
Tests / test (push) Successful in 22s
script-src now permits 'unsafe-inline' and https://unpkg.com so the
autocomplete script and HTMX library load correctly.
2026-03-22 17:22:31 +00:00
2b072e4de3 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
2026-03-22 16:49:24 +00:00
a316763aca fix(test): update CORS preflight test for deny-all default
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 3s
Tests / test (push) Successful in 24s
Empty CORSConfig now means no CORS headers, matching the security fix.
Test explicitly configures an origin to test preflight behavior.
2026-03-22 16:38:03 +00:00
5884c080fd Merge branch 'security/hardening-sast-fixes'
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 3s
Tests / test (push) Failing after 19s
2026-03-22 16:31:57 +00:00
b3e3123612 security: fix build errors, add honest Google UA, sanitize error msgs
- Fix config validation: upstream URLs allow private IPs (self-hosted)
- Fix util.SafeURLScheme to return parsed URL
- Replace spoofed GSA User-Agent with honest Kafka UA
- Sanitize all engine error messages (strip response bodies)
- Replace unused body reads with io.Copy(io.Discard, ...) for reuse
- Fix pre-existing braveapi_test using wrong struct type
- Fix ratelimit test reference to limiter variable
- Update ratelimit tests for new trusted proxy behavior
2026-03-22 16:27:49 +00:00