Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ export function DocumentTagsModal({

return (
<Modal open={open} onOpenChange={handleClose}>
<ModalContent size='sm'>
<ModalContent size='md'>
<ModalHeader>
<div className='flex items-center justify-between'>
<span>Document Tags</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ export function BaseTagsModal({ open, onOpenChange, knowledgeBaseId }: BaseTagsM
return (
<>
<Modal open={open} onOpenChange={handleClose}>
<ModalContent size='sm'>
<ModalContent size='md'>
<ModalHeader>
<div className='flex items-center justify-between'>
<span>Tags</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import { useCallback, useState } from 'react'
import { useCallback, useRef, useState } from 'react'
import { useParams, useRouter } from 'next/navigation'
import { Badge, DocumentAttachment, Tooltip } from '@/components/emcn'
import { formatAbsoluteDate, formatRelativeTime } from '@/lib/core/utils/formatting'
Expand Down Expand Up @@ -101,6 +101,23 @@ export function BaseCard({
const [isTagsModalOpen, setIsTagsModalOpen] = useState(false)
const [isDeleting, setIsDeleting] = useState(false)

/**
* Guards against context menu actions triggering card navigation.
* The card's onClick fires synchronously during the click event bubble phase,
* so the ref is checked before the setTimeout-0 callback resets it.
*/
const actionTakenRef = useRef(false)
const withActionGuard = useCallback((fn: () => void) => {
actionTakenRef.current = true
try {
fn()
} finally {
setTimeout(() => {
actionTakenRef.current = false
}, 0)
}
}, [])

const searchParams = new URLSearchParams({
kbName: title,
})
Expand All @@ -110,7 +127,7 @@ export function BaseCard({

const handleClick = useCallback(
(e: React.MouseEvent) => {
if (isContextMenuOpen) {
if (isContextMenuOpen || actionTakenRef.current) {
e.preventDefault()
return
}
Expand All @@ -130,20 +147,25 @@ export function BaseCard({
)

const handleOpenInNewTab = useCallback(() => {
window.open(href, '_blank')
}, [href])
withActionGuard(() => window.open(href, '_blank'))
}, [href, withActionGuard])

const handleViewTags = useCallback(() => {
setIsTagsModalOpen(true)
}, [])
withActionGuard(() => setIsTagsModalOpen(true))
}, [withActionGuard])

const handleEdit = useCallback(() => {
setIsEditModalOpen(true)
}, [])
withActionGuard(() => setIsEditModalOpen(true))
}, [withActionGuard])

const handleDelete = useCallback(() => {
setIsDeleteModalOpen(true)
}, [])
withActionGuard(() => setIsDeleteModalOpen(true))
}, [withActionGuard])

const handleCopyId = useCallback(() => {
if (!id) return
withActionGuard(() => navigator.clipboard.writeText(id))
}, [id, withActionGuard])

const handleConfirmDelete = useCallback(async () => {
if (!id || !onDelete) return
Expand Down Expand Up @@ -240,7 +262,7 @@ export function BaseCard({
onClose={closeContextMenu}
onOpenInNewTab={handleOpenInNewTab}
onViewTags={handleViewTags}
onCopyId={id ? () => navigator.clipboard.writeText(id) : undefined}
onCopyId={id ? handleCopyId : undefined}
onEdit={handleEdit}
onDelete={handleDelete}
showOpenInNewTab={true}
Expand Down
Loading