mirror of
https://github.com/langgenius/dify.git
synced 2026-04-05 16:36:28 +08:00
fix(skill-editor): fix blur effects of tool-picker and file-picker
This commit is contained in:
@@ -14,6 +14,8 @@ NEXT_PUBLIC_API_PREFIX=http://localhost:5001/console/api
|
||||
NEXT_PUBLIC_PUBLIC_API_PREFIX=http://localhost:5001/api
|
||||
# When the frontend and backend run on different subdomains, set NEXT_PUBLIC_COOKIE_DOMAIN=1.
|
||||
NEXT_PUBLIC_COOKIE_DOMAIN=
|
||||
# WebSocket server URL.
|
||||
NEXT_PUBLIC_SOCKET_URL=ws://localhost:5001
|
||||
|
||||
# Dev-only Hono proxy targets.
|
||||
# The frontend keeps requesting http://localhost:5001 directly,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import { CLEAR_HIDE_MENU_TIMEOUT } from '../plugins/workflow-variable-block'
|
||||
import SandboxPlaceholder from '../sandbox-placeholder'
|
||||
|
||||
const mocks = vi.hoisted(() => {
|
||||
@@ -100,7 +99,6 @@ describe('SandboxPlaceholder', () => {
|
||||
expect(mocks.selectEnd).toHaveBeenCalledTimes(1)
|
||||
expect(mocks.createTextNode).toHaveBeenCalledWith('/')
|
||||
expect(mocks.insertNodes).toHaveBeenCalledWith([{ text: '/' }])
|
||||
expect(mocks.editor.dispatchCommand).toHaveBeenCalledWith(CLEAR_HIDE_MENU_TIMEOUT, undefined)
|
||||
})
|
||||
|
||||
it('should insert at-sign and clear the hide timeout when clicking tools', () => {
|
||||
@@ -113,7 +111,6 @@ describe('SandboxPlaceholder', () => {
|
||||
expect(mocks.selectEnd).toHaveBeenCalledTimes(1)
|
||||
expect(mocks.createTextNode).toHaveBeenCalledWith('@')
|
||||
expect(mocks.insertNodes).toHaveBeenCalledWith([{ text: '@' }])
|
||||
expect(mocks.editor.dispatchCommand).toHaveBeenCalledWith(CLEAR_HIDE_MENU_TIMEOUT, undefined)
|
||||
})
|
||||
|
||||
it('should not trigger editor insertion when placeholder is not editable', () => {
|
||||
|
||||
@@ -39,9 +39,9 @@ import {
|
||||
Fragment,
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
|
||||
@@ -5,7 +5,6 @@ import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { $createCustomTextNode } from './plugins/custom-text/node'
|
||||
import { CLEAR_HIDE_MENU_TIMEOUT } from './plugins/workflow-variable-block'
|
||||
|
||||
type SandboxPlaceholderTokenProps = {
|
||||
actionLabel?: string
|
||||
@@ -74,7 +73,6 @@ const SandboxPlaceholder: FC<SandboxPlaceholderProps> = ({
|
||||
editor.update(() => {
|
||||
$getRoot().selectEnd()
|
||||
$insertNodes([$createCustomTextNode(trigger)])
|
||||
editor.dispatchCommand(CLEAR_HIDE_MENU_TIMEOUT, undefined)
|
||||
})
|
||||
})
|
||||
}, [editor])
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from '@/app/components/base/ui/popover'
|
||||
import { FilePickerPanel } from './file-picker-panel'
|
||||
import { $createFileReferenceNode } from './file-reference-block/node'
|
||||
import { useEditorBlur } from './hooks/use-editor-blur'
|
||||
|
||||
class FilePickerMenuOption extends MenuOption {
|
||||
constructor() {
|
||||
@@ -30,6 +31,8 @@ const FilePickerBlock = () => {
|
||||
|
||||
const options = useMemo(() => [new FilePickerMenuOption()], [])
|
||||
|
||||
const { blurHidden } = useEditorBlur(editor)
|
||||
|
||||
const insertFileReference = useCallback((resourceId: string) => {
|
||||
editor.update(() => {
|
||||
const match = checkForTriggerMatch('/', editor)
|
||||
@@ -46,6 +49,8 @@ const FilePickerBlock = () => {
|
||||
anchorElementRef: React.RefObject<HTMLElement | null>,
|
||||
{ selectOptionAndCleanUp }: { selectOptionAndCleanUp: (option: MenuOption) => void },
|
||||
) => {
|
||||
if (blurHidden)
|
||||
return null
|
||||
if (!anchorElementRef.current)
|
||||
return null
|
||||
|
||||
@@ -75,7 +80,7 @@ const FilePickerBlock = () => {
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}, [insertFileReference, options])
|
||||
}, [blurHidden, insertFileReference, options])
|
||||
|
||||
return (
|
||||
<LexicalTypeaheadMenuPlugin
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import type { LexicalEditor } from 'lexical'
|
||||
import { BLUR_COMMAND, COMMAND_PRIORITY_EDITOR, FOCUS_COMMAND, mergeRegister } from 'lexical'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
|
||||
export const useEditorBlur = (editor: LexicalEditor) => {
|
||||
const [blurHidden, setBlurHidden] = useState(false)
|
||||
const blurTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
|
||||
const clearBlurTimer = useCallback(() => {
|
||||
if (blurTimerRef.current) {
|
||||
clearTimeout(blurTimerRef.current)
|
||||
blurTimerRef.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const unregister = mergeRegister(
|
||||
editor.registerCommand(
|
||||
BLUR_COMMAND,
|
||||
() => {
|
||||
clearBlurTimer()
|
||||
blurTimerRef.current = setTimeout(() => setBlurHidden(true), 200)
|
||||
return false
|
||||
},
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
),
|
||||
editor.registerCommand(
|
||||
FOCUS_COMMAND,
|
||||
() => {
|
||||
clearBlurTimer()
|
||||
setBlurHidden(false)
|
||||
return false
|
||||
},
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
),
|
||||
)
|
||||
|
||||
return () => {
|
||||
if (blurTimerRef.current)
|
||||
clearTimeout(blurTimerRef.current)
|
||||
unregister()
|
||||
}
|
||||
}, [editor, clearBlurTimer])
|
||||
|
||||
return {
|
||||
blurHidden,
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import { toolParametersToFormSchemas } from '@/app/components/tools/utils/to-for
|
||||
import ToolPicker from '@/app/components/workflow/block-selector/tool-picker'
|
||||
import { START_TAB_ID } from '@/app/components/workflow/skill/constants'
|
||||
import { useWorkflowStore } from '@/app/components/workflow/store'
|
||||
import { useEditorBlur } from '../hooks/use-editor-blur'
|
||||
import { $createToolBlockNode } from './node'
|
||||
import { useToolBlockContext } from './tool-block-context'
|
||||
import { $createToolGroupBlockNode } from './tool-group-block-node'
|
||||
@@ -52,6 +53,8 @@ const ToolPickerBlock = ({ scope = 'all', enableAutoDefault = false }: ToolPicke
|
||||
const isUsingExternalMetadata = Boolean(onMetadataChange)
|
||||
const [queryString, setQueryString] = useState('')
|
||||
|
||||
const { blurHidden } = useEditorBlur(editor)
|
||||
|
||||
const canUseAutoByType = useCallback(
|
||||
(type: string) => ![FormTypeEnum.modelSelector, FormTypeEnum.appSelector].includes(type as FormTypeEnum),
|
||||
[],
|
||||
@@ -159,6 +162,8 @@ const ToolPickerBlock = ({ scope = 'all', enableAutoDefault = false }: ToolPicke
|
||||
anchorElementRef: React.RefObject<HTMLElement | null>,
|
||||
{ selectOptionAndCleanUp }: { selectOptionAndCleanUp: (option: MenuOption) => void },
|
||||
) => {
|
||||
if (blurHidden)
|
||||
return null
|
||||
if (!anchorElementRef.current)
|
||||
return null
|
||||
|
||||
@@ -199,7 +204,7 @@ const ToolPickerBlock = ({ scope = 'all', enableAutoDefault = false }: ToolPicke
|
||||
/>,
|
||||
anchorElementRef.current,
|
||||
)
|
||||
}, [insertTools, options, queryString, scope])
|
||||
}, [blurHidden, insertTools, options, queryString, scope])
|
||||
|
||||
return (
|
||||
<LexicalTypeaheadMenuPlugin
|
||||
|
||||
@@ -148,20 +148,11 @@
|
||||
"erasable-syntax-only/enums": {
|
||||
"count": 1
|
||||
},
|
||||
"style/indent": {
|
||||
"count": 175
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 5
|
||||
},
|
||||
"unused-imports/no-unused-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/account/(commonLayout)/account-page/index.tsx": {
|
||||
"style/indent": {
|
||||
"count": 93
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
@@ -369,9 +360,6 @@
|
||||
},
|
||||
"app/components/app/app-publisher/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
@@ -391,9 +379,6 @@
|
||||
}
|
||||
},
|
||||
"app/components/app/configuration/config-prompt/advanced-prompt-input.tsx": {
|
||||
"perfectionist/sort-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 2
|
||||
}
|
||||
@@ -412,19 +397,13 @@
|
||||
}
|
||||
},
|
||||
"app/components/app/configuration/config-prompt/simple-prompt-input.tsx": {
|
||||
"perfectionist/sort-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 3
|
||||
}
|
||||
},
|
||||
"app/components/app/configuration/config-var/config-modal/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 6
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/app/configuration/config-var/config-modal/type-select.tsx": {
|
||||
@@ -435,9 +414,6 @@
|
||||
"app/components/app/configuration/config-var/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
},
|
||||
"react-refresh/only-export-components": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/app/configuration/config-var/select-var-type.tsx": {
|
||||
@@ -639,9 +615,6 @@
|
||||
}
|
||||
},
|
||||
"app/components/app/configuration/dataset-config/settings-modal/index.tsx": {
|
||||
"perfectionist/sort-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"react/set-state-in-effect": {
|
||||
"count": 2
|
||||
},
|
||||
@@ -708,9 +681,6 @@
|
||||
}
|
||||
},
|
||||
"app/components/app/configuration/debug/index.tsx": {
|
||||
"perfectionist/sort-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"react/set-state-in-effect": {
|
||||
"count": 2
|
||||
},
|
||||
@@ -723,26 +693,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/app/configuration/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"perfectionist/sort-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"react/set-state-in-effect": {
|
||||
"count": 1
|
||||
},
|
||||
"style/multiline-ternary": {
|
||||
"count": 2
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 24
|
||||
},
|
||||
"unused-imports/no-unused-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/app/configuration/prompt-value-panel/index.spec.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 2
|
||||
@@ -758,19 +708,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/app/configuration/tools/external-data-tool-modal.tsx": {
|
||||
"perfectionist/sort-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"app/components/app/configuration/tools/index.tsx": {
|
||||
"perfectionist/sort-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/app/create-app-dialog/app-card/index.spec.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
@@ -798,17 +735,8 @@
|
||||
"erasable-syntax-only/enums": {
|
||||
"count": 1
|
||||
},
|
||||
"perfectionist/sort-imports": {
|
||||
"count": 2
|
||||
},
|
||||
"react-refresh/only-export-components": {
|
||||
"count": 1
|
||||
},
|
||||
"react/set-state-in-effect": {
|
||||
"count": 2
|
||||
},
|
||||
"unused-imports/no-unused-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/app/duplicate-modal/index.tsx": {
|
||||
@@ -830,9 +758,6 @@
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"perfectionist/sort-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"react/set-state-in-effect": {
|
||||
"count": 6
|
||||
},
|
||||
@@ -889,9 +814,6 @@
|
||||
"no-restricted-imports": {
|
||||
"count": 3
|
||||
},
|
||||
"perfectionist/sort-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"react/set-state-in-effect": {
|
||||
"count": 3
|
||||
},
|
||||
@@ -908,25 +830,13 @@
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
},
|
||||
"perfectionist/sort-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"react/set-state-in-effect": {
|
||||
"count": 1
|
||||
},
|
||||
"unused-imports/no-unused-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/app/text-generate/item/index.tsx": {
|
||||
"react-refresh/only-export-components": {
|
||||
"count": 1
|
||||
},
|
||||
"react/set-state-in-effect": {
|
||||
"count": 3
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 3
|
||||
}
|
||||
},
|
||||
"app/components/app/text-generate/item/result-tab.tsx": {
|
||||
@@ -964,11 +874,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/apps/app-card.tsx": {
|
||||
"perfectionist/sort-imports": {
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"app/components/apps/list.tsx": {
|
||||
"react-hooks/exhaustive-deps": {
|
||||
"count": 1
|
||||
@@ -4022,11 +3927,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/tools/workflow-tool/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/tools/workflow-tool/method-selector.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
|
||||
Reference in New Issue
Block a user