Commit Graph

22 Commits

Author SHA1 Message Date
bvandeusen 5b7e033d0c feat(suggestions): auto-accept threshold + retroactive blocklist sweep
Two capabilities that work together to turn the suggestion system from
a 'review every noisy chip' UX into a 'curated tags only, configurable
auto-apply' UX for a solo-user library.

Auto-accept threshold
  Service (tag_suggestions.py):
    - New default auto_accept_general_threshold = 0.95 in _DEFAULTS
    - get_suggestions splits general-category hits at/above the threshold
      into a separate auto_accept_candidates list; the core 'character'/
      'copyright'/'general' keys stay the same shape for existing callers
    - get_bulk_suggestions ignores the new key (no side effects in bulk)
  Route (main.py GET /image/<id>/suggestions):
    - For each candidate: find-or-create Tag(kind='user', name=name), attach
      to image, log SuggestionFeedback(source='wd14'|etc, decision='accepted')
    - Response adds 'auto_accepted: [{id, name, display_name, kind,
      confidence, source}, ...]' so the modal can review + undo
  Config endpoints (main.py):
    GET  /api/suggestions/config/auto-accept-threshold
    POST /api/suggestions/config/auto-accept-threshold {threshold}
    Values > 1.0 effectively disable the feature
  UI (view-modal.js):
    - New renderAutoAccepted block at top of suggestions section with
      green-tinted chips carrying ✕ (undo for this image, logs rejection)
      and ⊘ (blocklist + remove from all images)
    - loadSuggestions refreshes the tag pill list when auto_accepted has
      items so the user sees them in the Tags section too
  Settings:
    - Maintenance tab gains a number input + save button backed by
      /api/suggestions/config/auto-accept-threshold

Retroactive blocklist sweep
  New Celery task app.tasks.maintenance.sweep_blocklisted_tag_from_images
  on the maintenance queue. Enqueued automatically when a name is added
  via POST /api/suggestions/blocklist or appears new in the bulk replace.
  Task body: finds kind='user' Tag matching the name, deletes its
  image_tags rows, deletes the Tag itself. Scope limited to kind='user'
  so deliberate character/fandom/artist tags sharing a blocklisted name
  aren't silently destroyed.

Verification (local dev):
  - Threshold GET/POST round-trip correct (default 0.95, persisted in
    tag_suggestion_config)
  - Image 633 at threshold=0.9: 4 general tags auto-applied, each with
    a SuggestionFeedback row, returned in auto_accepted
  - Blocklist add of 'sweeptestonly' with an attached test tag: Redis
    maintenance queue depth = 1, task body removes 1 image_tags row +
    the Tag row
  - get_bulk_suggestions still works (auto_accept_candidates key skipped)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 17:23:25 -04:00
bvandeusen a510665a17 feat(suggestions): user-managed blocklist for noisy auto-tags
Adds a tag_suggestion_blocklist table + service-layer filter so the
user can permanently suppress specific canonical tag names from the
suggestion stream (WD14 anatomy/composition tags like '1girl',
'breasts', 'nipples' that aren't useful for the discoverability use
case this app targets).

Data model
  - migration k26042201: tag_suggestion_blocklist(name TEXT PK, created_at)
  - model TagSuggestionBlocklistEntry

Service
  - tag_suggestions._blocklisted_names() snapshots the current set
  - get_suggestions filters merged results before grouping, so both
    WD14- and embedding-sourced suggestions respect the blocklist
  - get_bulk_suggestions inherits the filter via its per-image call
    to get_suggestions

API
  - GET  /api/suggestions/blocklist           -> {ok, names}
  - POST /api/suggestions/blocklist           -> add one
  - POST /api/suggestions/blocklist/delete    -> remove one
  - POST /api/suggestions/blocklist/bulk      -> replace the whole list
    (backs the settings textarea save button)

UI
  - modal suggestion chip gets a third action button (⊘) alongside
    accept (✓) / reject (✕). Clicking it adds the name to the
    blocklist, logs a rejection for ML feedback on this image, and
    sweeps every chip on the page carrying that same name.
  - Settings -> Maintenance -> Suggestion blocklist section with a
    monospaced textarea (one name per line) + Save. Loads current
    entries on mount.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 08:41:44 -04:00
bvandeusen 934d6ce111 chore: remove sync_character_fandoms maintenance task
tag.fandom_id is now authoritative. set_tag_fandom auto-applies the
fandom tag inline; add_tag already did. There's no drift to sync on
a schedule anymore.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 15:54:08 -04:00
bvandeusen 3944605d33 feat(bulk): consensus ML suggestions + frontend polish
Bulk editor now loads consensus ML suggestions across the current
selection via a new POST /api/suggestions/bulk endpoint, powered by
get_bulk_suggestions() in tag_suggestions.py. A tag is surfaced only if
it was suggested for or already applied to >= 80% of the selection;
coverage counts include images that already have the tag so a near-
universal tag isn't penalized for dropping out of suggestion lists.
Accept-only chips (no reject) match the rest of the suggestions UX.

Frontend polish in the same commit:
- Showcase session dedup (exclude seen ids, reset on second lap) and
  aspect-ratio-aware placeholder heights for better column balancing
- Modal touch-swipe navigation on the image wrapper, auto-focus of the
  tag input on desktop (>=768px), and autocomplete flip-up when the
  dropdown would cover the Suggestions section
- Settings template inline styles replaced with utility classes
- Inline series-editor script extracted to gallery-series-editor.js

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 18:39:47 -04:00
bvandeusen 0f35a0c484 feat: ML tag suggestions, character/fandom integrity, underscores, modal polish
Consolidated merge of feat/tag-suggestions branch. Original 64-commit history
was lost to git-object corruption in a Nextcloud-synced checkout; this single
commit captures the equivalent diff.

Includes:
- pgvector-backed tag suggestion infra (WD14 + SigLIP centroids, ml-worker
  container, Celery tasks, suggestion service, accept/reject endpoints + modal
  UI with green/red chip buttons)
- Character/fandom integrity: title-case normalization on every write path,
  fandom-id backfill, maintenance task + settings button, migrations g26041901
  + h26041901 to canonicalize legacy rows with case-only duplicate merging
- Tag-underscores + modal polish: WD14 name canonicalization at emit + accept
  + add/bulk-add paths, migration i26041901 for legacy-row rename-or-merge
  across character/fandom/NULL kinds, suggestion-accept refresh parity via
  awaited loadTags, persistent chip tint
2026-04-19 19:50:58 -04:00
bvandeusen 52d783546e replace client-side tab toggling with server-side tab routing
Each settings tab is now a separate page load via ?tab= query param.
Flask renders only the active panel - no CSS or JS visibility logic needed.
Tab bar uses <a> links instead of buttons. Eliminates the display:none
cascade issue that was preventing Import and Maintenance tabs from showing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 17:06:11 -04:00
bvandeusen e37ce27256 fix settings tabs by using inline style.display instead of CSS class toggling
The class-based is-active/display approach was unreliable due to CSS cascade
issues. Switching to direct style.display manipulation bypasses specificity
entirely. Also added cache-busting ?v=2 to style.css link.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 16:52:45 -04:00
bvandeusen c16d0a38a8 feat: add tabbed layout to settings page with Overview, Import, Maintenance tabs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 12:20:03 -04:00
bvandeusen c95896693f import filters for pixiv sidecars, tag filtering, and favicon generation 2026-02-04 19:08:50 -05:00
Bryan Van Deusen 17403c4e6b fixed some errors with archive handling 2026-02-02 23:36:16 -05:00
Bryan Van Deusen 09883960d4 tuning job log views and deep scan archive processing. 2026-02-02 18:48:01 -05:00
Bryan Van Deusen 9ebeeed133 series management tuning and import archive extraction troubleshooting 2026-01-23 23:05:25 -05:00
Bryan Van Deusen d3e73b9533 changes to series order system. add artist retagging tool 2026-01-23 08:38:07 -05:00
Bryan Van Deusen 041a3dbfb4 fixed metadata time import error and moved quest status widget to top of settings. 2026-01-21 11:30:45 -05:00
Bryan Van Deusen dc45a09681 further transparency tuning 2026-01-21 09:37:29 -05:00
Bryan Van Deusen ede1457abc added dedup tools to settings. first pass of mass tag editting to gallery view. 2026-01-20 23:45:54 -05:00
Bryan Van Deusen cb3897c0b0 changes to mobile styling in modal view, complete reword of backend worker system 2026-01-20 13:14:13 -05:00
Bryan Van Deusen 482c5a8ead import filter work and cleanup 2026-01-18 21:57:08 -05:00
Bryan Van Deusen b3331bad76 additional tag edditing functionality. and delete by tag. added import tuning options to settings 2026-01-18 16:52:32 -05:00
Bryan Van Deusen 63918363c1 improved Modal with zoom and pagination. also implementing thumbnail generation 2025-08-01 23:19:17 -04:00
Bryan Van Deusen e3a4c348f1 major styling pass, artist list, and import processing improvements 2025-07-31 23:48:37 -04:00
Bryan Van Deusen c67f1afc1f initial functionality and styling pass 2025-07-29 00:34:03 -04:00