feat: use new styles

This commit is contained in:
crazywoola 2025-12-26 12:40:38 +08:00
parent 0f69e2f6ab
commit f2363fc458
3 changed files with 181 additions and 70 deletions

View File

@ -326,6 +326,20 @@ export const useWorkflowVibe = () => {
}
}, [defaultModel])
const getLatestModelConfig = useCallback(() => {
if (typeof window === 'undefined')
return modelConfig
const stored = localStorage.getItem('auto-gen-model')
if (!stored)
return modelConfig
try {
return JSON.parse(stored) as Model
}
catch {
return modelConfig
}
}, [modelConfig])
const availableNodesList = useMemo(() => {
if (!nodesMetaDataMap)
return []
@ -650,7 +664,8 @@ export const useWorkflowVibe = () => {
return
}
if (!modelConfig && !isMermaidFlowchart(trimmed)) {
const latestModelConfig = getLatestModelConfig()
if (!latestModelConfig && !isMermaidFlowchart(trimmed)) {
Toast.notify({ type: 'error', message: t('workflow.vibe.modelUnavailable') })
return
}
@ -667,6 +682,7 @@ export const useWorkflowVibe = () => {
showVibePanel: true,
isVibeGenerating: true,
vibePanelMermaidCode: '',
vibePanelInstruction: trimmed,
}))
try {
@ -701,7 +717,7 @@ export const useWorkflowVibe = () => {
if (!isMermaidFlowchart(trimmed)) {
const { error, flowchart } = await generateFlowchart({
instruction: trimmed,
model_config: modelConfig,
model_config: latestModelConfig,
available_nodes: availableNodesPayload,
existing_nodes: existingNodesPayload,
available_tools: toolsPayload,
@ -738,7 +754,6 @@ export const useWorkflowVibe = () => {
availableNodesList,
getNodesReadOnly,
handleSyncWorkflowDraft,
modelConfig,
nodeTypeLookup,
nodesMetaDataMap,
saveStateToHistory,
@ -746,6 +761,7 @@ export const useWorkflowVibe = () => {
t,
toolLookup,
toolOptions,
getLatestModelConfig,
])
const handleRegenerate = useCallback(async () => {

View File

@ -1,13 +1,23 @@
'use client'
import type { FC } from 'react'
import { RiCheckLine, RiCloseLine, RiLoader2Line, RiRefreshLine } from '@remixicon/react'
import { useCallback } from 'react'
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { CompletionParams, Model } from '@/types/app'
import { RiCheckLine, RiRefreshLine } from '@remixicon/react'
import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import ResPlaceholder from '@/app/components/app/configuration/config/automatic/res-placeholder'
import Button from '@/app/components/base/button'
import { Generator } from '@/app/components/base/icons/src/vender/other'
import Loading from '@/app/components/base/loading'
import Flowchart from '@/app/components/base/mermaid'
import { cn } from '@/utils/classnames'
import { VIBE_ACCEPT_EVENT, VIBE_REGENERATE_EVENT } from '../../constants'
import Modal from '@/app/components/base/modal'
import Textarea from '@/app/components/base/textarea'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
import { ModelModeType } from '@/types/app'
import { VIBE_ACCEPT_EVENT, VIBE_COMMAND_EVENT, VIBE_REGENERATE_EVENT } from '../../constants'
import { useStore } from '../../store'
const VibePanel: FC = () => {
@ -18,6 +28,57 @@ const VibePanel: FC = () => {
const setVibePanelMermaidCode = useStore(s => s.setVibePanelMermaidCode)
const isVibeGenerating = useStore(s => s.isVibeGenerating)
const setIsVibeGenerating = useStore(s => s.setIsVibeGenerating)
const vibePanelInstruction = useStore(s => s.vibePanelInstruction)
const setVibePanelInstruction = useStore(s => s.setVibePanelInstruction)
const localModel = localStorage.getItem('auto-gen-model')
? JSON.parse(localStorage.getItem('auto-gen-model') as string) as Model
: null
const [model, setModel] = useState<Model>(localModel || {
name: '',
provider: '',
mode: ModelModeType.chat,
completion_params: {} as CompletionParams,
})
const { defaultModel } = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.textGeneration)
useEffect(() => {
if (defaultModel) {
const localModel = localStorage.getItem('auto-gen-model')
? JSON.parse(localStorage.getItem('auto-gen-model') || '')
: null
if (localModel) {
setModel(localModel)
}
else {
setModel(prev => ({
...prev,
name: defaultModel.model,
provider: defaultModel.provider.provider,
}))
}
}
}, [defaultModel])
const handleModelChange = useCallback((newValue: { modelId: string, provider: string, mode?: string, features?: string[] }) => {
const newModel = {
...model,
provider: newValue.provider,
name: newValue.modelId,
mode: newValue.mode as ModelModeType,
}
setModel(newModel)
localStorage.setItem('auto-gen-model', JSON.stringify(newModel))
}, [model])
const handleCompletionParamsChange = useCallback((newParams: FormValue) => {
const newModel = {
...model,
completion_params: newParams as CompletionParams,
}
setModel(newModel)
localStorage.setItem('auto-gen-model', JSON.stringify(newModel))
}, [model])
const handleClose = useCallback(() => {
setShowVibePanel(false)
@ -25,6 +86,13 @@ const VibePanel: FC = () => {
setIsVibeGenerating(false)
}, [setShowVibePanel, setVibePanelMermaidCode, setIsVibeGenerating])
const handleGenerate = useCallback(() => {
const event = new CustomEvent(VIBE_COMMAND_EVENT, {
detail: { dsl: vibePanelInstruction },
})
document.dispatchEvent(event)
}, [vibePanelInstruction])
const handleAccept = useCallback(() => {
if (vibePanelMermaidCode) {
const event = new CustomEvent(VIBE_ACCEPT_EVENT, {
@ -44,78 +112,101 @@ const VibePanel: FC = () => {
if (!showVibePanel)
return null
return (
<div
className={cn(
'absolute bottom-0 right-0 top-0 z-20',
'flex flex-col',
'w-[600px] border-l border-divider-subtle',
'bg-components-panel-bg backdrop-blur-[10px]',
'rounded-xl shadow-xl',
)}
>
{/* Header */}
<div className="flex items-center justify-between border-b border-divider-subtle px-4 py-3">
<div className="text-sm font-semibold text-text-primary">
{t('workflow.vibe.panelTitle')}
</div>
<button
onClick={handleClose}
className="rounded-lg p-1 transition-colors hover:bg-state-base-hover"
>
<RiCloseLine className="h-4 w-4 text-text-tertiary" />
</button>
</div>
const renderLoading = (
<div className="flex h-full w-0 grow flex-col items-center justify-center space-y-3">
<Loading />
<div className="text-[13px] text-text-tertiary">{t('workflow.vibe.generatingFlowchart')}</div>
</div>
)
{/* Content */}
<div className="flex-1 overflow-y-auto p-4">
{isVibeGenerating && !vibePanelMermaidCode
? (
<div className="flex h-full flex-col items-center justify-center gap-4">
<RiLoader2Line className="h-4 w-4 animate-spin text-text-tertiary" />
<div className="text-sm text-text-tertiary">
{t('workflow.vibe.generatingFlowchart')}
return (
<Modal
isShow={showVibePanel}
onClose={handleClose}
className="min-w-[1140px] !p-0"
>
<div className="flex h-[680px] flex-wrap">
<div className="h-full w-[570px] shrink-0 overflow-y-auto border-r border-divider-regular p-6">
<div className="mb-5">
<div className="text-lg font-bold leading-[28px] text-text-primary">{t('app.gotoAnything.actions.vibeTitle')}</div>
<div className="mt-1 text-[13px] font-normal text-text-tertiary">{t('app.gotoAnything.actions.vibeDesc')}</div>
</div>
<div>
<ModelParameterModal
popupClassName="!w-[520px]"
portalToFollowElemContentClassName="z-[1000]"
isAdvancedMode={true}
provider={model.provider}
completionParams={model.completion_params}
modelId={model.name}
setModel={handleModelChange}
onCompletionParamsChange={handleCompletionParamsChange}
hideDebugWithMultipleModel
/>
</div>
<div className="mt-4">
<div className="system-sm-semibold-uppercase mb-1.5 text-text-secondary">{t('appDebug.generate.instruction')}</div>
<Textarea
className="min-h-[240px] resize-none rounded-[10px] px-4 pt-3"
placeholder={t('workflow.vibe.missingInstruction')}
value={vibePanelInstruction}
onChange={e => setVibePanelInstruction(e.target.value)}
/>
</div>
<div className="mt-7 flex justify-end space-x-2">
<Button onClick={handleClose}>{t('appDebug.generate.dismiss')}</Button>
<Button
className="flex space-x-1"
variant="primary"
onClick={handleGenerate}
disabled={isVibeGenerating}
>
<Generator className="h-4 w-4" />
<span className="text-xs font-semibold">{t('appDebug.generate.generate')}</span>
</Button>
</div>
</div>
{!isVibeGenerating && vibePanelMermaidCode && (
<div className="h-full w-0 grow bg-background-default-subtle p-6 pb-0">
<div className="flex h-full flex-col">
<div className="mb-3 flex shrink-0 items-center justify-between">
<div className="shrink-0 text-base font-semibold leading-[160%] text-text-secondary">{t('workflow.vibe.panelTitle')}</div>
<div className="flex items-center space-x-2">
<Button
variant="secondary"
size="medium"
onClick={handleRegenerate}
>
<RiRefreshLine className="mr-1 h-4 w-4" />
{t('workflow.vibe.regenerate')}
</Button>
<Button
variant="primary"
size="medium"
onClick={handleAccept}
>
<RiCheckLine className="mr-1 h-4 w-4" />
{t('workflow.vibe.accept')}
</Button>
</div>
</div>
)
: vibePanelMermaidCode
? (
<div className="h-full">
<div className="flex grow flex-col overflow-y-auto pb-6">
<div className="grow">
<Flowchart
PrimitiveCode={vibePanelMermaidCode}
theme="light"
/>
</div>
)
: (
<div className="flex h-full flex-col items-center justify-center gap-2 text-sm text-text-tertiary">
<div>{t('workflow.vibe.noFlowchartYet')}</div>
</div>
)}
</div>
</div>
</div>
)}
{isVibeGenerating && renderLoading}
{!isVibeGenerating && !vibePanelMermaidCode && <ResPlaceholder />}
</div>
{/* Footer Actions */}
{vibePanelMermaidCode && !isVibeGenerating && (
<div className="flex items-center justify-end gap-2 border-t border-divider-subtle px-4 py-3">
<Button
variant="secondary"
size="medium"
onClick={handleRegenerate}
>
<RiRefreshLine className="mr-1 h-4 w-4" />
{t('workflow.vibe.regenerate')}
</Button>
<Button
variant="primary"
size="medium"
onClick={handleAccept}
>
<RiCheckLine className="mr-1 h-4 w-4" />
{t('workflow.vibe.accept')}
</Button>
</div>
)}
</div>
</Modal>
)
}

View File

@ -30,6 +30,8 @@ export type PanelSliceShape = {
setVibePanelMermaidCode: (vibePanelMermaidCode: string) => void
isVibeGenerating: boolean
setIsVibeGenerating: (isVibeGenerating: boolean) => void
vibePanelInstruction: string
setVibePanelInstruction: (vibePanelInstruction: string) => void
}
export const createPanelSlice: StateCreator<PanelSliceShape> = set => ({
@ -56,4 +58,6 @@ export const createPanelSlice: StateCreator<PanelSliceShape> = set => ({
setVibePanelMermaidCode: vibePanelMermaidCode => set(() => ({ vibePanelMermaidCode })),
isVibeGenerating: false,
setIsVibeGenerating: isVibeGenerating => set(() => ({ isVibeGenerating })),
vibePanelInstruction: '',
setVibePanelInstruction: vibePanelInstruction => set(() => ({ vibePanelInstruction })),
})