feat(explore): auto-focus the tag input on every image change
The workspace is built for rapid walk-and-tag, but the tag field was only focused once (TagAutocomplete's on-mount autofocus) — walking to a neighbour left focus behind, so the operator had to click the field each time (operator-asked 2026-06-26). TagPanel now exposes focusTagInput; ExploreView watches the focused image id and re-focuses the field on seed + every walk via nextTick. Reuses the existing focus path, so TagAutocomplete's mobile guard (no soft-keyboard pop on touch) is preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -86,7 +86,11 @@ async function onNavigate(tag) {
|
|||||||
// Return focus to the tag input after a suggestion is accepted (from the
|
// Return focus to the tag input after a suggestion is accepted (from the
|
||||||
// Suggestions panel or the autocomplete dropdown) so the operator can keep
|
// 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).
|
// typing the next tag without re-clicking the field (operator-asked 2026-06-08).
|
||||||
|
// Exposed so a host surface (the Explore workspace) can re-focus the field when
|
||||||
|
// it swaps the focused image, without re-clicking. The field's own mobile guard
|
||||||
|
// (TagAutocomplete) is preserved.
|
||||||
function focusTagInput() { tagInputRef.value?.focus?.() }
|
function focusTagInput() { tagInputRef.value?.focus?.() }
|
||||||
|
defineExpose({ focusTagInput })
|
||||||
|
|
||||||
async function onRemove(tagId) {
|
async function onRemove(tagId) {
|
||||||
errorMsg.value = null
|
errorMsg.value = null
|
||||||
|
|||||||
@@ -98,7 +98,10 @@
|
|||||||
|
|
||||||
<!-- RIGHT: the modal's tag rail, hosted on the anchor. -->
|
<!-- RIGHT: the modal's tag rail, hosted on the anchor. -->
|
||||||
<aside class="fc-ex__rail" aria-label="Tags for the focused image">
|
<aside class="fc-ex__rail" aria-label="Tags for the focused image">
|
||||||
<TagPanel v-if="store.currentImageId != null" :host="store" />
|
<TagPanel
|
||||||
|
v-if="store.currentImageId != null"
|
||||||
|
ref="tagPanelRef" :host="store"
|
||||||
|
/>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -106,7 +109,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useApi } from '../composables/useApi.js'
|
import { useApi } from '../composables/useApi.js'
|
||||||
import { useExploreStore } from '../stores/explore.js'
|
import { useExploreStore } from '../stores/explore.js'
|
||||||
@@ -125,6 +128,16 @@ const modal = useModalStore()
|
|||||||
const anchorId = computed(() => route.params.imageId || null)
|
const anchorId = computed(() => route.params.imageId || null)
|
||||||
const seeding = ref(false)
|
const seeding = ref(false)
|
||||||
const seedError = ref(null)
|
const seedError = ref(null)
|
||||||
|
const tagPanelRef = ref(null)
|
||||||
|
|
||||||
|
// Auto-focus the tag input whenever the focused image changes (seed + every
|
||||||
|
// walk) so tagging needs no extra click — the whole point of the workspace
|
||||||
|
// (operator-asked 2026-06-26). Reuses TagPanel/TagAutocomplete's focus, which
|
||||||
|
// keeps its own mobile guard (no soft-keyboard pop on touch).
|
||||||
|
watch(
|
||||||
|
() => store.currentImageId,
|
||||||
|
(id) => { if (id != null) nextTick(() => tagPanelRef.value?.focusTagInput?.()) },
|
||||||
|
)
|
||||||
|
|
||||||
// The route is the source of truth. With an anchor, walk it; without one (the
|
// The route is the source of truth. With an anchor, walk it; without one (the
|
||||||
// bare /explore nav entry) seed a RANDOM image so the tab kick-starts a rabbit
|
// bare /explore nav entry) seed a RANDOM image so the tab kick-starts a rabbit
|
||||||
|
|||||||
Reference in New Issue
Block a user