prithivMLmods commited on
Commit
d06260a
·
verified ·
1 Parent(s): 71f220c

update user api re-cycling

Browse files
Files changed (1) hide show
  1. Home.tsx +72 -15
Home.tsx CHANGED
@@ -98,8 +98,8 @@ const cropImage = (
98
 
99
  // FIX: Changed to an async function declaration to avoid JSX parsing issues with Promise return types.
100
  // FIX: Renamed `history` parameter to `descriptions` to avoid conflict with the browser's built-in `History` type.
101
- async function serviceDescribeImage(imageDataUrl: string, descriptions: ImageDescription[]): Promise<ImageDescription> {
102
- const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
103
  const parts = imageDataUrl.split(',');
104
  const mimeType = parts[0].match(/:(.*?);/)?.[1] || 'image/png';
105
  const base64Data = parts[1];
@@ -175,8 +175,8 @@ ${descriptions.length ? descriptions.filter(Boolean).map((desc,index)=>`${index+
175
  }
176
 
177
  // FIX: Changed to an async function declaration to avoid JSX parsing issues with Promise return types.
178
- async function serviceEnhance(croppedImageDataUrl: string, history: string[]): Promise<{ imageSrc: string }> {
179
- const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
180
  const base64Data = croppedImageDataUrl.split(',')[1] || '';
181
  const imagePart = {
182
  inlineData: {
@@ -804,6 +804,10 @@ export default function Home() {
804
  const containerRef = useRef<HTMLDivElement>(null);
805
  const imageObjectURLRef = useRef<string | null>(null);
806
  const fileInputRef = useRef<HTMLInputElement>(null);
 
 
 
 
807
 
808
  const loadInitialImage = useCallback(async () => {
809
  if (imageObjectURLRef.current) {
@@ -928,17 +932,34 @@ export default function Home() {
928
 
929
  const handleProcessClick = useCallback(() => {
930
  if (!stagedSelection) return;
931
- startEnhancementProcess(stagedSelection.originalRect, stagedSelection.screenRect, stagedSelection.canvasDataUrl);
932
- setStagedSelection(null);
933
- }, [stagedSelection, startEnhancementProcess]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
934
 
935
  const runEnhancementJob = useCallback(async () => {
936
- if (!enhancementJob || !image) return;
937
  setIsProcessing(true);
938
  try {
939
  const { originalRect, canvasWithSelectionDataUrl, pixelatedSrc } = enhancementJob;
940
  const descriptionHistory = history.slice(0, historyIndex + 1).map(h => h.description).filter((d): d is ImageDescription => d !== null);
941
- const description = await serviceDescribeImage(canvasWithSelectionDataUrl, descriptionHistory);
942
  setNewHistoryEntryData({ description, originalRect });
943
  const sourceImageWidth = image.naturalWidth;
944
  const sourceImageHeight = image.naturalHeight;
@@ -957,7 +978,7 @@ export default function Home() {
957
  const targetHeight = Math.round(targetWidth * aspect);
958
  const croppedForEnhancement = await cropImage(image, paddedRect, targetWidth, targetHeight, false);
959
  const prompts = [...descriptionHistory.map(d=>(d.prompt || '')), description.prompt || ''];
960
- const { imageSrc: enhancedPaddedSrc } = await serviceEnhance(croppedForEnhancement, prompts);
961
 
962
  const enhancedPaddedImage = await new Promise<HTMLImageElement>((resolve, reject) => {
963
  const img = new Image();
@@ -988,7 +1009,7 @@ export default function Home() {
988
  setEnhancementJob(null);
989
  setIsProcessing(false);
990
  }
991
- }, [enhancementJob, image, history, historyIndex]);
992
 
993
  const handleEnhancementComplete = useCallback(() => {
994
  if (enhancedImageSrc && newHistoryEntryData) {
@@ -1046,7 +1067,7 @@ export default function Home() {
1046
  }, [history, historyIndex, appState, isGeneratingGif]);
1047
 
1048
  const handleRegenerate = useCallback(async () => {
1049
- if (historyIndex <= 0 || appState === AppState.ENHANCING || isGeneratingGif) return;
1050
  setAppState(AppState.ENHANCING);
1051
  setStagedSelection(null);
1052
  const previousStep = history[historyIndex - 1];
@@ -1058,7 +1079,7 @@ export default function Home() {
1058
  try {
1059
  const descriptionHistory = history.slice(0, historyIndex).map(h => h.description).filter((d): d is ImageDescription => d !== null);
1060
  const croppedForDescription = await cropImage(sourceImage, originalRect, originalRect.w, originalRect.h, false);
1061
- const description = await serviceDescribeImage(croppedForDescription, descriptionHistory);
1062
  const sourceImageWidth = sourceImage.naturalWidth;
1063
  const sourceImageHeight = sourceImage.naturalHeight;
1064
  const padding = 0.5;
@@ -1076,7 +1097,7 @@ export default function Home() {
1076
  const targetHeight = Math.round(targetWidth * aspect);
1077
  const croppedForEnhancement = await cropImage(sourceImage, paddedRect, targetWidth, targetHeight, false);
1078
  const prompts = [...descriptionHistory.map(d=>(d.prompt || '')), description.prompt || ''];
1079
- const { imageSrc: enhancedPaddedSrc } = await serviceEnhance(croppedForEnhancement, prompts);
1080
 
1081
  const enhancedPaddedImage = await new Promise<HTMLImageElement>((resolve, reject) => {
1082
  const img = new Image(); img.crossOrigin = "anonymous"; img.onload = () => resolve(img); img.onerror = reject; img.src = enhancedPaddedSrc;
@@ -1100,7 +1121,7 @@ export default function Home() {
1100
  } catch (error) { console.error("Regeneration failed:", error); setAppState(AppState.LOADED); }
1101
  };
1102
  sourceImage.src = previousStep.imageSrc;
1103
- }, [history, historyIndex, appState, isGeneratingGif]);
1104
 
1105
  const handleExportGif = useCallback(async () => {
1106
  if (historyIndex < 1) return;
@@ -1169,6 +1190,42 @@ export default function Home() {
1169
  )}
1170
  <input type="file" ref={fileInputRef} onChange={handleFileSelect} style={{ display: 'none' }} accept="image/*" />
1171
  <StatusBar state={appState} useFixedSelectionBox={useFixedSelectionBox} isInitialState={history.length <= 1} onUploadClick={handleUploadClick}/>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1172
  </div>
1173
  );
1174
  }
 
98
 
99
  // FIX: Changed to an async function declaration to avoid JSX parsing issues with Promise return types.
100
  // FIX: Renamed `history` parameter to `descriptions` to avoid conflict with the browser's built-in `History` type.
101
+ async function serviceDescribeImage(imageDataUrl: string, descriptions: ImageDescription[], apiKey: string): Promise<ImageDescription> {
102
+ const ai = new GoogleGenAI({ apiKey });
103
  const parts = imageDataUrl.split(',');
104
  const mimeType = parts[0].match(/:(.*?);/)?.[1] || 'image/png';
105
  const base64Data = parts[1];
 
175
  }
176
 
177
  // FIX: Changed to an async function declaration to avoid JSX parsing issues with Promise return types.
178
+ async function serviceEnhance(croppedImageDataUrl: string, history: string[], apiKey: string): Promise<{ imageSrc: string }> {
179
+ const ai = new GoogleGenAI({ apiKey });
180
  const base64Data = croppedImageDataUrl.split(',')[1] || '';
181
  const imagePart = {
182
  inlineData: {
 
804
  const containerRef = useRef<HTMLDivElement>(null);
805
  const imageObjectURLRef = useRef<string | null>(null);
806
  const fileInputRef = useRef<HTMLInputElement>(null);
807
+
808
+ // New state for API key management
809
+ const [apiKey, setApiKey] = useState<string | null>(null);
810
+ const [showApiKeyModal, setShowApiKeyModal] = useState(false);
811
 
812
  const loadInitialImage = useCallback(async () => {
813
  if (imageObjectURLRef.current) {
 
932
 
933
  const handleProcessClick = useCallback(() => {
934
  if (!stagedSelection) return;
935
+ if (apiKey) {
936
+ startEnhancementProcess(stagedSelection.originalRect, stagedSelection.screenRect, stagedSelection.canvasDataUrl);
937
+ setStagedSelection(null);
938
+ } else {
939
+ setShowApiKeyModal(true);
940
+ }
941
+ }, [stagedSelection, startEnhancementProcess, apiKey]);
942
+
943
+ const handleApiKeySubmit = async (e: React.FormEvent<HTMLFormElement>) => {
944
+ e.preventDefault();
945
+ const newApiKey = e.currentTarget.apiKey.value;
946
+ if (newApiKey) {
947
+ setApiKey(newApiKey);
948
+ setShowApiKeyModal(false);
949
+ if (stagedSelection) {
950
+ startEnhancementProcess(stagedSelection.originalRect, stagedSelection.screenRect, stagedSelection.canvasDataUrl);
951
+ setStagedSelection(null);
952
+ }
953
+ }
954
+ };
955
 
956
  const runEnhancementJob = useCallback(async () => {
957
+ if (!enhancementJob || !image || !apiKey) return;
958
  setIsProcessing(true);
959
  try {
960
  const { originalRect, canvasWithSelectionDataUrl, pixelatedSrc } = enhancementJob;
961
  const descriptionHistory = history.slice(0, historyIndex + 1).map(h => h.description).filter((d): d is ImageDescription => d !== null);
962
+ const description = await serviceDescribeImage(canvasWithSelectionDataUrl, descriptionHistory, apiKey);
963
  setNewHistoryEntryData({ description, originalRect });
964
  const sourceImageWidth = image.naturalWidth;
965
  const sourceImageHeight = image.naturalHeight;
 
978
  const targetHeight = Math.round(targetWidth * aspect);
979
  const croppedForEnhancement = await cropImage(image, paddedRect, targetWidth, targetHeight, false);
980
  const prompts = [...descriptionHistory.map(d=>(d.prompt || '')), description.prompt || ''];
981
+ const { imageSrc: enhancedPaddedSrc } = await serviceEnhance(croppedForEnhancement, prompts, apiKey);
982
 
983
  const enhancedPaddedImage = await new Promise<HTMLImageElement>((resolve, reject) => {
984
  const img = new Image();
 
1009
  setEnhancementJob(null);
1010
  setIsProcessing(false);
1011
  }
1012
+ }, [enhancementJob, image, history, historyIndex, apiKey]);
1013
 
1014
  const handleEnhancementComplete = useCallback(() => {
1015
  if (enhancedImageSrc && newHistoryEntryData) {
 
1067
  }, [history, historyIndex, appState, isGeneratingGif]);
1068
 
1069
  const handleRegenerate = useCallback(async () => {
1070
+ if (historyIndex <= 0 || appState === AppState.ENHANCING || isGeneratingGif || !apiKey) return;
1071
  setAppState(AppState.ENHANCING);
1072
  setStagedSelection(null);
1073
  const previousStep = history[historyIndex - 1];
 
1079
  try {
1080
  const descriptionHistory = history.slice(0, historyIndex).map(h => h.description).filter((d): d is ImageDescription => d !== null);
1081
  const croppedForDescription = await cropImage(sourceImage, originalRect, originalRect.w, originalRect.h, false);
1082
+ const description = await serviceDescribeImage(croppedForDescription, descriptionHistory, apiKey);
1083
  const sourceImageWidth = sourceImage.naturalWidth;
1084
  const sourceImageHeight = sourceImage.naturalHeight;
1085
  const padding = 0.5;
 
1097
  const targetHeight = Math.round(targetWidth * aspect);
1098
  const croppedForEnhancement = await cropImage(sourceImage, paddedRect, targetWidth, targetHeight, false);
1099
  const prompts = [...descriptionHistory.map(d=>(d.prompt || '')), description.prompt || ''];
1100
+ const { imageSrc: enhancedPaddedSrc } = await serviceEnhance(croppedForEnhancement, prompts, apiKey);
1101
 
1102
  const enhancedPaddedImage = await new Promise<HTMLImageElement>((resolve, reject) => {
1103
  const img = new Image(); img.crossOrigin = "anonymous"; img.onload = () => resolve(img); img.onerror = reject; img.src = enhancedPaddedSrc;
 
1121
  } catch (error) { console.error("Regeneration failed:", error); setAppState(AppState.LOADED); }
1122
  };
1123
  sourceImage.src = previousStep.imageSrc;
1124
+ }, [history, historyIndex, appState, isGeneratingGif, apiKey]);
1125
 
1126
  const handleExportGif = useCallback(async () => {
1127
  if (historyIndex < 1) return;
 
1190
  )}
1191
  <input type="file" ref={fileInputRef} onChange={handleFileSelect} style={{ display: 'none' }} accept="image/*" />
1192
  <StatusBar state={appState} useFixedSelectionBox={useFixedSelectionBox} isInitialState={history.length <= 1} onUploadClick={handleUploadClick}/>
1193
+
1194
+ {/* API Key Modal */}
1195
+ {showApiKeyModal && (
1196
+ <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
1197
+ <div className="bg-white rounded-lg shadow-xl max-w-md w-full p-6">
1198
+ <div className="flex justify-between items-start mb-4">
1199
+ <h3 className="text-xl font-bold text-gray-700">
1200
+ Add Gemini API Key
1201
+ </h3>
1202
+ <button
1203
+ onClick={() => setShowApiKeyModal(false)}
1204
+ className="text-gray-400 hover:text-gray-500">
1205
+ <span className="text-2xl">&times;</span>
1206
+ </button>
1207
+ </div>
1208
+ <p className="text-gray-600 mb-4">
1209
+ Add the API key to process the request. The API key will be
1210
+ removed if the app page is refreshed or closed.
1211
+ </p>
1212
+ <form onSubmit={handleApiKeySubmit}>
1213
+ <input
1214
+ type="password"
1215
+ name="apiKey"
1216
+ className="w-full p-2 border-2 border-gray-300 rounded-md mb-4 text-black"
1217
+ placeholder="Enter your Gemini API Key"
1218
+ required
1219
+ />
1220
+ <button
1221
+ type="submit"
1222
+ className="w-full bg-black text-white p-2 rounded-md hover:bg-gray-800 transition-colors">
1223
+ Submit
1224
+ </button>
1225
+ </form>
1226
+ </div>
1227
+ </div>
1228
+ )}
1229
  </div>
1230
  );
1231
  }