"use client"; import type { CSSProperties } from "react"; import { Image as ImageIcon, LoaderCircle, MessageSquare, Music2, Play, Settings2, Video } from "lucide-react"; import { Button, Segmented } from "antd"; import { ModelPicker } from "@/components/model-picker"; import { defaultConfig, useConfigStore, useEffectiveConfig, type AiConfig } from "@/stores/use-config-store"; import { CreditSymbol, requestCreditCost } from "@/constant/credits"; import { canvasThemes } from "@/lib/canvas-theme"; import { useThemeStore } from "@/stores/use-theme-store"; import { CanvasImageSettingsPopover } from "./canvas-image-settings-popover"; import { CanvasAudioSettingsPopover, type CanvasAudioSettingKey } from "./canvas-audio-settings-popover"; import { CanvasVideoSettingsPopover } from "./canvas-video-settings-popover"; import type { CanvasGenerationMode, CanvasNodeData, CanvasNodeMetadata } from "../types"; type CanvasConfigNodePanelProps = { node: CanvasNodeData; isRunning: boolean; inputSummary: { textCount: number; imageCount: number; videoCount: number; audioCount: number }; onConfigChange: (nodeId: string, patch: Partial) => void; onGenerate: (nodeId: string) => void; onComposerToggle: () => void; }; export function CanvasConfigNodePanel({ node, isRunning, inputSummary, onConfigChange, onGenerate, onComposerToggle }: CanvasConfigNodePanelProps) { const globalConfig = useEffectiveConfig(); const modelCosts = useConfigStore((state) => state.publicSettings?.modelChannel.modelCosts); const openConfigDialog = useConfigStore((state) => state.openConfigDialog); const theme = canvasThemes[useThemeStore((state) => state.theme)]; const mode = node.metadata?.generationMode || "image"; const config = buildNodeConfig(globalConfig, node, mode); const count = Math.max(1, Math.min(15, Math.floor(Math.abs(Number(config.count)) || 1))); const credits = requestCreditCost({ channelMode: config.channelMode, modelCosts, model: config.model, count: mode === "image" ? count : 1 }); const chipStyle = { background: theme.node.fill, borderColor: theme.node.stroke, color: theme.node.text }; const hasAnyInput = Boolean(inputSummary.textCount || inputSummary.imageCount || inputSummary.videoCount || inputSummary.audioCount); const hasComposerContent = Boolean((node.metadata?.composerContent ?? node.metadata?.prompt ?? "").trim()); const canGenerate = hasComposerContent || (mode === "audio" ? inputSummary.textCount > 0 : hasAnyInput); return (
event.stopPropagation()}>
生成配置
event.stopPropagation()}> onConfigChange(node.id, { generationMode: value as CanvasGenerationMode })} options={[ { value: "image", label: ( 生图 ), }, { value: "text", label: ( 文本 ), }, { value: "video", label: ( ), }, { value: "audio", label: ( 音频 ), }, ]} />
event.stopPropagation()}> onConfigChange(node.id, { model })} capability={mode} onMissingConfig={() => openConfigDialog(true)} fullWidth /> {mode === "video" ? ( onConfigChange(node.id, videoConfigPatch(key, value))} /> ) : mode === "image" ? ( onConfigChange(node.id, key === "count" ? { count: Number(value) || 1 } : { [key]: value })} /> ) : mode === "audio" ? ( onConfigChange(node.id, audioConfigPatch(key, value))} /> ) : null}
); } function InputChip({ label, value, style }: { label: string; value: string; style: CSSProperties }) { return (
{label} {value}
); } function buildNodeConfig(globalConfig: AiConfig, node: CanvasNodeData, mode: CanvasGenerationMode): AiConfig { const defaultModel = mode === "image" ? globalConfig.imageModel : mode === "video" ? globalConfig.videoModel : mode === "audio" ? globalConfig.audioModel : globalConfig.textModel; return { ...globalConfig, model: node.metadata?.model || defaultModel || (mode === "audio" ? defaultConfig.audioModel : globalConfig.model || defaultConfig.model), quality: node.metadata?.quality || globalConfig.quality || defaultConfig.quality, size: node.metadata?.size || globalConfig.size || defaultConfig.size, videoSeconds: node.metadata?.seconds || globalConfig.videoSeconds || defaultConfig.videoSeconds, vquality: node.metadata?.vquality || globalConfig.vquality || defaultConfig.vquality, videoGenerateAudio: node.metadata?.generateAudio || globalConfig.videoGenerateAudio || defaultConfig.videoGenerateAudio, videoWatermark: node.metadata?.watermark || globalConfig.videoWatermark || defaultConfig.videoWatermark, audioVoice: node.metadata?.audioVoice || globalConfig.audioVoice || defaultConfig.audioVoice, audioFormat: node.metadata?.audioFormat || globalConfig.audioFormat || defaultConfig.audioFormat, audioSpeed: node.metadata?.audioSpeed || globalConfig.audioSpeed || defaultConfig.audioSpeed, audioInstructions: node.metadata?.audioInstructions || globalConfig.audioInstructions || defaultConfig.audioInstructions, count: String(node.metadata?.count || (mode === "image" ? globalConfig.canvasImageCount || globalConfig.count : globalConfig.count) || defaultConfig.count), }; } function videoConfigPatch(key: keyof AiConfig, value: string) { if (key === "videoSeconds") return { seconds: value }; if (key === "videoGenerateAudio") return { generateAudio: value }; if (key === "videoWatermark") return { watermark: value }; return { [key]: value }; } function audioConfigPatch(key: CanvasAudioSettingKey, value: string) { if (key === "audioVoice") return { audioVoice: value }; if (key === "audioFormat") return { audioFormat: value }; if (key === "audioSpeed") return { audioSpeed: value }; return { audioInstructions: value }; }