Elias207 commited on
Commit
8b15e94
·
verified ·
1 Parent(s): afc5c29

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +83 -102
index.html CHANGED
@@ -29,7 +29,6 @@
29
  flex-direction: column;
30
  }
31
 
32
- /* هدر */
33
  .header {
34
  text-align: center;
35
  padding: 20px 0 30px;
@@ -52,7 +51,6 @@
52
  opacity: 0.8;
53
  }
54
 
55
- /* تب‌ها */
56
  .tabs {
57
  display: flex;
58
  background: rgba(255,255,255,0.15);
@@ -79,7 +77,6 @@
79
  box-shadow: 0 4px 12px rgba(0,0,0,0.2);
80
  }
81
 
82
- /* کارت اصلی */
83
  .card {
84
  background: rgba(255,255,255,0.2);
85
  border-radius: 20px;
@@ -96,7 +93,6 @@
96
  display: flex;
97
  }
98
 
99
- /* فرم */
100
  .form-group {
101
  margin-bottom: 20px;
102
  }
@@ -133,7 +129,6 @@
133
  color: #666;
134
  }
135
 
136
- /* فایل آپلود */
137
  .file-upload {
138
  position: relative;
139
  background: rgba(255,255,255,0.9);
@@ -185,7 +180,6 @@
185
  font-weight: 500;
186
  }
187
 
188
- /* دکمه */
189
  .btn {
190
  width: 100%;
191
  padding: 16px;
@@ -222,7 +216,6 @@
222
  transform: translateY(0);
223
  }
224
 
225
- /* نتایج */
226
  .result {
227
  flex: 1;
228
  background: rgba(255,255,255,0.15);
@@ -252,16 +245,41 @@
252
  animation: zoomIn 0.4s ease;
253
  }
254
 
255
- .result-text {
 
 
256
  background: rgba(255,255,255,0.2);
257
  border-radius: 8px;
258
- padding: 15px;
 
 
 
 
 
 
 
 
259
  word-break: break-all;
260
  line-height: 1.5;
261
- animation: slideUp 0.4s ease;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  }
263
 
264
- /* لودر */
265
  .loader {
266
  width: 40px;
267
  height: 40px;
@@ -271,20 +289,10 @@
271
  animation: spin 1s linear infinite;
272
  }
273
 
274
- /* انیمیشن‌ها */
275
- @keyframes spin {
276
- 0% { transform: rotate(0deg); }
277
- 100% { transform: rotate(360deg); }
278
- }
279
- @keyframes zoomIn {
280
- from { opacity: 0; transform: scale(0.8); }
281
- to { opacity: 1; transform: scale(1); }
282
- }
283
- @keyframes slideUp {
284
- from { opacity: 0; transform: translateY(20px); }
285
- to { opacity: 1; transform: translateY(0); }
286
- }
287
- /* آیکون‌ها */
288
  .icon {
289
  display: inline-block;
290
  margin-left: 8px;
@@ -293,14 +301,12 @@
293
  </head>
294
  <body>
295
  <div class="container">
296
- <!-- هدر -->
297
  <div class="header">
298
  <div class="logo">📱</div>
299
  <h1 class="title">QR کد ساز</h1>
300
  <p class="subtitle">ساخت و خواندن آسان کیوآر کد</p>
301
  </div>
302
 
303
- <!-- تب‌ها -->
304
  <div class="tabs">
305
  <div class="tab active" data-tab="generate">
306
  <span class="icon">➕</span>
@@ -312,11 +318,11 @@
312
  </div>
313
  </div>
314
 
315
- <!-- کارت ساخت QR کد -->
316
  <div class="card tab-content active" id="generate-content">
317
  <div class="form-group">
318
  <label class="label">متن یا لینک خود را بنویسید:</label>
319
- <textarea id="text-input" class="input" placeholder="مثال: https://google.com یا سلام دنیا">سلام دنیا</textarea>
 
320
  </div>
321
  <div class="result" id="qr-result">
322
  <div id="generate-loader" class="loader" style="display: none;"></div>
@@ -330,12 +336,10 @@
330
  </button>
331
  </div>
332
 
333
- <!-- کارت خواندن QR کد -->
334
  <div class="card tab-content" id="read-content">
335
  <div class="form-group">
336
  <label class="label">تصویر QR کد را انتخاب کنید:</label>
337
  <div class="file-upload" onclick="document.getElementById('file-input').click()">
338
- <!-- ===== CHANGE HERE: The `accept` attribute is removed ===== -->
339
  <input type="file" id="file-input">
340
  <div class="upload-content">
341
  <div class="upload-icon">📷</div>
@@ -359,7 +363,6 @@
359
  <script>
360
  const SPACE_URL = "https://cultrix-qrcode-read-generate.hf.space";
361
 
362
- // عناصر DOM
363
  const tabs = document.querySelectorAll('.tab');
364
  const tabContents = document.querySelectorAll('.tab-content');
365
  const generateBtn = document.getElementById('generate-btn');
@@ -370,56 +373,51 @@
370
  const decodedText = document.getElementById('decoded-text');
371
  const generateLoader = document.getElementById('generate-loader');
372
  const readLoader = document.getElementById('read-loader');
373
-
374
  const fileUpload = document.querySelector('.file-upload');
375
  const uploadContent = document.querySelector('.upload-content');
376
 
377
- // تعویض تب‌ها
 
 
 
 
 
 
 
 
 
378
  tabs.forEach(tab => {
379
  tab.addEventListener('click', () => {
380
  tabs.forEach(t => t.classList.remove('active'));
381
  tabContents.forEach(tc => tc.classList.remove('active'));
382
-
383
  tab.classList.add('active');
384
- const targetTab = tab.getAttribute('data-tab');
385
- document.getElementById(targetTab + '-content').classList.add('active');
386
  });
387
  });
388
 
389
  fileInput.addEventListener('change', () => {
390
  const file = fileInput.files[0];
391
  if (file) {
392
- // Check if the file is an image before creating a preview
393
  if (file.type.startsWith('image/')) {
394
  const reader = new FileReader();
395
  reader.onload = function(e) {
396
  const previewImage = document.createElement('img');
397
  previewImage.src = e.target.result;
398
-
399
  uploadContent.style.display = 'none';
400
- if(fileUpload.querySelector('img')) {
401
- fileUpload.querySelector('img').remove();
402
- }
403
  fileUpload.appendChild(previewImage);
404
  fileUpload.classList.add('has-preview');
405
  }
406
  reader.readAsDataURL(file);
407
  } else {
408
- // If not an image, just show the file name
409
  uploadContent.style.display = 'block';
410
- if(fileUpload.querySelector('img')) {
411
- fileUpload.querySelector('img').remove();
412
- }
413
  fileUpload.classList.remove('has-preview');
414
- // Display file name instead of icon
415
  uploadContent.querySelector('.upload-icon').textContent = '📄';
416
  uploadContent.querySelector('.upload-text').textContent = file.name;
417
  }
418
  } else {
419
- // Reset if no file is selected
420
- if(fileUpload.querySelector('img')) {
421
- fileUpload.querySelector('img').remove();
422
- }
423
  uploadContent.style.display = 'block';
424
  uploadContent.querySelector('.upload-icon').textContent = '📷';
425
  uploadContent.querySelector('.upload-text').textContent = 'انتخاب تصویر';
@@ -427,7 +425,6 @@
427
  }
428
  });
429
 
430
- // توابع کمکی
431
  const generateSessionHash = () => Math.random().toString(36).substring(2, 15);
432
 
433
  const listenForData = (sessionHash, onResult, onError) => {
@@ -449,33 +446,23 @@
449
  };
450
  };
451
 
452
- // ساخت QR کد
453
  generateBtn.addEventListener('click', async () => {
454
  const text = textInput.value.trim();
455
- if (!text) {
456
- alert("لطفاً متنی را وارد کنید.");
457
- return;
458
- }
459
  generateLoader.style.display = 'block';
460
  qrDisplay.innerHTML = '';
461
  generateBtn.disabled = true;
462
- const sessionHash = generateSessionHash();
463
  try {
464
- const joinResponse = await fetch(`${SPACE_URL}/gradio_api/queue/join`, {
 
465
  method: 'POST',
466
  headers: { 'Content-Type': 'application/json' },
467
  body: JSON.stringify({ fn_index: 0, data: [text], session_hash: sessionHash })
468
  });
469
- if (!joinResponse.ok) throw new Error('خطا در ارسال درخواست');
470
- listenForData(
471
- sessionHash,
472
- (result) => {
473
- qrDisplay.innerHTML = (result && result[0]) ? result[0] : '<span class="result-empty">خطا در ساخت QR کد</span>';
474
- },
475
- (error) => {
476
- alert(`خطا: ${error}`);
477
- qrDisplay.innerHTML = '<span class="result-empty">خطا در ساخت</span>';
478
- }
479
  );
480
  } catch (error) {
481
  alert(`خطا: ${error.message}`);
@@ -486,42 +473,41 @@
486
  }
487
  });
488
 
489
- // خواندن QR کد
490
  readBtn.addEventListener('click', async () => {
491
  const file = fileInput.files[0];
492
- if (!file) {
493
- alert("لطفاً یک تصویر انتخاب کنید.");
494
- return;
495
- }
496
- if (!file.type.startsWith('image/')) {
497
- alert("لطفاً یک فایل تصویری انتخاب کنید.");
498
- return;
499
- }
500
  readLoader.style.display = 'block';
501
  decodedText.innerHTML = '';
502
  readBtn.disabled = true;
503
  try {
504
  const formData = new FormData();
505
  formData.append('files', file);
506
- const uploadResponse = await fetch(`${SPACE_URL}/gradio_api/upload`, { method: 'POST', body: formData });
507
- if (!uploadResponse.ok) throw new Error('خطا در آپلود فایل');
508
- const uploadResult = await uploadResponse.json();
509
- const serverFilePath = uploadResult[0];
510
- const fileDataObject = { path: serverFilePath, url: `${SPACE_URL}/gradio_api/file=${serverFilePath}`, orig_name: file.name };
511
  const sessionHash = generateSessionHash();
512
- const joinResponse = await fetch(`${SPACE_URL}/gradio_api/queue/join`, {
513
  method: 'POST',
514
  headers: { 'Content-Type': 'application/json' },
515
- body: JSON.stringify({ fn_index: 1, data: [fileDataObject], session_hash: sessionHash })
516
  });
517
- if (!joinResponse.ok) throw new Error('خطا در پردازش');
518
- listenForData(
519
- sessionHash,
520
- (result) => { decodedText.innerHTML = `<div class="result-text">📝 ${result[0]}</div>`; },
521
- (error) => {
522
- alert(`خطا: ${error}`);
523
- decodedText.innerHTML = '<span class="result-empty">خطا در خواندن</span>';
524
- }
 
 
 
 
 
525
  );
526
  } catch (error) {
527
  alert(`خطا: ${error.message}`);
@@ -532,12 +518,8 @@
532
  }
533
  });
534
 
535
- // Drag & Drop
536
  ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
537
- fileUpload.addEventListener(eventName, (e) => {
538
- e.preventDefault();
539
- e.stopPropagation();
540
- });
541
  });
542
  ['dragenter', 'dragover'].forEach(eventName => {
543
  fileUpload.addEventListener(eventName, () => {
@@ -551,10 +533,9 @@
551
  fileUpload.style.transform = 'translateY(0)';
552
  });
553
  });
554
- fileUpload.addEventListener('drop', (e) => {
555
- const files = e.dataTransfer.files;
556
- if (files.length > 0) {
557
- fileInput.files = files;
558
  fileInput.dispatchEvent(new Event('change'));
559
  }
560
  });
 
29
  flex-direction: column;
30
  }
31
 
 
32
  .header {
33
  text-align: center;
34
  padding: 20px 0 30px;
 
51
  opacity: 0.8;
52
  }
53
 
 
54
  .tabs {
55
  display: flex;
56
  background: rgba(255,255,255,0.15);
 
77
  box-shadow: 0 4px 12px rgba(0,0,0,0.2);
78
  }
79
 
 
80
  .card {
81
  background: rgba(255,255,255,0.2);
82
  border-radius: 20px;
 
93
  display: flex;
94
  }
95
 
 
96
  .form-group {
97
  margin-bottom: 20px;
98
  }
 
129
  color: #666;
130
  }
131
 
 
132
  .file-upload {
133
  position: relative;
134
  background: rgba(255,255,255,0.9);
 
180
  font-weight: 500;
181
  }
182
 
 
183
  .btn {
184
  width: 100%;
185
  padding: 16px;
 
216
  transform: translateY(0);
217
  }
218
 
 
219
  .result {
220
  flex: 1;
221
  background: rgba(255,255,255,0.15);
 
245
  animation: zoomIn 0.4s ease;
246
  }
247
 
248
+ /* ===== CHANGE HERE: Styles for the copy button and its container ===== */
249
+ .result-text-wrapper {
250
+ width: 100%;
251
  background: rgba(255,255,255,0.2);
252
  border-radius: 8px;
253
+ padding: 10px 15px;
254
+ display: flex;
255
+ align-items: center;
256
+ justify-content: space-between;
257
+ animation: slideUp 0.4s ease;
258
+ }
259
+ .result-text {
260
+ flex-grow: 1;
261
+ margin-left: 10px;
262
  word-break: break-all;
263
  line-height: 1.5;
264
+ text-align: right;
265
+ }
266
+ .copy-btn {
267
+ background: #fff;
268
+ color: #54a0ff;
269
+ border: none;
270
+ border-radius: 6px;
271
+ padding: 8px 12px;
272
+ font-family: 'Vazirmatn', sans-serif;
273
+ font-weight: 600;
274
+ cursor: pointer;
275
+ transition: background 0.2s ease;
276
+ flex-shrink: 0;
277
+ }
278
+ .copy-btn:hover {
279
+ background: #eef;
280
  }
281
 
282
+
283
  .loader {
284
  width: 40px;
285
  height: 40px;
 
289
  animation: spin 1s linear infinite;
290
  }
291
 
292
+ @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
293
+ @keyframes zoomIn { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }
294
+ @keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
295
+
 
 
 
 
 
 
 
 
 
 
296
  .icon {
297
  display: inline-block;
298
  margin-left: 8px;
 
301
  </head>
302
  <body>
303
  <div class="container">
 
304
  <div class="header">
305
  <div class="logo">📱</div>
306
  <h1 class="title">QR کد ساز</h1>
307
  <p class="subtitle">ساخت و خواندن آسان کیوآر کد</p>
308
  </div>
309
 
 
310
  <div class="tabs">
311
  <div class="tab active" data-tab="generate">
312
  <span class="icon">➕</span>
 
318
  </div>
319
  </div>
320
 
 
321
  <div class="card tab-content active" id="generate-content">
322
  <div class="form-group">
323
  <label class="label">متن یا لینک خود را بنویسید:</label>
324
+ <!-- ===== CHANGE HERE: Default text "سلام دنیا" is removed ===== -->
325
+ <textarea id="text-input" class="input" placeholder="مثال: https://google.com یا سلام دنیا"></textarea>
326
  </div>
327
  <div class="result" id="qr-result">
328
  <div id="generate-loader" class="loader" style="display: none;"></div>
 
336
  </button>
337
  </div>
338
 
 
339
  <div class="card tab-content" id="read-content">
340
  <div class="form-group">
341
  <label class="label">تصویر QR کد را انتخاب کنید:</label>
342
  <div class="file-upload" onclick="document.getElementById('file-input').click()">
 
343
  <input type="file" id="file-input">
344
  <div class="upload-content">
345
  <div class="upload-icon">📷</div>
 
363
  <script>
364
  const SPACE_URL = "https://cultrix-qrcode-read-generate.hf.space";
365
 
 
366
  const tabs = document.querySelectorAll('.tab');
367
  const tabContents = document.querySelectorAll('.tab-content');
368
  const generateBtn = document.getElementById('generate-btn');
 
373
  const decodedText = document.getElementById('decoded-text');
374
  const generateLoader = document.getElementById('generate-loader');
375
  const readLoader = document.getElementById('read-loader');
 
376
  const fileUpload = document.querySelector('.file-upload');
377
  const uploadContent = document.querySelector('.upload-content');
378
 
379
+ // ===== CHANGE HERE: New function to copy text to clipboard =====
380
+ function copyToClipboard(text) {
381
+ navigator.clipboard.writeText(text).then(() => {
382
+ alert('متن با موفقیت کپی شد!');
383
+ }).catch(err => {
384
+ console.error('Failed to copy text: ', err);
385
+ alert('کپی کردن با خطا مواجه شد.');
386
+ });
387
+ }
388
+
389
  tabs.forEach(tab => {
390
  tab.addEventListener('click', () => {
391
  tabs.forEach(t => t.classList.remove('active'));
392
  tabContents.forEach(tc => tc.classList.remove('active'));
 
393
  tab.classList.add('active');
394
+ document.getElementById(tab.getAttribute('data-tab') + '-content').classList.add('active');
 
395
  });
396
  });
397
 
398
  fileInput.addEventListener('change', () => {
399
  const file = fileInput.files[0];
400
  if (file) {
 
401
  if (file.type.startsWith('image/')) {
402
  const reader = new FileReader();
403
  reader.onload = function(e) {
404
  const previewImage = document.createElement('img');
405
  previewImage.src = e.target.result;
 
406
  uploadContent.style.display = 'none';
407
+ if(fileUpload.querySelector('img')) fileUpload.querySelector('img').remove();
 
 
408
  fileUpload.appendChild(previewImage);
409
  fileUpload.classList.add('has-preview');
410
  }
411
  reader.readAsDataURL(file);
412
  } else {
413
+ if(fileUpload.querySelector('img')) fileUpload.querySelector('img').remove();
414
  uploadContent.style.display = 'block';
 
 
 
415
  fileUpload.classList.remove('has-preview');
 
416
  uploadContent.querySelector('.upload-icon').textContent = '📄';
417
  uploadContent.querySelector('.upload-text').textContent = file.name;
418
  }
419
  } else {
420
+ if(fileUpload.querySelector('img')) fileUpload.querySelector('img').remove();
 
 
 
421
  uploadContent.style.display = 'block';
422
  uploadContent.querySelector('.upload-icon').textContent = '📷';
423
  uploadContent.querySelector('.upload-text').textContent = 'انتخاب تصویر';
 
425
  }
426
  });
427
 
 
428
  const generateSessionHash = () => Math.random().toString(36).substring(2, 15);
429
 
430
  const listenForData = (sessionHash, onResult, onError) => {
 
446
  };
447
  };
448
 
 
449
  generateBtn.addEventListener('click', async () => {
450
  const text = textInput.value.trim();
451
+ if (!text) return alert("لطفاً متنی را وارد کنید.");
 
 
 
452
  generateLoader.style.display = 'block';
453
  qrDisplay.innerHTML = '';
454
  generateBtn.disabled = true;
 
455
  try {
456
+ const sessionHash = generateSessionHash();
457
+ const res = await fetch(`${SPACE_URL}/gradio_api/queue/join`, {
458
  method: 'POST',
459
  headers: { 'Content-Type': 'application/json' },
460
  body: JSON.stringify({ fn_index: 0, data: [text], session_hash: sessionHash })
461
  });
462
+ if (!res.ok) throw new Error('خطا در ارسال درخواست');
463
+ listenForData(sessionHash,
464
+ (result) => qrDisplay.innerHTML = (result && result[0]) ? result[0] : '<span class="result-empty">خطا در ساخت</span>',
465
+ (error) => { alert(`خطا: ${error}`); qrDisplay.innerHTML = '<span class="result-empty">خطا</span>'; }
 
 
 
 
 
 
466
  );
467
  } catch (error) {
468
  alert(`خطا: ${error.message}`);
 
473
  }
474
  });
475
 
 
476
  readBtn.addEventListener('click', async () => {
477
  const file = fileInput.files[0];
478
+ if (!file) return alert("لطفاً یک تصویر انتخاب کنید.");
479
+ if (!file.type.startsWith('image/')) return alert("لطفاً یک فایل تصویری انتخاب کنید.");
480
+
 
 
 
 
 
481
  readLoader.style.display = 'block';
482
  decodedText.innerHTML = '';
483
  readBtn.disabled = true;
484
  try {
485
  const formData = new FormData();
486
  formData.append('files', file);
487
+ const uploadRes = await fetch(`${SPACE_URL}/gradio_api/upload`, { method: 'POST', body: formData });
488
+ if (!uploadRes.ok) throw new Error('خطا در آپلود فایل');
489
+ const [serverFilePath] = await uploadRes.json();
490
+ const fileData = { path: serverFilePath, url: `${SPACE_URL}/gradio_api/file=${serverFilePath}`, orig_name: file.name };
491
+
492
  const sessionHash = generateSessionHash();
493
+ const joinRes = await fetch(`${SPACE_URL}/gradio_api/queue/join`, {
494
  method: 'POST',
495
  headers: { 'Content-Type': 'application/json' },
496
+ body: JSON.stringify({ fn_index: 1, data: [fileData], session_hash: sessionHash })
497
  });
498
+ if (!joinRes.ok) throw new Error('خطا در پردازش');
499
+
500
+ listenForData(sessionHash,
501
+ (result) => {
502
+ const textToCopy = result[0];
503
+ // ===== CHANGE HERE: Result now includes a copy button =====
504
+ decodedText.innerHTML = `
505
+ <div class="result-text-wrapper">
506
+ <span class="result-text">📝 ${textToCopy}</span>
507
+ <button class="copy-btn" onclick="copyToClipboard('${textToCopy.replace(/'/g, "\\'")}')">کپی</button>
508
+ </div>`;
509
+ },
510
+ (error) => { alert(`خطا: ${error}`); decodedText.innerHTML = '<span class="result-empty">خطا در خواندن</span>'; }
511
  );
512
  } catch (error) {
513
  alert(`خطا: ${error.message}`);
 
518
  }
519
  });
520
 
 
521
  ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
522
+ fileUpload.addEventListener(eventName, e => { e.preventDefault(); e.stopPropagation(); });
 
 
 
523
  });
524
  ['dragenter', 'dragover'].forEach(eventName => {
525
  fileUpload.addEventListener(eventName, () => {
 
533
  fileUpload.style.transform = 'translateY(0)';
534
  });
535
  });
536
+ fileUpload.addEventListener('drop', e => {
537
+ if (e.dataTransfer.files.length > 0) {
538
+ fileInput.files = e.dataTransfer.files;
 
539
  fileInput.dispatchEvent(new Event('change'));
540
  }
541
  });