feat(modal): applied tags drop from search instantly; manual add == accepting a suggestion
CI / lint (push) Successful in 4s
CI / frontend-build (push) Successful in 40s
CI / backend-lint-and-test (push) Successful in 2m50s
CI / integration (push) Successful in 3m53s

Three tag-flow gaps in the view modal (and the Explore workspace, which
shares TagPanel):
- the type-to-add dropdown now filters both its sections against the
  imageledger applied tags reactively, so a just-added tag disappears
  from search the moment the chip rail updates instead of after a
  modal refresh
- manually picking or creating a tag the model also suggested routes
  through the suggestion-accept flow: the acceptance is recorded for
  head training and the row leaves the panel, instead of the add
  silently bypassing the feedback loop
- removing a tag reloads the suggestion lists, so a model-suggested tag
  returns to the suggestions area (flagged rejected, one-click
  reversible) rather than vanishing until the next modal open

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
This commit is contained in:
2026-07-02 22:00:40 -04:00
parent b54243a1ff
commit 6d314d662f
6 changed files with 193 additions and 6 deletions
+3
View File
@@ -176,6 +176,9 @@ export const useExploreStore = defineStore('explore', () => {
}
if (anchor.value?.id !== id) return // walked away mid-create
await addExistingTag(tag.id)
// Returned so TagPanel can match the created tag against a pending
// suggestion and record the acceptance (manual add == accept, 2026-07-03).
return tag
}
// No overlay to dismiss in the Explore workspace — the chip-body "show me
+3
View File
@@ -170,6 +170,9 @@ export const useModalStore = defineStore('modal', () => {
}
if (currentImageId.value !== imageId) return // navigated away
await addExistingTag(tag.id)
// Returned so TagPanel can match the created tag against a pending
// suggestion and record the acceptance (manual add == accept, 2026-07-03).
return tag
}
const isOpen = computed(() => currentImageId.value !== null)
+25 -1
View File
@@ -169,6 +169,29 @@ export const useSuggestionsStore = defineStore('suggestions', () => {
toast({ text: `Alias removed: ${suggestion.display_name}`, type: 'success' })
}
// Find a live (non-rejected) pending suggestion matching a manually-picked
// tag — by canonical id when known, else by (kind, name). Manually adding a
// tag the model also suggested must be indistinguishable from accepting the
// suggestion (recorded + dropped from the panel), so TagPanel routes matches
// through accept() (operator-asked 2026-07-03). Searches the full dropdown
// set first, then the panel list (loadAll is best-effort and can be empty).
function findPending(kind, name, tagId = null) {
const lname = (name || '').toLowerCase()
for (const map of [allByCategory.value, byCategory.value]) {
for (const list of Object.values(map)) {
for (const s of list || []) {
if (s.rejected) continue
if (tagId != null && s.canonical_tag_id === tagId) return s
if (
s.category === kind &&
(s.display_name || '').toLowerCase() === lname
) return s
}
}
}
return null
}
async function dismiss(suggestion) {
const imageId = currentImageId
if (imageId == null) return
@@ -202,6 +225,7 @@ export const useSuggestionsStore = defineStore('suggestions', () => {
return {
byCategory, allByCategory, loading, error,
load, loadAll, accept, aliasAccept, removeAlias, dismiss, undismiss
load, loadAll, accept, aliasAccept, removeAlias, dismiss, undismiss,
findPending
}
})