- Detail page: in-page zoomable image viewer with shortcut
v, dimmed backdrop, click-outside-to-close, and 0.5x-4x zoom range. - Reader: clicking a page opens the in-page image viewer.
- Footer shows "N hidden" next to the match count under an active rating ceiling.
- Rating ceiling extends to sidebar counts, relations hub and browse counters, relations bulk-delete, and batch / auto-tag.
- Relations hub and browse counters count chains and trees, not edges.
- Unified
/relations/browse?kind=with tabs per kind; group tabs carry per-card checkboxes for multi-select merge. /images/{id}/relationsshows the full version chain (left-to-right with arrows) and derivative tree; current image accented.- Comparison table on pair-shape views (session, browse, per-image): resolution, file size, added-on, tag count, tag delta, format.
- Session Duplicate decision opens a Delete-now / Keep modal with an optional copy-tags pre-step; Esc and Enter default to Keep, D triggers delete.
- Add-relation dialog: preview tile per side; pasting
/images/{id}or a full URL fills the id. - Browse cards: declared-at line under the toolbar and an ingest-date pill on every thumbnail.
- Two-column hub (Find / Browse) with state-aware accent; Start a session disables with a tooltip when the queue is empty.
- Marked-duplicates walker grows a Marked-at column.
- Plain-language relations labels on user-facing surfaces: Same image, Variant, Newer revision, Based on, Not related. Search keywords, schema, REST API, and CSS classes stay on technical names.
- Session UI rebuild: Swap and Compare on a centred bridge between thumbs, accent per primary decision button, button hover writes the committed
{left}/{right}sentence to the prompt; remaining-pair counter moves into the topbar; compare slider gains a chevron handle and Left/Right caption strip. - Marked-duplicates walker sorts rows newest-first and badges the Original column.
- Version chain: extending an existing chain (X -> Y, then Y -> Z) is allowed; only schema-enforced collisions and cycle closures reject.
- Derivative edge: two-node loops (A -> B then B -> A) and longer cycles caught by walking the source chain.
- Chain edge replace drops only the rows that collide with the new pair, so other chain entries survive when overwriting one step.
- Browse cards, duplicates walkers, session pair feeder, and hub queue counters honour the rating ceiling; empty-queue branch offers a one-click ceiling raise.
- Session inline 1-6 legend (the
?cheat-sheet still documents the shortcuts).
- Relations system. New
/relationstop-level page (between Tags and Settings) with Find / Duplicates / Browse sections. A 64-bit perceptual hash (pHash) computed at ingest, with a backfill job for existing libraries, feeds a background find-pairs job that fills a queue of near-duplicate candidates. Sessions step through the queue in a swipe-style UI: decide Duplicates / Alternates / Version / Derivative / Not related / Skip with keyboard shortcuts; Compare opens a draggable before/after slider; Swap-sides flips parent/child for directional types. Browse-pair (with a Not-related tab and unlink) and Browse-groups pages round out the surface. Settings -> Maintenance growsCompute perceptual hashesandRebuild pair queuebuttons; a daily scheduler phase runs find-pairs on its own. - Detail-page Related entries panel above Similar entries (with collection siblings and a see-all) and a full
/images/{id}/relationsgrid sectioned by relation type. The detail page grows a pHash row (copyable, with a~4link that runs the matching Hamming-distance search) and an Add-relation dialog with parent/child swap, autocomplete, an Overwrite button for conflicts, and an adaptive sentence. - Search keywords. Twelve metadata filters:
name,size,mime,ratio,tagcount,duration,hash,prompt,model,sampler,seed,via.id:Nexact match.phash:bare,phash:<hex>exact, andphash:<hex>~<d>Hamming-distance lookup.relation:<type>closed vocabulary coveringduplicate,original,alternate,version,derivative,source,collection,any,none. - Search autocomplete: level-2 suggestions for
name,model,sampler,prompt; values containing spaces are quoted automatically; thesystem:cheat-sheet color-codes category rows and caps the dropdown at 60vh so the 40+ rows no longer hide behind a tiny scrollbar. - Sidebar Inbox, Favorites, and Sources panels matching the existing Tags / Folders / Collections layout; clicking a section name toggles it.
- Settings rework. Numeric inputs become sliders with bounds, ticks, and updated ranges; scheduler time-of-day moves to a 24h HH:MM text input; schedule checkboxes lay out in three columns; Stats is a 2-column block with stabilised size cells. New Relations subsection collects the find-pairs schedule and default session order. Default
max_file_size_mbraised to 512; default auto-taggeridle_release_after_minutesraised to 15. - Detail page (non-relations): copyable Image ID row above Path; click SHA-256 to copy; autocomplete in the source edit dialog; Source value rendered as a search link; flash confirmations after source / url / collection / order edits; editable metadata rows moved below a separator.
- API:
GET /api/v1/images/{id}/relations,POST /api/v1/relations,DELETE /api/v1/relations.GET /api/v1/images/{id}grows aphashfield. - In-app help: search-syntax table subcategorised.
- Footer shows the gallery's collection count instead of the saved-search count.
- Settings -> Maintenance:
Merge general tags(superseded by the v1.7 category dispatchers).
- Search:
date:>=YYYY-MM-DDanddate:<=YYYY-MM-DDextend to end-of-day; bare<category>:routes tocat:<category>instead of match-all; leadingORwith no left operand is dropped;name,folder,source,collection, andcategory:filters are case-insensitive. - API: aliases populated in the search response; batch tag add / remove route through the same single-image helpers as the UI (fan-out, alias resolution, implication propagation).
- Upload: View-inbox count is ceiling-aware on the OOB swap.
- Maintenance:
removeDuplicatestakes a job slot and batches its DELETE in chunks. - Gallery:
DeleteImageunlink gated onPathInsideso an alias outside the gallery root can't be removed via a delete request; image decode bounded by a megapixel cap so a giant source can't OOM-kill the thumbnailer or tagger;batchDeletefetches its targets in a singleINquery instead of N+1. - Detail page: SHA-256 digest aligned with its label; external-edit dialogs close only on form responses.
- Source autocomplete quotes values with spaces.
- Search latency: NOCASE-collated partial visible indexes for
source/folder/series; AND-driver materialisation bounded to the bucket window; category-qualified tag pre-resolved to skip a per-row 2-table join; partial seed indexes onsd_metadataandcomfyui_metadata; sort-index hint trimmed to filters without a covering column index;basename_lowerindexed column added; sidebar fan-out reduced toimage_tagsandsaved_searches; re-ANALYZEon boot after v1.7.2 schema additions. - Schema additions auto-applied on upgrade:
images.phash,images.duration_seconds,images.basename_lower, the relations tables (dup_groups,dup_group_members,alt_groups,alt_group_members,version_edges,derivative_edges,not_related_pairs,potential_relation_pairs,relation_session). Per-gallery BK-tree built at startup and kept in lockstep with row writes via thegallery.PhashHooks.OnStoredhook. New SQL scalarsbasename()andhammingdist()registered at bootstrap.
- Auto-tagger runs in a
tagger-workersubprocess by default. ORT runtime and CUDA libraries live in the child; releasing idle (or the newFree memorybutton) SIGTERMs the worker so its ~1 GB RSS returns to the kernel rather than staying mapped to the parent. Settings → Stats grows an Auto-tagger row with worker PID, mode, cache state, and per-job RSS; cache transitions log on load / reuse / release. CUDA JIT cache persists under<data_path>/.nv-cacheto speed up next starts. - Settings → Maintenance:
Free memorybutton runs the same reclaim bundle the idle ticker uses (SQLite caches, Go heap, tagger worker SIGTERM) on demand and reports freed bytes. - Settings → Auto-Tagger form exposes
idle_release_after_minutesnext touse_cudaandparallel - Search autocomplete covers
source:label values. - Tags page: the Alias→ / Repoint→ dialog mints a canonical when the typed name doesn't yet exist, matching the Create-alias dialog.
- Auto-tagger default
idle_release_after_minutesdrops from 30 to 10. - Tags page: Alias/Repoint redirects to
/tags?origin=alias&q=<source>so the user lands on the fresh alias row. - Stats panel: Native row no longer counts the auto-tagger; the worker's ORT heap is on its own row.
- Detail page: tag-focus mode anchors the cursor to the previous tag after a delete instead of falling back to the first row.
- Detail page:
tshortcut finds the Auto-tag button. - Tags page:
delete-searchreloads the listing once the background job finishes instead of on a fixed 800 ms timer; create-tag redirect lands on/tags?q=<name>instead of being clobbered by a reload race; create-tag and create-alias names URL-escape in the redirect. - Dialogs: autocomplete dropdowns render past the dialog edge instead of being trapped in an inner scrollbar.
- Gallery: inbox-filter tooltip count honours the rating ceiling.
- Search:
date:>=YYYY-MM-DDanddate:<=YYYY-MM-DDparse correctly (the two-char operators were never tried before the single-char ones).
- Auto-tagger Backend interface lifts the cache, session pool, and per-image inference loop behind a single abstraction; the in-process implementation registers itself via
SetBackend. The IPC backend pairs with atagger-workersubcommand and a Unix-domain socket;MONBOORU_TAGGER_BACKEND=inprocremains as an escape hatch for a regression. - Auto-tagger memory: glibc arena trim every 256 processed images bounds mid-run RSS; CUDA provider options retuned for batch=1 inference (HEURISTIC algo search, kSameAsRequested arena, in-default-stream copies); CPU initializer copies dropped when the CUDA EP is enabled; ORT intra/inter-op thread budget split across
parallelworkers. - Auto-tagger hot path: float32 input buffer pooled by tile size; pad-white walk fused with the canvas zero; label-routing slice computed once per loaded tagger and reused.
- API:
GET /api/v1/images/{id}/tagsreturns the per-image tag list with the same shape as the POST/DELETE counterparts. - Search:
*xyzparses as a suffix wildcard;collection:values autocomplete. - Tags page: alias rows accept a category change; the create-alias dialog auto-creates a missing canonical target.
- Tag and alias mutations surface their result in the tag-flash slot.
- Detail page: topbar reshuffled; the inbox button labels by current state.
- Batch: selection bar and Actions chooser share one ordering; new
Toggle favoriteaction;Send to inbox/Remove from inboxcollapse intoToggle inbox.
- Logger levels reorder
Debug < Info < Warnto match slog; runtime semantics unchanged.
- Auto-tagger: pad-and-resize peak allocation capped to
size^2so large sources can't OOM-kill the container with multiple taggers loaded. - Search:
tagged:true/autotagged:truepartition counts cached and invalidated alongside image-tag membership writes;inbox:shapes pinidx_images_ingested_visibleto stay inside the search budget. - Search: bare
system:returns no-match instead of matching every image; random sort with a small seed actually shuffles. - Reader and gallery: out-of-range
?page=N,?page=0, and?page=-1303 to the clamped value so the URL agrees with what the pager renders. - Detail page: clearing the collection nulls the stored order; pasting many tags commits in a single transaction.
- Tags page: duplicate category names surface as a friendly error; zero-usage rows skip the origin badge; alias canonical-suggest dropdown anchors to the input width.
- Categories: new-category color picker aligns with the text and Add controls.
- API:
tagsis required on POST/DELETE image-tag mutations;page_sizealiaseslimit;viarejects selector-breaking characters; cbz tag mutation drops its leftover 415; path-mode ingest enforces gallery-root containment and rejects non-decodable files; delete cleans up an emptied source folder by default. - Maintenance: prune-missing runs as a job, blocking concurrent submissions.
- Gallery: alias path collisions caught before the move transaction; watcher suppresses events during delete and tag jobs; thumb checkbox blurs on change so keyboard shortcuts fire after a click; bulk delete skips
RemoveMangaCachefor non-cbz rows. - Tagger service: rating-category lookup miss is logged instead of silently producing a Service with
ratingCatID=0. - Router:
custom_csscontainment check resolves symlinks first so an operator can't accidentally point it at a symlinked escape. - Scheduler: cancel aborts the whole run instead of just the current phase.
- UI: toolbar tooltip keeps the inbox count visible when a rating ceiling is applied.
- Batch tag: chunk size drops to 100 rows when at least one resolved tag has implications so foreground requests slip between commits.
- DB bootstrap: ANALYZE gated on
PRAGMA user_version; partial-index ANALYZE bounded withanalysis_limit = 400. Steady-state cold starts skip the pass. internal/web/handlers_batch.goextractsresolveBatchScope;gallery.Syncsplits into per-case helpers;galleryCtxcached-count accessors share one helper; favorite/inbox toggle handlers sharetoggleBoolColumn.
- Comics & manga.
.cbz/.ziparchives ingest as single entries withpage_count, optionalCollectionand order, and a parsedComicInfo.xmlpanel. In app reader (/images/{id}/read?page=N) with bottom-bar controls and side click-to-prev/next chevrons; pages grid (/images/{id}/pages) with keyboard nav. Auto-tagger iterates every archive page. Search keywordstype:image/type:archive/pages:<op><n>. Detail-pageOpen in reader (R)andSee all pages (P)buttons. MangaN pagespill on gallery thumbnails. - Collections. Any image can carry a
Collectionlabel and an optional order integer (rendered#N). Surfaces: detail-page edit dialog with autocomplete, search keywordcollection:<value>, sidebar Collections section above Folders, batchSet collectionaction,Ordersort (collection then order), thumbnail pill bottom-left. - Search keyword
type:animatedreplacesanimated:true/animated:false. Thetype:vocabulary unionsimage,archive, andanimated; static-only is reachable as-type:animated. - Detail-page e-leader chord shortcuts:
e oorder,e ssource,e ccollection,e uurl. - Auto-tagger: frequency-gated merge with a per-category top-K cap (new
tagger.aggregation.min_hit_fractionknob). Manually re-adding an auto-tagged tag promotes the row to user-owned. Cancellation now responds inside the manga page loop, and status emission is serialised across parallel workers so progress reads as a stable side-by-side list. - Upload: live progress feedback (
Uploading… <loaded> / <total> (NN%)) and aSaving on server…label once bytes are fully sent. - Detail page: orange warn-flash on mixed-outcome tag adds (some accepted, some rejected) bundling every applicable part; inline flash when a manual rating overwrites another.
- Settings: confirm dialog before regenerating the API token.
- Gallery grid:
ArrowDownon the last visible row scrolls the layout to its bottom (andArrowUpon the first to its top) so the search bar and pagination stay keyboard-reachable.
- Detail-page metadata rows reorder to
Added via/Source/URL/Collection/Order/Pages.[edit]markers render as inline text links. Duplicate-paths section relabelledDuplicatesand restructured as a vertical list.Similar imagesrenamed toSimilar entriesand partitioned by file type. - Sidebar heading
AI sourceshortened toAI; Collections section moves above Folders. - Maintenance:
VACUUMruns in a goroutine instead of on the request thread. - Gallery export JSON bumped to v2 (adds
manga_metadataarray); v1 imports stay supported. Light-manifest version split from the full-export version so it stays at1. tagged:true/autotagged:trueexact partition counts computevisible_total - untagged_visibleso the two halves sum to the visible total.
- Sync: in-place edits (file rewritten at same byte length) detected via a new
image_paths.mtimecolumn; sha, dimensions, side-table metadata, and thumbnail refresh in place so user-curated tags survive. - Ingest / sync: when a known SHA reappears at a new path, the previous canonical row is demoted to alias instead of being silently rewritten in place.
- Watcher: re-debounces on
Writeevents so slow.cbzcopies aren't ingested mid-flush. - Search: saved searches persist
sort/order/seed;View inboxlink on the upload page shows a ceiling-aware count; bare-category-prefix filter (?q=character:) routes to the category-only filter; pages clamped past the end issue303(orHX-Push-Url) so the URL matches what the user sees. - Gallery grid: overlay badges (favourite, inbox, missing) swap unicode for inline SVG so glyphs align across fonts; manga
N pagespill anchors bottom-right of.thumb-card. - Settings: active-gallery row counts pinned to the
baseDatasnapshot so footer and table cells agree; password verification gates on the stored hash, not theEnablePasswordflag; password-set form gets a hiddenusernamefield for password managers and accessibility tools. - Maintenance: alias / duplicate-path deletes route through
gallery.PathInsideso they can't unlink files outside the gallery root; direct hits on/settings/maintenance/duplicates-list303 to/settings#maintenance. - Server:
custom_csspath validated against a trusted-dir allowlist at config load;/favicon.icoaliases to/static/favicon.png. - Layout: footer wraps on viewports under 600 px; switch-gallery dialog closes on the success branch.
- Jobs:
Manager.CancelreleasesscheduleHeldso an early-return doesn't leak the reservation;prune-thumbsjoins the cancel-button gate. - Scheduler: cache invalidation moved into a
galleryCtx.Syncwrapper so manual and scheduled syncs both refresh per-cx tag / folder / source caches.
internal/web/handlers.gosplit into 15 per-surface files. No behaviour change.- Schema bootstrap (additive):
images.page_count,images.series,images.series_order,image_paths.mtime,saved_searches.sort/order/seed. Newmanga_metadatatable; newidx_images_seriespartial index. - Per-page thumbnails pre-generated at ingest so the first
/pagesrender is a static-file serve. - Batch tag operations chunk the per-tag transaction (one tx per 500 rows), mirroring
runBulkDelete. - Tests added: auto-tagger
storeResultsscope + rating prune, archive path-traversal, implication cycle / self-edge / depth-bound, user / auto tag-removal scope, bulk-delete cascade, vacuum handler, image-serve path-traversal, EXIF UserComment fixtures. - Docker / Podman image refresh.
- Inbox/archive triage. Newly ingested or uploaded images land in the inbox (
is_inbox=1); the user flips them to archived once curated. Pre-existing libraries upgrade as fully archived (the migration backfillsis_inbox=0so nothing dumps into the inbox view on first boot). Surfaces:inbox:true/inbox:falsesearch keywords, a toolbarInboxtoggle with a live count, a thumbnail grid badge, a detail-page button (ishortcut), and a single Send-to-inbox / Archive batch toggle on the selection bar and the Actions chooser. - Upload page: inbox backlog count rendered next to a
View inboxlink. - Favourite styling switched from the yellow ★ glyph to a red heart (♥/♡) across the gallery grid overlay, the toolbar
Favouritesfilter button, and the detail-page action row. - Per-image external metadata: new
images.source(free-form provenance label, e.g. site name) andimages.url(canonical web URL) columns, edited from the detail page via small[edit]pop-ins. The URL field is rendered as anhttp(s)-only target=_blank link. - Search keyword
source:my_labelfor exact-match against the newimages.sourcefield, ridingidx_images_source. - Keyboard shortcut overhault:
- Keyboard shortcut overlay: press
?anywhere to display the cheat-sheet. - Chord-based navigation:
g g,g u,g c,g t,g s,g hjump to gallery / upload / categories / tags / settings / help. - Vim aliases:
h j k lfor the gallery grid cursor,[and]for prev/next page,g g/Gfor first/last page,pfor the page-jump dialog. Detail-pageh l j kalias the prev/next-image arrows. - Gallery view-level shortcuts:
F/Itoggle the favorites / inbox filters,Rrandom-sorts,Ocycles sort,Dflips direction,Sopens save-search directly,Home/Endjump to the first / last visible thumbnail. - Selection-bar shortcuts:
mmoves,itoggles inbox/archive,Deletedeletes (with confirm), in addition to the existinga/r/t. - Detail page shortcuts:
mopens the move dialog,oopens the original in a new tab,Backspacereturns to the gallery. - Tags page shortcuts:
nopens the create-tag dialog,Nopens the create-alias dialog, page navigation parity with the gallery. - Categories page:
nfocuses the Add category form. Settings page:1-6jump to the section headings. - Anywhere:
Yclicks the topbar Sync button,,/.walk the SFW ceiling,\opens the gallery-switch dialog when more than one gallery is configured.
- Keyboard shortcut overlay: press
- Settings → Stats section: per-gallery DB size and a memory containment tree.
- Footer: server-side render time displayed.
- Detail-page metadata row labelled
Source(showing the ingest origin) renamed toAdded via, freeing theSourcerow for the new operator-edited label. - Sidebar heading
Sourcerenamed toAI sourceto disambiguate from the new free-form source field. - Search keyword
source:renamed toai:for the AI-generation-tool filter family. The omnibus valuesource:aibecomesai:any. The other values (a1111,comfyui,none,sd) are unchanged. Saved searches and bookmarks must be updated by hand; there is no auto-rewrite. - REST API field
sourceonPOST /api/v1/imagesandPOST /api/v1/images/{id}/tagsrenamed toviato match the per-imageAdded viasemantics. - Tags-page row button
Merge→relabelledAlias→(and its dialog prompt toAlias <name> → to:with submit textAlias). - Detail page: implied tags and the alias section under the image fold into one collapsed
<details>(closed by default). Summary text isImplied tags,Aliases, orImplied tags and aliasesdepending on what is present. Alias chips drop the merge-arrow + canonical span. - Manual rating-tag adds now overwrite any existing rating on the image; the auto-tagger continues to keep the highest-rank rating it predicted.
GET /api/v1/tagsdefaults toshow_zero=1to match the /tags page; passshow_zero=0to hide declared-but-unused tags.- /tags page row controls: Rename / Alias / Delete are hidden on the four canonical rating rows (they're reserved); the Category cell on rating rows and on alias rows is rendered read-only. Delete on a rating row still works as a usage-strip.
- /tags page: filter and pagination state preserved across rename, alias create, and alias/repoint merge so the operator stays on the same view after a write.
- /tags page: past-the-end page numbers clamp to the last populated page instead of returning empty.
- /tags page: tokens that match a category prefix only (
character:) surface the category-only filter instead of being silently dropped. - Detail page: prev/next layout no longer jumps when one neighbour is missing.
- Detail page: tag-focus mode survives the post-delete htmx swap; the cursor re-anchors to the next tag in the row.
- Detail page: partial-duplicate tag adds surface the duplicate token instead of silently dropping it.
- Gallery / detail: download, favourite, inbox, and external buttons share the same height across the action row; the favourite (♥/♡) glyph swap no longer reflows the row.
- Gallery: thumbnail badges share identical pixel dimensions (square, centred) so a card lines up with its peers.
- Search: gallery adjacency cache is invalidated on every membership write (tag add/remove, batch ops, alias merges) and on every
InvalidateCachescall, so navigation no longer surfaces stale neighbours. - Search: rating-ceiling chain is dropped from the loose-bound when the user expression has no upper bound, so cookie-applied SFW chains stop adding redundant work; ceiling+search COUNT defers to the slow exact COUNT instead of an inaccurate fast estimate.
- Search:
RankInQueryfires on random sort under the existing gates (was over-conservative) and the cold-path worst case is bounded so a popular-tag detail page no longer walks an unbounded carrier set. - Detail back link: page number aligns with the current image when the adjacency cache is warm.
- Scheduler: orphan-thumbnail sweep takes a job slot and observes context cancellation.
- API:
addImageTags/removeImageTagspre-check image existence and return 404 instead of a write error;POST /tagger/{name}validates the tagger name from the URL against an allowlist; OpenAPI spec updated for thesource/via/urlrename.
- BREAKING: search keyword
source:(the AI filter form) is gone; useai:instead.source:now means exact-match against the newimages.sourcefield. - BREAKING: REST API field
sourceis removed entirely from the two endpoints above. Usevia. No deprecation period. - BREAKING: gallery
h/lpage-navigation shortcuts; use[/]instead. The vim letters now alias the grid cursor. - Keyboard shortcuts
PgUp/PgDn(use[/]),c(useg c),g r(useR), and the detail-pagedshortcut are dropped to match the new chord scheme.
- New
idx_images_source(source)and renamedidx_images_source_type(source_type)(the previousidx_images_sourceindex was onsource_type). fastCountSourcehelper renamed tofastCountAI; the slow-pathbuildFilterExprflipscase "source":tocase "ai":and adds a separatesource:exact-match branch.- Search adjacency cache: gallery match-id list cached for adjacency reuse and reused by the gallery handler for multi-page searches; cap raised to 20 000 IDs; populated asynchronously through a singleflight gate.
- Search: covering index on
image_tags(tag_id, image_id); recent-id bound on multi-leg INTERSECT for newest sort (skipped whenorder=asc); multi-leg AND-driver INTERSECT capped at the two least-popular legs; AND-driver skipped in adjacency when the bucket gate fires; adjacency bucket gate skipped when the candidate set is small. - Search:
suggestContextCaptightened to 1 000 to bound the with-context autocomplete query under contention. - DB: SQLite page cache capped at 2 MiB per connection; per-conn cache trimmed and idle write-pool conns closed after a quiet period.
- Tags:
ChunkedDeleteWithTagRecalcextracted and folded into three callers;suggestUsageRankedextracted, replacingsearch.suggestByUsage;mergeTagsPostroutes the canonical input throughresolveCanonicalTagso alias / category resolution lines up across the tag-mutation surface. - Maintenance:
prune-orphaned-thumbnailsruns as a background job through the scheduler instead of inline.
- Camie v2 auto-tagger as a third catalog entry (
camie-v2). 789 MB ONNX, 70k+ tags across 7 categories. It's currently the only supported model that natively predicts artist (~7k tags) and copyright (~5k tags). - Per-gallery enabling for auto-taggers. Each tagger gains an optional
galleries = [...]TOML key; empty / missing means every gallery (the legacy default), a non-empty list restricts the tagger to the named subset. Managed via Settings → Auto-Tagger → Galleries; Per-job entry points (gallery, detail page, upload, scheduler, REST API) all honour the per-gallery filter. - Newly discovered tagger model folders are persisted as enabled instead of staying implicit; the operator can disable from the table afterwards.
- Gallery batch-mode click toggle: while the batch bar is up, a plain left-click on a thumbnail toggles its checkbox.
- Manual and auto-tagger rating-tag adds prune lower-rank rating rows from the same image inside the write transaction. Pre-existing multi-rating rows from older data are left alone; only writes that fire the rule clean up.
- Auto-tagger thresholds are now per-category: each tagger has a global threshold plus an optional
category_thresholdsmap. Catalog rows ship with sensible defaults (wd-swinv2 carriescharacter = 0.85; joytag stays single-knob since its profile only emitsgeneral). Edit per-tagger via Settings → Auto-Tagger → Configure; - Settings → Auto-Tagger table redesigned: Downloaded and Enabled fold into a single Status column; the Confidence number-input is replaced by a Configure button + inline summary; a new Galleries column follows the same pattern; column widths pinned so rows align across taggers.
- Catalog default thresholds rebased according to models suggestions.
- Detail-page X/Y position counter. The counter sat next to the back link and required a SUM/COUNT walk over the visible set on every detail-page load whenever the back-search carried no tag-shaped predicate to short-circuit on, affecting performance on large galleries. Adjacent prev/next navigation is unaffected.
- Auto-tagger inference is profile-driven. Lifted the WD14-vs-JoyTag bool to a Profile struct capturing input size, layout, channels, normalize, pad, activation, label format, category scheme, and output index. Profiles resolve through embedded defaults at
internal/tagger/profile_default/<name>.json, an optional<modelPath>/<name>/tagger.jsonsidecar, then a heuristic from the label-file extension. Profile fingerprint joins the cache reuse signature so a sidecar edit invalidates the session set. - Auto-tagger label loaders gained a third format:
camie_jsonparses Camie'sdataset_info.tag_mapping.idx_to_tag+tag_to_categoryshape, populatingtagLabel.categoryNamefor thename_stringcategory-resolution path. - Search: AND-driver materialises wildcard canonicals, so
blue*at root substitutes a literal IN(...) instead of a per-row LIST SUBQUERY scan; popular AND-of-tags chains (every leaf above the rare-tag threshold) intersect offidx_image_tags_tagrather than nesting EXISTS over a full visible scan; a single-leaf AND-driver is now allowed under random sort. - Search: rating-ceiling chain peels off
AndExpr{userExpr, chain}so the count fast path applies to rating-filtered user searches, with a sum-of-usage upper bound on the chain leg; fast counts added forrating:,folder:recursive,source:csv,tagged:/autotagged:, andgenerated:filters;idx_images_ingested_visiblepinned for thetagged:/autotagged:data path. - Search: id-bucket gate for newest-sort sparse multi-AND prev/next adjacency.
- Tag implications: declare
parent → childso adding the parent stamps the child on every image carrying the parent, and removing the parent walks the transitive closure to strip children that were only justified by it. Declaring or deleting an implication runs a chunked background propagation job. - /tags page rework: create-tag button and dialog; ascending/descending order filter; direct alias CRUD with category change; zero-usage filter (
Show/Only/Hide) for declared-but-unused triage; danger button to delete every tag in the current search. - Built-in
ratingtag category with canonical tagsgeneral,sensitive,questionable,explicit. The category is reserved, locked (no rename / merge / delete / move-category), and accepts only the four canonical names. Search supportsrating:explicitwith highest-wins semantics: an image carrying multiple ratings matches only the highest. The detail-page sidebar lifts the rating group to the top. - Footer rating-ceiling selector. The footer cluster
rating: sfw . sensitive . questionable . explicitposts to/internal/rating-ceilingand writes themonbooru_rating_ceilingcookie; gallery, sidebar, related-images, and detail-page adjacency then hide images carrying any rating above the ceiling. The first labelsfwis a display alias forgeneral. Folder tree, source counts, and the cached visible-count deliberately ignore the ceiling. - Built-in tag categories
medium,person, andyear, so Hydrus-stylemedium:digital,person:alice,year:2026tokens round-trip into typed categories instead of landing as literalgeneraltokens. Pre-existing user-created custom categories with these names are promoted to built-in on the next bootstrap. Hydruscreator:is rewritten toartist:, andseries:/studio:tocopyright:, on sidecar import. - Operator-supplied
custom.cssoverride loaded after the built-in stylesheet, so a self-hoster can tweak the look without forking the binary. - Gallery Actions chooser collapses Tag all / Remove tags / Auto-tag / Move / Delete behind one button, with arrow-key focus cycling, sub-dialogs (Remove tags strip, Auto-tag), and Escape / Cancel popping back to the chooser.
- Batch operations on the current search: auto-tag all, and move all to a folder.
- Gallery batch-selection keyboard shortcuts: Space toggles,
aadds tags,rremoves tags,topens auto-tag,Ctrl+Aselects all,Escclears. - Detail page: tag-focus mode (
renters, arrows cycle on-image tags, Enter removes); a dim Aliases group lists aliases pointing at on-image tags;topens the per-image Auto-tag dialog. - Search:
system:cheat-sheet autocomplete covers filter keywords and category drill-ins, with a description column on cheat-sheet rows.
- Auto-tagger: WD14 rating labels (
general,sensitive,questionable,explicit, plus therating:*variants) now route to theratingcategory instead ofmeta. Pre-existing libraries withmeta:general/meta:sensitive/etc. tags are not migrated automatically; re-run the tagger or move tags manually to switch them over. - Zero-usage tags are no longer auto-pruned. Use the /tags Zero-usage filter for manual cleanup.
- Delete on a rating tag strips its usage from every image but keeps the row, since the four canonical ratings are reserved.
- Tag implications integrity:
MergeTagsrepointstag_implicationsto the canonical and promotes the canonical row when the alias was user-owned but the canonical was implied;DeleteTagsweeps the implied closure on every carrier image before dropping;CreateAliasrepointstag_implicationswhen upgrading or repointing an existing row. - Watcher: marking a file missing decrements
usage_countof every tag the image carried and prunes any that hit zero, in the same write transaction as the is_missing flip. Tag counts no longer drift between watcher events and the next manual recompute. Alias paths are ignored in the mark-missing fallback, and the alias is promoted to canonical when the canonical's old path is gone. - Search:
date:filter acceptsYYYYandYYYY-MMand is now validated; bare wildcard tokens become no-match instead of a full scan; random-sort seed is clamped to 31 bits. - Saved searches: duplicate names are rejected.
- Import: type-to-confirm is validated before the upload is buffered; imported DB is bootstrapped before sanitisers run; per-entry decompressed size on zip archives is capped; light merge skips the wipe when the archive carries no files.
- Config: non-bcrypt
password_hashis rejected on startup; non-positiveSessionLifetimeDaysis clamped to the default;MaxFileSizeMB <= 0is treated as no per-file cap. - Auth: same-origin Referer check rejects non-http(s) schemes.
- API: trailing slash on
base_urlis trimmed before the CORS check. - Footer: cached counts are invalidated on tag and saved-search mutations.
- Daily schedule: the Recompute tag counts and Vacuum database toggles. Vacuum stays available as a manual button under Settings → Maintenance. Existing
recompute_tags/vacuum_dbkeys inmonbooru.tomlare ignored on load and dropped on the next save. - Settings → Tag actions: Run untagged / Run all (rolled into the gallery Actions chooser).
MONBOORU_UI_PAGE_SIZEenv override.
- Search: multi-AND drives off the smallest tag's
image_tagsrows;rating:filter short-circuits when no image carries the level; suggest candidate cap halved to lower with-context contention; partial index for alias rows oncanonical_tag_id. - Tags:
MergeTagsfolded into three set-based statements;DeleteTagbulk-deletes instead of per-image looping. - Auto-tagger: per-model label dispatch tables embedded in the binary.
- Single source of truth for filter-keyword vocabulary across search and autocomplete.
- Auto-tagger: ORT environment and per-tagger sessions are cached across runs so back-to-back jobs no longer leak ~440 MB of glibc-arena memory each. The cache is torn down after
tagger.idle_release_after_minutes(default 30) of inactivity, on Settings save, and onuse_cudaflips. - Auto-tagger: completion summary now reports
auto-tagged X of N image(s), K skippedwhen images are skipped (missing rows, no extractable frames, store failures), instead of always reporting the submit count.
- Search: prev/next adjacency on the detail page uses a row-value cursor and pins the partial sort index, dropping cursor lookups from seconds to milliseconds on large libraries; random-sort + tag-predicate adjacency bounds the outer scan to a 2000-id bucket around the current image; random-sort wildcard searches materialize the EXISTS body as an IN-list instead of re-evaluating per row.
- Search: detail-page X/Y counter is skipped for tag-predicate and folder-filtered searches; cursor-based prev/next still works and the template hides the counter when the total is zero.
- Memory: read connections run
PRAGMA shrink_memoryandruntime/debug.FreeOSMemory()every 5 minutes when the job manager is idle, so RSS plateaus instead of holding the post-burst peak.
- Search: fast-path counts for pure-tag, wildcard, NOT, AND, OR, and category-qualified queries; pinned partial sort index for the unfiltered-newest path; fast-path approximations gated on a per-query usage threshold.
- Search: autocomplete candidate and context caps tightened; context CTE capped at 50k rows.
- Tags:
RelatedImagesdrops popular tags from the seed set and uses a smaller candidate buffer; tag-count recalc chunks bytag_idrange to release the SQLite writer.
- Tag names accept
?,>,<,=,^. - Enabled auto-taggers whose model files have gone missing are auto-disabled at startup instead of failing silently at inference time.
- Settings → Auto-Tagger: per-tagger download instructions.
- Detail page: "Remove all tags" confirms before running.
- Auto-tag picker on the detail and upload pages is a radio list rather than a dropdown.
- Schedule defaults:
run_auto_taggersisfalseso freshly-installed instances don't auto-run taggers on every cycle; - Gallery import dialog defaults to Merge.
- Settings → General reshuffled into side-by-side rows: Files / UI, GPU / Parallel workers, Password / API token; Schedule checks render as a 2x3 grid; tags filters are clickable buttons rather than dropdowns; the tags-page filter submit is inline with a shorter label; auto-tagger actions are grouped under "Tag actions"; copy in Tag actions and Auto-Tagger sections is tightened.
- Galleries table: Info column renamed to Content and the saved-count dropped; per-action columns; actions right-aligned; Add gallery moved into a dialog opened by a button below the table.
- Auto-Tagger table: Disable column renamed to Enable; Downloaded moved left of Enabled; Status column width reserved so active and default badges stay side by side; tagger name column widened, status columns shrunk; bulk auto-tagging columns right-aligned and compacted.
- Detail page: columns stack on narrow viewports; "Tag all" is hidden while thumbnails are selected; a separator sits between Tag selected and Move selected.
- Upload UI: drop zone enlarged with bottom fields split into two columns; drop-zone prompt centered; reset button subdued.
- Built-in category Action cells expose a labelled Delete button instead of a generic icon.
- Dialog placeholders and microcopy refreshed across the UI; the batch-tag dialog placeholder spells out Add/remove.
- Auto-Tagger: emoticon-only labels (
:3,:o-style) survivesanitizeLabelinstead of being dropped;_unsupported_<idx>placeholder labels are filtered at inference time. - Recalc and merge-general-tags invalidate the affected caches so stale counts don't survive a tag rewrite.
- Search-suggest hover is suppressed until the mouse moves, so the dropdown no longer lights up under a stationary cursor.
- Upload action buttons stay fixed when the auto-tag picker reveals, instead of shifting.
- Add-gallery dialog fields stack on their own lines so long paths don't overflow.
- Settings action headers align with the buttons in their column; settings tables wrap in a scroll container so they stay readable on narrow viewports.
- Performance: per-gallery distinct-tagger and tag/saved-count caches;
SuggestTagsWithFiltermaterialises its filter context once and caps candidate tags before combo counting; partial index pinned for the unfiltered-newest sort; partial index onis_missing=0; index onimage_tags(tagger_name)foris_auto=1distinct scans; lazy sidebar URLs carry page IDs; theDiscoverTaggerswalk is shared between settings and bulk-tagger rows.
- Import hydrus network and bloombooru exports directly from the gallery import dialog. (images and tags)
- Add gallery form accepts an optional
.db/.json/.zipupload; the new gallery is created and the file imported into it in the same step. Add and merge import now switch the active gallery automatically. - Settings → Auto-Tagger lists a catalog of suggested taggers (WD14 SwinV2, JoyTag) with copy-paste install commands relative to the host working directory;
<model_path>/models.jsonoverrides extend the catalog. Disabled tagger rows expose a Delete button. - Batch tag add/remove from search and from selection: a Tag all button next to Delete all in the gallery header and a Tag selected button in the batch bar share a dialog with autocomplete and an Add/Remove radio.
- Bulk tag removal moved to its own Settings → Tags section so it stays reachable when no on-disk tagger is configured.
- Settings → General is one form with a single Save.
- Styled 404 page with a back link instead of bare text.
- Built-in category Action cells are labelled
(built-in).
- Detail image floored at 200 px so tiny files no longer render as a dot.
- Upload list shows
<1 KiBfor sub-1 KB files instead of0 KiB. - The
?thumbnail fallback explains via a hover title why the thumbnail is unavailable. - Gallery status reads "N matches" instead of "match N".
- Download button uses
⤓to disambiguate from the sort-direction↓. - Gallery batch action bar reordered: Tag selected sits left of Delete selected; Move/Delete are right-aligned and separated from Tag selected.
- Add gallery form is single-row with equal-height controls, drops the verbose hint, shows a busy state on submit, and renders a success flash before the page reload. The gallery import dialog drops its redundant hint too.
- Search/UI: search input blurs on submit and the suggest dropdown is dismissed; stale suggest swaps no longer race the new query; long tag chip names ellipsize on the detail page; the detail breadcrumb back-link stays on one line when the filename is long; the gallery toolbar (Save / Tag all / Delete all) tracks the current search via OOB swap.
- Search: LIKE metacharacters in tag prefix/substring and folder filters are escaped; malformed queries return 400 instead of being silently parsed away.
- API:
tag_warningsare returned fromaddImageTagsand surfaced in the upload flash; compat-imported tags are credited to the originating provider instead of alwaysimport. - Auth/audit: settings audit logs record the
clientIPso reverse-proxy IPs are captured; login backoff is capped at 30 s as the spec documents. - Web:
syncTriggerredirects only to a same-origin Referer; export warns when a gallery file is skipped because its path is unreadable. - Tags/gallery: errors propagate from
DeleteCategoryMoveOrDelete's delete-all path,Sync's scan loop, and adjacent-image lookups instead of being dropped; orphaned-thumbnail prune bails on a partial id scan rather than deleting valid thumbnails. - Config:
ui.page_sizeis clamped to its default when a non-positive value is configured.
tools/blombooru-export.py: superseded by the in-app blombooru import.
- Compat layer split into
internal/web/compatibility/with per-app translators (blombooru, hydrus) self-registering against a package-level provider table. Adding a third format is a new file with one Register call. - Performance: cache the general-category id on the gallery context so
parseTagInputskips a SELECT; narrowRelatedImagesto the only column the template uses; scopeRecalcAndPruneto touched tags in bulk removals; add partial indexes ongeneration_hashfor both metadata tables. - Docs: new migration guide (
docs/MIGRATING.md)
- Per-gallery import/export controls in Settings → Galleries. Imports can replace current gallery or merge with it. You can export/import only the database (as full .db or .json or as a minimal .json) or the database+images as .zip.
- Tag aliases: adding or searching a tag under any of its alias names resolves to the canonical tag. Auto-tagger output goes through the same alias resolution before it lands on a row.
- Gallery delete/replace dialog requires typing the gallery name before it confirms.
- Rebuild-thumbs is auto-queued after a gallery import.
- New
tools/blombooru-to-light.pyexporter that converts a blombooru install into a Monbooru light archive ready for the import flow.
- Gallery thumbnail focus outline thickened so keyboard focus is easier to spot.
- Aliases now live in the main tags table instead of a separate one, so they share the tag pipeline end to end.
- Gallery: scan errors from Ingest's duplicate-path lookup surface to the caller instead of being swallowed. Re-extract per-image work runs in a transaction.
- Web: per-file size cap is enforced on uploads. Archive entries are checked for path containment with
filepath.Rel, and the watcher uses the same approach for gallery-path containment. Removing a gallery folder refuses to follow a symlink. - Search/UI: the favorites filter button stays active across composed queries. The login-rate-limiter shift is clamped to a non-negative range.
warmCachesnil-deref race and deadHX-Refreshflashes resolved. - Tags: errors from
RecalcAndPruneIDspropagate and are logged at callers.folderonly,tagged, andautotaggedare reserved as category names. Remainingrows.Scanand mark-missing loops surface their errors. - Gallery video probe: ffmpeg invocations terminate option parsing with
--before output paths so filenames with leading dashes can't be parsed as flags. - Filter-keyword set is hoisted to a single canonical source shared between search and web.
- Docker Compose example image path on GitHub points at the right repository.
- Cleanup pass on stale comments and dead notes; readme updates; assets recompressed.
- Status bar row under the gallery header and on the detail topbar.
- Tmux-style footer with gallery / tags / saved-search counts;
- Images: per-image
originrecording how the file entered the gallery (ingestfor watcher/sync pickups,uploadfor web and multipart-API uploads, or a caller-supplied string via a newsourcefield onPOST /api/v1/images). Surfaced on the detail metadata panel and in the APIImageresponse. - API:
POST /api/v1/imagesandPOST /images/{id}/tagsaccept asourcefield for manual tags. The detail page splits the user section into "Tags added by the user" plus one bucket per third-party source. - Detail page action row: Move image and Delete are grouped together on the right of the Danger zone.
- Some UI style changes
- Destructive buttons (delete, delete-all, delete-selected, remove-all) now render as solid red.
- Settings → Run Auto-Tagger: bulk run buttons relabeled to "Auto-tag all untagged images" / "Auto-tag all images"; the three Remove-all buttons move to a new "Bulk tag removal" subsection so destructive actions live apart from autotag-triggering ones.
- Detail page tag chips drop the "auto" badge; chip names are colored by category instead.
- Deleting an image reached through a Similar-images chain walks browser history one hop back (so Escape on
A → B → Creturns toB, thenA) instead of pushing a fresh history entry and dropping the chain. Deleting the chain's source falls back to the referring search, then the gallery. - Search and sort state is dropped when switching galleries, so the new gallery opens on a clean view instead of inheriting an irrelevant query.
- Detail-page panels and header align with the gallery frame.
- Detail-page tag chip names render in their category color.
- Excessive margin on the topbar sync button.
- Detail page: gallery-style search bar at the top of the page; submits as a plain GET
/so the next view is a full gallery render with the chosen query, sort, and order. The input autocompletes against tags the same way the gallery input does. - Detail page: folder, source, and saved-search sections appear in the sidebar below the image's tag groups, lazy-loaded so the image tags paint first.
- Detail page: position/total counter (e.g.
34/243) renders between the back link and the filename when the page was opened from a search, computed from the same key-column comparison as the prev/next arrows. - Detail page: deleting an image moves to the next image in the referring search (falling back to prev, then the gallery) instead of bouncing back to the grid.
- Detail page: videos autoplay muted with
playsinline, and spacebar toggles play/pause (suppressed while typing in tag/search inputs). - Similar-image navigation: clicking a related image carries a back-ref so Escape (and the "← Previous image" link) unwinds chains of any depth one hop at a time via the browser history. The gallery-context UI (X/Y counter, prev/next arrows, "← Images" back link) is hidden once you've switched images, since the current image isn't necessarily in the referring result set.
- Keyboard:
sfocuses whichever#search-inputis on-screen on any page;tkeeps focusing the tag input on the detail page, andfkeeps toggling favorite.
- Related-images probe caps only the
generalbucket to the 15 rarest tags. Previously capping every non-meta category could flatten character/artist/copyright signal to the same 15-slot budget as the noisy general bucket; now those categories pass through uncapped while1girl-style tags no longer drag tens of thousands of rows into the candidateGROUP BY. - Per-gallery sidebar caches (folder tree, source counts, visible count) pre-warm at startup instead of populating lazily in parallel on the first cold render.
- Sidebar searches skip the count pass; it was a second full filter evaluation for a number the handler never surfaced. A new partial index on
file_size(visible rows only) turns sort-by-size over large libraries into an index lookup. - Detail-page header controls (input, buttons, selects) share a single 28 px height and consistent padding, so buttons no longer render taller than the selects next to them.
- Tag names containing
:(like:3) round-trip cleanly through the search parser, the auto-tagger, and the category-qualified API DELETE endpoint, without colliding with thecategory:tagsyntax. - Detail page: filename next to the back link and in the topbar/title; double disclosure marker on the metadata panel dropped; ComfyUI refs scroll to the referenced node; invalid-tag error clears while typing; search autocomplete no longer rewrites the URL.
- Dialogs: move-image dialog shows the current folder; move/delete-selected dialogs pluralize correctly; "1 image" no longer renders as "1 images"; merge-dialog autocomplete anchors below the input.
- Maintenance: destructive and long-running actions confirm before running and use action-named OK buttons.
- Settings: Schedule section shows last/next run; the two General Save buttons are disambiguated; gallery status renders as two distinct badges; login form is disabled when password auth is off.
- Gallery: upload and delete-all are gated on a degraded gallery; gallery add rejects unreadable and absolute folder paths; page-jump dialog clamps out-of-range entries; the toolbar wraps on narrow viewports; the top-nav stays reachable on narrow viewports; sync-missing now labels images "missing" instead of "removed".
- Sidebar: source-filter tree shows per-source counts; the
[·]shortcut forfolderonly:PATHis now visible at the same size as the folder name instead of a hover-only middle dot. - Watcher now watches every configured gallery, not just the active one.
- Auto-tagger: empty subfolders are hidden; unavailable rows are marked n/a; the detail-page Auto-tag button is hidden in the noop build with the real reason surfaced.
- API:
/api/v1/docsshows a banner when the API is disabled and gets a Back link at the top; category-qualified DELETE falls through to a literal match when the qualified lookup misses. - Web: missing
_hover.webpthumbnails return 204; random sort is visible in the gallery sort dropdown; Save-search and Delete-all hide when there's no query or empty result set; job-flash auto-dismiss shortens once a client has seen it.
- Move images into another folder from the UI: Move image in the detail page's action row, Move selected in the gallery's batch bar. Folder input autocompletes against existing folders; missing folders are created, filename collisions are auto-suffixed, and empty source folders are cleaned up after the move.
- Sidebar folder tree: each node's count now includes images in every descendant folder, so a parent with only subfolder content shows a non-zero figure.
folder:PATHin the search bar is recursive to match.folder:with no value is now a recursive root (matches every non-missing image); use the newfolderonly:for the old root-only match. - Each folder row in the sidebar (including
/) now has a·shortcut that runsfolderonly:PATHto show only the images directly in that folder, without the rolled-up subfolder content.
- Sidebar folder tree: deeply nested folders no longer slide off the right edge of the sidebar. Indent is now a fixed 12 px per level instead of the quadratic
depth × 12that accumulated through nested<li>padding boxes.
- Multi-gallery support. Galleries are named directories with their own SQLite DB and thumbnails under
<paths.data_path>/<name>/. Create, rename, and delete galleries from Settings → Galleries; switch at runtime with the topbar button. The REST API targets a specific gallery via?gallery=<name>or theX-Monbooru-Galleryheader; unknown names return 400. - Daily maintenance schedule: pick a time of day to run sync, auto-tag, recompute counts, merge general tags, and vacuum. All actions enabled by default; toggle per-action in Settings.
- New search filters
tagged:true/falseandautotagged:true/falseto scope results by tagging state. - Sync, delete, and re-extract jobs are now cancellable from the status bar, matching the existing auto-tag cancel behavior.
- Sync reconcile reports live progress and the gallery grid refreshes mid-run; delete and re-extract jobs also refresh the grid in-flight, so ingested or removed images appear as jobs run.
- Auto-tag groups on the image detail page are ordered by confidence and show a percentage next to each tag.
- Gallery configuration no longer stores
db_pathorthumbnails_path. Each gallery lives under<paths.data_path>/<name>/monbooru.db+/thumbnails/, created on demand.active_galleryis renamed todefault_galleryand only controls the startup pick; the topbar switcher changes the runtime active gallery without persisting. Legacy[paths]migration is removed - existingmonbooru.tomlfiles must be rewritten as[[galleries]]entries on a fresh config. - Settings → Auto-Tagger section now sits above Authentication.
- "Delete all" is hidden while a batch-delete selection is in progress, to avoid two conflicting destructive buttons.
- Sync on large libraries: duplicate hashing and per-file
chownare skipped; alias lookups and missing-row updates are batched, so idle syncs on 50k+ libraries finish in seconds rather than minutes. - Gallery page caches the unfiltered visible count and the per-gallery folder tree, cutting redundant SQL scans on every render.
- Search: chained
ORterms parse correctly (the tail of a 3+ term chain was being dropped). Non-numericwidth:/height:filters are rejected with a clear error.-missing:falsereturns missing images. Autocomplete drops tags already in the query; the suggest swap target is pinned withHX-Retargetso results render in the correct place. - Errors from form parsing, cursor iteration, folder deletion, config save, tagger result storage, and tag prune now surface to the user instead of being silently dropped.
- Tags: add-tag check is atomic with the insert (no read/write-pool race).
MergeTagscounts non-missing rows only. Typed filter input survives a category change on the tags page. - Tagger: label filenames are sanitised before they become tag rows. Discovered taggers are preserved across a settings save.
- Jobs: scheduled maintenance holds a schedule reservation while running so a manual action cannot start mid-flight. Rebuild-thumbs honors cancellation. Sync respects cancellation during mark-missing and tag recalc. Job-status auto-clear re-arms on every surface event. Watcher ingests surface while a long-running job is in flight. Scheduler cancellation reports a clean summary instead of "context canceled".
- Gallery: out-of-range page numbers clamp to the last valid page. Switching gallery from the image detail page redirects to home. Per-gallery thumbnail URLs render correctly after a switch; switch errors surface in a flash. Sidebar and tag-list search links carry the active category prefix.
- Web: WAL is truncated after vacuum and the total database footprint is reported in the flash. Upload and categories pages receive the gallery switcher data. Settings → Galleries shows the API suffix instead of the full URL.
DeleteImageno longer runs an unscoped tag prune. - Sync progress bar no longer double-counts the reconcile phase.
MONBOORU_PATHS_GALLERY,MONBOORU_PATHS_DB, andMONBOORU_PATHS_THUMBNAILSenv overrides. Replaced byMONBOORU_PATHS_DATA_PATH.
Initial release.