1
+ import React , { useState , useRef , useEffect } from 'react' ;
2
+ import { Image } from 'lucide-react' ;
3
+ import { SettingsService } from '../../services/settings-service' ;
4
+ import { AIServiceCapability } from '../../types/capabilities' ;
5
+ import ProviderIcon from '../ui/ProviderIcon' ;
6
+ import { useTranslation } from '../../hooks/useTranslation' ;
7
+
8
+ interface ImageGenerationButtonProps {
9
+ onImageGenerate ?: ( prompt : string , provider : string , model : string ) => void ;
10
+ disabled ?: boolean ;
11
+ }
12
+
13
+ interface ProviderModel {
14
+ providerName : string ;
15
+ modelId : string ;
16
+ modelName : string ;
17
+ }
18
+
19
+ const ImageGenerationButton : React . FC < ImageGenerationButtonProps > = ( {
20
+ disabled = false
21
+ } ) => {
22
+ const { t } = useTranslation ( ) ;
23
+ const [ isPopupOpen , setIsPopupOpen ] = useState ( false ) ;
24
+ const [ providers , setProviders ] = useState < ProviderModel [ ] > ( [ ] ) ;
25
+ const [ selectedProvider , setSelectedProvider ] = useState < string | null > ( null ) ;
26
+ const [ selectedModel , setSelectedModel ] = useState < string | null > ( null ) ;
27
+ const popupRef = useRef < HTMLDivElement > ( null ) ;
28
+ const buttonRef = useRef < HTMLButtonElement > ( null ) ;
29
+
30
+ // Load available image generation providers and models
31
+ useEffect ( ( ) => {
32
+ const loadProviders = ( ) => {
33
+ const settingsService = SettingsService . getInstance ( ) ;
34
+ const availableProviders : ProviderModel [ ] = [ ] ;
35
+
36
+ // Get all providers from settings
37
+ const settings = settingsService . getSettings ( ) ;
38
+ const providerIds = Object . keys ( settings . providers ) ;
39
+
40
+ for ( const providerId of providerIds ) {
41
+ // Get the provider's settings
42
+ const providerSettings = settingsService . getProviderSettings ( providerId ) ;
43
+
44
+ if ( providerSettings . models ) {
45
+ // For now, just assume all models have image generation capability
46
+ // This would need to be updated once proper capability detection is implemented
47
+ for ( const model of providerSettings . models ) {
48
+ // Check if model has image generation capability
49
+ if ( model . modelCapabilities ?. includes ( AIServiceCapability . ImageGeneration ) ) {
50
+ availableProviders . push ( {
51
+ providerName : providerId ,
52
+ modelId : model . modelId ,
53
+ modelName : model . modelName
54
+ } ) ;
55
+ }
56
+ }
57
+ }
58
+ }
59
+
60
+ setProviders ( availableProviders ) ;
61
+
62
+ // Set default selected provider and model if available
63
+ if ( availableProviders . length > 0 ) {
64
+ setSelectedProvider ( availableProviders [ 0 ] . providerName ) ;
65
+ setSelectedModel ( availableProviders [ 0 ] . modelId ) ;
66
+ }
67
+ } ;
68
+
69
+ loadProviders ( ) ;
70
+ } , [ ] ) ;
71
+
72
+ // Handle click outside to close popup
73
+ useEffect ( ( ) => {
74
+ const handleClickOutside = ( event : MouseEvent ) => {
75
+ if (
76
+ popupRef . current &&
77
+ ! popupRef . current . contains ( event . target as Node ) &&
78
+ buttonRef . current &&
79
+ ! buttonRef . current . contains ( event . target as Node )
80
+ ) {
81
+ setIsPopupOpen ( false ) ;
82
+ }
83
+ } ;
84
+
85
+ document . addEventListener ( 'mousedown' , handleClickOutside ) ;
86
+ return ( ) => {
87
+ document . removeEventListener ( 'mousedown' , handleClickOutside ) ;
88
+ } ;
89
+ } , [ ] ) ;
90
+
91
+ const togglePopup = ( ) => {
92
+ setIsPopupOpen ( ! isPopupOpen ) ;
93
+ } ;
94
+
95
+ const handleProviderModelSelect = ( providerName : string , modelId : string ) => {
96
+ setSelectedProvider ( providerName ) ;
97
+ setSelectedModel ( modelId ) ;
98
+ setIsPopupOpen ( false ) ;
99
+ } ;
100
+
101
+ const isButtonEnabled = ! disabled && providers . length > 0 ;
102
+
103
+ return (
104
+ < div className = "relative" >
105
+ < button
106
+ ref = { buttonRef }
107
+ type = "button"
108
+ onClick = { togglePopup }
109
+ disabled = { ! isButtonEnabled }
110
+ className = "flex items-center justify-center w-8 h-8 rounded-full image-generation-button focus:outline-none"
111
+ title = { isButtonEnabled ? t ( 'chat.generateImage' ) : t ( 'chat.imageGenerationNotAvailable' ) }
112
+ >
113
+ < Image size = { 20 } />
114
+ </ button >
115
+
116
+ { isPopupOpen && (
117
+ < div
118
+ ref = { popupRef }
119
+ className = "absolute z-10 mt-2 image-generation-popup"
120
+ style = { { bottom : '100%' , left : 0 , minWidth : '220px' } }
121
+ >
122
+ < div className = "p-2" >
123
+ < div className = "mb-2 text-sm font-medium text-gray-700" >
124
+ { t ( 'chat.selectImageProvider' ) }
125
+ </ div >
126
+ < div className = "overflow-y-auto max-h-60" >
127
+ { providers . map ( ( provider ) => (
128
+ < div
129
+ key = { `${ provider . providerName } -${ provider . modelId } ` }
130
+ className = { `flex items-center px-3 py-2 cursor-pointer rounded-md ${
131
+ selectedProvider === provider . providerName && selectedModel === provider . modelId
132
+ ? 'image-generation-provider-selected'
133
+ : 'image-generation-provider-item'
134
+ } `}
135
+ onClick = { ( ) => handleProviderModelSelect ( provider . providerName , provider . modelId ) }
136
+ >
137
+ < ProviderIcon providerName = { provider . providerName } className = "w-5 h-5 mr-2" />
138
+ < div className = "flex flex-col" >
139
+ < span className = "text-sm font-medium" > { provider . providerName } </ span >
140
+ < span className = "text-xs text-gray-500" > { provider . modelName } </ span >
141
+ </ div >
142
+ </ div >
143
+ ) ) }
144
+
145
+ { providers . length === 0 && (
146
+ < div className = "px-3 py-2 text-sm text-gray-500" >
147
+ { t ( 'chat.noImageProvidersAvailable' ) }
148
+ </ div >
149
+ ) }
150
+ </ div >
151
+ </ div >
152
+ </ div >
153
+ ) }
154
+ </ div >
155
+ ) ;
156
+ } ;
157
+
158
+ export default ImageGenerationButton ;
0 commit comments