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 @@ -28,7 +28,7 @@ import {
import {
checkTagTrigger,
TagDropdown,
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown'
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-value'
import { WandPromptBar } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/wand-prompt-bar/wand-prompt-bar'
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
import {
checkTagTrigger,
TagDropdown,
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown'
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-value'
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
import { useTagSelection } from '@/hooks/use-tag-selection'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Input } from '@/components/ui/input'
import { MAX_TAG_SLOTS } from '@/lib/knowledge/consts'
import { cn } from '@/lib/utils'
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/formatted-text'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-input'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-value'
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Textarea } from '@/components/emcn/components/textarea/textarea'
import { Label } from '@/components/ui/label'
import { cn } from '@/lib/utils'
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/formatted-text'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-input'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-value'
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Input } from '@/components/emcn/components/input/input'
import { Label } from '@/components/ui/label'
import { cn } from '@/lib/utils'
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/formatted-text'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-input'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-value'
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/
import {
checkTagTrigger,
TagDropdown,
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown'
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
import type { SubBlockConfig } from '@/blocks/types'
import { useKnowledgeBaseTagDefinitions } from '@/hooks/use-knowledge-base-tag-definitions'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/
import {
checkTagTrigger,
TagDropdown,
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown'
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-value'
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
import { useMcpTools } from '@/hooks/use-mcp-tools'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { ComboboxOption } from '@/components/emcn/components/combobox/combo
import { Label } from '@/components/ui/label'
import { cn } from '@/lib/utils'
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/formatted-text'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-input'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-value'
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EnvVarDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/env-var-dropdown'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'

/**
* Props for the SubBlockDropdowns component.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type React from 'react'
import { EnvVarDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/env-var-dropdown'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-input'
import type { SubBlockConfig } from '@/blocks/types'
import { useTagSelection } from '@/hooks/use-tag-selection'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { createLogger } from '@/lib/logs/console/logger'
import { cn } from '@/lib/utils'
import { EnvVarDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/env-var-dropdown'
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/formatted-text'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-input'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-value'
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { KeyboardNavigationHandler } from './keyboard-navigation-handler'
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
import { useEffect, useMemo } from 'react'
import { usePopoverContext } from '@/components/emcn'
import type { BlockTagGroup, NestedBlockTagGroup } from '../types'

/**
* Keyboard navigation handler component that uses popover context
* to enable folder navigation with arrow keys
*/
interface KeyboardNavigationHandlerProps {
visible: boolean
selectedIndex: number
setSelectedIndex: (index: number) => void
flatTagList: Array<{ tag: string; group?: BlockTagGroup }>
nestedBlockTagGroups: NestedBlockTagGroup[]
handleTagSelect: (tag: string, group?: BlockTagGroup) => void
}

export const KeyboardNavigationHandler: React.FC<KeyboardNavigationHandlerProps> = ({
visible,
selectedIndex,
setSelectedIndex,
flatTagList,
nestedBlockTagGroups,
handleTagSelect,
}) => {
const { openFolder, closeFolder, isInFolder, currentFolder } = usePopoverContext()

const visibleIndices = useMemo(() => {
const indices: number[] = []

if (isInFolder && currentFolder) {
for (const group of nestedBlockTagGroups) {
for (const nestedTag of group.nestedTags) {
const folderId = `${group.blockId}-${nestedTag.key}`
if (folderId === currentFolder && nestedTag.children) {
// First, add the parent tag itself (so it's navigable as the first item)
if (nestedTag.parentTag) {
const parentIdx = flatTagList.findIndex((item) => item.tag === nestedTag.parentTag)
if (parentIdx >= 0) {
indices.push(parentIdx)
}
}
// Then add all children
for (const child of nestedTag.children) {
const idx = flatTagList.findIndex((item) => item.tag === child.fullTag)
if (idx >= 0) {
indices.push(idx)
}
}
break
}
}
}
} else {
// We're at root level, show all non-child items
// (variables and parent tags, but not their children)
for (let i = 0; i < flatTagList.length; i++) {
const tag = flatTagList[i].tag

// Check if this is a child of a parent folder
let isChild = false
for (const group of nestedBlockTagGroups) {
for (const nestedTag of group.nestedTags) {
if (nestedTag.children) {
for (const child of nestedTag.children) {
if (child.fullTag === tag) {
isChild = true
break
}
}
}
if (isChild) break
}
if (isChild) break
}

if (!isChild) {
indices.push(i)
}
}
}

return indices
}, [isInFolder, currentFolder, flatTagList, nestedBlockTagGroups])

// Auto-select first visible item when entering/exiting folders
useEffect(() => {
if (!visible || visibleIndices.length === 0) return

if (!visibleIndices.includes(selectedIndex)) {
setSelectedIndex(visibleIndices[0])
}
}, [visible, isInFolder, currentFolder, visibleIndices, selectedIndex, setSelectedIndex])

useEffect(() => {
if (!visible || !flatTagList.length) return

// Helper to open a folder with proper selection callback and parent selection
const openFolderWithSelection = (
folderId: string,
folderTitle: string,
parentTag: string,
group: BlockTagGroup
) => {
const selectionCallback = () => handleTagSelect(parentTag, group)

// Find parent tag index (which is first in visible items when in folder)
let parentIndex = 0
for (const g of nestedBlockTagGroups) {
for (const nestedTag of g.nestedTags) {
if (nestedTag.parentTag === parentTag) {
const idx = flatTagList.findIndex((item) => item.tag === nestedTag.parentTag)
parentIndex = idx >= 0 ? idx : 0
break
}
}
}

openFolder(folderId, folderTitle, undefined, selectionCallback)
setSelectedIndex(parentIndex)
}

const handleKeyboardEvent = (e: KeyboardEvent) => {
const selected = flatTagList[selectedIndex]
if (!selected && e.key !== 'ArrowDown' && e.key !== 'ArrowUp') return

let currentFolderInfo: {
id: string
title: string
parentTag: string
group: BlockTagGroup
} | null = null

if (selected) {
for (const group of nestedBlockTagGroups) {
for (const nestedTag of group.nestedTags) {
if (
nestedTag.parentTag === selected.tag &&
nestedTag.children &&
nestedTag.children.length > 0
) {
currentFolderInfo = {
id: `${selected.group?.blockId}-${nestedTag.key}`,
title: nestedTag.display,
parentTag: nestedTag.parentTag,
group,
}
break
}
}
if (currentFolderInfo) break
}
}

switch (e.key) {
case 'ArrowDown':
e.preventDefault()
e.stopPropagation()
if (visibleIndices.length > 0) {
const currentVisibleIndex = visibleIndices.indexOf(selectedIndex)
if (currentVisibleIndex === -1) {
setSelectedIndex(visibleIndices[0])
} else if (currentVisibleIndex < visibleIndices.length - 1) {
setSelectedIndex(visibleIndices[currentVisibleIndex + 1])
}
}
break
case 'ArrowUp':
e.preventDefault()
e.stopPropagation()
if (visibleIndices.length > 0) {
const currentVisibleIndex = visibleIndices.indexOf(selectedIndex)
if (currentVisibleIndex === -1) {
setSelectedIndex(visibleIndices[0])
} else if (currentVisibleIndex > 0) {
setSelectedIndex(visibleIndices[currentVisibleIndex - 1])
}
}
break
case 'Enter':
e.preventDefault()
e.stopPropagation()
if (selected && selectedIndex >= 0 && selectedIndex < flatTagList.length) {
if (currentFolderInfo && !isInFolder) {
// It's a folder, open it
openFolderWithSelection(
currentFolderInfo.id,
currentFolderInfo.title,
currentFolderInfo.parentTag,
currentFolderInfo.group
)
} else {
// Not a folder, select it
handleTagSelect(selected.tag, selected.group)
}
}
break
case 'ArrowRight':
if (currentFolderInfo && !isInFolder) {
e.preventDefault()
e.stopPropagation()
openFolderWithSelection(
currentFolderInfo.id,
currentFolderInfo.title,
currentFolderInfo.parentTag,
currentFolderInfo.group
)
}
break
case 'ArrowLeft':
if (isInFolder) {
e.preventDefault()
e.stopPropagation()
closeFolder()
let firstRootIndex = 0
for (let i = 0; i < flatTagList.length; i++) {
const tag = flatTagList[i].tag
const isVariable = !tag.includes('.')
let isParent = false
for (const group of nestedBlockTagGroups) {
for (const nestedTag of group.nestedTags) {
if (nestedTag.parentTag === tag) {
isParent = true
break
}
}
if (isParent) break
}
if (isVariable || isParent) {
firstRootIndex = i
break
}
}
setSelectedIndex(firstRootIndex)
}
break
}
}

window.addEventListener('keydown', handleKeyboardEvent, true)
return () => window.removeEventListener('keydown', handleKeyboardEvent, true)
}, [
visible,
selectedIndex,
visibleIndices,
flatTagList,
nestedBlockTagGroups,
openFolder,
closeFolder,
isInFolder,
setSelectedIndex,
handleTagSelect,
])

return null
}
Loading