Elias207 commited on
Commit
eba0b23
·
verified ·
1 Parent(s): c24ad72

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +345 -89
index.html CHANGED
@@ -3,130 +3,386 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>کلاینت QR Code (اجرای محلی)</title>
 
7
  <style>
8
- body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; background-color: #f0f2f5; display: flex; justify-content: center; align-items: flex-start; padding: 20px; gap: 30px; flex-wrap: wrap; }
9
- .container { background-color: #fff; padding: 25px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); width: 400px; }
10
- h2 { margin: 0 0 15px 0; color: #0056b3; border-bottom: 2px solid #0056b3; padding-bottom: 5px; }
11
- textarea, input[type="file"] { width: 100%; padding: 10px; margin-bottom: 10px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }
12
- button { background-color: #007bff; color: white; padding: 12px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; width: 100%; }
13
- button:disabled { background-color: #a0c9ff; }
14
- .result-box { margin-top: 15px; padding: 15px; border: 1px dashed #ddd; border-radius: 4px; min-height: 50px; display: flex; justify-content: center; align-items: center; background-color: #fafafa; word-break: break-all; }
15
- #qr-image { max-width: 100%; height: auto; }
16
- .loader { border: 4px solid #f3f3f3; border-radius: 50%; border-top: 4px solid #3498db; width: 30px; height: 30px; animation: spin 1s linear infinite; }
17
- @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
18
- .hidden { display: none; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </style>
20
  </head>
21
  <body>
22
 
 
 
 
23
  <div class="container">
24
- <h2>ساخت QR Code</h2>
25
- <textarea id="text-input" placeholder="متن مورد نظر را اینجا وارد کنید..." rows="4">این بار حتما کار می‌کند!</textarea>
26
- <button id="generate-btn" onclick="generateQRCode()">ساخت کد</button>
 
 
 
 
27
  <div class="result-box">
28
- <div id="generate-loader" class="loader hidden"></div>
29
- <img id="qr-image" class="hidden" />
30
- <span id="generate-placeholder">تصویر QR Code اینجا نمایش داده می‌شود.</span>
31
  </div>
32
  </div>
33
 
 
34
  <div class="container">
35
- <h2>خواندن QR Code</h2>
36
- <input type="file" id="file-input" accept="image/*">
37
- <button id="read-btn" onclick="readQRCode()">خواندن کد</button>
 
 
 
 
 
 
 
 
 
 
 
38
  <div class="result-box">
39
- <div id="read-loader" class="loader hidden"></div>
40
- <p id="read-text">نتیجه خواندن کد اینجا نمایش داده می‌شود.</p>
41
  </div>
42
  </div>
43
 
44
  <script>
45
- // آدرس اسپیسی که API آن فعال است
46
- const API_ROOT_URL = "https://cultrix-qrcode-read-generate.hf.space/";
47
- const API_BASE_URL = API_ROOT_URL + "gradio_api/";
48
 
49
- function generateSessionHash() { return Math.random().toString(36).substring(2, 11); }
50
-
51
- async function callGradioApi(fnIndex, data, sessionHash) {
52
- await fetch(API_BASE_URL + "queue/join?", {
53
- method: "POST", headers: { "Content-Type": "application/json" },
54
- body: JSON.stringify({ "fn_index": fnIndex, "data": data, "session_hash": sessionHash }),
55
- });
56
-
57
- return new Promise((resolve, reject) => {
58
- const interval = setInterval(async () => {
59
- try {
60
- const response = await fetch(API_BASE_URL + `queue/data?session_hash=${sessionHash}`);
61
- if (!response.ok) { clearInterval(interval); reject(new Error("Network Error")); return; }
62
-
63
- const text = await response.text();
64
- const lastLine = text.trim().split('\n').pop();
65
-
66
- if (lastLine && lastLine.startsWith('data:')) {
67
- const result = JSON.parse(lastLine.substring(5).trim());
68
- if (result.msg === "process_completed") {
69
- clearInterval(interval); resolve(result);
70
- } else if (result.msg === "unexpected_error") {
71
- clearInterval(interval); reject(new Error("API Error"));
72
- }
73
- }
74
- } catch (e) { if (!(e instanceof SyntaxError)) { clearInterval(interval); reject(e); } }
75
- }, 1000);
76
- });
77
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
- async function generateQRCode() {
80
- const text = document.getElementById('text-input').value.trim();
81
- if (!text) { alert("لطفاً متنی وارد کنید."); return; }
 
 
 
82
 
83
- const btn = document.getElementById('generate-btn'), loader = document.getElementById('generate-loader');
84
- const placeholder = document.getElementById('generate-placeholder'), qrImage = document.getElementById('qr-image');
 
 
85
 
86
- btn.disabled = true; loader.classList.remove('hidden'); placeholder.classList.add('hidden'); qrImage.classList.add('hidden');
 
 
87
 
 
 
88
  try {
89
- const result = await callGradioApi(0, [text], generateSessionHash());
90
- if (result.output?.data?.[0]?.url) {
91
- qrImage.src = result.output.data[0].url;
92
- qrImage.classList.remove('hidden');
93
- } else { throw new Error("پاسخ نامعتبر از سرور"); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  } catch (error) {
95
- alert("خطا: " + error.message);
96
- placeholder.classList.remove('hidden');
97
  } finally {
98
- btn.disabled = false; loader.classList.add('hidden');
 
99
  }
100
- }
101
 
102
- async function readQRCode() {
103
- const fileInput = document.getElementById('file-input');
104
- if (fileInput.files.length === 0) { alert("لطفا فایلی انتخاب کنید."); return; }
 
105
 
106
- const btn = document.getElementById('read-btn'), loader = document.getElementById('read-loader'), readText = document.getElementById('read-text');
107
- btn.disabled = true; loader.classList.remove('hidden'); readText.textContent = "در حال پردازش...";
 
108
 
109
  try {
110
- const file = fileInput.files[0];
111
  const formData = new FormData();
112
- formData.append("files", file);
113
- const uploadRes = await fetch(API_BASE_URL + "upload", { method: "POST", body: formData });
114
- if (!uploadRes.ok) throw new Error("آپلود فایل ناموفق بود.");
115
 
116
- const uploadResult = await uploadRes.json();
117
- const fileData = [{ ...uploadResult[0], meta: {"_type": "gradio.FileData"} }];
 
 
 
 
 
 
118
 
119
- const result = await callGradioApi(1, fileData, generateSessionHash());
120
- if (result.output?.data?.[0]) {
121
- readText.textContent = "نتیجه: " + result.output.data[0];
122
- } else { readText.textContent = "نتیجه‌ای یافت نشد."; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  } catch (error) {
124
- alert("خطا: " + error.message);
125
- readText.textContent = "خطا در خواندن کد.";
126
  } finally {
127
- btn.disabled = false; loader.classList.add('hidden');
 
 
128
  }
129
- }
130
  </script>
131
  </body>
132
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ابزار مدرن QR Code</title>
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/rastikerdar/vazirmatn@v33.003/Vazirmatn-font-face.css">
8
  <style>
9
+ :root {
10
+ --primary-color: #8e44ad;
11
+ --secondary-color: #3498db;
12
+ --background-start: #141e30;
13
+ --background-end: #243b55;
14
+ --text-color: #ecf0f1;
15
+ --input-bg: rgba(255, 255, 255, 0.1);
16
+ --container-bg: rgba(255, 255, 255, 0.15);
17
+ --border-color: rgba(255, 255, 255, 0.2);
18
+ }
19
+
20
+ body {
21
+ font-family: 'Vazirmatn', sans-serif;
22
+ background: linear-gradient(135deg, var(--background-end), var(--background-start));
23
+ color: var(--text-color);
24
+ display: flex;
25
+ justify-content: center;
26
+ align-items: center;
27
+ min-height: 100vh;
28
+ padding: 20px;
29
+ gap: 40px;
30
+ flex-wrap: wrap;
31
+ }
32
+
33
+ .main-title {
34
+ width: 100%;
35
+ text-align: center;
36
+ font-size: 2.5rem;
37
+ font-weight: 300;
38
+ margin-bottom: 20px;
39
+ text-shadow: 0 2px 4px rgba(0,0,0,0.3);
40
+ }
41
+ .main-title span {
42
+ font-weight: 700;
43
+ color: #fff;
44
+ }
45
+
46
+ .container {
47
+ background: var(--container-bg);
48
+ padding: 30px;
49
+ border-radius: 15px;
50
+ box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
51
+ backdrop-filter: blur(10px);
52
+ -webkit-backdrop-filter: blur(10px);
53
+ border: 1px solid var(--border-color);
54
+ width: 100%;
55
+ max-width: 450px;
56
+ display: flex;
57
+ flex-direction: column;
58
+ gap: 20px;
59
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
60
+ }
61
+ .container:hover {
62
+ transform: translateY(-5px);
63
+ box-shadow: 0 12px 40px 0 rgba(0, 0, 0, 0.45);
64
+ }
65
+
66
+ h2 {
67
+ text-align: center;
68
+ color: #fff;
69
+ margin-top: 0;
70
+ font-weight: 500;
71
+ border-bottom: 1px solid var(--secondary-color);
72
+ padding-bottom: 15px;
73
+ }
74
+
75
+ label {
76
+ font-size: 0.9rem;
77
+ margin-bottom: -10px;
78
+ color: #bdc3c7;
79
+ }
80
+
81
+ textarea, .file-label {
82
+ width: 100%;
83
+ padding: 12px;
84
+ border-radius: 8px;
85
+ border: 1px solid var(--border-color);
86
+ box-sizing: border-box;
87
+ font-size: 1rem;
88
+ background: var(--input-bg);
89
+ color: var(--text-color);
90
+ transition: border-color 0.3s, box-shadow 0.3s;
91
+ }
92
+ textarea::placeholder {
93
+ color: #95a5a6;
94
+ }
95
+ textarea:focus, .file-label:hover {
96
+ outline: none;
97
+ border-color: var(--secondary-color);
98
+ box-shadow: 0 0 10px rgba(52, 152, 219, 0.5);
99
+ }
100
+
101
+ /* Custom file input */
102
+ .file-wrapper {
103
+ position: relative;
104
+ cursor: pointer;
105
+ }
106
+ input[type="file"] {
107
+ display: none;
108
+ }
109
+ .file-label {
110
+ display: flex;
111
+ justify-content: center;
112
+ align-items: center;
113
+ gap: 10px;
114
+ }
115
+ #file-name {
116
+ margin-top: 10px;
117
+ font-size: 0.85rem;
118
+ color: #bdc3c7;
119
+ text-align: center;
120
+ height: 1.2em;
121
+ }
122
+
123
+ button {
124
+ padding: 14px 20px;
125
+ background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
126
+ color: white;
127
+ border: none;
128
+ border-radius: 8px;
129
+ cursor: pointer;
130
+ font-size: 1.1rem;
131
+ font-weight: 600;
132
+ transition: all 0.3s ease;
133
+ display: flex;
134
+ justify-content: center;
135
+ align-items: center;
136
+ gap: 10px;
137
+ box-shadow: 0 4px 15px rgba(0,0,0,0.2);
138
+ }
139
+ button:hover:not(:disabled) {
140
+ transform: translateY(-3px);
141
+ box-shadow: 0 6px 20px rgba(0,0,0,0.3);
142
+ }
143
+ button:disabled {
144
+ background: #555;
145
+ cursor: not-allowed;
146
+ opacity: 0.7;
147
+ }
148
+
149
+ .result-box {
150
+ margin-top: 15px;
151
+ padding: 20px;
152
+ background: var(--input-bg);
153
+ border: 1px solid var(--border-color);
154
+ border-radius: 8px;
155
+ min-height: 100px;
156
+ display: flex;
157
+ justify-content: center;
158
+ align-items: center;
159
+ text-align: center;
160
+ word-wrap: break-word;
161
+ color: #ecf0f1;
162
+ font-size: 1rem;
163
+ }
164
+
165
+ #qr-code-display img {
166
+ max-width: 100%;
167
+ height: auto;
168
+ border-radius: 8px;
169
+ border: 2px solid white;
170
+ animation: fadeIn 0.5s ease-in-out;
171
+ }
172
+
173
+ .loader {
174
+ border: 4px solid #555;
175
+ border-top: 4px solid var(--secondary-color);
176
+ border-radius: 50%;
177
+ width: 30px;
178
+ height: 30px;
179
+ animation: spin 1s linear infinite;
180
+ display: none;
181
+ }
182
+
183
+ @keyframes spin {
184
+ 0% { transform: rotate(0deg); }
185
+ 100% { transform: rotate(360deg); }
186
+ }
187
+ @keyframes fadeIn {
188
+ from { opacity: 0; transform: scale(0.9); }
189
+ to { opacity: 1; transform: scale(1); }
190
+ }
191
  </style>
192
  </head>
193
  <body>
194
 
195
+ <h1 class="main-title">ابزار مدرن <span>QR Code</span></h1>
196
+
197
+ <!-- بخش ساخت QR Code -->
198
  <div class="container">
199
+ <h2>ساخت QR Code از متن</h2>
200
+ <label for="text-input">متن خود را وارد کنید:</label>
201
+ <textarea id="text-input" rows="4" placeholder="مثلا: https://google.com">سلام دنیا</textarea>
202
+ <button id="generate-btn">
203
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2v0z"/><path d="M12 8l4 4-4 4"/><path d="M8 12h7"/></svg>
204
+ <span>ساخت QR Code</span>
205
+ </button>
206
  <div class="result-box">
207
+ <div id="generate-loader" class="loader"></div>
208
+ <div id="qr-code-display"></div>
 
209
  </div>
210
  </div>
211
 
212
+ <!-- بخش خواندن QR Code -->
213
  <div class="container">
214
+ <h2>خواندن متن از QR Code</h2>
215
+ <label for="file-input">فایل تصویر QR Code را انتخاب کنید:</label>
216
+ <div class="file-wrapper">
217
+ <input type="file" id="file-input" accept="image/*">
218
+ <label for="file-input" class="file-label">
219
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
220
+ <span>انتخاب فایل تصویر</span>
221
+ </label>
222
+ </div>
223
+ <p id="file-name"></p>
224
+ <button id="read-btn">
225
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
226
+ <span>خواندن QR Code</span>
227
+ </button>
228
  <div class="result-box">
229
+ <div id="read-loader" class="loader"></div>
230
+ <p id="decoded-text">نتیجه در اینجا نمایش داده می‌شود.</p>
231
  </div>
232
  </div>
233
 
234
  <script>
235
+ // --- کد جاوااسکریپت بدون هیچ تغییری باقی مانده است ---
 
 
236
 
237
+ const SPACE_URL = "https://cultrix-qrcode-read-generate.hf.space";
238
+
239
+ // --- دریافت عناصر DOM ---
240
+ const generateBtn = document.getElementById('generate-btn');
241
+ const readBtn = document.getElementById('read-btn');
242
+ const textInput = document.getElementById('text-input');
243
+ const fileInput = document.getElementById('file-input');
244
+ const qrCodeDisplay = document.getElementById('qr-code-display');
245
+ const decodedText = document.getElementById('decoded-text');
246
+ const generateLoader = document.getElementById('generate-loader');
247
+ const readLoader = document.getElementById('read-loader');
248
+ const fileNameDisplay = document.getElementById('file-name');
249
+
250
+ // نمایش نام فایل انتخاب شده
251
+ fileInput.addEventListener('change', () => {
252
+ if (fileInput.files.length > 0) {
253
+ fileNameDisplay.textContent = `فایل انتخاب شده: ${fileInput.files[0].name}`;
254
+ } else {
255
+ fileNameDisplay.textContent = '';
256
+ }
257
+ });
258
+
259
+ // --- توابع کمکی ---
260
+ const generateSessionHash = () => Math.random().toString(36).substring(2, 15);
261
+
262
+ const listenForData = (sessionHash, onResult, onError) => {
263
+ const eventSource = new EventSource(`${SPACE_URL}/gradio_api/queue/data?session_hash=${sessionHash}`);
264
+
265
+ eventSource.onmessage = (event) => {
266
+ const data = JSON.parse(event.data);
267
+ if (data.msg === "process_completed") {
268
+ eventSource.close();
269
+ if (data.output.error) {
270
+ onError(data.output.error);
271
+ } else {
272
+ onResult(data.output.data);
273
+ }
274
+ } else if (data.msg === "process_starts") {
275
+ console.log("پردازش شروع شد...");
276
+ } else if (data.msg === "process_failed") {
277
+ eventSource.close();
278
+ onError("پردازش با خطا مواجه شد.");
279
+ }
280
+ };
281
 
282
+ eventSource.onerror = (err) => {
283
+ console.error("EventSource failed:", err);
284
+ eventSource.close();
285
+ onError("خطا در ارتباط با سرور.");
286
+ };
287
+ };
288
 
289
+ // --- منطق ساخت QR Code ---
290
+ generateBtn.addEventListener('click', async () => {
291
+ const text = textInput.value.trim();
292
+ if (!text) return alert("لطفاً متنی را وارد کنید.");
293
 
294
+ generateLoader.style.display = 'block';
295
+ qrCodeDisplay.innerHTML = '';
296
+ generateBtn.disabled = true;
297
 
298
+ const sessionHash = generateSessionHash();
299
+
300
  try {
301
+ const joinResponse = await fetch(`${SPACE_URL}/gradio_api/queue/join`, {
302
+ method: 'POST',
303
+ headers: { 'Content-Type': 'application/json' },
304
+ body: JSON.stringify({ fn_index: 0, data: [text], session_hash: sessionHash })
305
+ });
306
+
307
+ if (!joinResponse.ok) throw new Error(`خطا در ارسال درخواست: ${joinResponse.statusText}`);
308
+
309
+ listenForData(
310
+ sessionHash,
311
+ (result) => {
312
+ if (result && result[0]) {
313
+ qrCodeDisplay.innerHTML = result[0];
314
+ } else {
315
+ qrCodeDisplay.innerText = "خطا: خروجی معتبر دریافت نشد.";
316
+ }
317
+ },
318
+ (error) => {
319
+ alert(`خطا در پردازش: ${error}`);
320
+ qrCodeDisplay.innerText = "خطا در ساخت تصویر.";
321
+ }
322
+ );
323
  } catch (error) {
324
+ alert(`یک خطا رخ داد: ${error.message}`);
 
325
  } finally {
326
+ generateLoader.style.display = 'none';
327
+ generateBtn.disabled = false;
328
  }
329
+ });
330
 
331
+ // --- منطق خواندن QR Code ---
332
+ readBtn.addEventListener('click', async () => {
333
+ const file = fileInput.files[0];
334
+ if (!file) return alert("لطفاً یک فایل تصویر انتخاب کنید.");
335
 
336
+ readLoader.style.display = 'block';
337
+ decodedText.innerText = '';
338
+ readBtn.disabled = true;
339
 
340
  try {
 
341
  const formData = new FormData();
342
+ formData.append('files', file);
 
 
343
 
344
+ const uploadResponse = await fetch(`${SPACE_URL}/gradio_api/upload`, {
345
+ method: 'POST',
346
+ body: formData
347
+ });
348
+ if (!uploadResponse.ok) throw new Error(`خطا در آپلود فایل: ${uploadResponse.statusText}`);
349
+
350
+ const uploadResult = await uploadResponse.json();
351
+ const serverFilePath = uploadResult[0];
352
 
353
+ const fileDataObject = {
354
+ path: serverFilePath,
355
+ url: `${SPACE_URL}/gradio_api/file=${serverFilePath}`,
356
+ orig_name: file.name,
357
+ };
358
+
359
+ const sessionHash = generateSessionHash();
360
+ const joinResponse = await fetch(`${SPACE_URL}/gradio_api/queue/join`, {
361
+ method: 'POST',
362
+ headers: { 'Content-Type': 'application/json' },
363
+ body: JSON.stringify({ fn_index: 1, data: [fileDataObject], session_hash: sessionHash })
364
+ });
365
+ if (!joinResponse.ok) throw new Error(`خطا در ارسال به صف: ${joinResponse.statusText}`);
366
+
367
+ listenForData(
368
+ sessionHash,
369
+ (result) => {
370
+ decodedText.innerText = `متن خوانده شده: \n ${result[0]}`;
371
+ },
372
+ (error) => {
373
+ alert(`خطا در پردازش: ${error}`);
374
+ decodedText.innerText = "خطا در خواندن کد.";
375
+ }
376
+ );
377
  } catch (error) {
378
+ alert(`یک خطا رخ داد: ${error.message}`);
379
+ decodedText.innerText = "نتیجه در اینجا نمایش داده می‌شود.";
380
  } finally {
381
+ readLoader.style.display = 'none';
382
+ readBtn.disabled = false;
383
+ // fileInput.value = ''; // ریست کردن اینپوت فایل باعث حذف نام فایل از نمایش می‌شود
384
  }
385
+ });
386
  </script>
387
  </body>
388
  </html>