Let library prompt previews escape the video tile.
The info popover was clipped to the card, so the prompt sat inside the thumbnail frame instead of over the page. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
<template>
|
||||
<div class="group relative shrink-0" @wheel.prevent="onIconWheel">
|
||||
<div
|
||||
ref="root"
|
||||
class="relative shrink-0"
|
||||
@mouseenter="open"
|
||||
@mouseleave="scheduleClose"
|
||||
@focusin="open"
|
||||
@focusout="onFocusOut"
|
||||
@wheel.prevent="onIconWheel"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="relative z-10 flex h-6 w-6 items-center justify-center rounded-full text-zinc-500 hover:text-amber-200"
|
||||
@@ -12,14 +20,19 @@
|
||||
<path d="M12 16v-4M12 8h.01" stroke-linecap="round" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="invisible absolute right-0 top-full z-20 -mt-1 pt-2 group-hover:visible group-focus-within:visible">
|
||||
<Teleport to="body">
|
||||
<div
|
||||
v-if="show"
|
||||
ref="panel"
|
||||
class="pointer-events-auto max-h-48 w-64 max-w-[calc(100vw-2rem)] overflow-y-auto overscroll-contain whitespace-pre-wrap break-words rounded-xl border border-white/10 bg-zinc-950 p-3 text-xs leading-relaxed text-zinc-300 shadow-2xl"
|
||||
class="fixed z-[80] max-h-[min(20rem,50vh)] w-[min(28rem,calc(100vw-1.5rem))] overflow-y-auto overscroll-contain whitespace-pre-wrap break-words rounded-xl border border-white/10 bg-zinc-950 p-3 text-xs leading-relaxed text-zinc-300 shadow-2xl"
|
||||
:style="panelStyle"
|
||||
@mouseenter="open"
|
||||
@mouseleave="scheduleClose"
|
||||
@click.stop
|
||||
>
|
||||
{{ text }}
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -33,11 +46,75 @@ const emit = defineEmits<{
|
||||
copy: [text: string]
|
||||
}>()
|
||||
|
||||
const root = ref<HTMLElement | null>(null)
|
||||
const panel = ref<HTMLElement | null>(null)
|
||||
const show = ref(false)
|
||||
const panelStyle = ref<Record<string, string>>({})
|
||||
let closeTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
function place() {
|
||||
const el = root.value
|
||||
if (!el) return
|
||||
const rect = el.getBoundingClientRect()
|
||||
const width = Math.min(448, window.innerWidth - 24)
|
||||
const maxHeight = Math.min(320, window.innerHeight * 0.5)
|
||||
let left = rect.right - width
|
||||
left = Math.min(Math.max(12, left), window.innerWidth - width - 12)
|
||||
const below = rect.bottom + 8
|
||||
const above = rect.top - 8 - maxHeight
|
||||
const top = below + maxHeight <= window.innerHeight - 12 ? below : Math.max(12, above)
|
||||
panelStyle.value = {
|
||||
top: `${Math.round(top)}px`,
|
||||
left: `${Math.round(left)}px`,
|
||||
width: `${Math.round(width)}px`
|
||||
}
|
||||
}
|
||||
|
||||
function open() {
|
||||
if (closeTimer) {
|
||||
clearTimeout(closeTimer)
|
||||
closeTimer = null
|
||||
}
|
||||
show.value = true
|
||||
nextTick(place)
|
||||
}
|
||||
|
||||
function close() {
|
||||
show.value = false
|
||||
}
|
||||
|
||||
function scheduleClose() {
|
||||
if (closeTimer) clearTimeout(closeTimer)
|
||||
closeTimer = setTimeout(close, 120)
|
||||
}
|
||||
|
||||
function onFocusOut(event: FocusEvent) {
|
||||
const next = event.relatedTarget as Node | null
|
||||
if (root.value?.contains(next) || panel.value?.contains(next)) return
|
||||
scheduleClose()
|
||||
}
|
||||
|
||||
function onIconWheel(event: WheelEvent) {
|
||||
const el = panel.value
|
||||
if (!el) return
|
||||
el.scrollTop += event.deltaY
|
||||
}
|
||||
|
||||
watch(show, (value) => {
|
||||
if (typeof window === 'undefined') return
|
||||
if (value) {
|
||||
window.addEventListener('resize', place)
|
||||
window.addEventListener('scroll', place, true)
|
||||
return
|
||||
}
|
||||
window.removeEventListener('resize', place)
|
||||
window.removeEventListener('scroll', place, true)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (closeTimer) clearTimeout(closeTimer)
|
||||
if (typeof window === 'undefined') return
|
||||
window.removeEventListener('resize', place)
|
||||
window.removeEventListener('scroll', place, true)
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user