mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +08:00
feat: paste operation for skill file tree
This commit is contained in:
@ -17,6 +17,7 @@ import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { CONTEXT_MENU_TYPE, ROOT_ID } from '../constants'
|
||||
import { useInlineCreateNode } from '../hooks/use-inline-create-node'
|
||||
import { usePasteOperation } from '../hooks/use-paste-operation'
|
||||
import { useRootFileDrop } from '../hooks/use-root-file-drop'
|
||||
import { useSkillAssetTreeData } from '../hooks/use-skill-asset-tree'
|
||||
import { useSkillShortcuts } from '../hooks/use-skill-shortcuts'
|
||||
@ -127,18 +128,20 @@ const FileTree: React.FC<FileTreeProps> = ({ className }) => {
|
||||
}, [storeApi])
|
||||
|
||||
const handleBlankAreaClick = useCallback(() => {
|
||||
treeRef.current?.deselectAll()
|
||||
storeApi.getState().clearSelection()
|
||||
}, [storeApi])
|
||||
}, [storeApi, treeRef])
|
||||
|
||||
const handleBlankAreaContextMenu = useCallback((e: React.MouseEvent) => {
|
||||
e.preventDefault()
|
||||
treeRef.current?.deselectAll()
|
||||
storeApi.getState().clearSelection()
|
||||
storeApi.getState().setContextMenu({
|
||||
top: e.clientY,
|
||||
left: e.clientX,
|
||||
type: CONTEXT_MENU_TYPE.BLANK,
|
||||
})
|
||||
}, [storeApi])
|
||||
}, [storeApi, treeRef])
|
||||
|
||||
useSyncTreeWithActiveTab({
|
||||
treeRef,
|
||||
@ -147,6 +150,11 @@ const FileTree: React.FC<FileTreeProps> = ({ className }) => {
|
||||
|
||||
useSkillShortcuts({ treeRef })
|
||||
|
||||
usePasteOperation({
|
||||
treeRef,
|
||||
treeData: treeData ?? undefined,
|
||||
})
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className={cn('flex min-h-0 flex-1 items-center justify-center', className)}>
|
||||
@ -208,6 +216,7 @@ const FileTree: React.FC<FileTreeProps> = ({ className }) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
data-skill-tree-container
|
||||
className={cn(
|
||||
'flex min-h-0 flex-1 flex-col',
|
||||
isMutating && 'pointer-events-none opacity-50',
|
||||
|
||||
@ -53,7 +53,7 @@ const labelVariants = cva('system-sm-regular text-text-secondary', {
|
||||
export type MenuItemProps = {
|
||||
icon: React.ElementType
|
||||
label: string
|
||||
kbd?: string[]
|
||||
kbd?: readonly string[]
|
||||
onClick: React.MouseEventHandler<HTMLButtonElement>
|
||||
disabled?: boolean
|
||||
} & VariantProps<typeof menuItemVariants>
|
||||
|
||||
@ -31,6 +31,10 @@ export const MENU_CONTAINER_STYLES = [
|
||||
'bg-components-panel-bg-blur p-1 shadow-lg backdrop-blur-[5px]',
|
||||
] as const
|
||||
|
||||
const KBD_COPY = ['ctrl', 'c'] as const
|
||||
const KBD_CUT = ['ctrl', 'x'] as const
|
||||
const KBD_PASTE = ['ctrl', 'v'] as const
|
||||
|
||||
type NodeMenuProps = {
|
||||
type: NodeMenuType
|
||||
nodeId?: string
|
||||
@ -176,14 +180,14 @@ const NodeMenu: FC<NodeMenuProps> = ({
|
||||
<MenuItem
|
||||
icon={RiFileCopyLine}
|
||||
label={t('skillSidebar.menu.copy')}
|
||||
kbd={['ctrl', 'c']}
|
||||
kbd={KBD_COPY}
|
||||
onClick={handleCopy}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<MenuItem
|
||||
icon={RiScissorsLine}
|
||||
label={t('skillSidebar.menu.cut')}
|
||||
kbd={['ctrl', 'x']}
|
||||
kbd={KBD_CUT}
|
||||
onClick={handleCut}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
@ -194,7 +198,7 @@ const NodeMenu: FC<NodeMenuProps> = ({
|
||||
<MenuItem
|
||||
icon={RiClipboardLine}
|
||||
label={t('skillSidebar.menu.paste')}
|
||||
kbd={['ctrl', 'v']}
|
||||
kbd={KBD_PASTE}
|
||||
onClick={handlePaste}
|
||||
disabled={isLoading || !hasClipboard}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user