From e2deaa806602300be04bf4c8d687176e9ced70e6 Mon Sep 17 00:00:00 2001
From: Julio Ruiz
Date: Mon, 25 May 2026 21:04:05 -0500
Subject: [PATCH] Adding new clipboard funtionality and panel
---
app/components/PublicationDetail.vue | 169 +++++++++++++++++++++++----
app/utils/textUtilities.ts | 21 +++-
2 files changed, 165 insertions(+), 25 deletions(-)
diff --git a/app/components/PublicationDetail.vue b/app/components/PublicationDetail.vue
index 7f79b34..d21e4db 100644
--- a/app/components/PublicationDetail.vue
+++ b/app/components/PublicationDetail.vue
@@ -6,6 +6,7 @@ import { useHistoryStore } from '~/stores/history'
import { storeToRefs } from 'pinia'
import { useSettingsStore } from '~/stores/settings'
import type { SearchHit } from '~/types'
+import { select } from '#build/ui'
interface TypesenseHighlight {
field?: string
@@ -344,7 +345,7 @@ const currentMatchIdx = ref(0)
function clearMatchMarks() {
const container = paragraphsContainer.value
if (!container) return
- container.querySelectorAll('mark.search-match').forEach(m => {
+ container.querySelectorAll('mark.search-match').forEach((m) => {
const parent = m.parentNode
if (parent) {
parent.replaceChild(document.createTextNode(m.textContent || ''), m)
@@ -753,13 +754,43 @@ function highlightTextNodes(root: HTMLElement, terms: string[]): number {
}
return matches.length
}
+const isPopOverOpen = ref(false)
+const anchor = ref({ x: 0, y: 0 })
+const virtualElement = computed(() => ({
+ getBoundingClientRect: () =>
+ ({
+ width: 0,
+ height: 0,
+ left: anchor.value.x,
+ right: anchor.value.x,
+ top: anchor.value.y,
+ bottom: anchor.value.y,
+ ...anchor.value
+ } as DOMRect)
+}))
+
+function handleSelection(event: MouseEvent) {
+ const selection = window.getSelection()
+ if (selection && selection.toString().length > 0) {
+ anchor.value = { x: event.clientX, y: event.clientY }
+ isPopOverOpen.value = true
+ } else {
+ isPopOverOpen.value = false
+ }
+}
-
+
@@ -770,9 +801,14 @@ function highlightTextNodes(root: HTMLElement, terms: string[]): number {
-
+
@@ -798,39 +834,91 @@ function highlightTextNodes(root: HTMLElement, terms: string[]): number {
{{ safeLocation() }}
-
+
-
+
-
+
-
+
-
+
+ :class="matchElements.length ? 'text-toned font-medium' : 'text-dimmed'"
+ >
{{ matchElements.length ? `${currentMatchIdx + 1} / ${matchElements.length}` : '0 / 0' }}
-
+
-
+
@@ -846,6 +934,42 @@ function highlightTextNodes(root: HTMLElement, terms: string[]): number {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -855,15 +979,16 @@ function highlightTextNodes(root: HTMLElement, terms: string[]): number {
:label="`${hit.document.number}`"
size="md"
variant="ghost"
- @click="copyToClipboard(hit.document.text, hit.document.type)"
class="text-gray-300 hover:text-white"
- :class="accentColor === 'blue' ? 'hover:bg-carpablue' : 'hover:bg-carpagreen'"
+ :class="(hit.document.type=='activities'?'hover:bg-carpagreen':'hover:bg-carpablue')"
+ @click="isPopOverOpen = !isPopOverOpen"
/>
diff --git a/app/utils/textUtilities.ts b/app/utils/textUtilities.ts
index d3ae1a2..fd96da8 100644
--- a/app/utils/textUtilities.ts
+++ b/app/utils/textUtilities.ts
@@ -2,15 +2,30 @@
export async function copyToClipboard(textToCopy: string, type: string) {
const toast = useToast()
+ const selection = window.getSelection()
+
+ const container = document.createElement('div')
+ const range = selection?.getRangeAt(0)
+ container.appendChild(range.cloneContents())
+
+ const htmlOutput = container.innerHTML
+ const textOutput = selection?.toString()
+
try {
- await navigator.clipboard.writeText(textToCopy)
+ // Modern Clipboard API requires ClipboardItem
+ const clipboardItem = new ClipboardItem({
+ 'text/html': new Blob([htmlOutput], { type: 'text/html' }),
+ 'text/plain': new Blob([textOutput], { type: 'text/plain' })
+ })
+
+ await navigator.clipboard.write([clipboardItem])
toast.add({
title: 'Texto copiado!',
- description: `${textToCopy}`,
+ description: `${textOutput}`,
icon: 'ph-clipboard-text',
color: type == 'activities' ? 'success' : 'info'
})
} catch (err) {
console.error('Failed to copy: ', err)
}
-}
\ No newline at end of file
+}