mirror of
https://github.com/langgenius/dify.git
synced 2026-04-30 05:06:29 +08:00
feat: modify shortcutsplugin
This commit is contained in:
parent
a9ea8cfd1c
commit
465e978209
@ -56,7 +56,7 @@ import { VariableValueBlockNode } from './plugins/variable-value-block/node'
|
|||||||
import { CustomTextNode } from './plugins/custom-text/node'
|
import { CustomTextNode } from './plugins/custom-text/node'
|
||||||
import OnBlurBlock from './plugins/on-blur-or-focus-block'
|
import OnBlurBlock from './plugins/on-blur-or-focus-block'
|
||||||
import UpdateBlock from './plugins/update-block'
|
import UpdateBlock from './plugins/update-block'
|
||||||
import ShortcutsPopupPlugin from './plugins/shortcuts-popup-plugin'
|
import ShortcutsPopupPlugin, { type Hotkey } from './plugins/shortcuts-popup-plugin'
|
||||||
import { textToEditorState } from './utils'
|
import { textToEditorState } from './utils'
|
||||||
import type {
|
import type {
|
||||||
ContextBlockType,
|
ContextBlockType,
|
||||||
@ -97,6 +97,7 @@ export type PromptEditorProps = {
|
|||||||
workflowVariableBlock?: WorkflowVariableBlockType
|
workflowVariableBlock?: WorkflowVariableBlockType
|
||||||
hitlInputBlock?: HITLInputBlockType
|
hitlInputBlock?: HITLInputBlockType
|
||||||
isSupportFileVar?: boolean
|
isSupportFileVar?: boolean
|
||||||
|
shortcutPopups?: Array<{ hotkey: Hotkey; Popup: React.ComponentType<{ onClose: () => void }> }>
|
||||||
}
|
}
|
||||||
|
|
||||||
const PromptEditor: FC<PromptEditorProps> = ({
|
const PromptEditor: FC<PromptEditorProps> = ({
|
||||||
@ -121,6 +122,7 @@ const PromptEditor: FC<PromptEditorProps> = ({
|
|||||||
workflowVariableBlock,
|
workflowVariableBlock,
|
||||||
hitlInputBlock,
|
hitlInputBlock,
|
||||||
isSupportFileVar,
|
isSupportFileVar,
|
||||||
|
shortcutPopups = [],
|
||||||
}) => {
|
}) => {
|
||||||
const { eventEmitter } = useEventEmitterContextContext()
|
const { eventEmitter } = useEventEmitterContextContext()
|
||||||
const initialConfig = {
|
const initialConfig = {
|
||||||
@ -197,22 +199,11 @@ const PromptEditor: FC<PromptEditorProps> = ({
|
|||||||
}
|
}
|
||||||
ErrorBoundary={LexicalErrorBoundary}
|
ErrorBoundary={LexicalErrorBoundary}
|
||||||
/>
|
/>
|
||||||
<ShortcutsPopupPlugin>
|
{shortcutPopups?.map(({ hotkey, Popup }, idx) => (
|
||||||
{closePortal => (
|
<ShortcutsPopupPlugin key={idx} hotkey={hotkey} >
|
||||||
<div>
|
{closePortal => <Popup onClose={closePortal} />}
|
||||||
<div>test content</div>
|
</ShortcutsPopupPlugin>
|
||||||
<button
|
))}
|
||||||
className='rounded border border-text-secondary text-xs text-text-primary'
|
|
||||||
onMouseDown={(e) => {
|
|
||||||
e.preventDefault() // necessary, otherwise the editor will lose focus
|
|
||||||
closePortal()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
close
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</ShortcutsPopupPlugin>
|
|
||||||
<ComponentPickerBlock
|
<ComponentPickerBlock
|
||||||
triggerString='/'
|
triggerString='/'
|
||||||
contextBlock={contextBlock}
|
contextBlock={contextBlock}
|
||||||
|
|||||||
@ -15,7 +15,12 @@ import cn from '@/utils/classnames'
|
|||||||
|
|
||||||
export const SHORTCUTS_EMPTY_CONTENT = 'shortcuts_empty_content'
|
export const SHORTCUTS_EMPTY_CONTENT = 'shortcuts_empty_content'
|
||||||
|
|
||||||
type Hotkey = string | ((e: KeyboardEvent) => boolean)
|
// Hotkey can be:
|
||||||
|
// - string: 'mod+/'
|
||||||
|
// - string[]: ['mod', '/']
|
||||||
|
// - string[][]: [['mod', '/'], ['mod', 'shift', '/']] (any combo matches)
|
||||||
|
// - function: custom matcher
|
||||||
|
export type Hotkey = string | string[] | string[][] | ((e: KeyboardEvent) => boolean)
|
||||||
|
|
||||||
type ShortcutPopupPluginProps = {
|
type ShortcutPopupPluginProps = {
|
||||||
hotkey?: Hotkey
|
hotkey?: Hotkey
|
||||||
@ -48,58 +53,79 @@ function matchHotkey(event: KeyboardEvent, hotkey?: Hotkey) {
|
|||||||
if (typeof hotkey === 'function')
|
if (typeof hotkey === 'function')
|
||||||
return hotkey(event)
|
return hotkey(event)
|
||||||
|
|
||||||
const parts = hotkey.toLowerCase().split('+').map(t => t.trim()).filter(Boolean)
|
const matchCombo = (tokens: string[]) => {
|
||||||
let expectedKey: string | null = null
|
const parts = tokens.map(t => t.toLowerCase().trim()).filter(Boolean)
|
||||||
|
let expectedKey: string | null = null
|
||||||
|
|
||||||
let needMod = false
|
let needMod = false
|
||||||
let needCtrl = false
|
let needCtrl = false
|
||||||
let needMeta = false
|
let needMeta = false
|
||||||
let needAlt = false
|
let needAlt = false
|
||||||
let needShift = false
|
let needShift = false
|
||||||
|
|
||||||
for (const p of parts) {
|
for (const p of parts) {
|
||||||
if (p === 'mod') {
|
if (p === 'mod') {
|
||||||
needMod = true
|
needMod = true
|
||||||
continue
|
continue
|
||||||
|
}
|
||||||
|
if (CTRL_ALIASES.has(p)) {
|
||||||
|
needCtrl = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (META_ALIASES.has(p)) {
|
||||||
|
needMeta = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (ALT_ALIASES.has(p)) {
|
||||||
|
needAlt = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (SHIFT_ALIASES.has(p)) {
|
||||||
|
needShift = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
expectedKey = p
|
||||||
}
|
}
|
||||||
if (CTRL_ALIASES.has(p)) {
|
|
||||||
needCtrl = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (META_ALIASES.has(p)) {
|
|
||||||
needMeta = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (ALT_ALIASES.has(p)) {
|
|
||||||
needAlt = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (SHIFT_ALIASES.has(p)) {
|
|
||||||
needShift = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
expectedKey = p
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needMod && !(event.metaKey || event.ctrlKey))
|
if (needMod && !(event.metaKey || event.ctrlKey))
|
||||||
return false
|
|
||||||
if (needCtrl && !event.ctrlKey)
|
|
||||||
return false
|
|
||||||
if (needMeta && !event.metaKey)
|
|
||||||
return false
|
|
||||||
if (needAlt && !event.altKey)
|
|
||||||
return false
|
|
||||||
if (needShift && !event.shiftKey)
|
|
||||||
return false
|
|
||||||
|
|
||||||
if (expectedKey) {
|
|
||||||
const k = event.key.toLowerCase()
|
|
||||||
const normalized = k === ' ' ? 'space' : k
|
|
||||||
if (normalized !== expectedKey)
|
|
||||||
return false
|
return false
|
||||||
|
if (needCtrl && !event.ctrlKey)
|
||||||
|
return false
|
||||||
|
if (needMeta && !event.metaKey)
|
||||||
|
return false
|
||||||
|
if (needAlt && !event.altKey)
|
||||||
|
return false
|
||||||
|
if (needShift && !event.shiftKey)
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (expectedKey) {
|
||||||
|
const k = event.key.toLowerCase()
|
||||||
|
const normalized = k === ' ' ? 'space' : k
|
||||||
|
if (normalized !== expectedKey)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
if (Array.isArray(hotkey)) {
|
||||||
|
const isNested = hotkey.length > 0 && Array.isArray((hotkey as unknown[])[0])
|
||||||
|
if (isNested) {
|
||||||
|
const combos = hotkey as string[][]
|
||||||
|
return combos.some(tokens => matchCombo(tokens))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const tokens = hotkey as string[]
|
||||||
|
return matchCombo(tokens)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokensFromString = hotkey
|
||||||
|
.toLowerCase()
|
||||||
|
.split('+')
|
||||||
|
.map(t => t.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
return matchCombo(tokensFromString)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ShortcutsPopupPlugin({
|
export default function ShortcutsPopupPlugin({
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user