<template>
<Teleport to="body">
<div
v-if="visible"
class="selection-toolbar"
:style="{ top: pos.y + 'px', left: pos.x + 'px' }"
@mousedown.prevent
>
<GnButton variant="warning" size="sm" icon="ph-quotes" style="color:#000" title="Reply to selection" @click="handleReply">
Reply
</GnButton>
</div>
</Teleport>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { useQuoteReply } from '@/composables/useQuoteReply.js'
const { requestQuote } = useQuoteReply()
const visible = ref(false)
const pos = ref({ x: 0, y: 0 })
let selectedText = ''
function getAssistantSelection() {
const sel = window.getSelection()
if (!sel || sel.isCollapsed || !sel.toString().trim()) return null
const range = sel.getRangeAt(0)
const container = range.commonAncestorContainer
const el = container.nodeType === Node.TEXT_NODE ? container.parentElement : container
if (!el.closest('.msg-assistant-content')) return null
return { text: sel.toString().trim(), range }
}
function updatePos() {
const sel = window.getSelection()
if (!sel || sel.isCollapsed || !sel.rangeCount) { visible.value = false; return }
const range = sel.getRangeAt(0)
const rect = range.getBoundingClientRect()
if (!rect.width && !rect.height) { visible.value = false; return }
// viewport-relative coords — correct for position:fixed
pos.value = {
x: Math.round(rect.left + rect.width / 2),
y: Math.round(rect.top - 44),
}
}
function onMouseUp() {
setTimeout(() => {
const result = getAssistantSelection()
if (!result) { visible.value = false; return }
selectedText = result.text
updatePos()
visible.value = true
}, 10)
}
function onSelectionChange() {
const sel = window.getSelection()
if (!sel || sel.isCollapsed) visible.value = false
}
function handleReply() {
if (!selectedText) return
requestQuote(selectedText)
window.getSelection()?.removeAllRanges()
visible.value = false
}
onMounted(() => {
document.addEventListener('mouseup', onMouseUp)
document.addEventListener('selectionchange', onSelectionChange)
// capture:true catches scroll inside nested containers (message list)
document.addEventListener('scroll', updatePos, true)
})
onUnmounted(() => {
document.removeEventListener('mouseup', onMouseUp)
document.removeEventListener('selectionchange', onSelectionChange)
document.removeEventListener('scroll', updatePos, true)
})
</script>
<style scoped>
.selection-toolbar {
position: fixed;
transform: translateX(-50%);
z-index: 900;
pointer-events: all;
background: #e0af68;
color: black;
}
</style>