@@ -25,25 +25,15 @@ import {Select} from "@radix-ui/react-select";
2525import { IconChevronDown , IconPlayerStop , IconSend , IconSparkles2Filled } from "@tabler/icons-react" ;
2626import { AIService } from "@edition/ai/services/AI.service" ;
2727import { motion } from "framer-motion" ;
28- import { Flow , NamespaceProject , Subscription } from "@code0-tech/sagittarius-graphql-types" ;
29- import { useSubscription } from "@apollo/client/react" ;
30- import generateFlowSubscription from "@edition/ai/services/subscriptions/AI.generateFlow.subscription.graphql"
31-
32- const GENERATING_VARIANTS = [
33- "Generating..." ,
34- "Thinking..." ,
35- "Analyzing your prompt..." ,
36- "Composing flow..." ,
37- "Crafting nodes..." ,
38- "Wiring it up..." ,
39- "Almost there..."
40- ]
28+ import { AiGenerateFlowSubscriptionPayload , Flow , NamespaceProject } from "@code0-tech/sagittarius-graphql-types" ;
29+ import { useAIGenerationStore } from "@edition/ai/hooks/AI.generation.hook" ;
30+ import { AIGeneratingMessageComponent } from "@edition/ai/components/AIGeneratingMessageComponent" ;
4131
4232export interface AIChatComponentProps {
4333 projectId : NamespaceProject [ 'id' ]
4434 flowId ?: Flow [ 'id' ]
4535 prompt ?: string
46- onData ?: ( data : any ) => string | void
36+ onData ?: ( data : AiGenerateFlowSubscriptionPayload ) => string | void
4737
4838}
4939
@@ -53,70 +43,56 @@ export const AIChatComponent: React.FC<AIChatComponentProps> = (props) => {
5343
5444 const aiService = useService ( AIService )
5545 const aiStore = useStore ( AIService )
46+ const addGeneration = useAIGenerationStore ( s => s . addGeneration )
47+ const removeGeneration = useAIGenerationStore ( s => s . removeGeneration )
5648
5749 const [ promptState , setPromptState ] = React . useState < string > ( prompt )
5850 const [ model , setModel ] = React . useState < string | undefined > ( undefined )
5951 const [ executionIdentifier , setExecutionIdentifier ] = React . useState < string | null > ( null )
60- const [ isAIActive , setIsAIActive ] = React . useState ( false )
61- const [ aiVariantIndex , setAiVariantIndex ] = React . useState ( 0 )
6252 const [ aiErrorMessage , setAiErrorMessage ] = React . useState < string | null > ( null )
6353
54+ const isThisGenerating = useAIGenerationStore ( s =>
55+ ! ! executionIdentifier && s . hasGeneration ( executionIdentifier )
56+ )
57+
6458 const models = React . useMemo (
6559 ( ) => aiService . values ( ) ,
6660 [ aiStore ]
6761 )
6862
69- const { data} = useSubscription < Subscription > ( generateFlowSubscription , {
70- variables : { executionIdentifier : executionIdentifier } ,
71- skip : ! executionIdentifier ,
72- onData : ( data ) => {
73- setIsAIActive ( true )
74- if ( data . data . data ?. aiGenerateFlow ?. flow ) {
75- const result = onData ?.( data . data . data ?. aiGenerateFlow )
76- if ( typeof result === "string" ) {
77- setAiErrorMessage ( result )
78- } else {
79- setPromptState ( "" )
80- }
81- setExecutionIdentifier ( null )
82- } else if ( data . data . data ?. aiGenerateFlow ?. flow === null ) {
83- setExecutionIdentifier ( null )
84- setAiErrorMessage ( "Generation failed. Try another model." )
85- }
86- } ,
87- onComplete : ( ) => setIsAIActive ( false ) ,
88- onError : ( ) => {
89- setIsAIActive ( false )
90- setAiErrorMessage ( "Generation failed. Try another model." )
63+ React . useEffect ( ( ) => {
64+ if ( executionIdentifier && ! isThisGenerating ) {
65+ if ( ! aiErrorMessage ) setPromptState ( "" )
9166 setExecutionIdentifier ( null )
92- } ,
93- } )
67+ }
68+ } , [ executionIdentifier , isThisGenerating , aiErrorMessage ] )
9469
95- const aiLoading = React . useMemo (
96- ( ) => ( ( ! data || Object . keys ( data ) . length === 0 ) && executionIdentifier && isAIActive ) ,
97- [ executionIdentifier , data , isAIActive ]
98- )
70+ const aiLoading = ! ! executionIdentifier && isThisGenerating
9971
10072 const onSend = React . useCallback ( ( ) => {
73+ setAiErrorMessage ( null )
10174 aiService . generateFlow ( {
10275 prompt : promptState ,
10376 projectId : projectId ! ,
10477 modelIdentifier : model ! ,
10578 flowId : flowId ,
10679 } ) . then ( payload => {
107- if ( ( payload ?. errors ?. length ?? 0 ) <= 0 ) {
108- setExecutionIdentifier ( payload ?. executionIdentifier ?? null )
80+ if ( ( payload ?. errors ?. length ?? 0 ) <= 0 && payload ?. executionIdentifier ) {
81+ const id = payload . executionIdentifier
82+ setExecutionIdentifier ( id )
83+ addGeneration ( {
84+ executionIdentifier : id ,
85+ onData : ( data ) => onData ?.( data ) ,
86+ onError : ( message ) => setAiErrorMessage ( message ) ,
87+ } )
10988 }
11089 } )
111- } , [ aiService , model , promptState ] )
90+ } , [ aiService , model , promptState , projectId , flowId , onData , addGeneration ] )
11291
113- React . useEffect ( ( ) => {
114- const id = setInterval (
115- ( ) => setAiVariantIndex ( i => ( i + 1 ) % GENERATING_VARIANTS . length ) ,
116- 4000
117- )
118- return ( ) => clearInterval ( id )
119- } , [ ] )
92+ const onStop = React . useCallback ( ( ) => {
93+ if ( executionIdentifier ) removeGeneration ( executionIdentifier )
94+ setExecutionIdentifier ( null )
95+ } , [ executionIdentifier , removeGeneration ] )
12096
12197 React . useEffect ( ( ) => {
12298 setModel ( models . length > 0
@@ -186,30 +162,7 @@ export const AIChatComponent: React.FC<AIChatComponentProps> = (props) => {
186162 height : "2.3rem" ,
187163 } }
188164 >
189- < motion . div
190- key = { GENERATING_VARIANTS [ aiVariantIndex ] }
191- initial = { { y : 18 , opacity : 0 } }
192- animate = { { y : 0 , opacity : 1 } }
193- exit = { { y : - 18 , opacity : 0 } }
194- transition = { { duration : 0.45 , ease : [ 0.4 , 0 , 0.4 , 1 ] } }
195- >
196- < motion . div
197- style = { {
198- display : "block" ,
199- backgroundImage :
200- "linear-gradient(90deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.25) 40%, rgba(255,255,255,1) 50%, rgba(255,255,255,0.25) 60%, rgba(255,255,255,0.25) 100%)" ,
201- backgroundSize : "200% 100%" ,
202- WebkitBackgroundClip : "text" ,
203- backgroundClip : "text" ,
204- WebkitTextFillColor : "transparent" ,
205- color : "transparent" ,
206- } }
207- animate = { { backgroundPosition : [ "200% 0%" , "-200% 0%" ] } }
208- transition = { { duration : 3 , ease : "linear" , repeat : Infinity } }
209- >
210- < Text > { GENERATING_VARIANTS [ aiVariantIndex ] } </ Text >
211- </ motion . div >
212- </ motion . div >
165+ < AIGeneratingMessageComponent />
213166 </ div >
214167 ) : (
215168 < EditorInput
@@ -296,7 +249,7 @@ export const AIChatComponent: React.FC<AIChatComponentProps> = (props) => {
296249 </ Text >
297250 ) : null }
298251 { aiLoading ? (
299- < Button onClick = { ( ) => setExecutionIdentifier ( null ) } color = { "secondary" } >
252+ < Button onClick = { onStop } color = { "secondary" } >
300253 < IconPlayerStop size = { 13 } />
301254 </ Button >
302255 ) : (
0 commit comments