Skip to content

Tag editor: suggest existing names as you type - #1008

Merged
ajslater merged 1 commit into
developfrom
claude/polymorphic-wondering-allen-royhj4
Nov 7, 2026
Merged

ajslater merged 1 commit into
developfrom
claude/polymorphic-wondering-allen-royhj4

Conversation

@ajslater

@ajslater ajslater commented Nov 7, 2026

Copy link
Copy Markdown
Owner

Summary

The admin tag editor's Publisher, Imprint, Series and Volume inputs only offered the opened group's own values. The tag rows, Credits, Story Arcs and Universe inputs offered nothing. The importer matches names exactly, so a typo while correcting a name quietly creates a new row. Every named-tag input now suggests existing library names as you type, and nothing loads when the editor opens.

Backend

  • New GET /api/v4/admin/tag-names/<field>?q=… (codex/views/admin/tagnames.py), admin only.
    • It returns at most 100 names containing q (case-insensitive), prefix matches first, plus a complete flag. It fetches LIMIT+1 rows, so it needs no COUNT.
    • Fields come from an allowlist (publisher, imprint, series, volume, genres, characters, teams, locations, series_groups, stories, tags, universes). credits maps to CreditPerson and story_arcs to StoryArc, using the UI constants in codex/views/settings.py. Anything else returns 404.
    • Imprint narrows to the form's publisher, Series to publisher + imprint, and Volume to publisher + imprint + series. Blank values are ignored.
    • Names are DISTINCT because Imprint, Series and Volume names repeat across parents. Volume names stay integers and sort numerically. Null names are excluded, and so are blank names on text columns.
  • AdminTagNamesInputSerializer / AdminTagNamesSerializer in codex/serializers/admin/tagging.py. Query params are capped at MAX_NAME_LEN.

Frontend

  • getTagNames in src/api/v4/admin.js.
  • The metadata store caches results per field, then scope, then normalized query.
    • If a cached result for a shorter prefix is complete, it answers longer queries without a request, and Vuetify's filter narrows it.
    • An incomplete prefix stands in while the exact query's request is in flight.
    • Each field keeps one request in flight; a newer one aborts the stale one.
    • The cache clears with clearMetadata().
  • New TagNameCombobox wrapper. Attributes, v-model, listeners and slots pass through to the inner combobox.
    • It loads when the menu opens, then on typing with a 250 ms debounce. The fetch only runs while the menu is open, because Vuetify also emits update:search on model assignment, on a pick and after each chip add.
    • It sets hide-no-data to false so an empty list can still open the menu, and shows a no-data text.
    • It keeps Vuetify's local filter, with filtered-order="prefix".
  • edit-panel.vue uses it for publisher, imprint, series, volume, per-role credits, the enabled tag rows, story arcs and universe name. The group's own values come first in each list.

Test plan

  • tests/test_admin_tag_names.py (16 tests): non-staff 403, unknown field 404, prefix-first ordering, %/_ matched literally, blank imprint excluded, series deduped across imprints, imprint and series scoping, volume scoped by series and sorted as numbers, null volume excluded, complete false over a patched limit, credits/story arcs models, over-long q 400.
  • frontend/tests/unit/metadata-store-tag-names.test.js (9) and tag-name-combobox.test.js (10), including one that types into the real input to check the menu opens before update:search.
  • Existing edit-panel-*.test.js: 113 pass.
  • Full Python suite: 1767 passed, 2 skipped, 1 xfailed. Full vitest: 107 files, 1089 tests pass.
  • bin/lint-python.sh (ruff, basedpyright, vulture, complexipy/radon, codespell); ESLint and Prettier on the changed frontend files. ty check . reports only the two renamed-rule-in-ignore-comment warnings already on develop.
  • Browser check in the dev stack (2 imported comics plus seeded names):
    • no tag-names requests when the editor opens;
    • Publisher menu lists the group's publisher, then the others, from one publisher?q= request;
    • typing da narrows to "Dark Horse" with no new request;
    • picking DC Comics enables Save;
    • Imprint asks imprint?publisher=DC Comics and lists only Black Label and Vertigo;
    • Characters suggests Bat-Mite, Batgirl and Batman for bat, and still takes "Zatanna" as a new chip on Enter;
    • the Publisher clear icon still toggles the cleared state.

Generated by Claude Code

The tag editor's publisher, imprint, series, volume, credit, story arc,
universe and tag inputs only offered the opened group's own values, so
correcting a name meant retyping the library's spelling exactly; a typo
silently created a new row.

New admin endpoint GET /api/v4/admin/tag-names/<field>?q= returns at most
100 existing names containing q, prefix matches first, with a `complete`
flag. Imprint, series and volume narrow to the form's parent names.

A TagNameCombobox wrapper fetches only when its menu opens or the user
types with it open (debounced, aborting the stale request). Results cache
per field, scope and query in the metadata store until the dialog closes,
and a complete result for a shorter prefix answers longer queries without
another request. Free text is still accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UgiA5Rda3grk8WEu1YhY4S
@ajslater
ajslater marked this pull request as ready for review November 7, 2026 02:05
@ajslater
ajslater merged commit b2e0c02 into develop Nov 7, 2026
9 checks passed
ajslater pushed a commit that referenced this pull request Nov 7, 2026
Its feature commit landed in develop as b2e0c02; this keeps the tree
unchanged so the branch can be pushed without rewriting history.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UgiA5Rda3grk8WEu1YhY4S
@ajslater ajslater mentioned this pull request Nov 7, 2026
3 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants