@@ -7,6 +7,7 @@ import { ChevronDown, RefreshCw, Settings, Zap } from "lucide-react";
7
7
import { useTranslation } from "react-i18next" ;
8
8
import { AIService } from "../../services/ai-service" ;
9
9
import { OPENAI_PROVIDER_NAME } from "../../services/providers/openai-service" ;
10
+ import { FORGE_PROVIDER_NAME as TENSORBLOCK_PROVIDER_NAME } from "../../services/providers/forge-service" ;
10
11
import { ImageGenerationManager , ImageGenerationStatus , ImageGenerationHandler } from "../../services/image-generation-handler" ;
11
12
import { DatabaseIntegrationService } from "../../services/database-integration" ;
12
13
import { ImageGenerationResult } from "../../types/image" ;
@@ -29,9 +30,12 @@ export const ImageGenerationPage = () => {
29
30
const [ historyResults , setHistoryResults ] = useState < ImageGenerationResult [ ] > ( [ ] ) ;
30
31
const [ isLoadingHistory , setIsLoadingHistory ] = useState ( true ) ;
31
32
const [ isSettingsOpen , setIsSettingsOpen ] = useState ( false ) ;
33
+ const [ selectedProvider , setSelectedProvider ] = useState ( OPENAI_PROVIDER_NAME ) ;
32
34
33
35
const settingsButtonRef = useRef < HTMLButtonElement > ( null ) ;
34
36
const settingsPopupRef = useRef < HTMLDivElement > ( null ) ;
37
+ const providerDropdownRef = useRef < HTMLDivElement > ( null ) ;
38
+ const [ isProviderDropdownOpen , setIsProviderDropdownOpen ] = useState ( false ) ;
35
39
36
40
// Load image generation history from database
37
41
const refreshImageHistory = useCallback ( async ( ) => {
@@ -50,7 +54,7 @@ export const ImageGenerationPage = () => {
50
54
}
51
55
} , [ ] ) ;
52
56
53
- // Initialize image generation manager
57
+ // Initialize image generation manager and load settings
54
58
useEffect ( ( ) => {
55
59
const initialize = async ( ) => {
56
60
// Initialize image generation manager
@@ -67,6 +71,16 @@ export const ImageGenerationPage = () => {
67
71
const dbService = DatabaseIntegrationService . getInstance ( ) ;
68
72
await dbService . initialize ( ) ;
69
73
74
+ // Load settings
75
+ const settingsService = SettingsService . getInstance ( ) ;
76
+ await settingsService . initialize ( ) ;
77
+
78
+ // Load saved provider preference
79
+ const settings = settingsService . getSettings ( ) ;
80
+ if ( settings . imageGenerationProvider ) {
81
+ setSelectedProvider ( settings . imageGenerationProvider ) ;
82
+ }
83
+
70
84
// Load image generation history from database
71
85
await refreshImageHistory ( ) ;
72
86
@@ -82,18 +96,24 @@ export const ImageGenerationPage = () => {
82
96
83
97
// Check if API key is available
84
98
useEffect ( ( ) => {
85
- setIsApiKeyMissing ( ! SettingsService . getInstance ( ) . getApiKey ( ) ) ;
99
+ const checkApiKey = ( ) => {
100
+ // Check if the selected provider has an API key
101
+ const hasApiKey = ! ! SettingsService . getInstance ( ) . getApiKey ( selectedProvider ) ;
102
+ setIsApiKeyMissing ( ! hasApiKey ) ;
103
+ } ;
104
+
105
+ checkApiKey ( ) ;
86
106
87
107
const handleSettingsChange = ( ) => {
88
- setIsApiKeyMissing ( ! SettingsService . getInstance ( ) . getApiKey ( ) ) ;
108
+ checkApiKey ( ) ;
89
109
} ;
90
110
91
111
window . addEventListener ( SETTINGS_CHANGE_EVENT , handleSettingsChange ) ;
92
112
93
113
return ( ) => {
94
114
window . removeEventListener ( SETTINGS_CHANGE_EVENT , handleSettingsChange ) ;
95
115
} ;
96
- } , [ ] ) ;
116
+ } , [ selectedProvider ] ) ;
97
117
98
118
// Handle clicks outside the settings popup
99
119
useEffect ( ( ) => {
@@ -105,6 +125,14 @@ export const ImageGenerationPage = () => {
105
125
! settingsButtonRef . current . contains ( event . target as Node )
106
126
) {
107
127
setIsSettingsOpen ( false ) ;
128
+ setIsProviderDropdownOpen ( false ) ;
129
+ } else if (
130
+ providerDropdownRef . current &&
131
+ ! providerDropdownRef . current . contains ( event . target as Node ) &&
132
+ event . target instanceof Element &&
133
+ ! event . target . closest ( '.provider-dropdown-toggle' )
134
+ ) {
135
+ setIsProviderDropdownOpen ( false ) ;
108
136
}
109
137
} ;
110
138
@@ -114,18 +142,24 @@ export const ImageGenerationPage = () => {
114
142
} ;
115
143
} , [ ] ) ;
116
144
117
- // Handle generating an image using OpenAI's DALL-E 3
145
+ // Handle generating an image using selected provider
118
146
const handleGenerateImage = async ( ) => {
119
147
if ( ! prompt . trim ( ) ) return ;
120
148
121
149
setError ( null ) ;
122
150
123
151
try {
124
- // Get the OpenAI service from AIService
125
- const openaiService = AIService . getInstance ( ) . getProvider ( OPENAI_PROVIDER_NAME ) ;
152
+ let providerService ;
153
+
154
+ // Get the appropriate service based on selected provider
155
+ if ( selectedProvider === TENSORBLOCK_PROVIDER_NAME ) {
156
+ providerService = AIService . getInstance ( ) . getProvider ( TENSORBLOCK_PROVIDER_NAME ) ;
157
+ } else {
158
+ providerService = AIService . getInstance ( ) . getProvider ( OPENAI_PROVIDER_NAME ) ;
159
+ }
126
160
127
- if ( ! openaiService ) {
128
- throw new Error ( "OpenAI service not available" ) ;
161
+ if ( ! providerService ) {
162
+ throw new Error ( ` ${ selectedProvider } service not available` ) ;
129
163
}
130
164
131
165
// Create a new generation handler
@@ -135,7 +169,7 @@ export const ImageGenerationPage = () => {
135
169
seed : randomSeed ,
136
170
number : imageCount ,
137
171
aspectRatio : aspectRatio ,
138
- provider : OPENAI_PROVIDER_NAME ,
172
+ provider : selectedProvider ,
139
173
model : "dall-e-3" ,
140
174
} ) ;
141
175
@@ -153,7 +187,7 @@ export const ImageGenerationPage = () => {
153
187
} ;
154
188
155
189
// Generate the image
156
- const images = await openaiService . getImageGeneration ( prompt , {
190
+ const images = await providerService . getImageGeneration ( prompt , {
157
191
size : sizeMap [ aspectRatio ] || "1024x1024" ,
158
192
aspectRatio : aspectRatio as `${number } :${number } `,
159
193
style : "vivid"
@@ -223,6 +257,24 @@ export const ImageGenerationPage = () => {
223
257
// Toggle settings popup
224
258
const toggleSettings = ( ) => {
225
259
setIsSettingsOpen ( ! isSettingsOpen ) ;
260
+ setIsProviderDropdownOpen ( false ) ;
261
+ } ;
262
+
263
+ // Toggle provider dropdown
264
+ const toggleProviderDropdown = ( ) => {
265
+ setIsProviderDropdownOpen ( ! isProviderDropdownOpen ) ;
266
+ } ;
267
+
268
+ // Handle provider selection
269
+ const handleProviderSelect = async ( provider : string ) => {
270
+ setSelectedProvider ( provider ) ;
271
+ setIsProviderDropdownOpen ( false ) ;
272
+
273
+ // Save provider preference to settings
274
+ const settingsService = SettingsService . getInstance ( ) ;
275
+ await settingsService . updateSettings ( {
276
+ imageGenerationProvider : provider
277
+ } ) ;
226
278
} ;
227
279
228
280
return (
@@ -441,12 +493,38 @@ export const ImageGenerationPage = () => {
441
493
</ label >
442
494
< div className = "relative" >
443
495
< button
444
- className = "flex items-center justify-between w-full p-3 text-left input-box"
445
- disabled = { true }
496
+ className = "flex items-center justify-between w-full p-3 text-left provider-dropdown-toggle input-box"
497
+ onClick = { toggleProviderDropdown }
446
498
>
447
- < span > OpenAI </ span >
499
+ < span > { selectedProvider } </ span >
448
500
< ChevronDown size = { 18 } className = "text-gray-500" />
449
501
</ button >
502
+
503
+ { isProviderDropdownOpen && (
504
+ < div
505
+ ref = { providerDropdownRef }
506
+ className = "absolute z-20 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg"
507
+ >
508
+ < ul className = "py-1" >
509
+ < li
510
+ className = { `px-3 py-2 cursor-pointer hover:bg-gray-100 ${
511
+ selectedProvider === OPENAI_PROVIDER_NAME ? 'bg-gray-50 font-medium' : ''
512
+ } `}
513
+ onClick = { ( ) => handleProviderSelect ( OPENAI_PROVIDER_NAME ) }
514
+ >
515
+ OpenAI
516
+ </ li >
517
+ < li
518
+ className = { `px-3 py-2 cursor-pointer hover:bg-gray-100 ${
519
+ selectedProvider === TENSORBLOCK_PROVIDER_NAME ? 'bg-gray-50 font-medium' : ''
520
+ } `}
521
+ onClick = { ( ) => handleProviderSelect ( TENSORBLOCK_PROVIDER_NAME ) }
522
+ >
523
+ TensorBlock
524
+ </ li >
525
+ </ ul >
526
+ </ div >
527
+ ) }
450
528
</ div >
451
529
</ div >
452
530
0 commit comments