Znfeoqm commited on
Commit
bf94ab9
·
verified ·
1 Parent(s): 38fa334

Update src/App.jsx

Browse files
Files changed (1) hide show
  1. src/App.jsx +156 -51
src/App.jsx CHANGED
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef, useCallback } from 'react';
2
  import QRCode from 'qrcode';
3
  import jsQR from 'jsqr';
4
 
5
- // Custom Alert Modal Component (remains outside App)
6
  const CustomAlert = ({ message, onClose }) => {
7
  if (!message) return null;
8
 
@@ -21,24 +21,27 @@ const CustomAlert = ({ message, onClose }) => {
21
  );
22
  };
23
 
24
- // --- QRGeneratorTab Component (Moved outside App) ---
25
  const QRGeneratorTab = ({
26
  content, setContent, errorLevel, setErrorLevel, qrColor, setQrColor,
27
  isSuggestingIdeas, suggestIdeas, downloadQrCode, qrCanvasRef,
28
  isInputFocused, setIsInputFocused, currentThemeClasses,
29
  qrStyles, selectedStyleIndex, setSelectedStyleIndex, styleCanvasRefs, drawQrCode,
30
- suggestedIdeas, showSuggestedIdeas, setShowSuggestedIdeas
 
31
  }) => {
32
  // Effect to generate QR code on the main canvas (Generator Tab)
33
  useEffect(() => {
34
- drawQrCode(qrCanvasRef.current, content, qrColor, errorLevel);
 
35
  }, [content, qrColor, errorLevel, drawQrCode, qrCanvasRef]);
36
 
37
  // Effect to generate QR codes for style previews (Style Selection Panel)
38
  useEffect(() => {
39
  qrStyles.forEach((style, index) => {
40
  const canvas = styleCanvasRefs.current[index];
41
- drawQrCode(canvas, "Sample", style.fg, 'H', 80);
 
42
  });
43
  }, [qrStyles, drawQrCode, styleCanvasRefs]);
44
 
@@ -67,15 +70,15 @@ const QRGeneratorTab = ({
67
  <div className="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4 mt-4">
68
  <button
69
  className={`w-full sm:w-1/2 py-3 px-6 rounded-xl font-bold text-lg bg-gradient-to-r ${currentThemeClasses.primaryAccent} text-white shadow-lg ${currentThemeClasses.buttonGlow} transform hover:-translate-y-1 transition-all duration-300 ease-in-out flex items-center justify-center`}
70
- onClick={suggestIdeas} // Now calls suggestIdeas
71
- disabled={isSuggestingIdeas} // Use new loading state
72
  >
73
  {isSuggestingIdeas ? (
74
  <i className="fas fa-spinner fa-spin mr-2"></i>
75
  ) : (
76
- <i className="fas fa-lightbulb mr-2"></i> // Changed icon
77
  )}
78
- 💡 {isSuggestingIdeas ? 'Suggesting Ideas...' : 'Suggest Ideas'} {/* Changed text */}
79
  </button>
80
  <button
81
  className={`w-full sm:w-1/2 py-3 px-6 rounded-xl font-bold text-lg bg-gradient-to-r ${currentThemeClasses.secondaryAccent} text-white shadow-lg ${currentThemeClasses.buttonGlow} transform hover:-translate-y-1 transition-all duration-300 ease-in-out flex items-center justify-center`}
@@ -85,10 +88,10 @@ const QRGeneratorTab = ({
85
  </button>
86
  </div>
87
 
88
- {suggestedIdeas && ( // Display suggested ideas
89
  <div className={`mt-4 p-4 rounded-xl border ${currentThemeClasses.glassBorder} ${currentThemeClasses.glassBg} shadow-inner transition-all duration-300 overflow-hidden`}>
90
  <div className="flex justify-between items-center cursor-pointer" onClick={() => setShowSuggestedIdeas(!showSuggestedIdeas)}>
91
- <label className={`font-semibold ${currentThemeClasses.labelColor} font-inter`}>Suggested QR Ideas:</label> {/* Changed label */}
92
  <i className={`fas ${showSuggestedIdeas ? 'fa-chevron-up' : 'fa-chevron-down'} transition-transform duration-300`}></i>
93
  </div>
94
  <div className={`overflow-hidden transition-all duration-500 ease-in-out ${showSuggestedIdeas ? 'max-h-96 opacity-100 mt-2' : 'max-h-0 opacity-0'}`}>
@@ -118,6 +121,36 @@ const QRGeneratorTab = ({
118
  onChange={(e) => setQrColor(e.target.value)}
119
  />
120
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  <label className={`mt-6 mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter`}>QR Preview:</label>
122
  <div
123
  className={`relative flex justify-center items-center w-full min-h-[300px] rounded-2xl border-2 ${currentThemeClasses.qrPreviewBorder} ${currentThemeClasses.qrPreviewBg} p-4 shadow-inner overflow-hidden transform transition-all duration-300 hover:scale-[1.01] hover:-translate-y-1 ${currentThemeClasses.glow}`}
@@ -157,7 +190,7 @@ const QRGeneratorTab = ({
157
  );
158
  };
159
 
160
- // --- QRDecoderTab Component (Moved outside App) ---
161
  const QRDecoderTab = ({
162
  decodedContent, setDecodedContent,
163
  isSummarizing, summarizeContent, showAlert, decodeQrCode,
@@ -196,22 +229,22 @@ const QRDecoderTab = ({
196
 
197
  <button
198
  className={`mt-4 w-full py-3 px-6 rounded-xl font-bold text-lg bg-gradient-to-r ${currentThemeClasses.secondaryAccent} text-white shadow-lg ${currentThemeClasses.buttonGlow} transform hover:-translate-y-1 transition-all duration-300 ease-in-out flex items-center justify-center`}
199
- onClick={summarizeContent} // Now calls summarizeContent
200
  disabled={isSummarizing || !decodedContent.trim() || decodedContent === 'No QR code detected.'}
201
  >
202
  {isSummarizing ? (
203
  <i className="fas fa-spinner fa-spin mr-2"></i>
204
  ) : (
205
- <i className="fas fa-file-alt mr-2"></i> // Changed icon
206
  )}
207
- 📝 {isSummarizing ? 'Summarizing Content...' : 'Summarize Content'} {/* Changed text */}
208
  </button>
209
  </div>
210
  </div>
211
  );
212
  };
213
 
214
- // --- SettingsTab Component (Moved outside App) ---
215
  const SettingsTab = ({ currentTheme, setCurrentTheme, currentThemeClasses, themes }) => {
216
  return (
217
  <div className={`flex flex-col p-4 md:p-6 space-y-6 h-full ${currentThemeClasses.bg} ${currentThemeClasses.text}`}>
@@ -246,37 +279,53 @@ const App = () => {
246
  const [activeTab, setActiveTab] = useState('generator');
247
  const [content, setContent] = useState('');
248
  const [errorLevel, setErrorLevel] = useState('L');
249
- const [qrColor, setQrColor] = useState('#000000');
250
  const [decodedContent, setDecodedContent] = useState('');
251
  const [selectedStyleIndex, setSelectedStyleIndex] = useState(0);
252
- const [currentTheme, setCurrentTheme] = useState('dark');
253
  const [isSummarizing, setIsSummarizing] = useState(false);
254
- const [suggestedIdeas, setSuggestedIdeas] = useState(''); // Renamed state
255
- const [isSuggestingIdeas, setIsSuggestingIdeas] = useState(false); // Renamed state
256
- const [showSuggestedIdeas, setShowSuggestedIdeas] = useState(false); // Renamed state
257
  const [alertMessage, setAlertMessage] = useState(null);
258
  const [isInputFocused, setIsInputFocused] = useState(false);
 
 
259
 
260
  const qrCanvasRef = useRef(null);
261
  const styleCanvasRefs = useRef(Array(12).fill(null));
262
 
263
- // Define QR code styles (remains in App as it's static data used by QRGeneratorTab)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  const qrStyles = [
265
- { name: "Classic", fg: "#000000" },
266
- { name: "Blue", fg: "#0033CC" },
267
- { name: "Red", fg: "#CC3300" },
268
- { name: "Green", fg: "#006600" },
269
- { name: "Pink", fg: "#FF0099" },
270
- { name: "Teal", fg: "#008080" },
271
- { name: "Indigo", fg: "#4B0082" },
272
- { name: "Orange", fg: "#FF6600" },
273
- { name: "Gray", "fg": "#333333" },
274
- { name: "Crimson", "fg": "#DC143C" },
275
- { name: "Gold", "fg": "#FFD700" },
276
- { name: "Turquoise", "fg": "#40E0D0" },
277
  ];
278
 
279
- // Define themes (remains in App)
280
  const themes = {
281
  dark: {
282
  bg: 'bg-gradient-to-br from-[#0A0A0A] via-[#1A0A2A] to-[#0A0A0A] animate-gradient-shift',
@@ -436,13 +485,14 @@ const App = () => {
436
 
437
  const currentThemeClasses = themes[currentTheme];
438
 
439
- // Function to show custom alert (remains in App as it's a global utility)
440
  const showAlert = (message) => {
441
  setAlertMessage(message);
442
  };
443
 
444
- // Callback to generate QR code on a given canvas (remains in App, passed to generator)
445
- const drawQrCode = useCallback((canvas, text, color, errorLevel, size = 256) => {
 
446
  if (!canvas) {
447
  console.warn("Canvas element is null, cannot draw QR code.");
448
  return;
@@ -465,8 +515,8 @@ const App = () => {
465
  errorCorrectionLevel: errorLevel,
466
  width: size,
467
  color: {
468
- dark: color,
469
- light: '#FFFFFF'
470
  }
471
  }, function (error) {
472
  if (error) console.error("QR Code drawing error:", error);
@@ -474,25 +524,75 @@ const App = () => {
474
  }
475
  }, []);
476
 
477
- // Function to download the generated QR code (remains in App, passed to generator)
478
- const downloadQrCode = () => {
479
  if (content.trim() === '') {
480
  showAlert('Please enter content to generate QR code.');
481
  return;
482
  }
483
- const canvas = qrCanvasRef.current;
484
- if (canvas) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
485
  const pngUrl = canvas.toDataURL("image/png");
486
  const downloadLink = document.createElement('a');
487
  downloadLink.href = pngUrl;
488
- downloadLink.download = 'qrcode.png';
489
  document.body.appendChild(downloadLink);
490
  downloadLink.click();
491
  document.body.removeChild(downloadLink);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
492
  }
493
  };
494
 
495
- // Function to decode QR code from an uploaded image (remains in App, passed to decoder)
496
  const decodeQrCode = (event) => {
497
  const file = event.target.files[0];
498
  if (!file) return;
@@ -512,6 +612,7 @@ const App = () => {
512
  if (jsQR) {
513
  let code = jsQR(imageData.data, imageData.width, imageData.height);
514
 
 
515
  if (!code) {
516
  for (let i = 0; i < imageData.data.length; i += 4) {
517
  imageData.data[i] = 255 - imageData.data[i];
@@ -541,7 +642,7 @@ const App = () => {
541
  reader.readAsDataURL(file);
542
  };
543
 
544
- // Function to summarize content using Gemini API (NOW SUMMARIZES DECODED CONTENT)
545
  const summarizeContent = async () => {
546
  if (!decodedContent.trim() || decodedContent === 'No QR code detected.') {
547
  showAlert('Please decode a QR code first to summarize its content.');
@@ -573,6 +674,7 @@ const App = () => {
573
  setContent(text); // Update the generator's input field with summarized content
574
  showAlert('Content summarized and moved to QR Generator tab!');
575
  } else {
 
576
  showAlert("Failed to summarize content. Please try again.");
577
  }
578
  } catch (error) {
@@ -583,7 +685,7 @@ const App = () => {
583
  }
584
  };
585
 
586
- // Function to suggest ideas for QR code generation (RENAMED AND MOVED)
587
  const suggestIdeas = async () => {
588
  setIsSuggestingIdeas(true);
589
  setSuggestedIdeas('Thinking of ideas...');
@@ -621,6 +723,7 @@ const App = () => {
621
  const text = result.candidates[0].content.parts[0].text;
622
  setSuggestedIdeas(text);
623
  } else {
 
624
  setSuggestedIdeas("Could not suggest ideas. Please try again.");
625
  }
626
  } catch (error) {
@@ -670,20 +773,22 @@ const App = () => {
670
  content={content} setContent={setContent}
671
  errorLevel={errorLevel} setErrorLevel={setErrorLevel}
672
  qrColor={qrColor} setQrColor={setQrColor}
673
- isSuggestingIdeas={isSuggestingIdeas} suggestIdeas={suggestIdeas} // Pass new props
674
  downloadQrCode={downloadQrCode} qrCanvasRef={qrCanvasRef}
675
  isInputFocused={isInputFocused} setIsInputFocused={setIsInputFocused}
676
  currentThemeClasses={currentThemeClasses}
677
  qrStyles={qrStyles} selectedStyleIndex={selectedStyleIndex}
678
  setSelectedStyleIndex={setSelectedStyleIndex} styleCanvasRefs={styleCanvasRefs}
679
  drawQrCode={drawQrCode}
680
- suggestedIdeas={suggestedIdeas} showSuggestedIdeas={showSuggestedIdeas} setShowSuggestedIdeas={setShowSuggestedIdeas} // Pass new props for ideas display
 
 
681
  />
682
  )}
683
  {activeTab === 'decoder' && (
684
  <QRDecoderTab
685
  decodedContent={decodedContent} setDecodedContent={setDecodedContent}
686
- isSummarizing={isSummarizing} summarizeContent={summarizeContent} // Pass new props
687
  showAlert={showAlert} decodeQrCode={decodeQrCode}
688
  currentThemeClasses={currentThemeClasses}
689
  />
 
2
  import QRCode from 'qrcode';
3
  import jsQR from 'jsqr';
4
 
5
+ // Custom Alert Modal Component
6
  const CustomAlert = ({ message, onClose }) => {
7
  if (!message) return null;
8
 
 
21
  );
22
  };
23
 
24
+ // --- QRGeneratorTab Component ---
25
  const QRGeneratorTab = ({
26
  content, setContent, errorLevel, setErrorLevel, qrColor, setQrColor,
27
  isSuggestingIdeas, suggestIdeas, downloadQrCode, qrCanvasRef,
28
  isInputFocused, setIsInputFocused, currentThemeClasses,
29
  qrStyles, selectedStyleIndex, setSelectedStyleIndex, styleCanvasRefs, drawQrCode,
30
+ suggestedIdeas, showSuggestedIdeas, setShowSuggestedIdeas,
31
+ downloadFormat, setDownloadFormat, qrResolution, setQrResolution
32
  }) => {
33
  // Effect to generate QR code on the main canvas (Generator Tab)
34
  useEffect(() => {
35
+ // Always use black background for the main QR code preview
36
+ drawQrCode(qrCanvasRef.current, content, qrColor, errorLevel, 256, '#000000');
37
  }, [content, qrColor, errorLevel, drawQrCode, qrCanvasRef]);
38
 
39
  // Effect to generate QR codes for style previews (Style Selection Panel)
40
  useEffect(() => {
41
  qrStyles.forEach((style, index) => {
42
  const canvas = styleCanvasRefs.current[index];
43
+ // Use black background for style previews as well
44
+ drawQrCode(canvas, "Sample", style.fg, 'H', 80, '#000000');
45
  });
46
  }, [qrStyles, drawQrCode, styleCanvasRefs]);
47
 
 
70
  <div className="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4 mt-4">
71
  <button
72
  className={`w-full sm:w-1/2 py-3 px-6 rounded-xl font-bold text-lg bg-gradient-to-r ${currentThemeClasses.primaryAccent} text-white shadow-lg ${currentThemeClasses.buttonGlow} transform hover:-translate-y-1 transition-all duration-300 ease-in-out flex items-center justify-center`}
73
+ onClick={suggestIdeas}
74
+ disabled={isSuggestingIdeas}
75
  >
76
  {isSuggestingIdeas ? (
77
  <i className="fas fa-spinner fa-spin mr-2"></i>
78
  ) : (
79
+ <i className="fas fa-lightbulb mr-2"></i>
80
  )}
81
+ 💡 {isSuggestingIdeas ? 'Suggesting Ideas...' : 'Suggest Ideas'}
82
  </button>
83
  <button
84
  className={`w-full sm:w-1/2 py-3 px-6 rounded-xl font-bold text-lg bg-gradient-to-r ${currentThemeClasses.secondaryAccent} text-white shadow-lg ${currentThemeClasses.buttonGlow} transform hover:-translate-y-1 transition-all duration-300 ease-in-out flex items-center justify-center`}
 
88
  </button>
89
  </div>
90
 
91
+ {suggestedIdeas && (
92
  <div className={`mt-4 p-4 rounded-xl border ${currentThemeClasses.glassBorder} ${currentThemeClasses.glassBg} shadow-inner transition-all duration-300 overflow-hidden`}>
93
  <div className="flex justify-between items-center cursor-pointer" onClick={() => setShowSuggestedIdeas(!showSuggestedIdeas)}>
94
+ <label className={`font-semibold ${currentThemeClasses.labelColor} font-inter`}>Suggested QR Ideas:</label>
95
  <i className={`fas ${showSuggestedIdeas ? 'fa-chevron-up' : 'fa-chevron-down'} transition-transform duration-300`}></i>
96
  </div>
97
  <div className={`overflow-hidden transition-all duration-500 ease-in-out ${showSuggestedIdeas ? 'max-h-96 opacity-100 mt-2' : 'max-h-0 opacity-0'}`}>
 
121
  onChange={(e) => setQrColor(e.target.value)}
122
  />
123
 
124
+ {/* New: Download Format and Resolution Selection */}
125
+ <div className="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4 mt-4">
126
+ <div className="w-full sm:w-1/2">
127
+ <label className={`mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter block`}>Download Format:</label>
128
+ <select
129
+ className={`w-full p-3 rounded-xl border ${currentThemeClasses.inputBorder} ${currentThemeClasses.inputBg} ${currentThemeClasses.text} focus:outline-none focus:ring-2 focus:ring-blue-500 appearance-none transition-all duration-300 font-inter`}
130
+ value={downloadFormat}
131
+ onChange={(e) => setDownloadFormat(e.target.value)}
132
+ >
133
+ <option value="png">PNG (High Resolution)</option>
134
+ <option value="svg">SVG (Vector)</option>
135
+ </select>
136
+ </div>
137
+ {downloadFormat === 'png' && (
138
+ <div className="w-full sm:w-1/2">
139
+ <label className={`mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter block`}>PNG Resolution:</label>
140
+ <select
141
+ className={`w-full p-3 rounded-xl border ${currentThemeClasses.inputBorder} ${currentThemeClasses.inputBg} ${currentThemeClasses.text} focus:outline-none focus:ring-2 focus:ring-blue-500 appearance-none transition-all duration-300 font-inter`}
142
+ value={qrResolution}
143
+ onChange={(e) => setQrResolution(parseInt(e.target.value))}
144
+ >
145
+ <option value={1024}>1024x1024</option>
146
+ <option value={2048}>2048x2048</option>
147
+ <option value={4096}>4096x4096</option>
148
+ </select>
149
+ </div>
150
+ )}
151
+ </div>
152
+
153
+
154
  <label className={`mt-6 mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter`}>QR Preview:</label>
155
  <div
156
  className={`relative flex justify-center items-center w-full min-h-[300px] rounded-2xl border-2 ${currentThemeClasses.qrPreviewBorder} ${currentThemeClasses.qrPreviewBg} p-4 shadow-inner overflow-hidden transform transition-all duration-300 hover:scale-[1.01] hover:-translate-y-1 ${currentThemeClasses.glow}`}
 
190
  );
191
  };
192
 
193
+ // --- QRDecoderTab Component ---
194
  const QRDecoderTab = ({
195
  decodedContent, setDecodedContent,
196
  isSummarizing, summarizeContent, showAlert, decodeQrCode,
 
229
 
230
  <button
231
  className={`mt-4 w-full py-3 px-6 rounded-xl font-bold text-lg bg-gradient-to-r ${currentThemeClasses.secondaryAccent} text-white shadow-lg ${currentThemeClasses.buttonGlow} transform hover:-translate-y-1 transition-all duration-300 ease-in-out flex items-center justify-center`}
232
+ onClick={summarizeContent}
233
  disabled={isSummarizing || !decodedContent.trim() || decodedContent === 'No QR code detected.'}
234
  >
235
  {isSummarizing ? (
236
  <i className="fas fa-spinner fa-spin mr-2"></i>
237
  ) : (
238
+ <i className="fas fa-file-alt mr-2"></i>
239
  )}
240
+ 📝 {isSummarizing ? 'Summarizing Content...' : 'Summarize Content'}
241
  </button>
242
  </div>
243
  </div>
244
  );
245
  };
246
 
247
+ // --- SettingsTab Component ---
248
  const SettingsTab = ({ currentTheme, setCurrentTheme, currentThemeClasses, themes }) => {
249
  return (
250
  <div className={`flex flex-col p-4 md:p-6 space-y-6 h-full ${currentThemeClasses.bg} ${currentThemeClasses.text}`}>
 
279
  const [activeTab, setActiveTab] = useState('generator');
280
  const [content, setContent] = useState('');
281
  const [errorLevel, setErrorLevel] = useState('L');
282
+ const [qrColor, setQrColor] = useState('#00FFCC'); // Default to a vibrant neon cyan
283
  const [decodedContent, setDecodedContent] = useState('');
284
  const [selectedStyleIndex, setSelectedStyleIndex] = useState(0);
285
+ const [currentTheme, setCurrentTheme] = useState('dark'); // Initial state, will be updated by useEffect
286
  const [isSummarizing, setIsSummarizing] = useState(false);
287
+ const [suggestedIdeas, setSuggestedIdeas] = useState('');
288
+ const [isSuggestingIdeas, setIsSuggestingIdeas] = useState(false);
289
+ const [showSuggestedIdeas, setShowSuggestedIdeas] = useState(false);
290
  const [alertMessage, setAlertMessage] = useState(null);
291
  const [isInputFocused, setIsInputFocused] = useState(false);
292
+ const [downloadFormat, setDownloadFormat] = useState('png'); // Default download format
293
+ const [qrResolution, setQrResolution] = useState(2048); // Default high resolution for PNG
294
 
295
  const qrCanvasRef = useRef(null);
296
  const styleCanvasRefs = useRef(Array(12).fill(null));
297
 
298
+ // Detect user's preferred color scheme and set initial theme
299
+ useEffect(() => {
300
+ const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
301
+ setCurrentTheme(prefersDarkMode ? 'dark' : 'light');
302
+
303
+ // Listen for changes in the user's preferred color scheme
304
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
305
+ const handleChange = (e) => {
306
+ setCurrentTheme(e.matches ? 'dark' : 'light');
307
+ };
308
+ mediaQuery.addEventListener('change', handleChange);
309
+ return () => mediaQuery.removeEventListener('change', handleChange);
310
+ }, []);
311
+
312
+ // Define QR code styles (updated with more neon colors)
313
  const qrStyles = [
314
+ { name: "Neon Cyan", fg: "#00FFCC" },
315
+ { name: "Neon Green", fg: "#00FF00" },
316
+ { name: "Neon Magenta", fg: "#FF00FF" },
317
+ { name: "Electric Blue", fg: "#00BFFF" },
318
+ { name: "Laser Red", fg: "#FF3333" },
319
+ { name: "Vivid Yellow", fg: "#FFFF00" },
320
+ { name: "Deep Purple", fg: "#8A2BE2" },
321
+ { name: "Aqua", fg: "#00FFFF" },
322
+ { name: "Lime", fg: "#32CD32" },
323
+ { name: "Fuchsia", fg: "#FF00FF" },
324
+ { name: "Orange Glow", fg: "#FFA500" },
325
+ { name: "White Glow", fg: "#FFFFFF" }, // A white glow on black background
326
  ];
327
 
328
+ // Define themes
329
  const themes = {
330
  dark: {
331
  bg: 'bg-gradient-to-br from-[#0A0A0A] via-[#1A0A2A] to-[#0A0A0A] animate-gradient-shift',
 
485
 
486
  const currentThemeClasses = themes[currentTheme];
487
 
488
+ // Function to show custom alert
489
  const showAlert = (message) => {
490
  setAlertMessage(message);
491
  };
492
 
493
+ // Callback to generate QR code on a given canvas
494
+ // Added 'bg' parameter for background color, defaulting to white for general use, but overridden to black for QR
495
+ const drawQrCode = useCallback((canvas, text, color, errorLevel, size = 256, bg = '#FFFFFF') => {
496
  if (!canvas) {
497
  console.warn("Canvas element is null, cannot draw QR code.");
498
  return;
 
515
  errorCorrectionLevel: errorLevel,
516
  width: size,
517
  color: {
518
+ dark: color, // QR code dots color
519
+ light: bg // QR code background color
520
  }
521
  }, function (error) {
522
  if (error) console.error("QR Code drawing error:", error);
 
524
  }
525
  }, []);
526
 
527
+ // Function to download the generated QR code (now supports PNG and SVG)
528
+ const downloadQrCode = async () => {
529
  if (content.trim() === '') {
530
  showAlert('Please enter content to generate QR code.');
531
  return;
532
  }
533
+
534
+ if (downloadFormat === 'png') {
535
+ const canvas = document.createElement('canvas'); // Create an off-screen canvas for high-res
536
+ // Use a high resolution for the downloaded PNG
537
+ await new Promise(resolve => {
538
+ QRCode.toCanvas(canvas, content, {
539
+ errorCorrectionLevel: errorLevel,
540
+ width: qrResolution, // Use selected resolution
541
+ color: {
542
+ dark: qrColor,
543
+ light: '#000000' // Black background for downloaded PNG
544
+ }
545
+ }, function (error) {
546
+ if (error) {
547
+ console.error("QR Code PNG drawing error:", error);
548
+ showAlert("Failed to generate high-resolution PNG. Try again.");
549
+ }
550
+ resolve();
551
+ });
552
+ });
553
+
554
  const pngUrl = canvas.toDataURL("image/png");
555
  const downloadLink = document.createElement('a');
556
  downloadLink.href = pngUrl;
557
+ downloadLink.download = `qrcode_${qrResolution}px.png`;
558
  document.body.appendChild(downloadLink);
559
  downloadLink.click();
560
  document.body.removeChild(downloadLink);
561
+ showAlert(`QR Code downloaded as PNG (${qrResolution}x${qrResolution}px)!`);
562
+
563
+ } else if (downloadFormat === 'svg') {
564
+ try {
565
+ const svgString = await QRCode.toString(content, {
566
+ errorCorrectionLevel: errorLevel,
567
+ type: 'svg',
568
+ // Width here influences the SVG viewBox, but SVG itself scales losslessly
569
+ // We can set it to a reasonable base size, but it's not pixel resolution
570
+ width: qrResolution, // Reusing qrResolution for SVG viewBox consistency
571
+ color: {
572
+ dark: qrColor,
573
+ light: '#000000' // Black background for SVG
574
+ }
575
+ });
576
+
577
+ const blob = new Blob([svgString], { type: 'image/svg+xml' });
578
+ const url = URL.createObjectURL(blob);
579
+ const downloadLink = document.createElement('a');
580
+ downloadLink.href = url;
581
+ downloadLink.download = `qrcode.svg`;
582
+ document.body.appendChild(downloadLink);
583
+ downloadLink.click();
584
+ document.body.removeChild(downloadLink);
585
+ URL.revokeObjectURL(url); // Clean up the URL object
586
+ showAlert('QR Code downloaded as SVG!');
587
+
588
+ } catch (error) {
589
+ console.error("QR Code SVG generation error:", error);
590
+ showAlert("Failed to generate SVG. Please try again.");
591
+ }
592
  }
593
  };
594
 
595
+ // Function to decode QR code from an uploaded image
596
  const decodeQrCode = (event) => {
597
  const file = event.target.files[0];
598
  if (!file) return;
 
612
  if (jsQR) {
613
  let code = jsQR(imageData.data, imageData.width, imageData.height);
614
 
615
+ // Attempt inversion if initial scan fails
616
  if (!code) {
617
  for (let i = 0; i < imageData.data.length; i += 4) {
618
  imageData.data[i] = 255 - imageData.data[i];
 
642
  reader.readAsDataURL(file);
643
  };
644
 
645
+ // Function to summarize content using Gemini API
646
  const summarizeContent = async () => {
647
  if (!decodedContent.trim() || decodedContent === 'No QR code detected.') {
648
  showAlert('Please decode a QR code first to summarize its content.');
 
674
  setContent(text); // Update the generator's input field with summarized content
675
  showAlert('Content summarized and moved to QR Generator tab!');
676
  } else {
677
+ console.error("Gemini API response structure unexpected for summarizeContent:", result);
678
  showAlert("Failed to summarize content. Please try again.");
679
  }
680
  } catch (error) {
 
685
  }
686
  };
687
 
688
+ // Function to suggest ideas for QR code generation
689
  const suggestIdeas = async () => {
690
  setIsSuggestingIdeas(true);
691
  setSuggestedIdeas('Thinking of ideas...');
 
723
  const text = result.candidates[0].content.parts[0].text;
724
  setSuggestedIdeas(text);
725
  } else {
726
+ console.error("Gemini API response structure unexpected for suggestIdeas:", result);
727
  setSuggestedIdeas("Could not suggest ideas. Please try again.");
728
  }
729
  } catch (error) {
 
773
  content={content} setContent={setContent}
774
  errorLevel={errorLevel} setErrorLevel={setErrorLevel}
775
  qrColor={qrColor} setQrColor={setQrColor}
776
+ isSuggestingIdeas={isSuggestingIdeas} suggestIdeas={suggestIdeas}
777
  downloadQrCode={downloadQrCode} qrCanvasRef={qrCanvasRef}
778
  isInputFocused={isInputFocused} setIsInputFocused={setIsInputFocused}
779
  currentThemeClasses={currentThemeClasses}
780
  qrStyles={qrStyles} selectedStyleIndex={selectedStyleIndex}
781
  setSelectedStyleIndex={setSelectedStyleIndex} styleCanvasRefs={styleCanvasRefs}
782
  drawQrCode={drawQrCode}
783
+ suggestedIdeas={suggestedIdeas} showSuggestedIdeas={showSuggestedIdeas} setShowSuggestedIdeas={setShowSuggestedIdeas}
784
+ downloadFormat={downloadFormat} setDownloadFormat={setDownloadFormat}
785
+ qrResolution={qrResolution} setQrResolution={setQrResolution}
786
  />
787
  )}
788
  {activeTab === 'decoder' && (
789
  <QRDecoderTab
790
  decodedContent={decodedContent} setDecodedContent={setDecodedContent}
791
+ isSummarizing={isSummarizing} summarizeContent={summarizeContent}
792
  showAlert={showAlert} decodeQrCode={decodeQrCode}
793
  currentThemeClasses={currentThemeClasses}
794
  />