9726d6f4b5
When "Show hidden" is on, a review strip appears atop the gallery listing the auto-hidden chrome flagged "also looks like content" (most-concerning first): thumbnail + "also looks like <X>" + Keep hidden / Un-hide. Un-hide removes the presentation tag (image returns to the gallery) and trains the head; Keep resolves the flag. Self-hides when there's nothing to review; theme-token styled. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
85 lines
3.0 KiB
Vue
85 lines
3.0 KiB
Vue
<template>
|
|
<v-container fluid class="pt-2 pb-6">
|
|
<Teleport to="#fc-nav-actions">
|
|
<v-btn
|
|
:color="sel.isSelectMode ? 'accent' : undefined"
|
|
:variant="sel.isSelectMode ? 'flat' : 'tonal'"
|
|
size="small"
|
|
@click="sel.isSelectMode ? sel.exitSelectMode() : sel.enterSelectMode()"
|
|
>{{ sel.isSelectMode ? 'Done' : 'Select' }}</v-btn>
|
|
</Teleport>
|
|
|
|
<div class="fc-gallery-layout">
|
|
<div class="fc-gallery-layout__main">
|
|
<PostInfoHeader />
|
|
<GalleryFilterBar v-if="store.filter.post_id == null" />
|
|
<HiddenReviewStrip />
|
|
<EmptyState v-if="store.isEmpty" />
|
|
<GalleryGrid v-else @open="openImage" />
|
|
</div>
|
|
<TimelineSidebar
|
|
v-if="store.images.length > 0 && store.filter.similar_to == null"
|
|
class="fc-gallery-layout__sidebar"
|
|
/>
|
|
</div>
|
|
|
|
<BulkEditorPanel />
|
|
</v-container>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { onMounted, watch } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
import { useGalleryStore } from '../stores/gallery.js'
|
|
import { useModalStore } from '../stores/modal.js'
|
|
import GalleryGrid from '../components/gallery/GalleryGrid.vue'
|
|
import GalleryFilterBar from '../components/gallery/GalleryFilterBar.vue'
|
|
import HiddenReviewStrip from '../components/gallery/HiddenReviewStrip.vue'
|
|
import TimelineSidebar from '../components/gallery/TimelineSidebar.vue'
|
|
import EmptyState from '../components/gallery/EmptyState.vue'
|
|
import PostInfoHeader from '../components/gallery/PostInfoHeader.vue'
|
|
import BulkEditorPanel from '../components/gallery/BulkEditorPanel.vue'
|
|
import { useGallerySelectionStore } from '../stores/gallerySelection.js'
|
|
|
|
const store = useGalleryStore()
|
|
const modal = useModalStore()
|
|
const sel = useGallerySelectionStore()
|
|
const route = useRoute()
|
|
|
|
// The URL query is the single source of truth for filters. Apply it on
|
|
// mount and on any query change (filter bar pushes, back button, deep-link).
|
|
onMounted(() => store.applyFilterFromQuery(route.query))
|
|
|
|
watch(() => route.query, (q) => {
|
|
sel.clear() // result set changed — selected ids are no longer valid
|
|
store.applyFilterFromQuery(q)
|
|
})
|
|
|
|
function openImage(id) {
|
|
// Walk the current gallery filter in the modal (#1322) — prev/next moves
|
|
// through exactly the filtered set the operator is viewing, not global
|
|
// neighbours. Snapshot of the currently-loaded, filtered, ordered ids.
|
|
modal.open(id, { playlistIds: store.images.map((i) => i.id) })
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.fc-gallery-layout {
|
|
display: flex;
|
|
gap: 16px;
|
|
align-items: flex-start;
|
|
}
|
|
.fc-gallery-layout__main { flex: 1; min-width: 0; }
|
|
.fc-gallery-layout__sidebar { flex-shrink: 0; }
|
|
|
|
@media (max-width: 900px) {
|
|
.fc-gallery-layout { flex-direction: column-reverse; }
|
|
.fc-gallery-layout__sidebar { width: 100%; max-height: 200px; }
|
|
}
|
|
/* Phones: the year/month timeline strip eats vertical space and reads poorly
|
|
as a horizontal band — drop it; the gallery scroll is the primary nav here. */
|
|
@media (max-width: 600px) {
|
|
.fc-gallery-layout__sidebar { display: none; }
|
|
}
|
|
</style>
|