feat(gallery): tag→gallery nav from modal chips (#5) + OR/exclude tag scope (#6a)
Cluster A, milestone #97. #5: clicking an image-modal tag chip's body now closes the modal and opens the gallery filtered for that one tag (fresh filter); ✕/kebab stay as the explicit remove/rename controls. #6a (backend of OR/exclude filtering): gallery_service._apply_scope gains a structured tag model — tag_or_groups (AND-of-OR: one EXISTS(tag_id IN group) per group) + tag_exclude (NOT EXISTS(tag_id IN exclude)) — layered additively on the existing tag_ids AND path so cursors/facets/deep-links are untouched. Threaded through scroll/timeline/jump_cursor/facets/similar + facets common dict; _require_single_filter rejects post_id combined with OR/exclude. API parses tag_or (repeatable → one OR-group each) + tag_not (csv exclude). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XCUHUGQLrBrkgyk1t49kpX
This commit is contained in:
@@ -3,6 +3,10 @@
|
||||
<v-chip
|
||||
size="small" closable
|
||||
:color="store.colorFor(tag.kind)" variant="tonal"
|
||||
class="fc-tag-chip__nav"
|
||||
role="link"
|
||||
:title="`Browse images tagged “${tag.name}”`"
|
||||
@click="$emit('navigate', tag)"
|
||||
@click:close="$emit('remove', tag.id)"
|
||||
>
|
||||
<v-icon start size="x-small">{{ iconFor(tag.kind) }}</v-icon>
|
||||
@@ -30,7 +34,7 @@ import { useTagStore } from '../../stores/tags.js'
|
||||
import KebabMenu from '../common/KebabMenu.vue'
|
||||
|
||||
const props = defineProps({ tag: { type: Object, required: true } })
|
||||
defineEmits(['remove', 'rename', 'set-fandom'])
|
||||
defineEmits(['remove', 'rename', 'set-fandom', 'navigate'])
|
||||
|
||||
const store = useTagStore()
|
||||
|
||||
@@ -53,6 +57,13 @@ function iconFor (k) { return KIND_ICONS[k] || 'mdi-tag' }
|
||||
|
||||
<style scoped>
|
||||
.fc-tag-chip { display: inline-flex; align-items: center; gap: 1px; }
|
||||
/* The chip body navigates to the filtered gallery (#5); signal it's clickable.
|
||||
The close ✕ (remove) and the sibling kebab stay as the explicit controls. */
|
||||
.fc-tag-chip__nav { cursor: pointer; }
|
||||
.fc-tag-chip__nav:focus-visible {
|
||||
outline: 2px solid rgb(var(--v-theme-accent));
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.fc-tag-chip__kebab { opacity: 0.7; }
|
||||
.fc-tag-chip:hover .fc-tag-chip__kebab { opacity: 1; }
|
||||
.fc-tag-chip__fandom { opacity: 0.7; font-size: 0.85em; }
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
v-for="tag in modal.current?.tags || []"
|
||||
:key="tag.id" :tag="tag"
|
||||
@remove="onRemove" @rename="openRename" @set-fandom="openSetFandom"
|
||||
@navigate="onNavigate"
|
||||
/>
|
||||
<span v-if="!modal.current?.tags?.length" class="text-caption">No tags yet.</span>
|
||||
</div>
|
||||
@@ -51,6 +52,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useModalStore } from '../../stores/modal.js'
|
||||
import { useSuggestionsStore } from '../../stores/suggestions.js'
|
||||
import TagChip from './TagChip.vue'
|
||||
@@ -61,9 +63,18 @@ import FandomSetDialog from './FandomSetDialog.vue'
|
||||
|
||||
const modal = useModalStore()
|
||||
const suggestions = useSuggestionsStore()
|
||||
const router = useRouter()
|
||||
const errorMsg = ref(null)
|
||||
const tagInputRef = ref(null)
|
||||
|
||||
// #5: clicking a tag chip's body leaves the modal and opens the gallery
|
||||
// filtered for that single tag (a fresh filter — the obvious "show me more
|
||||
// like this tag" move). Rename/set-fandom (kebab) and remove (✕) stay put.
|
||||
async function onNavigate(tag) {
|
||||
await modal.close()
|
||||
router.push({ name: 'gallery', query: { tag_id: String(tag.id) } })
|
||||
}
|
||||
|
||||
// Return focus to the tag input after a suggestion is accepted (from the
|
||||
// Suggestions panel or the autocomplete dropdown) so the operator can keep
|
||||
// typing the next tag without re-clicking the field (operator-asked 2026-06-08).
|
||||
|
||||
Reference in New Issue
Block a user