Skip to content

Commit 5c7b057

Browse files
authored
fix(knowledge): prevent navigation on context menu actions and widen tags modal (#4015)
* fix(knowledge): prevent navigation on context menu actions and widen tags modal * fix(knowledge): guard onCopyId against navigation and use setTimeout for robustness * refactor(knowledge): extract withActionGuard helper to deduplicate context menu guard * fix(knowledge): wrap withActionGuard callback in try/finally to prevent stuck ref
1 parent ad100fa commit 5c7b057

File tree

3 files changed

+35
-13
lines changed

3 files changed

+35
-13
lines changed

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/components/document-tags-modal/document-tags-modal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ export function DocumentTagsModal({
383383

384384
return (
385385
<Modal open={open} onOpenChange={handleClose}>
386-
<ModalContent size='sm'>
386+
<ModalContent size='md'>
387387
<ModalHeader>
388388
<div className='flex items-center justify-between'>
389389
<span>Document Tags</span>

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/base-tags-modal/base-tags-modal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ export function BaseTagsModal({ open, onOpenChange, knowledgeBaseId }: BaseTagsM
261261
return (
262262
<>
263263
<Modal open={open} onOpenChange={handleClose}>
264-
<ModalContent size='sm'>
264+
<ModalContent size='md'>
265265
<ModalHeader>
266266
<div className='flex items-center justify-between'>
267267
<span>Tags</span>

apps/sim/app/workspace/[workspaceId]/knowledge/components/base-card/base-card.tsx

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

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

104+
/**
105+
* Guards against context menu actions triggering card navigation.
106+
* The card's onClick fires synchronously during the click event bubble phase,
107+
* so the ref is checked before the setTimeout-0 callback resets it.
108+
*/
109+
const actionTakenRef = useRef(false)
110+
const withActionGuard = useCallback((fn: () => void) => {
111+
actionTakenRef.current = true
112+
try {
113+
fn()
114+
} finally {
115+
setTimeout(() => {
116+
actionTakenRef.current = false
117+
}, 0)
118+
}
119+
}, [])
120+
104121
const searchParams = new URLSearchParams({
105122
kbName: title,
106123
})
@@ -110,7 +127,7 @@ export function BaseCard({
110127

111128
const handleClick = useCallback(
112129
(e: React.MouseEvent) => {
113-
if (isContextMenuOpen) {
130+
if (isContextMenuOpen || actionTakenRef.current) {
114131
e.preventDefault()
115132
return
116133
}
@@ -130,20 +147,25 @@ export function BaseCard({
130147
)
131148

132149
const handleOpenInNewTab = useCallback(() => {
133-
window.open(href, '_blank')
134-
}, [href])
150+
withActionGuard(() => window.open(href, '_blank'))
151+
}, [href, withActionGuard])
135152

136153
const handleViewTags = useCallback(() => {
137-
setIsTagsModalOpen(true)
138-
}, [])
154+
withActionGuard(() => setIsTagsModalOpen(true))
155+
}, [withActionGuard])
139156

140157
const handleEdit = useCallback(() => {
141-
setIsEditModalOpen(true)
142-
}, [])
158+
withActionGuard(() => setIsEditModalOpen(true))
159+
}, [withActionGuard])
143160

144161
const handleDelete = useCallback(() => {
145-
setIsDeleteModalOpen(true)
146-
}, [])
162+
withActionGuard(() => setIsDeleteModalOpen(true))
163+
}, [withActionGuard])
164+
165+
const handleCopyId = useCallback(() => {
166+
if (!id) return
167+
withActionGuard(() => navigator.clipboard.writeText(id))
168+
}, [id, withActionGuard])
147169

148170
const handleConfirmDelete = useCallback(async () => {
149171
if (!id || !onDelete) return
@@ -240,7 +262,7 @@ export function BaseCard({
240262
onClose={closeContextMenu}
241263
onOpenInNewTab={handleOpenInNewTab}
242264
onViewTags={handleViewTags}
243-
onCopyId={id ? () => navigator.clipboard.writeText(id) : undefined}
265+
onCopyId={id ? handleCopyId : undefined}
244266
onEdit={handleEdit}
245267
onDelete={handleDelete}
246268
showOpenInNewTab={true}

0 commit comments

Comments
 (0)