Alexey7878 commited on
Commit
a047768
·
verified ·
1 Parent(s): dae90a7

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +240 -414
index.html CHANGED
@@ -1,443 +1,269 @@
1
  <!DOCTYPE html>
2
-
3
  <html lang="ru">
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
- ```
9
- <title>FLUX.1-schnell — Hugging Face</title>
10
-
11
- <style>
12
- * {
13
- box-sizing: border-box;
14
- }
15
-
16
- body {
17
- font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
18
- max-width: 700px;
19
- margin: 0 auto;
20
- padding: 30px 20px;
21
- background: #f9fafb;
22
- color: #111827;
23
- }
24
-
25
- h1 {
26
- margin-top: 0;
27
- margin-bottom: 8px;
28
- }
29
-
30
- .subtitle {
31
- color: #6b7280;
32
- margin-bottom: 25px;
33
- }
34
-
35
- label {
36
- display: block;
37
- margin: 15px 0 6px;
38
- font-weight: 600;
39
- }
40
-
41
- input,
42
- textarea,
43
- select,
44
- button {
45
- width: 100%;
46
- padding: 12px;
47
- border-radius: 8px;
48
- border: 1px solid #d1d5db;
49
- font: inherit;
50
- }
51
-
52
- textarea {
53
- min-height: 120px;
54
- resize: vertical;
55
- }
56
-
57
- button {
58
- margin-top: 20px;
59
- border: none;
60
- background: #10b981;
61
- color: white;
62
- font-weight: 700;
63
- cursor: pointer;
64
- }
65
-
66
- button:hover {
67
- background: #059669;
68
- }
69
-
70
- button:disabled {
71
- background: #9ca3af;
72
- cursor: wait;
73
- }
74
-
75
- #status {
76
- margin-top: 15px;
77
- min-height: 24px;
78
- color: #4b5563;
79
- }
80
-
81
- #result {
82
- display: none;
83
- width: 100%;
84
- margin-top: 20px;
85
- border-radius: 10px;
86
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.12);
87
- }
88
-
89
- #download {
90
- display: none;
91
- margin-top: 15px;
92
- text-align: center;
93
- text-decoration: none;
94
- background: #374151;
95
- color: white;
96
- padding: 12px;
97
- border-radius: 8px;
98
- font-weight: 600;
99
- }
100
-
101
- #error {
102
- display: none;
103
- margin-top: 15px;
104
- padding: 15px;
105
- background: #fef2f2;
106
- border: 1px solid #fca5a5;
107
- border-radius: 8px;
108
- color: #991b1b;
109
- white-space: pre-wrap;
110
- word-break: break-word;
111
- }
112
-
113
- .warning {
114
- padding: 12px;
115
- margin-bottom: 20px;
116
- background: #fff7ed;
117
- border: 1px solid #fed7aa;
118
- border-radius: 8px;
119
- color: #9a3412;
120
- font-size: 14px;
121
- }
122
- </style>
123
- ```
124
-
125
  </head>
126
-
127
  <body>
128
 
129
- ```
130
- <h1>🎨 Генерация изображений</h1>
131
-
132
- <div class="subtitle">
133
- FLUX.1-schnell через Hugging Face Inference Providers
134
- </div>
135
-
136
- <div class="warning">
137
- ⚠️ Токен используется непосредственно в браузере.
138
- Для публичного приложения это небезопасно.
139
- </div>
140
-
141
- <label for="token">
142
- Hugging Face Access Token
143
- </label>
144
-
145
- <input
146
- id="token"
147
- type="password"
148
- placeholder="hf_..."
149
- autocomplete="off"
150
- >
151
-
152
- <label for="model">
153
- Модель
154
- </label>
155
-
156
- <select id="model">
157
- <option value="black-forest-labs/FLUX.1-schnell">
158
- FLUX.1-schnell
159
- </option>
160
- </select>
161
-
162
- <label for="prompt">
163
- Промпт
164
- </label>
165
-
166
- <textarea
167
- id="prompt"
168
- placeholder="A cinematic photo of a futuristic city at night, neon lights, rain, ultra detailed..."
169
- ></textarea>
170
-
171
- <button id="generate">
172
- Сгенерировать
173
- </button>
174
-
175
- <div id="status"></div>
176
-
177
- <div id="error"></div>
178
-
179
- <img
180
- id="result"
181
- alt="Generated image"
182
- >
183
-
184
- <a
185
- id="download"
186
- download="flux-generated.png"
187
- >
188
- Скачать изображение
189
- </a>
190
-
191
-
192
- <script type="module">
193
-
194
- /*
195
- * Загружаем официальный JS SDK Hugging Face
196
- * через ESM CDN.
197
- */
198
- import {
199
- InferenceClient
200
- } from "https://esm.sh/@huggingface/inference";
201
-
202
-
203
- const tokenInput =
204
- document.getElementById("token");
205
-
206
- const modelInput =
207
- document.getElementById("model");
208
-
209
- const promptInput =
210
- document.getElementById("prompt");
211
-
212
- const generateButton =
213
- document.getElementById("generate");
214
-
215
- const status =
216
- document.getElementById("status");
217
-
218
- const errorBox =
219
- document.getElementById("error");
220
-
221
- const resultImage =
222
- document.getElementById("result");
223
-
224
- const downloadLink =
225
- document.getElementById("download");
226
-
227
-
228
- let currentImageUrl = null;
229
-
230
-
231
- function showError(message) {
232
-
233
- errorBox.textContent =
234
- message;
235
-
236
- errorBox.style.display =
237
- "block";
238
-
239
- }
240
-
241
-
242
- function clearError() {
243
-
244
- errorBox.textContent = "";
245
-
246
- errorBox.style.display =
247
- "none";
248
-
249
- }
250
-
251
-
252
- function clearResult() {
253
-
254
- resultImage.style.display =
255
- "none";
256
-
257
- downloadLink.style.display =
258
- "none";
259
-
260
-
261
- if (currentImageUrl) {
262
-
263
- URL.revokeObjectURL(
264
- currentImageUrl
265
- );
266
-
267
- currentImageUrl = null;
268
-
269
  }
270
 
271
- }
272
-
273
-
274
- generateButton.addEventListener(
275
- "click",
276
- async () => {
277
-
278
  clearError();
279
-
280
  clearResult();
281
 
 
 
 
282
 
283
- const token =
284
- tokenInput.value.trim();
285
-
286
- const prompt =
287
- promptInput.value.trim();
288
-
289
- const model =
290
- modelInput.value;
291
-
292
 
293
  if (!token) {
294
-
295
- showError(
296
- "Введите Hugging Face Access Token."
297
- );
298
-
299
  return;
300
-
301
  }
302
-
303
-
304
  if (!prompt) {
305
-
306
- showError(
307
- "Введите промпт."
308
- );
309
-
310
  return;
311
-
312
  }
313
 
314
-
315
- generateButton.disabled =
316
- true;
317
-
318
-
319
- status.textContent =
320
- "⏳ Генерация изображения...";
321
-
322
 
323
  try {
324
-
325
- /*
326
- * Создаём клиент Hugging Face.
327
- *
328
- * provider не указываем:
329
- * Hugging Face автоматически
330
- * выбирает доступного provider.
331
- */
332
-
333
- const client =
334
- new InferenceClient(
335
- token
336
- );
337
-
338
-
339
- /*
340
- * Официальный метод
341
- * JavaScript SDK для Text-to-Image.
342
- *
343
- * Метод возвращает Blob.
344
- */
345
-
346
- const imageBlob =
347
- await client.textToImage({
348
-
349
- model:
350
- model,
351
-
352
- inputs:
353
- prompt
354
-
355
- });
356
-
357
-
358
- /*
359
- * Создаём локальный URL
360
- * для отображения картинки.
361
- */
362
-
363
- currentImageUrl =
364
- URL.createObjectURL(
365
- imageBlob
366
- );
367
-
368
-
369
- resultImage.src =
370
- currentImageUrl;
371
-
372
-
373
- resultImage.style.display =
374
- "block";
375
-
376
-
377
- /*
378
- * Добавляем кнопку скачивания.
379
- */
380
-
381
- downloadLink.href =
382
- currentImageUrl;
383
-
384
-
385
- downloadLink.style.display =
386
- "block";
387
-
388
-
389
- status.textContent =
390
- "✅ Изображение готово!";
391
-
392
- }
393
-
394
-
395
- catch (error) {
396
-
397
- console.error(
398
- error
399
- );
400
-
401
-
402
- let message =
403
- "Не удалось сгенерировать изображение.";
404
-
405
-
406
  if (error) {
407
-
408
- message +=
409
- "\n\n" +
410
- (
411
- error.message ||
412
- String(error)
413
- );
414
-
415
  }
416
-
417
-
418
- showError(
419
- message
420
- );
421
-
422
-
423
- status.textContent =
424
- "❌ Ошибка генерации.";
425
-
426
  }
427
-
428
-
429
- finally {
430
-
431
- generateButton.disabled =
432
- false;
433
-
434
- }
435
-
436
- }
437
- );
438
-
439
- </script>
440
- ```
441
-
442
  </body>
443
- </html>
 
1
  <!DOCTYPE html>
 
2
  <html lang="ru">
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Генерация изображений — Hugging Face SDK</title>
7
+ <style>
8
+ * { box-sizing: border-box; }
9
+ body {
10
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
11
+ max-width: 700px;
12
+ margin: 0 auto;
13
+ padding: 30px 20px;
14
+ background: #f9fafb;
15
+ color: #111827;
16
+ }
17
+ h1 { margin-top: 0; margin-bottom: 8px; }
18
+ .subtitle { color: #6b7280; margin-bottom: 20px; }
19
+ label { display: block; margin: 15px 0 6px; font-weight: 600; font-size: 14px; }
20
+ input, textarea, select, button {
21
+ width: 100%;
22
+ padding: 12px;
23
+ border-radius: 8px;
24
+ border: 1px solid #d1d5db;
25
+ font: inherit;
26
+ background: #fff;
27
+ }
28
+ textarea { min-height: 100px; resize: vertical; }
29
+
30
+ /* Gradio-style Accordion Settings */
31
+ .accordion {
32
+ border: 1px solid #e5e7eb;
33
+ border-radius: 8px;
34
+ margin: 20px 0;
35
+ background: #ffffff;
36
+ box-shadow: 0 1px 3px rgba(0,0,0,0.05);
37
+ }
38
+ .accordion summary {
39
+ padding: 12px 16px;
40
+ font-weight: 600;
41
+ cursor: pointer;
42
+ user-select: none;
43
+ background: #f3f4f6;
44
+ border-radius: 8px;
45
+ outline: none;
46
+ }
47
+ .accordion[open] summary {
48
+ border-bottom: 1px solid #e5e7eb;
49
+ border-bottom-left-radius: 0;
50
+ border-bottom-right-radius: 0;
51
+ }
52
+ .accordion-content {
53
+ padding: 15px;
54
+ }
55
+ .row {
56
+ display: flex;
57
+ gap: 15px;
58
+ }
59
+ .row > div {
60
+ flex: 1;
61
+ }
62
 
63
+ button {
64
+ margin-top: 10px;
65
+ border: none;
66
+ background: #10b981;
67
+ color: white;
68
+ font-weight: 700;
69
+ cursor: pointer;
70
+ }
71
+ button:hover { background: #059669; }
72
+ button:disabled { background: #9ca3af; cursor: wait; }
73
+
74
+ #status { margin-top: 15px; min-height: 24px; color: #4b5563; font-weight: 500; }
75
+ #result {
76
+ display: none;
77
+ width: 100%;
78
+ margin-top: 20px;
79
+ border-radius: 10px;
80
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.12);
81
+ }
82
+ #download {
83
+ display: none;
84
+ margin-top: 15px;
85
+ text-align: center;
86
+ text-decoration: none;
87
+ background: #374151;
88
+ color: white;
89
+ padding: 12px;
90
+ border-radius: 8px;
91
+ font-weight: 600;
92
+ }
93
+ #error {
94
+ display: none;
95
+ margin-top: 15px;
96
+ padding: 15px;
97
+ background: #fef2f2;
98
+ border: 1px solid #fca5a5;
99
+ border-radius: 8px;
100
+ color: #991b1b;
101
+ white-space: pre-wrap;
102
+ word-break: break-word;
103
+ }
104
+ .warning {
105
+ padding: 12px;
106
+ margin-bottom: 20px;
107
+ background: #fff7ed;
108
+ border: 1px solid #fed7aa;
109
+ border-radius: 8px;
110
+ color: #9a3412;
111
+ font-size: 14px;
112
+ }
113
+ </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  </head>
 
115
  <body>
116
 
117
+ <h1>🎨 Генерация изображений</h1>
118
+ <div class="subtitle">Интерфейс с настройками модели и параметрами (Gradio-style)</div>
119
+
120
+ <div class="warning">
121
+ ⚠️ Токен используется в браузере. Убедитесь, что используете Read-токен.
122
+ </div>
123
+
124
+ <label for="token">Hugging Face Access Token</label>
125
+ <input id="token" type="password" placeholder="hf_..." autocomplete="off">
126
+
127
+ <label for="model">Модель</label>
128
+ <select id="model">
129
+ <option value="black-forest-labs/FLUX.1-schnell">FLUX.1-schnell (Быстрая)</option>
130
+ <option value="black-forest-labs/FLUX.1-dev">FLUX.1-dev (Высокое качество)</option>
131
+ <option value="stabilityai/stable-diffusion-xl-base-1.0">Stable Diffusion XL (SDXL)</option>
132
+ </select>
133
+
134
+ <!-- Gradio-style Settings Accordion -->
135
+ <details class="accordion">
136
+ <summary>⚙️ Дополнительные параметры (Настройки генерации)</summary>
137
+ <div class="accordion-content">
138
+ <div class="row">
139
+ <div>
140
+ <label for="steps">Количество шагов (Steps)</label>
141
+ <input id="steps" type="number" value="4" min="1" max="50">
142
+ </div>
143
+ <div>
144
+ <label for="guidance">Сила промпта (Guidance Scale)</label>
145
+ <input id="guidance" type="number" value="3.5" step="0.5" min="1" max="20">
146
+ </div>
147
+ </div>
148
+ <div class="row">
149
+ <div>
150
+ <label for="width">Ширина (Width)</label>
151
+ <input id="width" type="number" value="1024" step="64" min="256" max="1440">
152
+ </div>
153
+ <div>
154
+ <label for="height">Высота (Height)</label>
155
+ <input id="height" type="number" value="1024" step="64" min="256" max="1440">
156
+ </div>
157
+ </div>
158
+ </div>
159
+ </details>
160
+
161
+ <label for="prompt">Промпт</label>
162
+ <textarea id="prompt" placeholder="A cinematic photo of a futuristic city at night, neon lights, rain, ultra detailed..."></textarea>
163
+
164
+ <button id="generate">Сгенерировать</button>
165
+
166
+ <div id="status"></div>
167
+ <div id="error"></div>
168
+ <img id="result" alt="Generated image">
169
+ <a id="download" download="flux-generated.png">Скачать изображение</a>
170
+
171
+ <script type="module">
172
+ import { InferenceClient } from "https://esm.sh/@huggingface/inference";
173
+
174
+ const tokenInput = document.getElementById("token");
175
+ const modelInput = document.getElementById("model");
176
+ const promptInput = document.getElementById("prompt");
177
+ const stepsInput = document.getElementById("steps");
178
+ const guidanceInput = document.getElementById("guidance");
179
+ const widthInput = document.getElementById("width");
180
+ const heightInput = document.getElementById("height");
181
+
182
+ const generateButton = document.getElementById("generate");
183
+ const status = document.getElementById("status");
184
+ const errorBox = document.getElementById("error");
185
+ const resultImage = document.getElementById("result");
186
+ const downloadLink = document.getElementById("download");
187
+
188
+ let currentImageUrl = null;
189
+
190
+ function showError(message) {
191
+ errorBox.textContent = message;
192
+ errorBox.style.display = "block";
193
+ }
194
+ function clearError() {
195
+ errorBox.textContent = "";
196
+ errorBox.style.display = "none";
197
+ }
198
+ function clearResult() {
199
+ resultImage.style.display = "none";
200
+ downloadLink.style.display = "none";
201
+ if (currentImageUrl) {
202
+ URL.revokeObjectURL(currentImageUrl);
203
+ currentImageUrl = null;
204
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  }
206
 
207
+ generateButton.addEventListener("click", async () => {
 
 
 
 
 
 
208
  clearError();
 
209
  clearResult();
210
 
211
+ const token = tokenInput.value.trim();
212
+ const prompt = promptInput.value.trim();
213
+ const model = modelInput.value;
214
 
215
+ const steps = parseInt(stepsInput.value) || 4;
216
+ const guidance = parseFloat(guidanceInput.value) || 3.5;
217
+ const width = parseInt(widthInput.value) || 1024;
218
+ const height = parseInt(heightInput.value) || 1024;
 
 
 
 
 
219
 
220
  if (!token) {
221
+ showError("Введите Hugging Face Access Token.");
 
 
 
 
222
  return;
 
223
  }
 
 
224
  if (!prompt) {
225
+ showError("Введите промпт.");
 
 
 
 
226
  return;
 
227
  }
228
 
229
+ generateButton.disabled = true;
230
+ status.textContent = "⏳ Генерация изображения...";
 
 
 
 
 
 
231
 
232
  try {
233
+ const client = new InferenceClient(token);
234
+
235
+ // Передаем параметры через объект parameters в соответствии с API
236
+ const imageBlob = await client.textToImage({
237
+ model: model,
238
+ inputs: prompt,
239
+ parameters: {
240
+ num_inference_steps: steps,
241
+ guidance_scale: guidance,
242
+ width: width,
243
+ height: height
244
+ }
245
+ });
246
+
247
+ currentImageUrl = URL.createObjectURL(imageBlob);
248
+ resultImage.src = currentImageUrl;
249
+ resultImage.style.display = "block";
250
+
251
+ downloadLink.href = currentImageUrl;
252
+ downloadLink.style.display = "block";
253
+ status.textContent = "✅ Изображение готово!";
254
+
255
+ } catch (error) {
256
+ console.error(error);
257
+ let message = "Не удалось сгенерировать изображение.";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  if (error) {
259
+ message += "\n\n" + (error.message || String(error));
 
 
 
 
 
 
 
260
  }
261
+ showError(message);
262
+ status.textContent = "❌ Ошибка генерации.";
263
+ } finally {
264
+ generateButton.disabled = false;
 
 
 
 
 
 
265
  }
266
+ });
267
+ </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  </body>
269
+ </html>