mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +08:00
feat: support hover hightlight cmd
This commit is contained in:
@ -1,16 +1,18 @@
|
||||
import type { ReactElement } from 'react'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { render } from '@testing-library/react'
|
||||
import SandboxPlaceholder from '../sandbox-placeholder'
|
||||
|
||||
vi.mock('react-i18next', () => ({
|
||||
Trans: ({ i18nKey, components = [] }: {
|
||||
i18nKey: string
|
||||
components?: ReactElement[]
|
||||
}) => (
|
||||
<div data-i18n-key={i18nKey} data-testid="sandbox-placeholder-trans">
|
||||
{components}
|
||||
</div>
|
||||
),
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => {
|
||||
const translations: Record<string, string> = {
|
||||
'promptEditor.placeholderSandboxPrefix': 'Write instructions here, ',
|
||||
'promptEditor.placeholderSandboxInsert': 'insert',
|
||||
'promptEditor.placeholderSandboxSeparator': ', ',
|
||||
'promptEditor.placeholderSandboxTools': 'tools',
|
||||
}
|
||||
return translations[key] ?? key
|
||||
},
|
||||
}),
|
||||
}))
|
||||
|
||||
describe('SandboxPlaceholder', () => {
|
||||
@ -24,10 +26,9 @@ describe('SandboxPlaceholder', () => {
|
||||
const { container } = render(<SandboxPlaceholder isSupportSandbox={false} />)
|
||||
|
||||
expect(container).toBeEmptyDOMElement()
|
||||
expect(screen.queryByTestId('sandbox-placeholder-trans')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render slash and insert tokens when tool blocks are disabled', () => {
|
||||
it('should render only the insert pair when tool blocks are disabled', () => {
|
||||
const { container } = render(
|
||||
<SandboxPlaceholder
|
||||
disableToolBlocks
|
||||
@ -35,25 +36,33 @@ describe('SandboxPlaceholder', () => {
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('sandbox-placeholder-trans')).toHaveAttribute('data-i18n-key', 'promptEditor.placeholderSandboxNoTools')
|
||||
|
||||
const spans = container.querySelectorAll('span')
|
||||
expect(spans).toHaveLength(2)
|
||||
expect(spans[0]).toHaveClass('inline-flex', 'bg-components-kbd-bg-gray', 'system-kbd')
|
||||
expect(spans[1]).toHaveClass('border-b', 'border-dotted', 'border-current')
|
||||
expect(container).toHaveTextContent('Write instructions here, /insert')
|
||||
expect(container.querySelector('.sandbox-placeholder-pair-insert')).toBeInTheDocument()
|
||||
expect(container.querySelector('.sandbox-placeholder-pair-tools')).not.toBeInTheDocument()
|
||||
expect(container.querySelector('.sandbox-placeholder-action-insert')).toHaveClass(
|
||||
'pointer-events-auto',
|
||||
'border-dotted',
|
||||
)
|
||||
})
|
||||
|
||||
it('should render slash insert at and tools tokens when tool blocks are enabled', () => {
|
||||
it('should render both insert and tools pairs with linked hover classes when tool blocks are enabled', () => {
|
||||
const { container } = render(<SandboxPlaceholder isSupportSandbox />)
|
||||
|
||||
expect(screen.getByTestId('sandbox-placeholder-trans')).toHaveAttribute('data-i18n-key', 'promptEditor.placeholderSandbox')
|
||||
|
||||
const spans = container.querySelectorAll('span')
|
||||
expect(spans).toHaveLength(4)
|
||||
expect(spans[0]).toHaveClass('inline-flex', 'bg-components-kbd-bg-gray', 'system-kbd')
|
||||
expect(spans[1]).toHaveClass('border-b', 'border-dotted', 'border-current')
|
||||
expect(spans[2]).toHaveClass('inline-flex', 'bg-components-kbd-bg-gray', 'system-kbd')
|
||||
expect(spans[3]).toHaveClass('border-b', 'border-dotted', 'border-current')
|
||||
expect(container).toHaveTextContent('Write instructions here, /insert, @tools')
|
||||
expect(container.querySelector('.sandbox-placeholder-kbd-insert')).toHaveTextContent('/')
|
||||
expect(container.querySelector('.sandbox-placeholder-kbd-tools')).toHaveTextContent('@')
|
||||
expect(container.querySelector('.sandbox-placeholder-action-insert')).toHaveTextContent('insert')
|
||||
expect(container.querySelector('.sandbox-placeholder-action-tools')).toHaveTextContent('tools')
|
||||
expect(container.querySelector('.sandbox-placeholder-pair-insert')).toHaveClass(
|
||||
'has-[.sandbox-placeholder-action-insert:hover]:[&_.sandbox-placeholder-kbd-insert]:bg-state-accent-hover-alt',
|
||||
'has-[.sandbox-placeholder-action-insert:hover]:[&_.sandbox-placeholder-action-insert]:bg-state-accent-hover',
|
||||
'has-[.sandbox-placeholder-action-insert:hover]:[&_.sandbox-placeholder-action-insert]:text-text-accent-secondary',
|
||||
)
|
||||
expect(container.querySelector('.sandbox-placeholder-pair-tools')).toHaveClass(
|
||||
'has-[.sandbox-placeholder-action-tools:hover]:[&_.sandbox-placeholder-kbd-tools]:bg-state-accent-hover-alt',
|
||||
'has-[.sandbox-placeholder-action-tools:hover]:[&_.sandbox-placeholder-action-tools]:bg-state-accent-hover',
|
||||
'has-[.sandbox-placeholder-action-tools:hover]:[&_.sandbox-placeholder-action-tools]:text-text-accent-secondary',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,22 +1,38 @@
|
||||
import type { FC, PropsWithChildren, ReactElement } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type SandboxPlaceholderTokenProps = PropsWithChildren<{
|
||||
variant: 'kbd' | 'action'
|
||||
}>
|
||||
|
||||
const SandboxPlaceholderToken: FC<SandboxPlaceholderTokenProps> = ({ variant, children }) => {
|
||||
if (variant === 'kbd') {
|
||||
return (
|
||||
<span className="inline-flex h-5 min-w-5 items-center justify-center rounded-[4px] bg-components-kbd-bg-gray px-1 text-text-tertiary system-kbd">
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
type SandboxPlaceholderTokenProps = {
|
||||
actionLabel?: string
|
||||
shortcut: '/' | '@'
|
||||
}
|
||||
|
||||
const SandboxPlaceholderToken: FC<SandboxPlaceholderTokenProps> = ({
|
||||
actionLabel,
|
||||
shortcut,
|
||||
}) => {
|
||||
return (
|
||||
<span className="border-b border-dotted border-current">
|
||||
{children}
|
||||
<span
|
||||
className={cn(
|
||||
'inline-flex cursor-pointer items-center gap-1 text-text-tertiary hover:text-components-button-secondary-accent-text',
|
||||
'group/placeholder-token',
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
'inline-flex h-5 min-w-5 items-center justify-center rounded-[4px] bg-components-kbd-bg-gray px-1 system-kbd',
|
||||
'group-hover/placeholder-token:bg-components-button-secondary-accent-text-disabled',
|
||||
)}
|
||||
>
|
||||
{shortcut}
|
||||
</span>
|
||||
<span
|
||||
className={cn(
|
||||
'pointer-events-auto border-b border-dotted border-current px-0.5 transition-colors',
|
||||
)}
|
||||
>
|
||||
{actionLabel}
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
@ -30,27 +46,28 @@ const SandboxPlaceholder: FC<SandboxPlaceholderProps> = ({
|
||||
disableToolBlocks,
|
||||
isSupportSandbox,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
if (!isSupportSandbox)
|
||||
return null
|
||||
|
||||
const components: ReactElement[] = [
|
||||
<SandboxPlaceholderToken key="slash" variant="kbd" />,
|
||||
<SandboxPlaceholderToken key="insert" variant="action" />,
|
||||
]
|
||||
|
||||
if (!disableToolBlocks) {
|
||||
components.push(
|
||||
<SandboxPlaceholderToken key="at" variant="kbd" />,
|
||||
<SandboxPlaceholderToken key="tools" variant="action" />,
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Trans
|
||||
i18nKey={disableToolBlocks ? 'promptEditor.placeholderSandboxNoTools' : 'promptEditor.placeholderSandbox'}
|
||||
ns="common"
|
||||
components={components}
|
||||
/>
|
||||
<span>
|
||||
{t('promptEditor.placeholderSandboxPrefix', { ns: 'common' })}
|
||||
<SandboxPlaceholderToken
|
||||
shortcut="/"
|
||||
actionLabel={t('promptEditor.placeholderSandboxInsert', { ns: 'common' })}
|
||||
/>
|
||||
{!disableToolBlocks && (
|
||||
<>
|
||||
{t('promptEditor.placeholderSandboxSeparator', { ns: 'common' })}
|
||||
<SandboxPlaceholderToken
|
||||
shortcut="@"
|
||||
actionLabel={t('promptEditor.placeholderSandboxTools', { ns: 'common' })}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -549,8 +549,10 @@
|
||||
"promptEditor.history.modal.title": "EXAMPLE",
|
||||
"promptEditor.history.modal.user": "Hello",
|
||||
"promptEditor.placeholder": "Write your prompt word here, enter '{' to insert a variable, enter '/' to insert a prompt content block",
|
||||
"promptEditor.placeholderSandbox": "Write instructions here, <0>/</0> <1>insert</1>, <2>@</2> <3>tools</3>",
|
||||
"promptEditor.placeholderSandboxNoTools": "Write instructions here, <0>/</0> <1>insert</1>",
|
||||
"promptEditor.placeholderSandboxInsert": "insert",
|
||||
"promptEditor.placeholderSandboxPrefix": "Write instructions here, ",
|
||||
"promptEditor.placeholderSandboxSeparator": ", ",
|
||||
"promptEditor.placeholderSandboxTools": "tools",
|
||||
"promptEditor.query.item.desc": "Insert user query template",
|
||||
"promptEditor.query.item.title": "Query",
|
||||
"promptEditor.requestURL.item.desc": "Insert request URL",
|
||||
|
||||
@ -534,8 +534,10 @@
|
||||
"promptEditor.history.modal.title": "例",
|
||||
"promptEditor.history.modal.user": "こんにちは",
|
||||
"promptEditor.placeholder": "ここにプロンプトワードを入力してください。変数を挿入するには「{」を、プロンプトコンテンツブロックを挿入するには「/」を入力します。",
|
||||
"promptEditor.placeholderSandbox": "ここに指示を書いて、<0>/</0> <1>挿入</1>、<2>@</2> <3>ツール</3>",
|
||||
"promptEditor.placeholderSandboxNoTools": "ここに指示を書いて、<0>/</0> <1>挿入</1>",
|
||||
"promptEditor.placeholderSandboxInsert": "挿入",
|
||||
"promptEditor.placeholderSandboxPrefix": "ここに指示を書いて、",
|
||||
"promptEditor.placeholderSandboxSeparator": "、",
|
||||
"promptEditor.placeholderSandboxTools": "ツール",
|
||||
"promptEditor.query.item.desc": "ユーザークエリテンプレートを挿入",
|
||||
"promptEditor.query.item.title": "クエリ",
|
||||
"promptEditor.requestURL.item.desc": "リクエストURLを挿入",
|
||||
|
||||
@ -549,8 +549,10 @@
|
||||
"promptEditor.history.modal.title": "示例",
|
||||
"promptEditor.history.modal.user": "你好",
|
||||
"promptEditor.placeholder": "在这里写你的提示词,输入'{' 插入变量、输入'/' 插入提示内容块",
|
||||
"promptEditor.placeholderSandbox": "在这里写你的指令,<0>/</0> <1>插入</1>,<2>@</2> <3>工具</3>",
|
||||
"promptEditor.placeholderSandboxNoTools": "在这里写你的指令,<0>/</0> <1>插入</1>",
|
||||
"promptEditor.placeholderSandboxInsert": "插入",
|
||||
"promptEditor.placeholderSandboxPrefix": "在这里写你的指令,",
|
||||
"promptEditor.placeholderSandboxSeparator": ",",
|
||||
"promptEditor.placeholderSandboxTools": "工具",
|
||||
"promptEditor.query.item.desc": "插入用户查询模板",
|
||||
"promptEditor.query.item.title": "查询内容",
|
||||
"promptEditor.requestURL.item.desc": "插入请求 URL",
|
||||
|
||||
@ -509,8 +509,10 @@
|
||||
"promptEditor.history.modal.title": "示例",
|
||||
"promptEditor.history.modal.user": "你好",
|
||||
"promptEditor.placeholder": "在這裡寫你的提示詞,輸入'{' 插入變數、輸入'/' 插入提示內容塊",
|
||||
"promptEditor.placeholderSandbox": "在這裡寫你的指令,<0>/</0> <1>插入</1>,<2>@</2> <3>工具</3>",
|
||||
"promptEditor.placeholderSandboxNoTools": "在這裡寫你的指令,<0>/</0> <1>插入</1>",
|
||||
"promptEditor.placeholderSandboxInsert": "插入",
|
||||
"promptEditor.placeholderSandboxPrefix": "在這裡寫你的指令,",
|
||||
"promptEditor.placeholderSandboxSeparator": ",",
|
||||
"promptEditor.placeholderSandboxTools": "工具",
|
||||
"promptEditor.query.item.desc": "插入使用者查詢模板",
|
||||
"promptEditor.query.item.title": "查詢內容",
|
||||
"promptEditor.requestURL.item.desc": "插入請求 URL",
|
||||
|
||||
Reference in New Issue
Block a user