feat: keyboard navigation for tag suggestion dropdowns Tab/ArrowDown cycles through suggestions, Enter selects the active one, ArrowUp goes backwards, Escape dismisses. Works on browse filter inputs, upload page, image detail page, and image viewer tag editor.
Maxwell Jensen maxwelljensen@posteo.net
Thu, 14 May 2026 17:20:59 +0200
feat: add weimar image rescan-innate CLI command Adds ListAllImages and UpdateImageInnateTags DB queries, and a rescan-innate subcommand that computes innate tags for all images missing them β useful after upgrading from an older version.
Maxwell Jensen maxwelljensen@posteo.net
Thu, 14 May 2026 16:45:35 +0200
feat: add innate tag system for media characteristics Automatically assigns tags based on file type, dimensions, and video duration: "image"/"video"/"gif"/"lowres"/"highres"/">10 sec"/"<10 sec". Stored as JSON in images.innate_tags, searchable via tag filter, clickable in UI with distinguished blue styling, and included in tag autocomplete suggestions.
Maxwell Jensen maxwelljensen@posteo.net
Thu, 14 May 2026 16:06:15 +0200
feat: use full-resolution download URL for og:image previews Images now point to the original file instead of the 300px thumbnail, giving Discord/Telegram the highest quality preview frame. For videos, the thumbnail is kept as a static poster since the download is a video file. The body image in the OG page uses the same URL. Assisted-by: Crush:deepseek-v4-flash
Maxwell Jensen maxwelljensen@posteo.net
Thu, 14 May 2026 11:34:32 +0200
feat: Open Graph link previews for image and video pages
Serves a static HTML page with full OG meta tags at /image/{id} for
social media crawlers (Discord, Telegram, Twitter, etc.). Real
browsers are redirected to the SPA via a script tag.
- og:title, og:description, og:url, og:image, og:image:secure_url
- og:type, og:locale, twitter:card=summary_large_image, theme-color
- og:video tags for video files with twitter:player for Discord embeds
- X-Forwarded-Proto support for reverse proxy HTTPS detection
- Canonical share URL changed to /image/{id} in the browse viewer
- Comprehensive test proving all required tags are served
Assisted-by: Crush:deepseek-v4-flash
Maxwell Jensen maxwelljensen@posteo.net
Thu, 14 May 2026 11:24:18 +0200
fix: resolve Svelte a11y warnings and unused CSS Replace non-interactive `<li role="button">` with proper `<button>` elements in all suggestion lists (upload, detail page, ImageViewer). Replace `<li role="option">` with `<button role="option" aria-selected>` in browse page filter dropdowns. Remove unused CSS selectors. Assisted-by: Crush:deepseek-v4-flash
Maxwell Jensen maxwelljensen@posteo.net
Thu, 14 May 2026 09:29:12 +0200
test: add tests for uploader filter, tag+uploader combo, and user suggestions Also fixes a bug in ListImages where placeholder args were in the wrong order when both tag and uploader filters were combined. Assisted-by: Crush:deepseek-v4-flash
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 21:29:58 +0200
feat: autocomplete suggestions for tag and uploader filters Tag filter shows usage count, uploader filter shows upload count. Both endpoints use requireAuthOrPublic for unauthenticated browse. 200ms debounce on input. Backend uses LIKE prefix match ordered by count descending. Assisted-by: Crush:deepseek-v4-flash
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 21:23:05 +0200
feat: filter images by uploader with clickable uploader names Adds `?uploader=<username>` query param support to GET /api/images. Uploader names are clickable throughout the UI (browse cards, viewer, detail page) to filter the gallery to that uploader's media. π Generated with Crush Assisted-by: Crush:deepseek-v4-flash
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 21:19:18 +0200
feat: full-screen drag-and-drop upload zone Document-level drag events trigger a full-screen overlay with dark backdrop and dashed red border, accepting the file on drop anywhere on the page. File can still be selected via button click. π Generated with Crush Assisted-by: Crush:deepseek-v4-flash
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 21:14:24 +0200
fix: graceful SIGINT shutdown and SIGHUP config reload SIGINT now exits cleanly with code 0 instead of propagating http.ErrServerClosed as a fatal error. SIGHUP re-reads the config file and atomically swaps the config pointer β settings like site_title, favicon_path, behind_proxy, and require_auth_for_browse take effect immediately without restarting. Config is stored as *config.Config throughout so handlers and middleware read the live value at request time. Co-authored-by: Crush
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 20:10:38 +0200
feat: configurable site title, favicon, and nav logo Add site_title, favicon_path, and logo_path config options. Default favicon is an embedded SVG with a red lowercase "w" on black. Custom favicons can be SVG, PNG, JPG, or ICO β the /favicon handler serves them with the correct Content-Type detected from the file extension. The /logo handler serves a nav logo; when set, the layout shows an <img> instead of the text site title. The GET /api/config endpoint exposes these URLs to the frontend. Includes example config at configs/example.toml. Co-authored-by: Crush
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 20:05:05 +0200
fix: security hardening β bcrypt cost, secure cookies, security headers, avatar auth Bump bcrypt cost from 10 to 12 for adequate brute-force resistance in 2026. Add behind_proxy config option that enables Secure flag on session cookies for deployments behind a TLS-terminating reverse proxy (Caddy, nginx). Add X-Content-Type-Options, X-Frame-Options, and Referrer-Policy security headers to every response. Fix avatar endpoint auth mismatch β avatars are now served publicly since their URLs are exposed in gallery listings anyway. Co-authored-by: Crush
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 19:56:35 +0200
feat: public browsing, shareable image URLs, and tag editing Public browsing is the new default β unauthenticated users can browse the gallery, view images, and download originals. Lock it down by setting `auth.require_auth_for_browse = true` in weimar.toml. Each image now gets a shareable URL when opened in the overlay viewer: `/browse?image=<id>`. Navigate to it directly and the viewer opens automatically (or redirects to the detail page if the image is on a different page). Image owners can now edit tags from the overlay viewer or the detail page. A text field with autocomplete and ADD/REMOVE buttons appears below the tag display on your own uploads. Backend: new public/optional auth middleware, AddImageTags/RemoveImageTags queries, is_owner field on image detail, require_auth_for_browse config option. Frontend: viewer tag editing, shareable URL via pushState, image detail page uses is_owner from backend instead of separate getMe() call. Co-authored-by: Crush
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 19:42:40 +0200
fix: reduce avatar processing to 128px, double viewer avatar to 80px π Generated with Crush Assisted-by: Crush:deepseek-reasoner
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 18:25:32 +0200
feat: default avatar SVG, nav bar avatar, fix settings preview - Avatar.svelte component: shows uploaded image or red SVG fallback with first letter of username - Nav bar shows avatar (28px) in top-right corner next to LOGOUT, links to /settings - Layout now fetches GET /api/users/me instead of pinging images endpoint for auth state - Settings page loads current avatar on mount via getMe() - ImageViewer uses Avatar component for uploader display - Cleaned up unused CSS classes π Generated with Crush Assisted-by: Crush:deepseek-reasoner
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 16:53:37 +0200
feat: add user avatars and settings page
Backend:
- Migration v7: avatar_path TEXT column on users table
- POST /api/users/me/avatar β upload avatar (resized to 256px, JPEG)
- GET /api/users/{id}/avatar β serve avatar image
- avatar_url included in both list and detail image responses
Frontend:
- New /settings page with avatar upload (file picker, preview, save)
- SETTINGS link in nav bar (visible when logged in)
- Uploader avatar shown in browse card overlay, ImageViewer info bar,
and image detail page
π Generated with Crush
Assisted-by: Crush:deepseek-reasoner
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 16:41:12 +0200
feat: show uploader username and database ID on media - Added UploaderUsername field to Image model, populated from users table in both list and detail API responses - Shows uploader username in browse card overlay, ImageViewer info bar, and image detail page - Shows database ID (#id) alongside metadata in all three locations for admin CLI convenience π Generated with Crush Assisted-by: Crush:deepseek-reasoner
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 16:31:26 +0200
fix: restore full-width grid by fixing :global target after layout refactor The layout changed from `<main>` to `<div class="main">`, breaking the browse page's `:global(main)` override. Updated to `:global(.main)`. π Generated with Crush Assisted-by: Crush:deepseek-reasoner
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 16:19:04 +0200
feat: bold Swiss/constructivist design overhaul Typography: - Inter for body text, Oswald for headings (bold condensed) - ALL CAPS labels, large type hierarchy, tight letter-spacing Colors: - Black (#1a1a1a) background on UI elements, white text - Red (#c62828) accent for hover states, underlines, CTA buttons - Light gray (#f5f5f5) page background for contrast Layout: - Black nav bar with red bottom border, red hover states - Red accent underline blocks (3px x 3rem) on page headings - No border-radius anywhere β sharp rectangular constructivist forms - Full-width 6-column gallery grid with 2px gaps All pages redesigned: home, browse, upload, login, image detail, and image viewer overlay. π Generated with Crush Assisted-by: Crush:deepseek-reasoner
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 16:14:34 +0200
feat: make tags clickable with usage counts, larger and more prominent
- Tags in ImageViewer and detail page are now clickable pill buttons
- Clicking a tag navigates to /browse?tag=<tagname>
with the filter auto-applied on mount
- Browse page reads ?tag= URL param on initial load
- Tags display usage count badge (sorted mostβleast frequent)
- Larger pill sizing with hover effects in both viewer and detail page
- New GetImageTagsWithCounts DB function joins tags+image_tags
for usage counts returned in GET /api/images/{id} as tags_detail
π Generated with Crush
Assisted-by: Crush:deepseek-reasoner
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 16:02:30 +0200
feat: make browse grid 6 columns and full-width - Grid layout set to repeat(6, 1fr) with responsive breakpoints for smaller screens - Removed layout max-width constraint on browse page via :global(main) override - Header and pagination get their own padding within the full-width layout - Increased per page to 60 for better coverage at 6 columns π Generated with Crush Assisted-by: Crush:deepseek-reasoner
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 15:50:15 +0200
feat: redesign browse grid with Chevereto-style card layout and hover overlay - Compact grid with 2px gaps and dark image backgrounds (aspect-ratio 16:10) - Image fills card via object-fit: cover with subtle scale-on-hover - Gradient overlay on hover shows filename, dimensions, and truncated tags - Removed rounded corners and info text below cards for cleaner look π Generated with Crush Assisted-by: Crush:deepseek-reasoner
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 15:44:59 +0200
feat: add image overlay viewer with keyboard navigation and idle UI fade Replaces full-page navigation with a Chevereto-inspired lightbox overlay when clicking thumbnails in the browse grid. Supports prev/next via buttons and arrow keys, close via Esc/click-outside, auto-hiding controls after 3s of inactivity, and a bottom info bar with metadata and download. π Generated with Crush Assisted-by: Crush:deepseek-reasoner
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 15:34:07 +0200
fix: preserve all tags on upload and return empty array on no match
Tags beyond the first were silently dropped because `r.FormValue("tags")`
returns only the first value when the frontend sends each tag as a
separate FormData field. Changed to `r.Form["tags"]`.
Nil slices in API responses (`var images []Image`) marshalled to JSON
`null`, causing a TypeError in the Svelte template (`null.length`),
making "Loading..." persist forever. Initialized all API-facing slices
with `make()` so they marshal as `[]`.
π Generated with Crush
Assisted-by: Crush:deepseek-reasoner
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 15:16:05 +0200
feat: implement core backend API and SvelteKit SPA Full MVP implementation of the weimar media repository: - CLI commands for user CRUD, image deletion, password reset - HTTP API with auth (cookie sessions), upload, browse, download, thumbnail serving, and tag autocomplete - SQLite-backed storage with migrations - Hash-partitioned disk storage with EXIF stripping and thumbnails - SvelteKit SPA with login, upload, browse grid, and image detail pages - Makefile build flow: SvelteKit build -> Go embed -> single binary
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 14:03:41 +0200
feat: initial project scaffolding Single-binary media repository for home servers β Go backend with embedded SvelteKit SPA. - Cobra CLI with viper config (TOML, env vars WEIMAR_*, flags) - Commands: serve, users, image, version - HTTP server with Go 1.22+ ServeMux, API route stubs, SPA fallback - SvelteKit frontend with adapter-static (SPA mode, 5 routes) - Build pipeline: Makefile, GoReleaser, UPX compression - ldflags-based version and build date injection π Generated with Crush Assisted-by: Crush:deepseek-reasoner
Maxwell Jensen maxwelljensen@posteo.net
Wed, 13 May 2026 11:05:44 +0200