Znfeoqm commited on
Commit
da83632
·
verified ·
1 Parent(s): 95899e9

Upload 9 files

Browse files
Files changed (9) hide show
  1. .gitignore +3 -0
  2. Dockerfile +20 -0
  3. package-lock.json +0 -0
  4. package.json +29 -0
  5. src/App.css +42 -0
  6. src/App.jsx +734 -0
  7. src/assets/react.svg +1 -0
  8. src/index.css +68 -0
  9. src/main.jsx +10 -0
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ node_modules/
2
+ dist/
3
+ .env
Dockerfile ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Stage 1: Build the React application
2
+ FROM node:18-alpine as builder
3
+
4
+ WORKDIR /app
5
+
6
+ COPY package*.json ./
7
+ RUN npm install
8
+
9
+ COPY . .
10
+ RUN npm run build
11
+
12
+ # Stage 2: Serve the application with Nginx
13
+ FROM nginx:alpine
14
+
15
+ # Copy the built React app from the builder stage
16
+ COPY --from=builder /app/dist /usr/share/nginx/html
17
+
18
+ EXPOSE 80
19
+
20
+ CMD ["nginx", "-g", "daemon off;"]
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "my-qr-app",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "lint": "eslint .",
10
+ "preview": "vite preview"
11
+ },
12
+ "dependencies": {
13
+ "jsqr": "^1.4.0",
14
+ "qrcode": "^1.5.4",
15
+ "react": "^19.1.0",
16
+ "react-dom": "^19.1.0"
17
+ },
18
+ "devDependencies": {
19
+ "@eslint/js": "^9.30.1",
20
+ "@types/react": "^19.1.8",
21
+ "@types/react-dom": "^19.1.6",
22
+ "@vitejs/plugin-react": "^4.6.0",
23
+ "eslint": "^9.30.1",
24
+ "eslint-plugin-react-hooks": "^5.2.0",
25
+ "eslint-plugin-react-refresh": "^0.4.20",
26
+ "globals": "^16.3.0",
27
+ "vite": "^7.0.4"
28
+ }
29
+ }
src/App.css ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #root {
2
+ max-width: 1280px;
3
+ margin: 0 auto;
4
+ padding: 2rem;
5
+ text-align: center;
6
+ }
7
+
8
+ .logo {
9
+ height: 6em;
10
+ padding: 1.5em;
11
+ will-change: filter;
12
+ transition: filter 300ms;
13
+ }
14
+ .logo:hover {
15
+ filter: drop-shadow(0 0 2em #646cffaa);
16
+ }
17
+ .logo.react:hover {
18
+ filter: drop-shadow(0 0 2em #61dafbaa);
19
+ }
20
+
21
+ @keyframes logo-spin {
22
+ from {
23
+ transform: rotate(0deg);
24
+ }
25
+ to {
26
+ transform: rotate(360deg);
27
+ }
28
+ }
29
+
30
+ @media (prefers-reduced-motion: no-preference) {
31
+ a:nth-of-type(2) .logo {
32
+ animation: logo-spin infinite 20s linear;
33
+ }
34
+ }
35
+
36
+ .card {
37
+ padding: 2em;
38
+ }
39
+
40
+ .read-the-docs {
41
+ color: #888;
42
+ }
src/App.jsx ADDED
@@ -0,0 +1,734 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+
3
+ // Main App component
4
+ const App = () => {
5
+ const [activeTab, setActiveTab] = useState('generator');
6
+ const [content, setContent] = useState('');
7
+ const [errorLevel, setErrorLevel] = useState('L');
8
+ const [qrColor, setQrColor] = useState('#000000');
9
+ const [decodedContent, setDecodedContent] = useState('');
10
+ const [selectedStyleIndex, setSelectedStyleIndex] = useState(0);
11
+ const [currentTheme, setCurrentTheme] = useState('dark'); // Default theme
12
+ const [isSummarizing, setIsSummarizing] = useState(false); // Loading state for summarization
13
+ const [suggestedActions, setSuggestedActions] = useState(''); // State for suggested actions
14
+ const [isSuggestingActions, setIsSuggestingActions] = useState(false); // Loading state for action suggestions
15
+ const [showSuggestedActions, setShowSuggestedActions] = useState(false); // State to toggle suggestions visibility
16
+
17
+ const qrCanvasRef = useRef(null); // Ref for the main QR code canvas
18
+ const styleCanvasRefs = useRef([]); // Refs for the style preview canvases
19
+
20
+ // Define QR code styles (mimicking the Python app's styles)
21
+ const qrStyles = [
22
+ { name: "Classic", fg: "#000000" },
23
+ { name: "Blue", fg: "#0033CC" },
24
+ { name: "Red", fg: "#CC3300" },
25
+ { name: "Green", fg: "#006600" },
26
+ { name: "Pink", fg: "#FF0099" },
27
+ { name: "Teal", fg: "#008080" },
28
+ { name: "Indigo", fg: "#4B0082" },
29
+ { name: "Orange", fg: "#FF6600" },
30
+ { name: "Gray", "fg": "#333333" },
31
+ { name: "Crimson", "fg": "#DC143C" },
32
+ { name: "Gold", "fg": "#FFD700" },
33
+ { name: "Turquoise", "fg": "#40E0D0" },
34
+ ];
35
+
36
+ // Define themes for the web application with Web 3.0 GUI elements
37
+ const themes = {
38
+ dark: {
39
+ // Full-screen animated gradient background (deep charcoal to subtle dark violet)
40
+ bg: 'bg-gradient-to-br from-[#0A0A0A] via-[#1A0A2A] to-[#0A0A0A] animate-gradient-shift',
41
+ text: 'text-gray-100',
42
+ primaryAccent: 'from-blue-500 to-purple-600',
43
+ secondaryAccent: 'from-cyan-400 to-teal-500',
44
+ // Stronger glassmorphism with subtle inner shadow
45
+ glassBg: 'bg-white/5 backdrop-blur-3xl',
46
+ glassBorder: 'border-white/10',
47
+ shadow: 'shadow-blue-500/20',
48
+ glow: 'shadow-lg shadow-blue-500/40',
49
+ buttonGlow: 'hover:shadow-xl hover:shadow-blue-400/50',
50
+ success: 'text-green-400',
51
+ alert: 'text-pink-500',
52
+ inputBg: 'bg-white/5',
53
+ inputBorder: 'border-white/10',
54
+ // Tab active state with distinct background and glowing border
55
+ tabActive: 'bg-white/10 border-blue-500 shadow-lg shadow-blue-500/20',
56
+ tabInactive: 'bg-transparent border-transparent',
57
+ cardBg: 'bg-white/5 backdrop-blur-md',
58
+ cardBorder: 'border-white/10',
59
+ qrPreviewBorder: 'border-blue-500/50',
60
+ qrPreviewBg: 'bg-gradient-to-br from-gray-900/30 to-gray-800/30 backdrop-blur-md',
61
+ headerGlow: 'from-cyan-400 to-magenta-600 animate-neon-glow' // Updated header glow
62
+ },
63
+ light: {
64
+ // Platinum to ice white gradient
65
+ bg: 'bg-gradient-to-br from-[#F8F8F8] via-[#E8E8E8] to-[#F8F8F8] animate-gradient-shift-light',
66
+ text: 'text-gray-800',
67
+ primaryAccent: 'from-blue-400 to-purple-500',
68
+ secondaryAccent: 'from-cyan-300 to-teal-400',
69
+ glassBg: 'bg-white/80 backdrop-blur-3xl',
70
+ glassBorder: 'border-gray-200',
71
+ shadow: 'shadow-gray-400/20',
72
+ glow: 'shadow-lg shadow-blue-300/40',
73
+ buttonGlow: 'hover:shadow-xl hover:shadow-blue-300/50',
74
+ success: 'text-green-600',
75
+ alert: 'text-red-600',
76
+ inputBg: 'bg-white/90',
77
+ inputBorder: 'border-gray-300',
78
+ tabActive: 'bg-white/90 border-blue-500 shadow-lg shadow-blue-300/20',
79
+ tabInactive: 'bg-transparent border-transparent',
80
+ cardBg: 'bg-white/90 backdrop-blur-md',
81
+ cardBorder: 'border-gray-200',
82
+ qrPreviewBorder: 'border-blue-400/50',
83
+ qrPreviewBg: 'bg-gradient-to-br from-gray-100/60 to-gray-200/60 backdrop-blur-md',
84
+ headerGlow: 'from-cyan-500 to-magenta-700 animate-neon-glow' // Updated header glow
85
+ },
86
+ 'forest-green': {
87
+ // Midnight green to neon teal
88
+ bg: 'bg-gradient-to-br from-[#0A2A1A] via-[#1A4A3A] to-[#0A3A2A] animate-gradient-shift',
89
+ text: 'text-white',
90
+ primaryAccent: 'from-green-500 to-emerald-600',
91
+ secondaryAccent: 'from-lime-400 to-green-500',
92
+ glassBg: 'bg-green-900/40 backdrop-blur-3xl',
93
+ glassBorder: 'border-green-800/20',
94
+ shadow: 'shadow-green-500/20',
95
+ glow: 'shadow-lg shadow-green-500/40',
96
+ buttonGlow: 'hover:shadow-xl hover:shadow-green-400/50',
97
+ success: 'text-lime-400',
98
+ alert: 'text-red-400',
99
+ inputBg: 'bg-green-950/30',
100
+ inputBorder: 'border-green-900/15',
101
+ tabActive: 'bg-green-800/20 border-green-500 shadow-lg shadow-green-500/20',
102
+ tabInactive: 'bg-transparent border-transparent',
103
+ cardBg: 'bg-green-950/30 backdrop-blur-md',
104
+ cardBorder: 'border-green-900/15',
105
+ qrPreviewBorder: 'border-green-500/50',
106
+ qrPreviewBg: 'bg-gradient-to-br from-green-900/30 to-green-800/30 backdrop-blur-md',
107
+ headerGlow: 'from-cyan-400 to-green-600 animate-neon-glow' // Updated header glow
108
+ },
109
+ 'ocean-blue': {
110
+ // Deep ocean to electric blue
111
+ bg: 'bg-gradient-to-br from-[#001A33] via-[#003366] to-[#002A5A] animate-gradient-shift',
112
+ text: 'text-white',
113
+ primaryAccent: 'from-blue-600 to-indigo-700',
114
+ secondaryAccent: 'from-sky-400 to-cyan-500',
115
+ glassBg: 'bg-blue-900/40 backdrop-blur-3xl',
116
+ glassBorder: 'border-blue-800/20',
117
+ shadow: 'shadow-blue-600/20',
118
+ glow: 'shadow-lg shadow-blue-600/40',
119
+ buttonGlow: 'hover:shadow-xl hover:shadow-blue-500/50',
120
+ success: 'text-sky-400',
121
+ alert: 'text-red-400',
122
+ inputBg: 'bg-blue-950/30',
123
+ inputBorder: 'border-blue-900/15',
124
+ tabActive: 'bg-blue-800/20 border-blue-500 shadow-lg shadow-blue-600/20',
125
+ tabInactive: 'bg-transparent border-transparent',
126
+ cardBg: 'bg-blue-950/30 backdrop-blur-md',
127
+ cardBorder: 'border-blue-900/15',
128
+ qrPreviewBorder: 'border-blue-600/50',
129
+ qrPreviewBg: 'bg-gradient-to-br from-blue-950/30 to-blue-900/30 backdrop-blur-md',
130
+ headerGlow: 'from-cyan-400 to-blue-600 animate-neon-glow' // Updated header glow
131
+ },
132
+ 'warm-grey': {
133
+ // Dark concrete to warm charcoal
134
+ bg: 'bg-gradient-to-br from-[#202020] via-[#404040] to-[#303030] animate-gradient-shift',
135
+ text: 'text-gray-100',
136
+ primaryAccent: 'from-gray-500 to-gray-700',
137
+ secondaryAccent: 'from-yellow-400 to-orange-500',
138
+ glassBg: 'bg-gray-700/40 backdrop-blur-3xl',
139
+ glassBorder: 'border-gray-600/20',
140
+ shadow: 'shadow-gray-500/20',
141
+ glow: 'shadow-lg shadow-gray-500/40',
142
+ buttonGlow: 'hover:shadow-xl hover:shadow-gray-400/50',
143
+ success: 'text-lime-400',
144
+ alert: 'text-red-400',
145
+ inputBg: 'bg-gray-800/30',
146
+ inputBorder: 'border-gray-700/15',
147
+ tabActive: 'bg-gray-600/20 border-gray-500 shadow-lg shadow-gray-500/20',
148
+ tabInactive: 'bg-transparent border-transparent',
149
+ cardBg: 'bg-gray-800/30 backdrop-blur-md',
150
+ cardBorder: 'border-gray-700/15',
151
+ qrPreviewBorder: 'border-gray-500/50',
152
+ qrPreviewBg: 'bg-gradient-to-br from-gray-800/30 to-gray-700/30 backdrop-blur-md',
153
+ headerGlow: 'from-cyan-300 to-blue-500 animate-neon-glow' // Updated header glow
154
+ },
155
+ 'deep-purple': {
156
+ // Deepest purple to dark magenta
157
+ bg: 'bg-gradient-to-br from-[#100830] via-[#201040] to-[#180C38] animate-gradient-shift',
158
+ text: 'text-white',
159
+ primaryAccent: 'from-purple-600 to-fuchsia-700',
160
+ secondaryAccent: 'from-pink-400 to-purple-500',
161
+ glassBg: 'bg-purple-900/40 backdrop-blur-3xl',
162
+ glassBorder: 'border-purple-800/20',
163
+ shadow: 'shadow-purple-600/20',
164
+ glow: 'shadow-lg shadow-purple-600/40',
165
+ buttonGlow: 'hover:shadow-xl hover:shadow-purple-500/50',
166
+ success: 'text-lime-400',
167
+ alert: 'text-red-400',
168
+ inputBg: 'bg-purple-950/30',
169
+ inputBorder: 'border-purple-900/15',
170
+ tabActive: 'bg-purple-800/20 border-purple-500 shadow-lg shadow-purple-600/20',
171
+ tabInactive: 'bg-transparent border-transparent',
172
+ cardBg: 'bg-purple-950/30 backdrop-blur-md',
173
+ cardBorder: 'border-purple-900/15',
174
+ qrPreviewBorder: 'border-purple-600/50',
175
+ qrPreviewBg: 'bg-gradient-to-br from-purple-950/30 to-purple-900/30 backdrop-blur-md',
176
+ headerGlow: 'from-cyan-400 to-purple-600 animate-neon-glow' // Updated header glow
177
+ },
178
+ 'sunrise-orange': {
179
+ // Deepest orange to fiery red
180
+ bg: 'bg-gradient-to-br from-[#A0300A] via-[#D0501A] to-[#B84012] animate-gradient-shift',
181
+ text: 'text-white',
182
+ primaryAccent: 'from-orange-600 to-red-700',
183
+ secondaryAccent: 'from-yellow-400 to-orange-500',
184
+ glassBg: 'bg-orange-800/40 backdrop-blur-3xl',
185
+ glassBorder: 'border-orange-700/20',
186
+ shadow: 'shadow-orange-600/20',
187
+ glow: 'shadow-lg shadow-orange-600/40',
188
+ buttonGlow: 'hover:shadow-xl hover:shadow-orange-500/50',
189
+ success: 'text-lime-400',
190
+ alert: 'text-red-400',
191
+ inputBg: 'bg-orange-900/30',
192
+ inputBorder: 'border-orange-800/15',
193
+ tabActive: 'bg-orange-700/20 border-orange-500 shadow-lg shadow-orange-600/20',
194
+ tabInactive: 'bg-transparent border-transparent',
195
+ cardBg: 'bg-orange-900/30 backdrop-blur-md',
196
+ cardBorder: 'border-orange-800/15',
197
+ qrPreviewBorder: 'border-orange-600/50',
198
+ qrPreviewBg: 'bg-gradient-to-br from-orange-900/30 to-orange-800/30 backdrop-blur-md',
199
+ headerGlow: 'from-cyan-500 to-orange-700 animate-neon-glow' // Updated header glow
200
+ },
201
+ };
202
+
203
+ const currentThemeClasses = themes[currentTheme];
204
+
205
+ // Effect to generate QR code on the main canvas
206
+ useEffect(() => {
207
+ if (qrCanvasRef.current && window.QRCode && content.trim() !== '') {
208
+ window.QRCode.toCanvas(qrCanvasRef.current, content, {
209
+ errorCorrectionLevel: errorLevel,
210
+ color: {
211
+ dark: qrStyles[selectedStyleIndex].fg,
212
+ light: '#FFFFFF'
213
+ }
214
+ }, function (error) {
215
+ if (error) console.error(error);
216
+ });
217
+ } else if (qrCanvasRef.current) {
218
+ // Clear canvas if content is empty
219
+ const context = qrCanvasRef.current.getContext('2d');
220
+ context.clearRect(0, 0, qrCanvasRef.current.width, qrCanvasRef.current.height);
221
+ }
222
+ }, [content, errorLevel, selectedStyleIndex, qrColor]);
223
+
224
+ // Effect to generate QR codes for style previews
225
+ useEffect(() => {
226
+ if (window.QRCode) {
227
+ qrStyles.forEach((style, index) => {
228
+ const canvas = styleCanvasRefs.current[index];
229
+ if (canvas) {
230
+ window.QRCode.toCanvas(canvas, "Sample", {
231
+ errorCorrectionLevel: 'H',
232
+ width: 80, // Fixed size for preview
233
+ color: {
234
+ dark: style.fg,
235
+ light: '#FFFFFF'
236
+ }
237
+ }, function (error) {
238
+ if (error) console.error(error);
239
+ });
240
+ }
241
+ });
242
+ }
243
+ }, [window.QRCode]);
244
+
245
+ // Function to download the generated QR code
246
+ const downloadQrCode = () => {
247
+ if (content.trim() === '') {
248
+ alert('Please enter content to generate QR code.');
249
+ return;
250
+ }
251
+ const canvas = qrCanvasRef.current;
252
+ if (canvas) {
253
+ const pngUrl = canvas.toDataURL("image/png");
254
+ const downloadLink = document.createElement('a');
255
+ downloadLink.href = pngUrl;
256
+ downloadLink.download = 'qrcode.png';
257
+ document.body.appendChild(downloadLink);
258
+ downloadLink.click();
259
+ document.body.removeChild(downloadLink);
260
+ }
261
+ };
262
+
263
+ // Function to decode QR code from an uploaded image
264
+ const decodeQrCode = (event) => {
265
+ const file = event.target.files[0];
266
+ if (!file) return;
267
+
268
+ const reader = new FileReader();
269
+ reader.onload = (e) => {
270
+ const img = new Image();
271
+ img.onload = () => {
272
+ const canvas = document.createElement('canvas');
273
+ const context = canvas.getContext('2d');
274
+ canvas.width = img.width;
275
+ canvas.height = img.height;
276
+ context.drawImage(img, 0, 0, img.width, img.height);
277
+
278
+ const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
279
+
280
+ if (window.jsQR) {
281
+ let code = window.jsQR(imageData.data, imageData.width, imageData.height);
282
+
283
+ if (!code) {
284
+ // Try inverting colors for decoding
285
+ for (let i = 0; i < imageData.data.length; i += 4) {
286
+ imageData.data[i] = 255 - imageData.data[i];
287
+ imageData.data[i + 1] = 255 - imageData.data[i + 1];
288
+ imageData.data[i + 2] = 255 - imageData.data[i + 2];
289
+ }
290
+ code = window.jsQR(imageData.data, imageData.width, imageData.height);
291
+ }
292
+
293
+ if (code) {
294
+ setDecodedContent(code.data);
295
+ setSuggestedActions(''); // Clear previous suggestions
296
+ setShowSuggestedActions(false); // Hide suggestions by default
297
+ } else {
298
+ setDecodedContent('No QR code detected.');
299
+ setSuggestedActions('');
300
+ setShowSuggestedActions(false);
301
+ }
302
+ } else {
303
+ setDecodedContent('QR decoder library (jsQR) not loaded. Please try again or check internet connection.');
304
+ setSuggestedActions('');
305
+ setShowSuggestedActions(false);
306
+ }
307
+ };
308
+ img.src = e.target.result;
309
+ };
310
+ reader.readAsDataURL(file);
311
+ };
312
+
313
+ // Function to summarize content using Gemini API
314
+ const summarizeContent = async () => {
315
+ if (!content.trim()) {
316
+ alert('Please enter content to summarize.');
317
+ return;
318
+ }
319
+
320
+ setIsSummarizing(true);
321
+ try {
322
+ let chatHistory = [];
323
+ const prompt = `Summarize the following text concisely: ${content}`;
324
+ chatHistory.push({ role: "user", parts: [{ text: prompt }] });
325
+ const payload = { contents: chatHistory };
326
+ const apiKey = ""; // Canvas will provide this at runtime
327
+ const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`;
328
+
329
+ const response = await fetch(apiUrl, {
330
+ method: 'POST',
331
+ headers: { 'Content-Type': 'application/json' },
332
+ body: JSON.stringify(payload)
333
+ });
334
+
335
+ const result = await response.json();
336
+
337
+ if (result.candidates && result.candidates.length > 0 &&
338
+ result.candidates[0].content && result.candidates[0].content.parts &&
339
+ result.candidates[0].content.parts.length > 0) {
340
+ const text = result.candidates[0].content.parts[0].text;
341
+ setContent(text); // Update the input field with summarized content
342
+ } else {
343
+ alert("Failed to summarize content. Please try again.");
344
+ }
345
+ } catch (error) {
346
+ console.error("Error summarizing content:", error);
347
+ alert("An error occurred while summarizing content.");
348
+ } finally {
349
+ setIsSummarizing(false);
350
+ }
351
+ };
352
+
353
+ // Function to suggest actions based on decoded content using Gemini API
354
+ const suggestActions = async () => {
355
+ if (!decodedContent.trim() || decodedContent === 'No QR code detected.') {
356
+ alert('Please decode a QR code first to suggest actions.');
357
+ return;
358
+ }
359
+
360
+ setIsSuggestingActions(true);
361
+ setSuggestedActions('Thinking...'); // Provide immediate feedback
362
+ setShowSuggestedActions(true); // Show the suggestions panel
363
+ try {
364
+ let chatHistory = [];
365
+ const prompt = `Given the following text from a QR code, suggest relevant actions a user might take. Format the suggestions as a bulleted list. Text: "${decodedContent}"`;
366
+ chatHistory.push({ role: "user", parts: [{ text: prompt }] });
367
+ const payload = { contents: chatHistory };
368
+ const apiKey = ""; // Canvas will provide this at runtime
369
+ const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`;
370
+
371
+ const response = await fetch(apiUrl, {
372
+ method: 'POST',
373
+ headers: { 'Content-Type': 'application/json' },
374
+ body: JSON.stringify(payload)
375
+ });
376
+
377
+ const result = await response.json();
378
+
379
+ if (result.candidates && result.candidates.length > 0 &&
380
+ result.candidates[0].content && result.candidates[0].content.parts &&
381
+ result.candidates[0].content.parts.length > 0) {
382
+ const text = result.candidates[0].content.parts[0].text;
383
+ setSuggestedActions(text);
384
+ } else {
385
+ setSuggestedActions("Could not suggest actions. Please try again.");
386
+ }
387
+ } catch (error) {
388
+ console.error("Error suggesting actions:", error);
389
+ setSuggestedActions("An error occurred while suggesting actions.");
390
+ } finally {
391
+ setIsSuggestingActions(false);
392
+ }
393
+ };
394
+
395
+
396
+ // QR Generator Tab Content
397
+ const QRGeneratorTab = () => (
398
+ <div className="flex flex-col lg:flex-row p-4 md:p-6 space-y-6 lg:space-y-0 lg:space-x-6 h-full">
399
+ {/* Left Panel: Input and Controls */}
400
+ <div className={`flex flex-col w-full lg:w-3/4 p-6 rounded-2xl border ${currentThemeClasses.glassBorder} ${currentThemeClasses.glassBg} shadow-2xl ${currentThemeClasses.shadow} transform transition-all duration-500 ease-in-out`}>
401
+ <label className={`mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter`}>Enter Content:</label>
402
+ <textarea
403
+ className={`w-full p-3 rounded-xl border ${currentThemeClasses.inputBorder} ${currentThemeClasses.inputBg} ${currentThemeClasses.text} focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all duration-300 font-inter`}
404
+ rows="6"
405
+ placeholder="Enter text or URL..."
406
+ value={content}
407
+ onChange={(e) => setContent(e.target.value)}
408
+ ></textarea>
409
+
410
+ <div className="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4 mt-4">
411
+ <button
412
+ 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`}
413
+ onClick={summarizeContent}
414
+ disabled={isSummarizing}
415
+ >
416
+ {isSummarizing ? (
417
+ <i className="fas fa-spinner fa-spin mr-2"></i>
418
+ ) : (
419
+ <i className="fas fa-magic mr-2"></i>
420
+ )}
421
+ ✨ {isSummarizing ? 'Summarizing...' : 'Summarize Content'}
422
+ </button>
423
+ <button
424
+ 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`}
425
+ onClick={downloadQrCode}
426
+ >
427
+ <i className="fas fa-save mr-2"></i> Save QR Code
428
+ </button>
429
+ </div>
430
+
431
+
432
+ <label className={`mt-4 mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter`}>Error Correction Level:</label>
433
+ <select
434
+ 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`}
435
+ value={errorLevel}
436
+ onChange={(e) => setErrorLevel(e.target.value)}
437
+ >
438
+ <option value="L">L - Low</option>
439
+ <option value="M">M - Medium</option>
440
+ <option value="Q">Q - Quality</option>
441
+ <option value="H">H - High</option>
442
+ </select>
443
+
444
+ <label className={`mt-4 mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter`}>Pick QR Color:</label>
445
+ <input
446
+ type="color"
447
+ className={`w-full h-10 rounded-xl border ${currentThemeClasses.inputBorder} p-1 cursor-pointer transition-all duration-300`}
448
+ value={qrColor}
449
+ onChange={(e) => setQrColor(e.target.value)}
450
+ />
451
+
452
+ <label className={`mt-6 mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter`}>QR Preview:</label>
453
+ <div
454
+ 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}`}
455
+ >
456
+ {content ? (
457
+ <canvas ref={qrCanvasRef} width="256" height="256" className="rounded-lg shadow-xl"></canvas>
458
+ ) : (
459
+ <p className={`${currentThemeClasses.labelColor} text-lg font-inter`}>Enter content to see QR preview</p>
460
+ )}
461
+ {/* Light scan effect */}
462
+ <div className="absolute inset-0 scan-line"></div>
463
+ </div>
464
+ </div>
465
+
466
+ {/* Right Panel: Style Selection */}
467
+ <div className={`w-full lg:w-1/4 p-6 rounded-2xl shadow-2xl border ${currentThemeClasses.glassBorder} ${currentThemeClasses.glassBg} ${currentThemeClasses.text} flex flex-col transform transition-all duration-500 ease-in-out`}>
468
+ <label className={`mb-4 font-semibold ${currentThemeClasses.labelColor} font-inter`}>Choose Style:</label>
469
+ <div className={`overflow-y-auto flex-grow rounded-xl p-2 ${currentThemeClasses.cardBg} custom-scrollbar`}>
470
+ <div className="grid grid-cols-2 gap-4">
471
+ {qrStyles.map((style, index) => (
472
+ <div
473
+ key={index}
474
+ className={`flex flex-col items-center justify-center p-2 rounded-xl cursor-pointer transition-all duration-200 ease-in-out
475
+ ${selectedStyleIndex === index ? `border-4 ${currentThemeClasses.qrPreviewBorder} shadow-lg scale-105 ${currentThemeClasses.glow}` : `border-2 ${currentThemeClasses.inputBorder} hover:border-blue-400/50 hover:scale-[1.02]`}
476
+ ${currentThemeClasses.inputBg}`}
477
+ onClick={() => {
478
+ setSelectedStyleIndex(index);
479
+ setQrColor(style.fg); // Update color picker with selected style's color
480
+ }}
481
+ >
482
+ <canvas ref={el => styleCanvasRefs.current[index] = el} width="80" height="80" className="rounded-lg"></canvas>
483
+ <span className={`mt-2 text-sm font-medium ${currentThemeClasses.labelColor} font-inter`}>{style.name}</span>
484
+ </div>
485
+ ))}
486
+ </div>
487
+ </div>
488
+ </div>
489
+ </div>
490
+ );
491
+
492
+ // QR Decoder Tab Content
493
+ const QRDecoderTab = () => (
494
+ <div className={`flex flex-col p-4 md:p-6 space-y-6 h-full ${currentThemeClasses.bg} ${currentThemeClasses.text}`}>
495
+ <div
496
+ className={`flex flex-col items-center justify-center p-10 border-2 border-dashed ${currentThemeClasses.glassBorder} rounded-2xl min-h-[200px] ${currentThemeClasses.glassBg} ${currentThemeClasses.text} transition-all duration-300 shadow-2xl ${currentThemeClasses.shadow} hover:scale-[1.01] hover:-translate-y-1`}
497
+ >
498
+ <p className={`text-lg ${currentThemeClasses.labelColor} mb-4 font-inter`}>Drag & Drop QR Image Here</p>
499
+ <input
500
+ type="file"
501
+ accept="image/*"
502
+ onChange={decodeQrCode}
503
+ className="hidden"
504
+ id="qr-upload-input"
505
+ />
506
+ <label
507
+ htmlFor="qr-upload-input"
508
+ className={`cursor-pointer 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`}
509
+ >
510
+ <i className="fas fa-upload mr-2 animate-bounce"></i> Upload QR Image to Decode
511
+ </label>
512
+ </div>
513
+
514
+ <div className={`p-6 rounded-2xl border ${currentThemeClasses.glassBorder} ${currentThemeClasses.glassBg} shadow-2xl ${currentThemeClasses.shadow} transform transition-all duration-500 ease-in-out`}>
515
+ <label className={`mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter`}>Decoded Content:</label>
516
+ <textarea
517
+ className={`w-full p-3 rounded-xl border ${currentThemeClasses.inputBorder} ${currentThemeClasses.inputBg} ${currentThemeClasses.text} focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all duration-300 font-inter`}
518
+ rows="8"
519
+ readOnly
520
+ value={decodedContent}
521
+ placeholder="Decoded content will appear here..."
522
+ ></textarea>
523
+
524
+ <button
525
+ 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`}
526
+ onClick={suggestActions}
527
+ disabled={isSuggestingActions || !decodedContent.trim() || decodedContent === 'No QR code detected.'}
528
+ >
529
+ {isSuggestingActions ? (
530
+ <i className="fas fa-spinner fa-spin mr-2"></i>
531
+ ) : (
532
+ <i className="fas fa-lightbulb mr-2"></i>
533
+ )}
534
+ ✨ {isSuggestingActions ? 'Suggesting Actions...' : 'Suggest Actions'}
535
+ </button>
536
+
537
+ {suggestedActions && (
538
+ <div className={`mt-4 p-4 rounded-xl border ${currentThemeClasses.glassBorder} ${currentThemeClasses.glassBg} shadow-inner transition-all duration-300 overflow-hidden`}>
539
+ <div className="flex justify-between items-center cursor-pointer" onClick={() => setShowSuggestedActions(!showSuggestedActions)}>
540
+ <label className={`font-semibold ${currentThemeClasses.labelColor} font-inter`}>Suggested Actions:</label>
541
+ <i className={`fas ${showSuggestedActions ? 'fa-chevron-up' : 'fa-chevron-down'} transition-transform duration-300`}></i>
542
+ </div>
543
+ <div className={`overflow-hidden transition-all duration-500 ease-in-out ${showSuggestedActions ? 'max-h-96 opacity-100 mt-2' : 'max-h-0 opacity-0'}`}>
544
+ <p className={`${currentThemeClasses.text} whitespace-pre-wrap text-sm font-inter`}>{suggestedActions}</p>
545
+ </div>
546
+ </div>
547
+ )}
548
+ </div>
549
+ </div>
550
+ );
551
+
552
+ // Settings Tab Content
553
+ const SettingsTab = () => (
554
+ <div className={`flex flex-col p-4 md:p-6 space-y-6 h-full ${currentThemeClasses.bg} ${currentThemeClasses.text}`}>
555
+ <div className={`p-6 rounded-2xl border ${currentThemeClasses.glassBorder} ${currentThemeClasses.glassBg} shadow-2xl ${currentThemeClasses.shadow} transform transition-all duration-500 ease-in-out`}>
556
+ <label className={`mb-2 font-semibold ${currentThemeClasses.labelColor} font-inter`}>Select Theme:</label>
557
+ <select
558
+ 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`}
559
+ value={currentTheme}
560
+ onChange={(e) => setCurrentTheme(e.target.value)}
561
+ >
562
+ {Object.keys(themes).map((themeName) => (
563
+ <option key={themeName} value={themeName}>
564
+ {themeName.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ')}
565
+ </option>
566
+ ))}
567
+ </select>
568
+
569
+ <button
570
+ className={`mt-6 w-full 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`}
571
+ onClick={() => setCurrentTheme('dark')}
572
+ >
573
+ Reset to Dark Theme
574
+ </button>
575
+ </div>
576
+ </div>
577
+ );
578
+
579
+ return (
580
+ <div className={`min-h-screen font-inter antialiased ${currentThemeClasses.bg} ${currentThemeClasses.text} transition-all duration-500 ease-in-out`}>
581
+ {/* Tailwind CSS CDN */}
582
+ <script src="https://cdn.tailwindcss.com"></script>
583
+ {/* Google Fonts - Inter for general text, Orbitron for headings */}
584
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Orbitron:wght@400;500;600;700;800;900&display=swap" rel="stylesheet" />
585
+ {/* Font Awesome for icons */}
586
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" crossOrigin="anonymous" referrerPolicy="no-referrer" />
587
+ {/* QR Code Generation Library (qrcode.js) */}
588
+ <script src="https://cdn.jsdelivr.net/npm/qrcode@1.4.4/build/qrcode.min.js"></script>
589
+ {/* QR Code Decoding Library (jsQR) */}
590
+ <script src="https://cdn.jsdelivr.net/npm/jsqr@1.1.1/dist/jsQR.min.js"></script>
591
+
592
+ {/* Custom CSS for animations and scrollbar */}
593
+ <style>
594
+ {`
595
+ .font-orbitron { font-family: 'Orbitron', sans-serif; }
596
+ .font-inter { font-family: 'Inter', sans-serif; }
597
+
598
+ /* Neon Glow for the header - Adjusted for readability */
599
+ @keyframes neon-glow {
600
+ 0%, 100% {
601
+ /* Base text shadow for sharp inner glow */
602
+ text-shadow:
603
+ 0 0 4px rgba(0, 255, 255, 0.8), /* Cyan */
604
+ 0 0 8px rgba(255, 0, 255, 0.6), /* Magenta */
605
+ 0 0 12px rgba(0, 150, 255, 0.4); /* Electric Blue */
606
+ transform: scale(1);
607
+ }
608
+ 50% {
609
+ /* Expanded text shadow for soft outer glow */
610
+ text-shadow:
611
+ 0 0 6px rgba(0, 255, 255, 1),
612
+ 0 0 12px rgba(0, 255, 255, 0.8),
613
+ 0 0 20px rgba(255, 0, 255, 0.7),
614
+ 0 0 30px rgba(255, 0, 255, 0.5),
615
+ 0 0 40px rgba(0, 150, 255, 0.6),
616
+ 0 0 50px rgba(0, 150, 255, 0.4);
617
+ transform: scale(1.01);
618
+ }
619
+ }
620
+ .animate-neon-glow {
621
+ animation: neon-glow 2s infinite alternate; /* Slower pulse for elegance */
622
+ }
623
+
624
+ @keyframes scan-light {
625
+ 0% {
626
+ transform: translateX(-100%);
627
+ }
628
+ 100% {
629
+ transform: translateX(100%);
630
+ }
631
+ }
632
+ .scan-line {
633
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
634
+ width: 100%;
635
+ height: 2px;
636
+ animation: scan-light 3s infinite linear;
637
+ }
638
+
639
+ .custom-scrollbar::-webkit-scrollbar {
640
+ width: 8px;
641
+ }
642
+ .custom-scrollbar::-webkit-scrollbar-track {
643
+ background: rgba(255, 255, 255, 0.05);
644
+ border-radius: 10px;
645
+ }
646
+ .custom-scrollbar::-webkit-scrollbar-thumb {
647
+ background: rgba(255, 255, 255, 0.2);
648
+ border-radius: 10px;
649
+ }
650
+ .custom-scrollbar::-webkit-scrollbar-thumb:hover {
651
+ background: rgba(255, 255, 255, 0.4);
652
+ }
653
+
654
+ /* Custom style for select dropdown arrow */
655
+ select {
656
+ -webkit-appearance: none;
657
+ -moz-appearance: none;
658
+ appearance: none;
659
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z' clip-rule='evenodd'%3E%3C/path%3E%3C/svg%3E");
660
+ background-repeat: no-repeat;
661
+ background-position: right 0.75rem center;
662
+ background-size: 1.5em 1.5em;
663
+ }
664
+
665
+ /* Animated background gradient - Cyberpunk style */
666
+ @keyframes cyberpunk-gradient-shift {
667
+ 0% { background-position: 0% 0%; background-color: #1a0033; } /* Deep Purple */
668
+ 25% { background-position: 25% 50%; background-color: #001a33; } /* Deep Blue */
669
+ 50% { background-position: 50% 100%; background-color: #33001a; } /* Dark Magenta */
670
+ 75% { background-position: 75% 50%; background-color: #003333; } /* Dark Teal */
671
+ 100% { background-position: 100% 0%; background-color: #1a0033; } /* Back to Deep Purple */
672
+ }
673
+ .animate-gradient-shift {
674
+ background-size: 400% 400%;
675
+ animation: cyberpunk-gradient-shift 20s ease infinite; /* Slower, more immersive shift */
676
+ }
677
+
678
+ /* Light theme animated background gradient (if needed, adjust colors) */
679
+ @keyframes gradient-shift-light {
680
+ 0% { background-position: 0% 0%; }
681
+ 25% { background-position: 25% 50%; }
682
+ 50% { background-position: 50% 100%; }
683
+ 75% { background-position: 75% 50%; }
684
+ 100% { background-position: 100% 0%; }
685
+ }
686
+ .animate-gradient-shift-light {
687
+ background-size: 400% 400%;
688
+ animation: gradient-shift-light 15s ease infinite;
689
+ }
690
+ `}
691
+ </style>
692
+
693
+ <div className="container mx-auto p-4 max-w-7xl">
694
+ <h1 className={`text-4xl lg:text-5xl font-extrabold text-center mb-8 bg-clip-text text-transparent bg-gradient-to-r ${currentThemeClasses.headerGlow} font-orbitron`}>
695
+ QuantumQR Studio
696
+ </h1>
697
+
698
+ {/* Tabs Navigation */}
699
+ <div className={`flex flex-wrap justify-center border-b-2 ${currentThemeClasses.glassBorder} mb-6 rounded-t-2xl overflow-hidden shadow-xl ${currentThemeClasses.shadow}`}>
700
+ <button
701
+ className={`flex-1 min-w-[150px] py-3 px-4 text-lg font-semibold rounded-t-2xl transition-all duration-300 relative z-10
702
+ ${activeTab === 'generator' ? `${currentThemeClasses.tabActive} ${currentThemeClasses.text} shadow-lg border-b-4 ${currentThemeClasses.primaryAccent}` : `${currentThemeClasses.tabInactive} ${currentThemeClasses.text} hover:bg-white/5 hover:border-b-2 hover:border-blue-500/50`} font-inter`}
703
+ onClick={() => setActiveTab('generator')}
704
+ >
705
+ <i className="fas fa-paint-brush mr-2"></i> QR Generator
706
+ </button>
707
+ <button
708
+ className={`flex-1 min-w-[150px] py-3 px-4 text-lg font-semibold rounded-t-2xl transition-all duration-300 relative z-10
709
+ ${activeTab === 'decoder' ? `${currentThemeClasses.tabActive} ${currentThemeClasses.text} shadow-lg border-b-4 ${currentThemeClasses.primaryAccent}` : `${currentThemeClasses.tabInactive} ${currentThemeClasses.text} hover:bg-white/5 hover:border-b-2 hover:border-blue-500/50`} font-inter`}
710
+ onClick={() => setActiveTab('decoder')}
711
+ >
712
+ <i className="fas fa-qrcode mr-2"></i> QR Decoder
713
+ </button>
714
+ <button
715
+ className={`flex-1 min-w-[150px] py-3 px-4 text-lg font-semibold rounded-t-2xl transition-all duration-300 relative z-10
716
+ ${activeTab === 'settings' ? `${currentThemeClasses.tabActive} ${currentThemeClasses.text} shadow-lg border-b-4 ${currentThemeClasses.primaryAccent}` : `${currentThemeClasses.tabInactive} ${currentThemeClasses.text} hover:bg-white/5 hover:border-b-2 hover:border-blue-500/50`} font-inter`}
717
+ onClick={() => setActiveTab('settings')}
718
+ >
719
+ <i className="fas fa-cog mr-2"></i> Settings
720
+ </button>
721
+ </div>
722
+
723
+ {/* Tab Content */}
724
+ <div className={`min-h-[600px] rounded-b-2xl rounded-tr-2xl shadow-2xl ${currentThemeClasses.glassBg} border ${currentThemeClasses.glassBorder} p-2`}>
725
+ {activeTab === 'generator' && <QRGeneratorTab />}
726
+ {activeTab === 'decoder' && <QRDecoderTab />}
727
+ {activeTab === 'settings' && <SettingsTab />}
728
+ </div>
729
+ </div>
730
+ </div>
731
+ );
732
+ };
733
+
734
+ export default App;
src/assets/react.svg ADDED
src/index.css ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
3
+ line-height: 1.5;
4
+ font-weight: 400;
5
+
6
+ color-scheme: light dark;
7
+ color: rgba(255, 255, 255, 0.87);
8
+ background-color: #242424;
9
+
10
+ font-synthesis: none;
11
+ text-rendering: optimizeLegibility;
12
+ -webkit-font-smoothing: antialiased;
13
+ -moz-osx-font-smoothing: grayscale;
14
+ }
15
+
16
+ a {
17
+ font-weight: 500;
18
+ color: #646cff;
19
+ text-decoration: inherit;
20
+ }
21
+ a:hover {
22
+ color: #535bf2;
23
+ }
24
+
25
+ body {
26
+ margin: 0;
27
+ display: flex;
28
+ place-items: center;
29
+ min-width: 320px;
30
+ min-height: 100vh;
31
+ }
32
+
33
+ h1 {
34
+ font-size: 3.2em;
35
+ line-height: 1.1;
36
+ }
37
+
38
+ button {
39
+ border-radius: 8px;
40
+ border: 1px solid transparent;
41
+ padding: 0.6em 1.2em;
42
+ font-size: 1em;
43
+ font-weight: 500;
44
+ font-family: inherit;
45
+ background-color: #1a1a1a;
46
+ cursor: pointer;
47
+ transition: border-color 0.25s;
48
+ }
49
+ button:hover {
50
+ border-color: #646cff;
51
+ }
52
+ button:focus,
53
+ button:focus-visible {
54
+ outline: 4px auto -webkit-focus-ring-color;
55
+ }
56
+
57
+ @media (prefers-color-scheme: light) {
58
+ :root {
59
+ color: #213547;
60
+ background-color: #ffffff;
61
+ }
62
+ a:hover {
63
+ color: #747bff;
64
+ }
65
+ button {
66
+ background-color: #f9f9f9;
67
+ }
68
+ }
src/main.jsx ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import { StrictMode } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import './index.css'
4
+ import App from './App.jsx'
5
+
6
+ createRoot(document.getElementById('root')).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>,
10
+ )