Update src/App.jsx
Browse files- src/App.jsx +103 -214
src/App.jsx
CHANGED
|
@@ -27,21 +27,21 @@ const QRGeneratorTab = ({
|
|
| 27 |
isSuggestingIdeas, suggestIdeas, downloadQrCode, qrCanvasRef,
|
| 28 |
isInputFocused, setIsInputFocused, currentThemeClasses,
|
| 29 |
qrStyles, selectedStyleIndex, setSelectedStyleIndex, styleCanvasRefs, drawQrCode,
|
| 30 |
-
|
| 31 |
-
|
| 32 |
}) => {
|
| 33 |
// Effect to generate QR code on the main canvas (Generator Tab)
|
| 34 |
useEffect(() => {
|
| 35 |
-
//
|
| 36 |
-
drawQrCode(qrCanvasRef.current, content, qrColor, errorLevel,
|
| 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
|
| 44 |
-
drawQrCode(canvas, "Sample", style.fg, 'H', 80
|
| 45 |
});
|
| 46 |
}, [qrStyles, drawQrCode, styleCanvasRefs]);
|
| 47 |
|
|
@@ -78,7 +78,7 @@ const QRGeneratorTab = ({
|
|
| 78 |
) : (
|
| 79 |
<i className="fas fa-lightbulb mr-2"></i>
|
| 80 |
)}
|
| 81 |
-
💡 {isSuggestingIdeas ? '
|
| 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,35 +88,19 @@ const QRGeneratorTab = ({
|
|
| 88 |
</button>
|
| 89 |
</div>
|
| 90 |
|
| 91 |
-
{/*
|
| 92 |
-
{
|
| 93 |
<div className={`mt-4 p-4 rounded-xl border ${currentThemeClasses.glassBorder} ${currentThemeClasses.glassBg} shadow-inner transition-all duration-300 overflow-hidden`}>
|
| 94 |
<div className="flex justify-between items-center cursor-pointer" onClick={() => setShowSuggestedIdeas(!showSuggestedIdeas)}>
|
| 95 |
-
<label className={`font-semibold ${currentThemeClasses.labelColor} font-inter`}>Suggested QR
|
| 96 |
<i className={`fas ${showSuggestedIdeas ? 'fa-chevron-up' : 'fa-chevron-down'} transition-transform duration-300`}></i>
|
| 97 |
</div>
|
| 98 |
<div className={`overflow-hidden transition-all duration-500 ease-in-out ${showSuggestedIdeas ? 'max-h-96 opacity-100 mt-2' : 'max-h-0 opacity-0'}`}>
|
| 99 |
-
<
|
| 100 |
-
{suggestedIdeaList.map((idea, index) => (
|
| 101 |
-
<button
|
| 102 |
-
key={index}
|
| 103 |
-
className={`flex items-center p-2 rounded-lg text-sm transition-all duration-200 ease-in-out
|
| 104 |
-
${currentThemeClasses.inputBg} ${currentThemeClasses.text} hover:bg-blue-500/20 hover:scale-[1.02]`}
|
| 105 |
-
onClick={() => {
|
| 106 |
-
setContent(idea.text);
|
| 107 |
-
setShowSuggestedIdeas(false); // Hide suggestions after selection
|
| 108 |
-
}}
|
| 109 |
-
>
|
| 110 |
-
<span className="mr-2">{idea.emoji}</span>
|
| 111 |
-
{idea.text}
|
| 112 |
-
</button>
|
| 113 |
-
))}
|
| 114 |
-
</div>
|
| 115 |
</div>
|
| 116 |
</div>
|
| 117 |
)}
|
| 118 |
|
| 119 |
-
|
| 120 |
<label className={`mt-4 mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter`}>Error Correction Level:</label>
|
| 121 |
<select
|
| 122 |
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`}
|
|
@@ -137,41 +121,24 @@ const QRGeneratorTab = ({
|
|
| 137 |
onChange={(e) => setQrColor(e.target.value)}
|
| 138 |
/>
|
| 139 |
|
| 140 |
-
{/* New: Download
|
| 141 |
-
<
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
</div>
|
| 153 |
-
{downloadFormat === 'png' && (
|
| 154 |
-
<div className="w-full sm:w-1/2">
|
| 155 |
-
<label className={`mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter block`}>PNG Resolution:</label>
|
| 156 |
-
<select
|
| 157 |
-
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`}
|
| 158 |
-
value={qrResolution}
|
| 159 |
-
onChange={(e) => setQrResolution(parseInt(e.target.value))}
|
| 160 |
-
>
|
| 161 |
-
<option value={1024}>1024x1024</option>
|
| 162 |
-
<option value={2048}>2048x2048</option>
|
| 163 |
-
<option value={4096}>4096x4096</option>
|
| 164 |
-
</select>
|
| 165 |
-
</div>
|
| 166 |
-
)}
|
| 167 |
-
</div>
|
| 168 |
-
|
| 169 |
|
| 170 |
<label className={`mt-6 mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter`}>QR Preview:</label>
|
| 171 |
<div
|
| 172 |
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}`}
|
| 173 |
>
|
| 174 |
-
<canvas ref={qrCanvasRef} width="
|
| 175 |
{!content && (
|
| 176 |
<p className={`${currentThemeClasses.labelColor} text-lg font-inter absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2`}>Enter content to see QR preview</p>
|
| 177 |
)}
|
|
@@ -209,7 +176,7 @@ const QRGeneratorTab = ({
|
|
| 209 |
// --- QRDecoderTab Component ---
|
| 210 |
const QRDecoderTab = ({
|
| 211 |
decodedContent, setDecodedContent,
|
| 212 |
-
isSummarizing,
|
| 213 |
currentThemeClasses
|
| 214 |
}) => {
|
| 215 |
return (
|
|
@@ -245,7 +212,7 @@ const QRDecoderTab = ({
|
|
| 245 |
|
| 246 |
<button
|
| 247 |
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`}
|
| 248 |
-
onClick={
|
| 249 |
disabled={isSummarizing || !decodedContent.trim() || decodedContent === 'No QR code detected.'}
|
| 250 |
>
|
| 251 |
{isSummarizing ? (
|
|
@@ -253,7 +220,7 @@ const QRDecoderTab = ({
|
|
| 253 |
) : (
|
| 254 |
<i className="fas fa-file-alt mr-2"></i>
|
| 255 |
)}
|
| 256 |
-
📝 {isSummarizing ? 'Summarizing
|
| 257 |
</button>
|
| 258 |
</div>
|
| 259 |
</div>
|
|
@@ -295,55 +262,38 @@ const App = () => {
|
|
| 295 |
const [activeTab, setActiveTab] = useState('generator');
|
| 296 |
const [content, setContent] = useState('');
|
| 297 |
const [errorLevel, setErrorLevel] = useState('L');
|
| 298 |
-
const [qrColor, setQrColor] = useState('#
|
| 299 |
const [decodedContent, setDecodedContent] = useState('');
|
| 300 |
const [selectedStyleIndex, setSelectedStyleIndex] = useState(0);
|
| 301 |
-
const [currentTheme, setCurrentTheme] = useState('dark');
|
| 302 |
const [isSummarizing, setIsSummarizing] = useState(false);
|
| 303 |
-
const [
|
| 304 |
-
const [suggestedIdeaList, setSuggestedIdeaList] = useState([]); // Stores the randomly selected subset
|
| 305 |
const [isSuggestingIdeas, setIsSuggestingIdeas] = useState(false);
|
| 306 |
const [showSuggestedIdeas, setShowSuggestedIdeas] = useState(false);
|
| 307 |
const [alertMessage, setAlertMessage] = useState(null);
|
| 308 |
const [isInputFocused, setIsInputFocused] = useState(false);
|
| 309 |
-
const [
|
| 310 |
-
const [qrResolution, setQrResolution] = useState(2048); // Default high resolution for PNG
|
| 311 |
|
| 312 |
const qrCanvasRef = useRef(null);
|
| 313 |
-
|
| 314 |
-
const styleCanvasRefs = useRef(Array(12).fill(null));
|
| 315 |
-
|
| 316 |
-
// Detect user's preferred color scheme and set initial theme
|
| 317 |
-
useEffect(() => {
|
| 318 |
-
const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
| 319 |
-
setCurrentTheme(prefersDarkMode ? 'dark' : 'light');
|
| 320 |
-
|
| 321 |
-
// Listen for changes in the user's preferred color scheme
|
| 322 |
-
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
| 323 |
-
const handleChange = (e) => {
|
| 324 |
-
setCurrentTheme(e.matches ? 'dark' : 'light');
|
| 325 |
-
};
|
| 326 |
-
mediaQuery.addEventListener('change', handleChange);
|
| 327 |
-
return () => mediaQuery.removeEventListener('change', handleChange);
|
| 328 |
-
}, []);
|
| 329 |
|
| 330 |
-
// Define QR code styles (
|
| 331 |
const qrStyles = [
|
| 332 |
-
{ name: "
|
| 333 |
-
{ name: "
|
| 334 |
-
{ name: "
|
| 335 |
-
{ name: "
|
| 336 |
-
{ name: "
|
| 337 |
-
{ name: "
|
| 338 |
-
{ name: "
|
| 339 |
-
{ name: "
|
| 340 |
-
{ name: "
|
| 341 |
-
{ name: "
|
| 342 |
-
{ name: "
|
| 343 |
-
{ name: "
|
| 344 |
];
|
| 345 |
|
| 346 |
-
// Define themes
|
| 347 |
const themes = {
|
| 348 |
dark: {
|
| 349 |
bg: 'bg-gradient-to-br from-[#0A0A0A] via-[#1A0A2A] to-[#0A0A0A] animate-gradient-shift',
|
|
@@ -509,7 +459,7 @@ const App = () => {
|
|
| 509 |
};
|
| 510 |
|
| 511 |
// Callback to generate QR code on a given canvas
|
| 512 |
-
const drawQrCode = useCallback((canvas, text, color, errorLevel, size = 256
|
| 513 |
if (!canvas) {
|
| 514 |
console.warn("Canvas element is null, cannot draw QR code.");
|
| 515 |
return;
|
|
@@ -532,8 +482,8 @@ const App = () => {
|
|
| 532 |
errorCorrectionLevel: errorLevel,
|
| 533 |
width: size,
|
| 534 |
color: {
|
| 535 |
-
dark: color,
|
| 536 |
-
light:
|
| 537 |
}
|
| 538 |
}, function (error) {
|
| 539 |
if (error) console.error("QR Code drawing error:", error);
|
|
@@ -541,69 +491,31 @@ const App = () => {
|
|
| 541 |
}
|
| 542 |
}, []);
|
| 543 |
|
| 544 |
-
// Function to download the generated QR code
|
| 545 |
-
const downloadQrCode =
|
| 546 |
if (content.trim() === '') {
|
| 547 |
showAlert('Please enter content to generate QR code.');
|
| 548 |
return;
|
| 549 |
}
|
| 550 |
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
errorCorrectionLevel: errorLevel,
|
| 556 |
-
width: qrResolution, // Use selected resolution
|
| 557 |
-
color: {
|
| 558 |
-
dark: qrColor,
|
| 559 |
-
light: '#000000' // Black background for downloaded PNG
|
| 560 |
-
}
|
| 561 |
-
}, function (error) {
|
| 562 |
-
if (error) {
|
| 563 |
-
console.error("QR Code PNG drawing error:", error);
|
| 564 |
-
showAlert("Failed to generate high-resolution PNG. Try again.");
|
| 565 |
-
}
|
| 566 |
-
resolve();
|
| 567 |
-
});
|
| 568 |
-
});
|
| 569 |
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
|
| 575 |
-
|
| 576 |
-
|
| 577 |
-
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
|
| 581 |
-
|
| 582 |
-
|
| 583 |
-
type: 'svg',
|
| 584 |
-
width: qrResolution, // Reusing qrResolution for SVG viewBox consistency
|
| 585 |
-
color: {
|
| 586 |
-
dark: qrColor,
|
| 587 |
-
light: '#000000' // Black background for SVG
|
| 588 |
-
}
|
| 589 |
-
});
|
| 590 |
-
|
| 591 |
-
const blob = new Blob([svgString], { type: 'image/svg+xml' });
|
| 592 |
-
const url = URL.createObjectURL(blob);
|
| 593 |
-
const downloadLink = document.createElement('a');
|
| 594 |
-
downloadLink.href = url;
|
| 595 |
-
downloadLink.download = `qrcode.svg`;
|
| 596 |
-
document.body.appendChild(downloadLink);
|
| 597 |
-
downloadLink.click();
|
| 598 |
-
document.body.removeChild(downloadLink);
|
| 599 |
-
URL.revokeObjectURL(url); // Clean up the URL object
|
| 600 |
-
showAlert('QR Code downloaded as SVG!');
|
| 601 |
-
|
| 602 |
-
} catch (error) {
|
| 603 |
-
console.error("QR Code SVG generation error:", error);
|
| 604 |
-
showAlert("Failed to generate SVG. Please try again.");
|
| 605 |
-
}
|
| 606 |
-
}
|
| 607 |
};
|
| 608 |
|
| 609 |
// Function to decode QR code from an uploaded image
|
|
@@ -626,8 +538,8 @@ const App = () => {
|
|
| 626 |
if (jsQR) {
|
| 627 |
let code = jsQR(imageData.data, imageData.width, imageData.height);
|
| 628 |
|
| 629 |
-
// Attempt inversion if initial scan fails
|
| 630 |
if (!code) {
|
|
|
|
| 631 |
for (let i = 0; i < imageData.data.length; i += 4) {
|
| 632 |
imageData.data[i] = 255 - imageData.data[i];
|
| 633 |
imageData.data[i + 1] = 255 - imageData.data[i + 1];
|
|
@@ -638,16 +550,16 @@ const App = () => {
|
|
| 638 |
|
| 639 |
if (code) {
|
| 640 |
setDecodedContent(code.data);
|
| 641 |
-
|
| 642 |
setShowSuggestedIdeas(false);
|
| 643 |
} else {
|
| 644 |
setDecodedContent('No QR code detected.');
|
| 645 |
-
|
| 646 |
setShowSuggestedIdeas(false);
|
| 647 |
}
|
| 648 |
} else {
|
| 649 |
showAlert('QR decoder library (jsQR) not loaded. Please try again or check internet connection.');
|
| 650 |
-
|
| 651 |
setShowSuggestedIdeas(false);
|
| 652 |
}
|
| 653 |
};
|
|
@@ -656,8 +568,8 @@ const App = () => {
|
|
| 656 |
reader.readAsDataURL(file);
|
| 657 |
};
|
| 658 |
|
| 659 |
-
// Function to summarize
|
| 660 |
-
const
|
| 661 |
if (!decodedContent.trim() || decodedContent === 'No QR code detected.') {
|
| 662 |
showAlert('Please decode a QR code first to summarize its content.');
|
| 663 |
return;
|
|
@@ -666,94 +578,72 @@ const App = () => {
|
|
| 666 |
setIsSummarizing(true);
|
| 667 |
try {
|
| 668 |
let chatHistory = [];
|
| 669 |
-
//
|
| 670 |
-
const prompt = `
|
| 671 |
chatHistory.push({ role: "user", parts: [{ text: prompt }] });
|
| 672 |
const payload = { contents: chatHistory };
|
| 673 |
const apiKey = ""; // Canvas will provide this at runtime
|
| 674 |
const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`;
|
| 675 |
|
| 676 |
-
console.log("Attempting to summarize XML content...");
|
| 677 |
const response = await fetch(apiUrl, {
|
| 678 |
method: 'POST',
|
| 679 |
headers: { 'Content-Type': 'application/json' },
|
| 680 |
body: JSON.stringify(payload)
|
| 681 |
});
|
| 682 |
|
| 683 |
-
console.log("Summarize XML API Response Status:", response.status, response.statusText);
|
| 684 |
-
if (!response.ok) {
|
| 685 |
-
const errorData = await response.text();
|
| 686 |
-
console.error("Summarize XML API Error Response:", errorData);
|
| 687 |
-
showAlert(`Failed to summarize XML: ${response.statusText}. Please try again.`);
|
| 688 |
-
return;
|
| 689 |
-
}
|
| 690 |
-
|
| 691 |
const result = await response.json();
|
| 692 |
-
console.log("Summarize XML API Result:", result);
|
| 693 |
|
| 694 |
if (result.candidates && result.candidates.length > 0 &&
|
| 695 |
result.candidates[0].content && result.candidates[0].content.parts &&
|
| 696 |
result.candidates[0].content.parts.length > 0) {
|
| 697 |
const text = result.candidates[0].content.parts[0].text;
|
| 698 |
setContent(text); // Update the generator's input field with summarized content
|
| 699 |
-
showAlert('Content summarized
|
| 700 |
} else {
|
| 701 |
-
|
| 702 |
-
showAlert("Failed to summarize XML. Unexpected API response. Please try again.");
|
| 703 |
}
|
| 704 |
} catch (error) {
|
| 705 |
-
console.error("Error summarizing
|
| 706 |
-
showAlert("An error occurred while summarizing
|
| 707 |
} finally {
|
| 708 |
setIsSummarizing(false);
|
| 709 |
}
|
| 710 |
};
|
| 711 |
|
| 712 |
-
// Function to
|
| 713 |
const suggestIdeas = async () => {
|
| 714 |
setIsSuggestingIdeas(true);
|
| 715 |
-
|
| 716 |
-
setShowSuggestedIdeas(true); //
|
| 717 |
|
| 718 |
try {
|
| 719 |
-
|
| 720 |
-
|
| 721 |
-
|
| 722 |
-
|
| 723 |
-
throw new Error(`HTTP error! status: ${response.status}`);
|
| 724 |
-
}
|
| 725 |
-
const text = await response.text();
|
| 726 |
-
const parsedIdeas = text.split('\n').filter(line => line.trim() !== '').map(line => {
|
| 727 |
-
const firstSpaceIndex = line.indexOf(' ');
|
| 728 |
-
if (firstSpaceIndex > -1) {
|
| 729 |
-
return {
|
| 730 |
-
emoji: line.substring(0, firstSpaceIndex).trim(),
|
| 731 |
-
text: line.substring(firstSpaceIndex + 1).trim()
|
| 732 |
-
};
|
| 733 |
-
}
|
| 734 |
-
return { emoji: '', text: line.trim() }; // Fallback if no space found
|
| 735 |
-
});
|
| 736 |
-
setAllAvailableIdeas(parsedIdeas);
|
| 737 |
-
console.log("All ideas loaded:", parsedIdeas.length);
|
| 738 |
}
|
|
|
|
| 739 |
|
| 740 |
-
//
|
| 741 |
-
const
|
| 742 |
-
const shuffled = [...allAvailableIdeas].sort(() => 0.5 - Math.random());
|
| 743 |
-
const selectedIdeas = shuffled.slice(0, numIdeasToShow);
|
| 744 |
-
setSuggestedIdeaList(selectedIdeas);
|
| 745 |
-
console.log("Random ideas selected:", selectedIdeas);
|
| 746 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 747 |
} catch (error) {
|
| 748 |
console.error("Error fetching or parsing suggested ideas:", error);
|
| 749 |
-
|
| 750 |
-
setSuggestedIdeaList([]); // Clear ideas on error
|
| 751 |
} finally {
|
| 752 |
setIsSuggestingIdeas(false);
|
| 753 |
}
|
| 754 |
};
|
| 755 |
|
| 756 |
-
|
| 757 |
return (
|
| 758 |
<div className={`min-h-screen font-inter antialiased ${currentThemeClasses.bg} ${currentThemeClasses.text} transition-all duration-500 ease-in-out`}>
|
| 759 |
<div className="container mx-auto p-4 max-w-7xl">
|
|
@@ -800,15 +690,14 @@ const App = () => {
|
|
| 800 |
qrStyles={qrStyles} selectedStyleIndex={selectedStyleIndex}
|
| 801 |
setSelectedStyleIndex={setSelectedStyleIndex} styleCanvasRefs={styleCanvasRefs}
|
| 802 |
drawQrCode={drawQrCode}
|
| 803 |
-
|
| 804 |
-
|
| 805 |
-
qrResolution={qrResolution} setQrResolution={setQrResolution}
|
| 806 |
/>
|
| 807 |
)}
|
| 808 |
{activeTab === 'decoder' && (
|
| 809 |
<QRDecoderTab
|
| 810 |
decodedContent={decodedContent} setDecodedContent={setDecodedContent}
|
| 811 |
-
isSummarizing={isSummarizing}
|
| 812 |
showAlert={showAlert} decodeQrCode={decodeQrCode}
|
| 813 |
currentThemeClasses={currentThemeClasses}
|
| 814 |
/>
|
|
|
|
| 27 |
isSuggestingIdeas, suggestIdeas, downloadQrCode, qrCanvasRef,
|
| 28 |
isInputFocused, setIsInputFocused, currentThemeClasses,
|
| 29 |
qrStyles, selectedStyleIndex, setSelectedStyleIndex, styleCanvasRefs, drawQrCode,
|
| 30 |
+
suggestedIdeas, showSuggestedIdeas, setShowSuggestedIdeas,
|
| 31 |
+
downloadResolution, setDownloadResolution // New prop for download resolution
|
| 32 |
}) => {
|
| 33 |
// Effect to generate QR code on the main canvas (Generator Tab)
|
| 34 |
useEffect(() => {
|
| 35 |
+
// Preview canvas is always 256x256
|
| 36 |
+
drawQrCode(qrCanvasRef.current, content, qrColor, errorLevel, 256);
|
| 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 a generic sample for style previews, not actual content
|
| 44 |
+
drawQrCode(canvas, "Sample", style.fg, 'H', 80);
|
| 45 |
});
|
| 46 |
}, [qrStyles, drawQrCode, styleCanvasRefs]);
|
| 47 |
|
|
|
|
| 78 |
) : (
|
| 79 |
<i className="fas fa-lightbulb mr-2"></i>
|
| 80 |
)}
|
| 81 |
+
💡 {isSuggestingIdeas ? 'Suggesting Idea...' : 'Suggest Idea'}
|
| 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 |
+
{/* Suggested Ideas Panel */}
|
| 92 |
+
{showSuggestedIdeas && suggestedIdeas && (
|
| 93 |
<div className={`mt-4 p-4 rounded-xl border ${currentThemeClasses.glassBorder} ${currentThemeClasses.glassBg} shadow-inner transition-all duration-300 overflow-hidden`}>
|
| 94 |
<div className="flex justify-between items-center cursor-pointer" onClick={() => setShowSuggestedIdeas(!showSuggestedIdeas)}>
|
| 95 |
+
<label className={`font-semibold ${currentThemeClasses.labelColor} font-inter`}>Suggested QR Idea:</label>
|
| 96 |
<i className={`fas ${showSuggestedIdeas ? 'fa-chevron-up' : 'fa-chevron-down'} transition-transform duration-300`}></i>
|
| 97 |
</div>
|
| 98 |
<div className={`overflow-hidden transition-all duration-500 ease-in-out ${showSuggestedIdeas ? 'max-h-96 opacity-100 mt-2' : 'max-h-0 opacity-0'}`}>
|
| 99 |
+
<p className={`${currentThemeClasses.text} whitespace-pre-wrap text-sm font-inter`}>{suggestedIdeas}</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
</div>
|
| 101 |
</div>
|
| 102 |
)}
|
| 103 |
|
|
|
|
| 104 |
<label className={`mt-4 mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter`}>Error Correction Level:</label>
|
| 105 |
<select
|
| 106 |
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`}
|
|
|
|
| 121 |
onChange={(e) => setQrColor(e.target.value)}
|
| 122 |
/>
|
| 123 |
|
| 124 |
+
{/* New: Download Resolution Selector */}
|
| 125 |
+
<label className={`mt-4 mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter`}>Download Resolution:</label>
|
| 126 |
+
<select
|
| 127 |
+
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`}
|
| 128 |
+
value={downloadResolution}
|
| 129 |
+
onChange={(e) => setDownloadResolution(Number(e.target.value))}
|
| 130 |
+
>
|
| 131 |
+
<option value={256}>256x256 (Preview Size)</option>
|
| 132 |
+
<option value={512}>512x512 (Medium)</option>
|
| 133 |
+
<option value={1024}>1024x1024 (High)</option>
|
| 134 |
+
<option value={2048}>2048x2048 (Very High)</option>
|
| 135 |
+
</select>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
|
| 137 |
<label className={`mt-6 mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter`}>QR Preview:</label>
|
| 138 |
<div
|
| 139 |
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}`}
|
| 140 |
>
|
| 141 |
+
<canvas ref={qrCanvasRef} width="256" height="256" className={`rounded-lg shadow-xl ${content ? '' : 'hidden'}`}></canvas>
|
| 142 |
{!content && (
|
| 143 |
<p className={`${currentThemeClasses.labelColor} text-lg font-inter absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2`}>Enter content to see QR preview</p>
|
| 144 |
)}
|
|
|
|
| 176 |
// --- QRDecoderTab Component ---
|
| 177 |
const QRDecoderTab = ({
|
| 178 |
decodedContent, setDecodedContent,
|
| 179 |
+
isSummarizing, summarizeContent, showAlert, decodeQrCode,
|
| 180 |
currentThemeClasses
|
| 181 |
}) => {
|
| 182 |
return (
|
|
|
|
| 212 |
|
| 213 |
<button
|
| 214 |
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`}
|
| 215 |
+
onClick={summarizeContent}
|
| 216 |
disabled={isSummarizing || !decodedContent.trim() || decodedContent === 'No QR code detected.'}
|
| 217 |
>
|
| 218 |
{isSummarizing ? (
|
|
|
|
| 220 |
) : (
|
| 221 |
<i className="fas fa-file-alt mr-2"></i>
|
| 222 |
)}
|
| 223 |
+
📝 {isSummarizing ? 'Summarizing Content...' : 'Summarize Content'}
|
| 224 |
</button>
|
| 225 |
</div>
|
| 226 |
</div>
|
|
|
|
| 262 |
const [activeTab, setActiveTab] = useState('generator');
|
| 263 |
const [content, setContent] = useState('');
|
| 264 |
const [errorLevel, setErrorLevel] = useState('L');
|
| 265 |
+
const [qrColor, setQrColor] = useState('#000000');
|
| 266 |
const [decodedContent, setDecodedContent] = useState('');
|
| 267 |
const [selectedStyleIndex, setSelectedStyleIndex] = useState(0);
|
| 268 |
+
const [currentTheme, setCurrentTheme] = useState('dark');
|
| 269 |
const [isSummarizing, setIsSummarizing] = useState(false);
|
| 270 |
+
const [suggestedIdeas, setSuggestedIdeas] = useState('');
|
|
|
|
| 271 |
const [isSuggestingIdeas, setIsSuggestingIdeas] = useState(false);
|
| 272 |
const [showSuggestedIdeas, setShowSuggestedIdeas] = useState(false);
|
| 273 |
const [alertMessage, setAlertMessage] = useState(null);
|
| 274 |
const [isInputFocused, setIsInputFocused] = useState(false);
|
| 275 |
+
const [downloadResolution, setDownloadResolution] = useState(512); // New state for download resolution
|
|
|
|
| 276 |
|
| 277 |
const qrCanvasRef = useRef(null);
|
| 278 |
+
const styleCanvasRefs = useRef(Array(12).fill(null));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
|
| 280 |
+
// Define QR code styles (remains in App as it's static data used by QRGeneratorTab)
|
| 281 |
const qrStyles = [
|
| 282 |
+
{ name: "Classic", fg: "#000000" },
|
| 283 |
+
{ name: "Blue", fg: "#0033CC" },
|
| 284 |
+
{ name: "Red", fg: "#CC3300" },
|
| 285 |
+
{ name: "Green", fg: "#006600" },
|
| 286 |
+
{ name: "Pink", fg: "#FF0099" },
|
| 287 |
+
{ name: "Teal", fg: "#008080" },
|
| 288 |
+
{ name: "Indigo", fg: "#4B0082" },
|
| 289 |
+
{ name: "Orange", fg: "#FF6600" },
|
| 290 |
+
{ name: "Gray", "fg": "#333333" },
|
| 291 |
+
{ name: "Crimson", "fg": "#DC143C" },
|
| 292 |
+
{ name: "Gold", "fg": "#FFD700" },
|
| 293 |
+
{ name: "Turquoise", "fg": "#40E0D0" },
|
| 294 |
];
|
| 295 |
|
| 296 |
+
// Define themes (remains in App)
|
| 297 |
const themes = {
|
| 298 |
dark: {
|
| 299 |
bg: 'bg-gradient-to-br from-[#0A0A0A] via-[#1A0A2A] to-[#0A0A0A] animate-gradient-shift',
|
|
|
|
| 459 |
};
|
| 460 |
|
| 461 |
// Callback to generate QR code on a given canvas
|
| 462 |
+
const drawQrCode = useCallback((canvas, text, color, errorLevel, size = 256) => {
|
| 463 |
if (!canvas) {
|
| 464 |
console.warn("Canvas element is null, cannot draw QR code.");
|
| 465 |
return;
|
|
|
|
| 482 |
errorCorrectionLevel: errorLevel,
|
| 483 |
width: size,
|
| 484 |
color: {
|
| 485 |
+
dark: color,
|
| 486 |
+
light: '#FFFFFF'
|
| 487 |
}
|
| 488 |
}, function (error) {
|
| 489 |
if (error) console.error("QR Code drawing error:", error);
|
|
|
|
| 491 |
}
|
| 492 |
}, []);
|
| 493 |
|
| 494 |
+
// Function to download the generated QR code
|
| 495 |
+
const downloadQrCode = () => {
|
| 496 |
if (content.trim() === '') {
|
| 497 |
showAlert('Please enter content to generate QR code.');
|
| 498 |
return;
|
| 499 |
}
|
| 500 |
|
| 501 |
+
// Create a temporary canvas for high-resolution download
|
| 502 |
+
const tempCanvas = document.createElement('canvas');
|
| 503 |
+
tempCanvas.width = downloadResolution;
|
| 504 |
+
tempCanvas.height = downloadResolution;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 505 |
|
| 506 |
+
// Draw the QR code onto the temporary canvas at the selected resolution
|
| 507 |
+
drawQrCode(tempCanvas, content, qrColor, errorLevel, downloadResolution);
|
| 508 |
+
|
| 509 |
+
// Get the data URL from the temporary canvas
|
| 510 |
+
const pngUrl = tempCanvas.toDataURL("image/png");
|
| 511 |
+
const downloadLink = document.createElement('a');
|
| 512 |
+
downloadLink.href = pngUrl;
|
| 513 |
+
downloadLink.download = `qrcode_${downloadResolution}x${downloadResolution}.png`;
|
| 514 |
+
document.body.appendChild(downloadLink);
|
| 515 |
+
downloadLink.click();
|
| 516 |
+
document.body.removeChild(downloadLink);
|
| 517 |
+
|
| 518 |
+
showAlert(`QR Code saved as ${downloadResolution}x${downloadResolution} PNG!`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 519 |
};
|
| 520 |
|
| 521 |
// Function to decode QR code from an uploaded image
|
|
|
|
| 538 |
if (jsQR) {
|
| 539 |
let code = jsQR(imageData.data, imageData.width, imageData.height);
|
| 540 |
|
|
|
|
| 541 |
if (!code) {
|
| 542 |
+
// Attempt to invert colors and try again
|
| 543 |
for (let i = 0; i < imageData.data.length; i += 4) {
|
| 544 |
imageData.data[i] = 255 - imageData.data[i];
|
| 545 |
imageData.data[i + 1] = 255 - imageData.data[i + 1];
|
|
|
|
| 550 |
|
| 551 |
if (code) {
|
| 552 |
setDecodedContent(code.data);
|
| 553 |
+
setSuggestedIdeas(''); // Clear previous suggestions when new QR is decoded
|
| 554 |
setShowSuggestedIdeas(false);
|
| 555 |
} else {
|
| 556 |
setDecodedContent('No QR code detected.');
|
| 557 |
+
setSuggestedIdeas('');
|
| 558 |
setShowSuggestedIdeas(false);
|
| 559 |
}
|
| 560 |
} else {
|
| 561 |
showAlert('QR decoder library (jsQR) not loaded. Please try again or check internet connection.');
|
| 562 |
+
setSuggestedIdeas('');
|
| 563 |
setShowSuggestedIdeas(false);
|
| 564 |
}
|
| 565 |
};
|
|
|
|
| 568 |
reader.readAsDataURL(file);
|
| 569 |
};
|
| 570 |
|
| 571 |
+
// Function to summarize content using Gemini API
|
| 572 |
+
const summarizeContent = async () => {
|
| 573 |
if (!decodedContent.trim() || decodedContent === 'No QR code detected.') {
|
| 574 |
showAlert('Please decode a QR code first to summarize its content.');
|
| 575 |
return;
|
|
|
|
| 578 |
setIsSummarizing(true);
|
| 579 |
try {
|
| 580 |
let chatHistory = [];
|
| 581 |
+
// Prompt to handle various content types for summarization
|
| 582 |
+
const prompt = `Summarize the following text concisely. If the text appears to be in XML, JSON, or another large structured format, extract the key information and present it as plain text. If it's a very long plain text, provide a brief summary. Text to summarize: "${decodedContent}"`;
|
| 583 |
chatHistory.push({ role: "user", parts: [{ text: prompt }] });
|
| 584 |
const payload = { contents: chatHistory };
|
| 585 |
const apiKey = ""; // Canvas will provide this at runtime
|
| 586 |
const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`;
|
| 587 |
|
|
|
|
| 588 |
const response = await fetch(apiUrl, {
|
| 589 |
method: 'POST',
|
| 590 |
headers: { 'Content-Type': 'application/json' },
|
| 591 |
body: JSON.stringify(payload)
|
| 592 |
});
|
| 593 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 594 |
const result = await response.json();
|
|
|
|
| 595 |
|
| 596 |
if (result.candidates && result.candidates.length > 0 &&
|
| 597 |
result.candidates[0].content && result.candidates[0].content.parts &&
|
| 598 |
result.candidates[0].content.parts.length > 0) {
|
| 599 |
const text = result.candidates[0].content.parts[0].text;
|
| 600 |
setContent(text); // Update the generator's input field with summarized content
|
| 601 |
+
showAlert('Content summarized and moved to QR Generator tab!');
|
| 602 |
} else {
|
| 603 |
+
showAlert("Failed to summarize content. Please try again.");
|
|
|
|
| 604 |
}
|
| 605 |
} catch (error) {
|
| 606 |
+
console.error("Error summarizing content:", error);
|
| 607 |
+
showAlert("An error occurred while summarizing content.");
|
| 608 |
} finally {
|
| 609 |
setIsSummarizing(false);
|
| 610 |
}
|
| 611 |
};
|
| 612 |
|
| 613 |
+
// Function to suggest ideas for QR code generation from suggestedIdeas.txt
|
| 614 |
const suggestIdeas = async () => {
|
| 615 |
setIsSuggestingIdeas(true);
|
| 616 |
+
setSuggestedIdeas('Fetching ideas...');
|
| 617 |
+
setShowSuggestedIdeas(true); // Always show the ideas panel when fetching
|
| 618 |
|
| 619 |
try {
|
| 620 |
+
// Fetch content from the suggestedIdeas.txt file
|
| 621 |
+
const response = await fetch('suggestedIdeas.txt');
|
| 622 |
+
if (!response.ok) {
|
| 623 |
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 624 |
}
|
| 625 |
+
const text = await response.text();
|
| 626 |
|
| 627 |
+
// Split the text into individual ideas, assuming each idea is on a new line
|
| 628 |
+
const ideasArray = text.split('\n').map(line => line.trim()).filter(line => line.length > 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 629 |
|
| 630 |
+
if (ideasArray.length > 0) {
|
| 631 |
+
// Select a random idea
|
| 632 |
+
const randomIndex = Math.floor(Math.random() * ideasArray.length);
|
| 633 |
+
const randomIdea = ideasArray[randomIndex];
|
| 634 |
+
setSuggestedIdeas(randomIdea); // Display the randomly selected idea
|
| 635 |
+
setContent(randomIdea); // Set the QR content to the suggested idea
|
| 636 |
+
} else {
|
| 637 |
+
setSuggestedIdeas("No ideas found in suggestedIdeas.txt.");
|
| 638 |
+
}
|
| 639 |
} catch (error) {
|
| 640 |
console.error("Error fetching or parsing suggested ideas:", error);
|
| 641 |
+
setSuggestedIdeas("An error occurred while fetching ideas.");
|
|
|
|
| 642 |
} finally {
|
| 643 |
setIsSuggestingIdeas(false);
|
| 644 |
}
|
| 645 |
};
|
| 646 |
|
|
|
|
| 647 |
return (
|
| 648 |
<div className={`min-h-screen font-inter antialiased ${currentThemeClasses.bg} ${currentThemeClasses.text} transition-all duration-500 ease-in-out`}>
|
| 649 |
<div className="container mx-auto p-4 max-w-7xl">
|
|
|
|
| 690 |
qrStyles={qrStyles} selectedStyleIndex={selectedStyleIndex}
|
| 691 |
setSelectedStyleIndex={setSelectedStyleIndex} styleCanvasRefs={styleCanvasRefs}
|
| 692 |
drawQrCode={drawQrCode}
|
| 693 |
+
suggestedIdeas={suggestedIdeas} showSuggestedIdeas={showSuggestedIdeas} setShowSuggestedIdeas={setShowSuggestedIdeas}
|
| 694 |
+
downloadResolution={downloadResolution} setDownloadResolution={setDownloadResolution} // Pass new prop
|
|
|
|
| 695 |
/>
|
| 696 |
)}
|
| 697 |
{activeTab === 'decoder' && (
|
| 698 |
<QRDecoderTab
|
| 699 |
decodedContent={decodedContent} setDecodedContent={setDecodedContent}
|
| 700 |
+
isSummarizing={isSummarizing} summarizeContent={summarizeContent}
|
| 701 |
showAlert={showAlert} decodeQrCode={decodeQrCode}
|
| 702 |
currentThemeClasses={currentThemeClasses}
|
| 703 |
/>
|