Skip to content

Commit 6957ca3

Browse files
committed
added more popover actions
1 parent 1d58f5f commit 6957ca3

File tree

14 files changed

+712
-53
lines changed

14 files changed

+712
-53
lines changed
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
'use client'
2+
3+
import { Popover, PopoverAnchor, PopoverContent, PopoverItem } from '@/components/emcn'
4+
5+
interface ChunkContextMenuProps {
6+
isOpen: boolean
7+
position: { x: number; y: number }
8+
menuRef: React.RefObject<HTMLDivElement | null>
9+
onClose: () => void
10+
/**
11+
* Chunk-specific actions (shown when right-clicking on a chunk)
12+
*/
13+
onOpenInNewTab?: () => void
14+
onEdit?: () => void
15+
onCopyContent?: () => void
16+
onToggleEnabled?: () => void
17+
onDelete?: () => void
18+
/**
19+
* Empty space action (shown when right-clicking on empty space)
20+
*/
21+
onAddChunk?: () => void
22+
/**
23+
* Whether the chunk is currently enabled
24+
*/
25+
isChunkEnabled?: boolean
26+
/**
27+
* Whether a chunk is selected (vs empty space)
28+
*/
29+
hasChunk: boolean
30+
/**
31+
* Whether toggle enabled is disabled
32+
*/
33+
disableToggleEnabled?: boolean
34+
/**
35+
* Whether delete is disabled
36+
*/
37+
disableDelete?: boolean
38+
/**
39+
* Whether add chunk is disabled
40+
*/
41+
disableAddChunk?: boolean
42+
}
43+
44+
/**
45+
* Context menu for chunks table.
46+
* Shows chunk actions when right-clicking a row, or "Create chunk" when right-clicking empty space.
47+
*/
48+
export function ChunkContextMenu({
49+
isOpen,
50+
position,
51+
menuRef,
52+
onClose,
53+
onOpenInNewTab,
54+
onEdit,
55+
onCopyContent,
56+
onToggleEnabled,
57+
onDelete,
58+
onAddChunk,
59+
isChunkEnabled = true,
60+
hasChunk,
61+
disableToggleEnabled = false,
62+
disableDelete = false,
63+
disableAddChunk = false,
64+
}: ChunkContextMenuProps) {
65+
return (
66+
<Popover open={isOpen} onOpenChange={onClose} variant='secondary' size='sm'>
67+
<PopoverAnchor
68+
style={{
69+
position: 'fixed',
70+
left: `${position.x}px`,
71+
top: `${position.y}px`,
72+
width: '1px',
73+
height: '1px',
74+
}}
75+
/>
76+
<PopoverContent ref={menuRef} align='start' side='bottom' sideOffset={4}>
77+
{hasChunk ? (
78+
<>
79+
{onOpenInNewTab && (
80+
<PopoverItem
81+
onClick={() => {
82+
onOpenInNewTab()
83+
onClose()
84+
}}
85+
>
86+
Open in new tab
87+
</PopoverItem>
88+
)}
89+
{onEdit && (
90+
<PopoverItem
91+
onClick={() => {
92+
onEdit()
93+
onClose()
94+
}}
95+
>
96+
Edit
97+
</PopoverItem>
98+
)}
99+
{onCopyContent && (
100+
<PopoverItem
101+
onClick={() => {
102+
onCopyContent()
103+
onClose()
104+
}}
105+
>
106+
Copy content
107+
</PopoverItem>
108+
)}
109+
{onToggleEnabled && (
110+
<PopoverItem
111+
disabled={disableToggleEnabled}
112+
onClick={() => {
113+
onToggleEnabled()
114+
onClose()
115+
}}
116+
>
117+
{isChunkEnabled ? 'Disable' : 'Enable'}
118+
</PopoverItem>
119+
)}
120+
{onDelete && (
121+
<PopoverItem
122+
disabled={disableDelete}
123+
onClick={() => {
124+
onDelete()
125+
onClose()
126+
}}
127+
>
128+
Delete
129+
</PopoverItem>
130+
)}
131+
</>
132+
) : (
133+
onAddChunk && (
134+
<PopoverItem
135+
disabled={disableAddChunk}
136+
onClick={() => {
137+
onAddChunk()
138+
onClose()
139+
}}
140+
>
141+
Create chunk
142+
</PopoverItem>
143+
)
144+
)}
145+
</PopoverContent>
146+
</Popover>
147+
)
148+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { ChunkContextMenu } from './chunk-context-menu'

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

Lines changed: 82 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ export function EditChunkModal({
5959
const [showUnsavedChangesAlert, setShowUnsavedChangesAlert] = useState(false)
6060
const [pendingNavigation, setPendingNavigation] = useState<(() => void) | null>(null)
6161
const [tokenizerOn, setTokenizerOn] = useState(false)
62-
const [scrollTop, setScrollTop] = useState(0)
6362
const textareaRef = useRef<HTMLTextAreaElement>(null)
6463

6564
const hasUnsavedChanges = editedContent !== (chunk?.content || '')
@@ -76,37 +75,76 @@ export function EditChunkModal({
7675
}, [editedContent, tokenizerOn, tokenStrings])
7776

7877
const TOKEN_BG_COLORS = [
79-
'rgba(185, 28, 28, 0.5)', // Red
80-
'rgba(194, 65, 12, 0.5)', // Orange
81-
'rgba(161, 98, 7, 0.5)', // Amber
82-
'rgba(77, 124, 15, 0.5)', // Lime
83-
'rgba(21, 128, 61, 0.5)', // Green
84-
'rgba(15, 118, 110, 0.5)', // Teal
85-
'rgba(3, 105, 161, 0.5)', // Sky
86-
'rgba(29, 78, 216, 0.5)', // Blue
87-
'rgba(109, 40, 217, 0.5)', // Violet
88-
'rgba(162, 28, 175, 0.5)', // Fuchsia
78+
'rgba(239, 68, 68, 0.55)', // Red
79+
'rgba(249, 115, 22, 0.55)', // Orange
80+
'rgba(234, 179, 8, 0.55)', // Yellow
81+
'rgba(132, 204, 22, 0.55)', // Lime
82+
'rgba(34, 197, 94, 0.55)', // Green
83+
'rgba(20, 184, 166, 0.55)', // Teal
84+
'rgba(6, 182, 212, 0.55)', // Cyan
85+
'rgba(59, 130, 246, 0.55)', // Blue
86+
'rgba(139, 92, 246, 0.55)', // Violet
87+
'rgba(217, 70, 239, 0.55)', // Fuchsia
8988
]
9089

9190
const getTokenBgColor = (index: number): string => {
9291
return TOKEN_BG_COLORS[index % TOKEN_BG_COLORS.length]
9392
}
9493

95-
const handleScroll = () => {
96-
if (textareaRef.current) {
97-
setScrollTop(textareaRef.current.scrollTop)
98-
}
99-
}
100-
94+
// #region agent log - style comparison
95+
const tokenizerDivRef = useRef<HTMLDivElement>(null)
10196
useEffect(() => {
102-
if (tokenizerOn && textareaRef.current) {
103-
requestAnimationFrame(() => {
104-
if (textareaRef.current) {
105-
setScrollTop(textareaRef.current.scrollTop)
106-
}
107-
})
97+
const logStyles = () => {
98+
const ta = textareaRef.current
99+
const tv = tokenizerDivRef.current
100+
if (ta && !tokenizerOn) {
101+
const s = window.getComputedStyle(ta)
102+
const rect = ta.getBoundingClientRect()
103+
fetch('http://127.0.0.1:7243/ingest/77a2b2bc-808d-4bfd-a366-739b0b04635d', {
104+
method: 'POST',
105+
headers: { 'Content-Type': 'application/json' },
106+
body: JSON.stringify({
107+
location: 'edit-chunk-modal.tsx:TEXTAREA',
108+
message: 'Textarea position',
109+
data: {
110+
top: rect.top,
111+
marginTop: s.marginTop,
112+
paddingTop: s.paddingTop,
113+
borderTopWidth: s.borderTopWidth,
114+
height: rect.height,
115+
},
116+
timestamp: Date.now(),
117+
sessionId: 'debug-session',
118+
hypothesisId: 'vertical',
119+
}),
120+
}).catch(() => {})
121+
}
122+
if (tv && tokenizerOn) {
123+
const s = window.getComputedStyle(tv)
124+
const rect = tv.getBoundingClientRect()
125+
fetch('http://127.0.0.1:7243/ingest/77a2b2bc-808d-4bfd-a366-739b0b04635d', {
126+
method: 'POST',
127+
headers: { 'Content-Type': 'application/json' },
128+
body: JSON.stringify({
129+
location: 'edit-chunk-modal.tsx:TOKENIZER',
130+
message: 'Tokenizer position',
131+
data: {
132+
top: rect.top,
133+
marginTop: s.marginTop,
134+
paddingTop: s.paddingTop,
135+
borderTopWidth: s.borderTopWidth,
136+
height: rect.height,
137+
},
138+
timestamp: Date.now(),
139+
sessionId: 'debug-session',
140+
hypothesisId: 'vertical',
141+
}),
142+
}).catch(() => {})
143+
}
108144
}
109-
}, [editedContent, tokenizerOn])
145+
setTimeout(logStyles, 100)
146+
}, [tokenizerOn])
147+
// #endregion
110148

111149
useEffect(() => {
112150
if (chunk?.content) {
@@ -283,45 +321,39 @@ export function EditChunkModal({
283321

284322
{/* Content Input Section */}
285323
<Label htmlFor='content'>Chunk</Label>
286-
<div className='relative'>
287-
{/* Token highlight overlay - behind textarea */}
288-
{tokenizerOn && (
289-
<div
290-
className='pointer-events-none absolute inset-0 overflow-hidden rounded-[4px] border border-transparent'
291-
aria-hidden='true'
292-
>
293-
<div
294-
className='whitespace-pre-wrap break-words px-[8px] py-[8px] font-medium font-sans text-sm'
295-
style={{ color: 'transparent', transform: `translateY(-${scrollTop}px)` }}
324+
{tokenizerOn ? (
325+
/* Tokenizer view - matches Textarea styling exactly (transparent border for spacing) */
326+
<div
327+
ref={tokenizerDivRef}
328+
className='h-[418px] overflow-y-auto whitespace-pre-wrap break-words rounded-[4px] border border-transparent bg-[var(--surface-5)] px-[8px] py-[8px] font-medium font-sans text-[var(--text-primary)] text-sm'
329+
style={{ minHeight: '418px' }}
330+
>
331+
{tokenStrings.map((token, index) => (
332+
<span
333+
key={index}
334+
style={{
335+
backgroundColor: getTokenBgColor(index),
336+
}}
296337
>
297-
{tokenStrings.map((token, index) => (
298-
<span
299-
key={index}
300-
style={{
301-
backgroundColor: getTokenBgColor(index),
302-
}}
303-
>
304-
{token}
305-
</span>
306-
))}
307-
</div>
308-
</div>
309-
)}
338+
{token}
339+
</span>
340+
))}
341+
</div>
342+
) : (
343+
/* Edit view - regular textarea */
310344
<Textarea
311345
ref={textareaRef}
312346
id='content'
313347
value={editedContent}
314348
onChange={(e) => setEditedContent(e.target.value)}
315-
onScroll={handleScroll}
316349
placeholder={
317350
userPermissions.canEdit ? 'Enter chunk content...' : 'Read-only view'
318351
}
319352
rows={20}
320353
disabled={isSaving || isNavigating || !userPermissions.canEdit}
321354
readOnly={!userPermissions.canEdit}
322-
className={tokenizerOn ? 'relative z-10 bg-transparent' : ''}
323355
/>
324-
</div>
356+
)}
325357
</div>
326358

327359
{/* Tokenizer Section */}

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export { ChunkContextMenu } from './chunk-context-menu'
12
export { CreateChunkModal } from './create-chunk-modal/create-chunk-modal'
23
export { DeleteChunkModal } from './delete-chunk-modal/delete-chunk-modal'
34
export { DocumentTagsModal } from './document-tags-modal/document-tags-modal'

0 commit comments

Comments
 (0)