Compare commits

..

3 Commits

Author SHA1 Message Date
yyh
59912d10fc refactor(web): remove code generator React.FC 2026-08-09 17:46:48 +08:00
yyh
b975ed698e refactor(web): use css icon for code generator 2026-08-09 17:45:32 +08:00
yyh
03ca1730c7 fix(web): name code generator action 2026-08-09 17:44:35 +08:00
5 changed files with 60 additions and 35 deletions

View File

@@ -5177,6 +5177,14 @@
"count": 1
}
},
"web/app/components/workflow/panel/chat-variable-panel/index.tsx": {
"jsx_a11y/click-events-have-key-events": {
"count": 1
},
"jsx_a11y/no-static-element-interactions": {
"count": 1
}
},
"web/app/components/workflow/panel/chat-variable-panel/type.ts": {
"erasable-syntax-only/enums": {
"count": 1

View File

@@ -0,0 +1,24 @@
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
import CodeGenerateBtn from '../code-generator-button'
vi.mock('@/app/components/app/configuration/config/code-generator/get-code-generator-res', () => ({
GetCodeGeneratorResModal: () => <div role="dialog" aria-label="code generator" />,
}))
vi.mock('@/app/components/workflow/hooks-store', () => ({
useHooksStore: (selector: (state: { configsMap: { flowId: string } }) => unknown) =>
selector({ configsMap: { flowId: 'flow-1' } }),
}))
describe('CodeGenerateBtn', () => {
it('should open the code generator from the named action', async () => {
const user = userEvent.setup()
render(<CodeGenerateBtn nodeId="node-1" codeLanguages={CodeLanguage.python3} />)
await user.click(screen.getByRole('button', { name: 'appDebug.operation.automatic' }))
expect(screen.getByRole('dialog', { name: 'code generator' })).toBeInTheDocument()
})
})

View File

@@ -1,13 +1,12 @@
'use client'
import type { FC } from 'react'
import type { CodeLanguage } from '../../code/types'
import type { GenRes } from '@/service/debug'
import { cn } from '@langgenius/dify-ui/cn'
import * as React from 'react'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { GetCodeGeneratorResModal } from '@/app/components/app/configuration/config/code-generator/get-code-generator-res'
import { ActionButton } from '@/app/components/base/action-button'
import { Generator } from '@/app/components/base/icons/src/vender/other'
import { AppModeEnum } from '@/types/app'
import { useHooksStore } from '../../../hooks-store'
@@ -19,13 +18,8 @@ type Props = Readonly<{
codeLanguages: CodeLanguage
}>
const CodeGenerateBtn: FC<Props> = ({
nodeId,
currentCode,
className,
codeLanguages,
onGenerated,
}) => {
const CodeGenerateBtn = ({ nodeId, currentCode, className, codeLanguages, onGenerated }: Props) => {
const { t } = useTranslation()
const [showAutomatic, setShowAutomatic] = useState(false)
const handleAutomaticRes = useCallback(
(res: GenRes) => {
@@ -38,8 +32,12 @@ const CodeGenerateBtn: FC<Props> = ({
return (
<div className={cn(className)}>
<ActionButton className="hover:bg-[#155EFF]/8" onClick={() => setShowAutomatic(true)}>
<Generator className="size-4 text-primary-600" />
<ActionButton
aria-label={t(($) => $['operation.automatic'], { ns: 'appDebug' })}
className="hover:bg-[#155EFF]/8"
onClick={() => setShowAutomatic(true)}
>
<span aria-hidden className="i-custom-vender-other-generator size-4 text-primary-600" />
</ActionButton>
{showAutomatic && (
<GetCodeGeneratorResModal

View File

@@ -197,16 +197,18 @@ describe('ChatVariablePanel', () => {
it('should toggle the tips area and close the panel', async () => {
const user = userEvent.setup()
render(<ChatVariablePanel />)
const { container } = render(<ChatVariablePanel />)
expect(screen.getByText('workflow.chatVariable.panelDescription')).toBeInTheDocument()
const toggleTipButton = screen.getByRole('button', { name: 'TIPS', expanded: true })
const toggleTipButton = screen.getAllByRole('button')[0]!
await user.click(toggleTipButton)
expect(screen.queryByText('workflow.chatVariable.panelDescription')).not.toBeInTheDocument()
expect(screen.getByRole('button', { name: 'TIPS', expanded: false })).toBeInTheDocument()
await user.click(screen.getByRole('button', { name: 'common.operation.close' }))
const closeButton = container.querySelector(
'.flex.size-6.cursor-pointer.items-center.justify-center',
) as HTMLElement
await user.click(closeButton)
expect(mockSetShowChatVariablePanel).toHaveBeenCalledWith(false)
})

View File

@@ -1,8 +1,14 @@
import type { ConversationVariable } from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
import { RiBookOpenLine, RiCloseLine } from '@remixicon/react'
import { memo, useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
import {
BubbleX,
LongArrowLeft,
LongArrowRight,
} from '@/app/components/base/icons/src/vender/line/others'
import BlockIcon from '@/app/components/workflow/block-icon'
import { webSocketClient } from '@/app/components/workflow/collaboration/core/websocket-manager'
import RemoveEffectVarConfirm from '@/app/components/workflow/nodes/_base/components/remove-effect-var-confirm'
@@ -206,21 +212,17 @@ const ChatVariablePanel = () => {
{t(($) => $['chatVariable.panelTitle'], { ns: 'workflow' })}
<div className="flex items-center gap-1">
<ActionButton
aria-label="TIPS"
aria-expanded={showTip}
state={showTip ? ActionButtonState.Active : undefined}
onClick={() => setShowTip(!showTip)}
>
<span aria-hidden="true" className="i-ri-book-open-line size-4" />
<RiBookOpenLine className="size-4" />
</ActionButton>
<button
type="button"
aria-label={t(($) => $['operation.close'], { ns: 'common' })}
<div
className="flex size-6 cursor-pointer items-center justify-center"
onClick={() => setShowChatVariablePanel(false)}
>
<span aria-hidden="true" className="i-ri-close-line size-4 text-text-tertiary" />
</button>
<RiCloseLine className="size-4 text-text-tertiary" />
</div>
</div>
</div>
{showTip && (
@@ -234,20 +236,14 @@ const ChatVariablePanel = () => {
</div>
<div className="flex items-center gap-2">
<div className="flex flex-col rounded-[10px] border border-workflow-block-border bg-workflow-block-bg p-3 pb-4 shadow-md">
<span
aria-hidden="true"
className="mb-1 i-custom-vender-line-others-bubble-x size-4 shrink-0 text-util-colors-teal-teal-700"
/>
<BubbleX className="mb-1 size-4 shrink-0 text-util-colors-teal-teal-700" />
<div className="system-xs-semibold text-text-secondary">conversation_var</div>
<div className="system-2xs-regular text-text-tertiary">String</div>
</div>
<div className="grow">
<div className="mb-2 flex items-center gap-2 py-1">
<div className="flex h-3 w-16 shrink-0 items-center gap-1 px-1">
<span
aria-hidden="true"
className="i-custom-vender-line-others-long-arrow-left h-2 grow text-text-quaternary"
/>
<LongArrowLeft className="h-2 grow text-text-quaternary" />
<div className="shrink-0 system-2xs-medium text-text-tertiary">WRITE</div>
</div>
<BlockIcon className="shrink-0" type={BlockEnum.Assigner} />
@@ -258,10 +254,7 @@ const ChatVariablePanel = () => {
<div className="flex items-center gap-2 py-1">
<div className="flex h-3 w-16 shrink-0 items-center gap-1 px-1">
<div className="shrink-0 system-2xs-medium text-text-tertiary">READ</div>
<span
aria-hidden="true"
className="i-custom-vender-line-others-long-arrow-right h-2 grow text-text-quaternary"
/>
<LongArrowRight className="h-2 grow text-text-quaternary" />
</div>
<BlockIcon className="shrink-0" type={BlockEnum.LLM} />
<div className="grow truncate system-xs-semibold text-text-secondary">