Srevarshan1502 commited on
Commit
bb8fa59
·
1 Parent(s): 266ed05

Add QRForge app files for Hugging Face Spaces deployment

Browse files
Files changed (3) hide show
  1. Dockerfile +27 -0
  2. app.py +80 -0
  3. index.html +611 -0
Dockerfile ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ # Set working directory
4
+ WORKDIR /app
5
+
6
+ # Copy application files
7
+ COPY app.py .
8
+ COPY index.html .
9
+
10
+ # Install system dependencies (for Pillow and reportlab)
11
+ RUN apt-get update && apt-get install -y \
12
+ libjpeg-dev \
13
+ zlib1g-dev \
14
+ && rm -rf /var/lib/apt/lists/*
15
+
16
+ # Install Python dependencies
17
+ RUN pip install --no-cache-dir flask qrcode pillow reportlab
18
+
19
+ # Expose port 7860 for Hugging Face Spaces
20
+ EXPOSE 7860
21
+
22
+ # Set environment variables
23
+ ENV FLASK_APP=app.py
24
+ ENV FLASK_ENV=production
25
+
26
+ # Run the Flask app
27
+ CMD ["flask", "run", "--host=0.0.0.0", "--port=7860"]
app.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify, send_file
2
+ import qrcode
3
+ from io import BytesIO
4
+ import base64
5
+ from reportlab.lib.pagesizes import letter
6
+ from reportlab.pdfgen import canvas
7
+ from PIL import Image
8
+
9
+ app = Flask(__name__)
10
+
11
+ @app.route('/')
12
+ def index():
13
+ return open('index.html').read()
14
+
15
+ @app.route('/generate_qr', methods=['POST'])
16
+ def generate_qr():
17
+ data = request.get_json()
18
+ url = data.get('url')
19
+
20
+ if not url:
21
+ return jsonify({'error': 'URL is required'}), 400
22
+
23
+ # Generate QR code
24
+ qr = qrcode.make(url)
25
+
26
+ # Save to BytesIO buffer as PNG for display
27
+ buffer = BytesIO()
28
+ qr.save(buffer, format='PNG')
29
+ buffer.seek(0)
30
+
31
+ # Convert to base64 for frontend display
32
+ qr_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
33
+ qr_image = f'data:image/png;base64,{qr_base64}'
34
+
35
+ return jsonify({'qr_image': qr_image})
36
+
37
+ @app.route('/download_qr', methods=['POST'])
38
+ def download_qr():
39
+ data = request.get_json()
40
+ url = data.get('url')
41
+ format = data.get('format', 'png').lower()
42
+
43
+ if not url:
44
+ return jsonify({'error': 'URL is required'}), 400
45
+
46
+ # Generate QR code
47
+ qr = qrcode.make(url)
48
+
49
+ buffer = BytesIO()
50
+
51
+ if format == 'png':
52
+ qr.save(buffer, format='PNG')
53
+ mime_type = 'image/png'
54
+ filename = 'qr_code.png'
55
+ elif format == 'jpg':
56
+ qr.save(buffer, format='PNG') # Save as PNG first
57
+ img = Image.open(buffer).convert('RGB')
58
+ buffer = BytesIO()
59
+ img.save(buffer, format='JPEG', quality=95)
60
+ mime_type = 'image/jpeg'
61
+ filename = 'qr_code.jpg'
62
+ elif format == 'pdf':
63
+ c = canvas.Canvas(buffer, pagesize=letter)
64
+ img_buffer = BytesIO()
65
+ qr.save(img_buffer, format='PNG')
66
+ img = Image.open(img_buffer)
67
+ img_width, img_height = img.size
68
+ c.drawImage(img_buffer, (letter[0] - img_width) / 2, (letter[1] - img_height) / 2, img_width, img_height)
69
+ c.showPage()
70
+ c.save()
71
+ mime_type = 'application/pdf'
72
+ filename = 'qr_code.pdf'
73
+ else:
74
+ return jsonify({'error': 'Unsupported format'}), 400
75
+
76
+ buffer.seek(0)
77
+ return send_file(buffer, mimetype=mime_type, as_attachment=True, download_name=filename)
78
+
79
+ if __name__ == '__main__':
80
+ app.run(host='0.0.0.0', port=7860, debug=False)
index.html ADDED
@@ -0,0 +1,611 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>URL to QR Generator</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ font-family: 'Orbitron', sans-serif;
13
+ }
14
+
15
+ body {
16
+ min-height: 100vh;
17
+ display: flex;
18
+ justify-content: center;
19
+ align-items: center;
20
+ background: #1a0d2b;
21
+ overflow: hidden;
22
+ position: relative;
23
+ }
24
+
25
+ /* Canvas for Gradient Smudge and Stars */
26
+ #spaceCanvas {
27
+ position: absolute;
28
+ top: 0;
29
+ left: 0;
30
+ width: 100%;
31
+ height: 100%;
32
+ z-index: 0;
33
+ }
34
+
35
+ /* Main Container with Enhanced Glassmorphism */
36
+ .container {
37
+ background: rgba(30, 15, 50, 0.15);
38
+ backdrop-filter: blur(20px);
39
+ -webkit-backdrop-filter: blur(20px);
40
+ border: 1px solid rgba(138, 43, 226, 0.2);
41
+ border-radius: 20px;
42
+ padding: 2.5rem;
43
+ width: 90%;
44
+ max-width: 600px;
45
+ box-shadow: 0 8px 40px rgba(138, 43, 226, 0.15);
46
+ text-align: center;
47
+ position: relative;
48
+ z-index: 1;
49
+ display: flex;
50
+ flex-direction: column;
51
+ align-items: center;
52
+ transition: transform 0.4s ease, box-shadow 0.4s ease;
53
+ }
54
+
55
+ .container:hover {
56
+ transform: scale(1.03);
57
+ box-shadow: 0 12px 48px rgba(138, 43, 226, 0.25);
58
+ }
59
+
60
+ h1 {
61
+ color: #d8b9ff;
62
+ font-size: 2.5rem;
63
+ margin-bottom: 2rem;
64
+ text-transform: uppercase;
65
+ letter-spacing: 2px;
66
+ text-shadow: 0 0 10px rgba(138, 43, 226, 0.8);
67
+ }
68
+
69
+ /* Input Field with Purple Glow */
70
+ .input-group {
71
+ position: relative;
72
+ margin-bottom: 2.5rem;
73
+ width: 100%;
74
+ }
75
+
76
+ input[type="url"] {
77
+ width: 100%;
78
+ padding: 1rem;
79
+ border: 2px solid #8a2be2;
80
+ border-radius: 10px;
81
+ background: rgba(0, 0, 0, 0.5);
82
+ color: #fff;
83
+ font-size: 1.1rem;
84
+ outline: none;
85
+ transition: all 0.3s ease;
86
+ box-shadow: 0 0 15px rgba(138, 43, 226, 0.3);
87
+ }
88
+
89
+ input[type="url"]::placeholder {
90
+ color: rgba(138, 43, 226, 0.6);
91
+ }
92
+
93
+ input[type="url"]:focus {
94
+ border-color: #e066ff;
95
+ box-shadow: 0 0 25px rgba(224, 102, 255, 0.5);
96
+ transform: scale(1.02);
97
+ }
98
+
99
+ /* Pulsating Button */
100
+ .btn {
101
+ display: inline-block;
102
+ padding: 1rem 2.5rem;
103
+ border: 2px solid #e066ff;
104
+ border-radius: 50px;
105
+ background: linear-gradient(45deg, #8a2be2, #e066ff);
106
+ color: #fff;
107
+ font-size: 1.2rem;
108
+ font-weight: 700;
109
+ text-transform: uppercase;
110
+ cursor: pointer;
111
+ position: relative;
112
+ overflow: hidden;
113
+ transition: all 0.3s ease;
114
+ animation: pulse 2s infinite;
115
+ }
116
+
117
+ @keyframes pulse {
118
+ 0% { box-shadow: 0 0 10px rgba(224, 102, 255, 0.5); }
119
+ 50% { box-shadow: 0 0 20px rgba(224, 102, 255, 0.8); }
120
+ 100% { box-shadow: 0 0 10px rgba(224, 102, 255, 0.5); }
121
+ }
122
+
123
+ .btn::before {
124
+ content: '';
125
+ position: absolute;
126
+ top: 50%;
127
+ left: 50%;
128
+ width: 0;
129
+ height: 0;
130
+ background: rgba(255, 255, 255, 0.2);
131
+ border-radius: 50%;
132
+ transform: translate(-50%, -50%);
133
+ transition: width 0.5s ease, height 0.5s ease;
134
+ }
135
+
136
+ .btn:hover::before {
137
+ width: 400px;
138
+ height: 400px;
139
+ }
140
+
141
+ .btn:hover {
142
+ transform: translateY(-5px);
143
+ box-shadow: 0 0 30px rgba(224, 102, 255, 0.7);
144
+ }
145
+
146
+ /* QR Code Display with Dynamic Design */
147
+ .qr-code {
148
+ margin-top: 2.5rem;
149
+ padding: 1.5rem;
150
+ background: #fff;
151
+ border-radius: 10px;
152
+ display: none;
153
+ justify-content: center;
154
+ align-items: center;
155
+ box-shadow: 0 0 20px rgba(138, 43, 226, 0.4);
156
+ transition: opacity 0.5s ease, transform 0.5s ease;
157
+ width: 100%;
158
+ max-width: 300px;
159
+ position: relative;
160
+ overflow: hidden;
161
+ }
162
+
163
+ .qr-code::before {
164
+ content: '';
165
+ position: absolute;
166
+ top: 0;
167
+ left: 0;
168
+ width: 100%;
169
+ height: 100%;
170
+ border: 3px solid transparent;
171
+ border-image: linear-gradient(45deg, #8a2be2, #e066ff) 1;
172
+ border-radius: 10px;
173
+ animation: borderGlow 3s infinite ease-in-out;
174
+ }
175
+
176
+ @keyframes borderGlow {
177
+ 0% { border-image: linear-gradient(45deg, #8a2be2, #e066ff) 1; }
178
+ 50% { border-image: linear-gradient(45deg, #e066ff, #8a2be2) 1; }
179
+ 100% { border-image: linear-gradient(45deg, #8a2be2, #e066ff) 1; }
180
+ }
181
+
182
+ .qr-code.show {
183
+ display: flex;
184
+ opacity: 1;
185
+ transform: scale(1);
186
+ }
187
+
188
+ .qr-code img {
189
+ max-width: 100%;
190
+ height: auto;
191
+ border-radius: 5px;
192
+ }
193
+
194
+ /* Export Options */
195
+ .export-options {
196
+ margin-top: 1rem;
197
+ display: none;
198
+ flex-direction: row;
199
+ flex-wrap: wrap;
200
+ justify-content: center;
201
+ gap: 0.5rem;
202
+ }
203
+
204
+ .export-options.show {
205
+ display: flex;
206
+ }
207
+
208
+ .export-btn {
209
+ display: inline-block;
210
+ padding: 0.5rem 1.5rem;
211
+ border: 2px solid #e066ff;
212
+ border-radius: 50px;
213
+ background: linear-gradient(45deg, #8a2be2, #e066ff);
214
+ color: #fff;
215
+ font-size: 0.9rem;
216
+ font-weight: 600;
217
+ text-transform: uppercase;
218
+ cursor: pointer;
219
+ position: relative;
220
+ overflow: hidden;
221
+ transition: all 0.3s ease;
222
+ animation: pulse 2s infinite;
223
+ }
224
+
225
+ .export-btn::before {
226
+ content: '';
227
+ position: absolute;
228
+ top: 50%;
229
+ left: 50%;
230
+ width: 0;
231
+ height: 0;
232
+ background: rgba(255, 255, 255, 0.2);
233
+ border-radius: 50%;
234
+ transform: translate(-50%, -50%);
235
+ transition: width 0.5s ease, height 0.5s ease;
236
+ }
237
+
238
+ .export-btn:hover::before {
239
+ width: 300px;
240
+ height: 300px;
241
+ }
242
+
243
+ .export-btn:hover {
244
+ transform: translateY(-3px);
245
+ box-shadow: 0 0 20px rgba(224, 102, 255, 0.7);
246
+ }
247
+
248
+ /* Success Message */
249
+ .success-message {
250
+ margin-top: 1rem;
251
+ color: #d8b9ff;
252
+ font-size: 0.9rem;
253
+ font-weight: 600;
254
+ text-align: center;
255
+ display: none;
256
+ text-shadow: 0 0 5px rgba(138, 43, 226, 0.8);
257
+ }
258
+
259
+ .success-message.show {
260
+ display: block;
261
+ }
262
+
263
+ /* Responsive Design */
264
+ @media (max-width: 600px) {
265
+ .container {
266
+ padding: 1.5rem;
267
+ width: 95%;
268
+ max-width: 400px;
269
+ }
270
+
271
+ h1 {
272
+ font-size: 1.8rem;
273
+ }
274
+
275
+ input[type="url"] {
276
+ padding: 0.8rem;
277
+ font-size: 1rem;
278
+ }
279
+
280
+ .btn {
281
+ padding: 0.8rem 2rem;
282
+ font-size: 1rem;
283
+ }
284
+
285
+ .qr-code {
286
+ max-width: 250px;
287
+ }
288
+
289
+ .export-btn {
290
+ padding: 0.4rem 1.2rem;
291
+ font-size: 0.8rem;
292
+ }
293
+
294
+ .success-message {
295
+ font-size: 0.8rem;
296
+ }
297
+ }
298
+
299
+ @media (min-width: 601px) and (max-width: 1024px) {
300
+ .container {
301
+ padding: 2rem;
302
+ width: 90%;
303
+ max-width: 500px;
304
+ }
305
+
306
+ h1 {
307
+ font-size: 2rem;
308
+ }
309
+
310
+ input[type="url"] {
311
+ padding: 0.9rem;
312
+ font-size: 1.05rem;
313
+ }
314
+
315
+ .btn {
316
+ padding: 0.9rem 2.2rem;
317
+ font-size: 1.1rem;
318
+ }
319
+
320
+ .qr-code {
321
+ max-width: 280px;
322
+ }
323
+
324
+ .export-btn {
325
+ padding: 0.5rem 1.3rem;
326
+ font-size: 0.9rem;
327
+ }
328
+
329
+ .success-message {
330
+ font-size: 0.85rem;
331
+ }
332
+ }
333
+
334
+ @media (min-width: 1025px) {
335
+ .container {
336
+ padding: 2.5rem;
337
+ max-width: 600px;
338
+ }
339
+
340
+ h1 {
341
+ font-size: 2.5rem;
342
+ }
343
+
344
+ input[type="url"] {
345
+ padding: 1rem;
346
+ font-size: 1.1rem;
347
+ }
348
+
349
+ .btn {
350
+ padding: 1rem 2.5rem;
351
+ font-size: 1.2rem;
352
+ }
353
+
354
+ .qr-code {
355
+ max-width: 300px;
356
+ }
357
+
358
+ .success-message {
359
+ font-size: 0.9rem;
360
+ }
361
+ }
362
+ </style>
363
+ </head>
364
+ <body>
365
+ <canvas id="spaceCanvas"></canvas>
366
+ <div class="container">
367
+ <h1>URL to QR Generator</h1>
368
+ <div class="input-group">
369
+ <input type="url" id="urlInput" placeholder="Enter URL (e.g., https://example.com)" required>
370
+ </div>
371
+ <button class="btn" onclick="generateQR()">Generate QR</button>
372
+ <div class="qr-code" id="qrCode">
373
+ <img id="qrImage" alt="QR Code">
374
+ </div>
375
+ <div class="export-options" id="exportOptions">
376
+ <button class="export-btn" onclick="downloadQR('png')">PNG</button>
377
+ <button class="export-btn" onclick="downloadQR('jpg')">JPG</button>
378
+ </div>
379
+ <div class="success-message" id="successMessage"></div>
380
+ </div>
381
+
382
+ <script>
383
+ // Canvas-based Gradient Smudge Space Effect with Stars and Shooting Stars
384
+ const canvas = document.getElementById('spaceCanvas');
385
+ const ctx = canvas.getContext('2d');
386
+ canvas.width = window.innerWidth;
387
+ canvas.height = window.innerHeight;
388
+
389
+ // Gradient Smudge Background
390
+ function drawBackground() {
391
+ const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
392
+ gradient.addColorStop(0, '#1a0d2b');
393
+ gradient.addColorStop(0.3, '#2a1b4a');
394
+ gradient.addColorStop(0.7, '#4b2e8a');
395
+ gradient.addColorStop(1, '#1a0d2b');
396
+ ctx.fillStyle = gradient;
397
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
398
+
399
+ // Add blurred smudge effect
400
+ ctx.filter = 'blur(50px)';
401
+ const smudgeGradient = ctx.createRadialGradient(
402
+ canvas.width / 2, canvas.height / 2, 100,
403
+ canvas.width / 2, canvas.height / 2, canvas.width / 2
404
+ );
405
+ smudgeGradient.addColorStop(0, 'rgba(138, 43, 226, 0.3)');
406
+ smudgeGradient.addColorStop(1, 'rgba(138, 43, 226, 0)');
407
+ ctx.fillStyle = smudgeGradient;
408
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
409
+ ctx.filter = 'none';
410
+ }
411
+
412
+ // Stars
413
+ const stars = [];
414
+ class Star {
415
+ constructor() {
416
+ this.x = Math.random() * canvas.width;
417
+ this.y = Math.random() * canvas.height;
418
+ this.size = Math.random() * 2 + 0.5;
419
+ this.opacity = Math.random() * 0.5 + 0.3;
420
+ this.twinkleSpeed = Math.random() * 0.02 + 0.01;
421
+ }
422
+
423
+ draw() {
424
+ ctx.fillStyle = `rgba(216, 185, 255, ${this.opacity})`;
425
+ ctx.beginPath();
426
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
427
+ ctx.fill();
428
+ }
429
+
430
+ update() {
431
+ this.opacity = 0.3 + Math.sin(Date.now() * this.twinkleSpeed) * 0.2;
432
+ }
433
+ }
434
+
435
+ // Shooting Stars
436
+ const shootingStars = [];
437
+ class ShootingStar {
438
+ constructor() {
439
+ this.x = Math.random() * canvas.width;
440
+ this.y = Math.random() * canvas.height / 2;
441
+ this.length = Math.random() * 50 + 20;
442
+ this.speed = Math.random() * 5 + 5;
443
+ this.angle = Math.PI / 4; // Diagonal trajectory
444
+ this.opacity = Math.random() * 0.5 + 0.5;
445
+ }
446
+
447
+ draw() {
448
+ ctx.strokeStyle = `rgba(216, 185, 255, ${this.opacity})`;
449
+ ctx.lineWidth = 2;
450
+ ctx.beginPath();
451
+ ctx.moveTo(this.x, this.y);
452
+ ctx.lineTo(
453
+ this.x - this.length * Math.cos(this.angle),
454
+ this.y - this.length * Math.sin(this.angle)
455
+ );
456
+ ctx.stroke();
457
+ }
458
+
459
+ update() {
460
+ this.x += this.speed * Math.cos(this.angle);
461
+ this.y += this.speed * Math.sin(this.angle);
462
+ this.opacity -= 0.02;
463
+ if (this.opacity <= 0) {
464
+ this.reset();
465
+ }
466
+ }
467
+
468
+ reset() {
469
+ this.x = Math.random() * canvas.width;
470
+ this.y = Math.random() * canvas.height / 2;
471
+ this.opacity = Math.random() * 0.5 + 0.5;
472
+ }
473
+ }
474
+
475
+ // Initialize Stars and Shooting Stars
476
+ function init() {
477
+ for (let i = 0; i < 200; i++) {
478
+ stars.push(new Star());
479
+ }
480
+ for (let i = 0; i < 2; i++) {
481
+ shootingStars.push(new ShootingStar());
482
+ }
483
+ }
484
+
485
+ // Animation Loop
486
+ let lastFrameTime = performance.now();
487
+ const frameInterval = 1000 / 60; // Target 60 FPS
488
+ function animate(currentTime) {
489
+ if (currentTime - lastFrameTime >= frameInterval) {
490
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
491
+ drawBackground();
492
+ stars.forEach(star => {
493
+ star.update();
494
+ star.draw();
495
+ });
496
+ shootingStars.forEach(star => {
497
+ star.update();
498
+ star.draw();
499
+ });
500
+ lastFrameTime = currentTime - (currentTime % frameInterval);
501
+ }
502
+ requestAnimationFrame(animate);
503
+ }
504
+
505
+ init();
506
+ animate(performance.now());
507
+
508
+ // Resize Handler
509
+ window.addEventListener('resize', () => {
510
+ canvas.width = window.innerWidth;
511
+ canvas.height = window.innerHeight;
512
+ drawBackground();
513
+ });
514
+
515
+ // QR Code Generation
516
+ async function generateQR() {
517
+ const urlInput = document.getElementById('urlInput').value;
518
+ const qrCodeDiv = document.getElementById('qrCode');
519
+ const qrImage = document.getElementById('qrImage');
520
+ const exportOptions = document.getElementById('exportOptions');
521
+ const successMessage = document.getElementById('successMessage');
522
+
523
+ if (!urlInput || !urlInput.startsWith('http')) {
524
+ alert('Please enter a valid URL starting with http:// or https://');
525
+ return;
526
+ }
527
+
528
+ try {
529
+ // Show loading state
530
+ qrCodeDiv.style.display = 'flex';
531
+ qrCodeDiv.style.opacity = '0.5';
532
+ qrImage.src = '';
533
+ exportOptions.classList.remove('show');
534
+ successMessage.classList.remove('show');
535
+ successMessage.textContent = '';
536
+
537
+ // Call Flask backend to generate QR code
538
+ const response = await fetch('/generate_qr', {
539
+ method: 'POST',
540
+ headers: { 'Content-Type': 'application/json' },
541
+ body: JSON.stringify({ url: urlInput })
542
+ });
543
+
544
+ if (!response.ok) throw new Error('Failed to generate QR code');
545
+
546
+ const data = await response.json();
547
+ qrImage.src = data.qr_image;
548
+ qrCodeDiv.classList.add('show');
549
+ qrCodeDiv.style.opacity = '1';
550
+ exportOptions.classList.add('show');
551
+ } catch (error) {
552
+ console.error('Error:', error);
553
+ alert('An error occurred while generating the QR code.');
554
+ qrCodeDiv.style.display = 'none';
555
+ exportOptions.classList.remove('show');
556
+ }
557
+ }
558
+
559
+ // Download QR Code
560
+ async function downloadQR(format) {
561
+ const urlInput = document.getElementById('urlInput').value;
562
+ const qrCodeDiv = document.getElementById('qrCode');
563
+ const qrImage = document.getElementById('qrImage');
564
+ const exportOptions = document.getElementById('exportOptions');
565
+ const successMessage = document.getElementById('successMessage');
566
+
567
+ try {
568
+ const response = await fetch('/download_qr', {
569
+ method: 'POST',
570
+ headers: { 'Content-Type': 'application/json' },
571
+ body: JSON.stringify({ url: urlInput, format: format })
572
+ });
573
+
574
+ if (!response.ok) throw new Error('Failed to download QR code');
575
+
576
+ const blob = await response.blob();
577
+ const url = window.URL.createObjectURL(blob);
578
+ const a = document.createElement('a');
579
+ a.href = url;
580
+ a.download = `qr_code.${format}`;
581
+ document.body.appendChild(a);
582
+ a.click();
583
+ document.body.removeChild(a);
584
+ window.URL.revokeObjectURL(url);
585
+
586
+ // Show success message
587
+ successMessage.textContent = `${format.toUpperCase()} downloaded successfully`;
588
+ successMessage.classList.add('show');
589
+
590
+ // Reset QR code display and export options after a delay
591
+ setTimeout(() => {
592
+ qrCodeDiv.classList.remove('show');
593
+ qrCodeDiv.style.display = 'none';
594
+ qrImage.src = '';
595
+ exportOptions.classList.remove('show');
596
+ successMessage.classList.remove('show');
597
+ successMessage.textContent = '';
598
+ }, 2000); // Message displays for 2 seconds
599
+ } catch (error) {
600
+ console.error('Error:', error);
601
+ alert('An error occurred while downloading the QR code.');
602
+ }
603
+ }
604
+
605
+ // Trigger generateQR on Enter key press
606
+ document.getElementById('urlInput').addEventListener('keypress', (e) => {
607
+ if (e.key === 'Enter') generateQR();
608
+ });
609
+ </script>
610
+ </body>
611
+ </html>