Spaces:
Running
Running
Download index.html from Alexey7878/shell: direct link, hf CLI and curl.
- Browser
- Download file 10.4 kB
-
https://huggingface.co/spaces/Alexey7878/shell/resolve/2cd33b4355141efb6bf191fd2ba76721f00984af/index.html
- Command line
-
hf download hf://spaces/Alexey7878/shell@2cd33b4355141efb6bf191fd2ba76721f00984af/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Alexey7878/shell/resolve/2cd33b4355141efb6bf191fd2ba76721f00984af/index.html
10.4 kB
| <html lang="ru"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| ``` | |
| <title>Hugging Face — Генерация изображений</title> | |
| <style> | |
| body { | |
| font-family: system-ui, sans-serif; | |
| max-width: 600px; | |
| margin: 40px auto; | |
| padding: 20px; | |
| background: #f9fafb; | |
| color: #111827; | |
| } | |
| h2 { | |
| margin-top: 0; | |
| } | |
| label { | |
| display: block; | |
| margin-bottom: 5px; | |
| font-weight: 500; | |
| font-size: 14px; | |
| } | |
| input, | |
| textarea, | |
| select, | |
| button { | |
| width: 100%; | |
| padding: 12px; | |
| margin-bottom: 15px; | |
| border-radius: 6px; | |
| border: 1px solid #d1d5db; | |
| box-sizing: border-box; | |
| font-size: 15px; | |
| } | |
| textarea { | |
| resize: vertical; | |
| min-height: 90px; | |
| } | |
| button { | |
| background: #10b981; | |
| color: white; | |
| border: none; | |
| font-weight: bold; | |
| cursor: pointer; | |
| font-size: 16px; | |
| } | |
| button:hover { | |
| background: #059669; | |
| } | |
| button:disabled { | |
| background: #9ca3af; | |
| cursor: not-allowed; | |
| } | |
| #outputImage { | |
| max-width: 100%; | |
| border-radius: 8px; | |
| display: none; | |
| margin-top: 15px; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| } | |
| #statusText { | |
| color: #4b5563; | |
| margin-bottom: 10px; | |
| min-height: 20px; | |
| } | |
| .error-block { | |
| background: #fef2f2; | |
| border: 2px solid #dc2626; | |
| border-radius: 8px; | |
| padding: 15px; | |
| margin-top: 10px; | |
| white-space: pre-wrap; | |
| word-wrap: break-word; | |
| } | |
| .error-block h3 { | |
| margin: 0 0 10px 0; | |
| color: #dc2626; | |
| font-size: 16px; | |
| } | |
| .error-block .code { | |
| background: #1f2937; | |
| color: #f9fafb; | |
| padding: 10px; | |
| border-radius: 4px; | |
| font-family: "Courier New", monospace; | |
| font-size: 13px; | |
| overflow-x: auto; | |
| } | |
| .info { | |
| background: #eff6ff; | |
| border: 1px solid #bfdbfe; | |
| color: #1e40af; | |
| padding: 12px; | |
| border-radius: 6px; | |
| margin-bottom: 20px; | |
| font-size: 14px; | |
| } | |
| </style> | |
| ``` | |
| </head> | |
| <body> | |
| ``` | |
| <h2>Генерация изображений через Hugging Face</h2> | |
| <div class="info"> | |
| Для работы нужен Hugging Face Access Token | |
| с разрешением Inference Providers. | |
| </div> | |
| <label for="hf-token"> | |
| Hugging Face Access Token: | |
| </label> | |
| <input | |
| type="password" | |
| id="hf-token" | |
| placeholder="hf_..." | |
| autocomplete="off" | |
| > | |
| <label for="modelSelect"> | |
| Модель: | |
| </label> | |
| <select id="modelSelect"> | |
| <option value="black-forest-labs/FLUX.1-schnell"> | |
| FLUX.1-schnell | |
| </option> | |
| <option value="black-forest-labs/FLUX.1-dev"> | |
| FLUX.1-dev | |
| </option> | |
| </select> | |
| <label for="prompt"> | |
| Промпт: | |
| </label> | |
| <textarea | |
| id="prompt" | |
| rows="4" | |
| placeholder="A cinematic shot of a cyberpunk city at night, neon lights, rain, highly detailed..." | |
| ></textarea> | |
| <button id="generateBtn"> | |
| Сгенерировать | |
| </button> | |
| <div id="statusText"></div> | |
| <div id="errorContainer"></div> | |
| <img | |
| id="outputImage" | |
| alt="Сгенерированное изображение" | |
| > | |
| ``` | |
| <script> | |
| const btn = | |
| document.getElementById("generateBtn"); | |
| const tokenInput = | |
| document.getElementById("hf-token"); | |
| const modelSelect = | |
| document.getElementById("modelSelect"); | |
| const promptInput = | |
| document.getElementById("prompt"); | |
| const statusText = | |
| document.getElementById("statusText"); | |
| const errorContainer = | |
| document.getElementById("errorContainer"); | |
| const outputImage = | |
| document.getElementById("outputImage"); | |
| let currentImageUrl = null; | |
| // ---------------------------------------- | |
| // Показ ошибки | |
| // ---------------------------------------- | |
| function showError(title, details) { | |
| errorContainer.innerHTML = ""; | |
| const block = | |
| document.createElement("div"); | |
| block.className = | |
| "error-block"; | |
| const heading = | |
| document.createElement("h3"); | |
| heading.textContent = | |
| "⚠️ " + title; | |
| const code = | |
| document.createElement("div"); | |
| code.className = | |
| "code"; | |
| code.textContent = | |
| details; | |
| block.appendChild(heading); | |
| block.appendChild(code); | |
| errorContainer.appendChild(block); | |
| } | |
| // ---------------------------------------- | |
| // Очистка ошибки | |
| // ---------------------------------------- | |
| function clearError() { | |
| errorContainer.innerHTML = ""; | |
| } | |
| // ---------------------------------------- | |
| // Очистка предыдущего изображения | |
| // ---------------------------------------- | |
| function clearImage() { | |
| outputImage.style.display = | |
| "none"; | |
| outputImage.removeAttribute("src"); | |
| if (currentImageUrl) { | |
| URL.revokeObjectURL( | |
| currentImageUrl | |
| ); | |
| currentImageUrl = null; | |
| } | |
| } | |
| // ---------------------------------------- | |
| // Разбор ошибки API | |
| // ---------------------------------------- | |
| async function getErrorText(response) { | |
| const text = | |
| await response.text(); | |
| if (!text) { | |
| return "Сервер вернул пустой ответ."; | |
| } | |
| try { | |
| const json = | |
| JSON.parse(text); | |
| return JSON.stringify( | |
| json, | |
| null, | |
| 2 | |
| ); | |
| } catch { | |
| return text; | |
| } | |
| } | |
| // ---------------------------------------- | |
| // Генерация изображения | |
| // ---------------------------------------- | |
| btn.addEventListener( | |
| "click", | |
| async () => { | |
| const token = | |
| tokenInput.value.trim(); | |
| const model = | |
| modelSelect.value; | |
| const prompt = | |
| promptInput.value.trim(); | |
| clearError(); | |
| clearImage(); | |
| // Проверка токена | |
| if (!token) { | |
| showError( | |
| "Ошибка", | |
| "Введите Hugging Face Access Token." | |
| ); | |
| return; | |
| } | |
| // Проверка промпта | |
| if (!prompt) { | |
| showError( | |
| "Ошибка", | |
| "Введите промпт для генерации." | |
| ); | |
| return; | |
| } | |
| btn.disabled = | |
| true; | |
| statusText.textContent = | |
| "Генерируем изображение..."; | |
| try { | |
| /* | |
| * Hugging Face Inference Providers. | |
| * | |
| * fal-ai выбран явно. | |
| * | |
| * Важно: | |
| * provider должен поддерживать | |
| * выбранную модель. | |
| */ | |
| const apiUrl = | |
| `https://router.huggingface.co/fal-ai/models/${model}`; | |
| const response = | |
| await fetch( | |
| apiUrl, | |
| { | |
| method: "POST", | |
| headers: { | |
| "Authorization": | |
| `Bearer ${token}`, | |
| "Content-Type": | |
| "application/json", | |
| "Accept": | |
| "image/png" | |
| }, | |
| body: | |
| JSON.stringify({ | |
| inputs: | |
| prompt, | |
| parameters: { | |
| num_inference_steps: | |
| 4 | |
| } | |
| }) | |
| } | |
| ); | |
| // -------------------------------- | |
| // Обработка ошибки | |
| // -------------------------------- | |
| if (!response.ok) { | |
| const errorText = | |
| await getErrorText( | |
| response | |
| ); | |
| showError( | |
| `Ошибка ${response.status} ${response.statusText}`, | |
| `URL: ${apiUrl}\n\nОтвет сервера:\n${errorText}` | |
| ); | |
| statusText.textContent = | |
| "Генерация не удалась."; | |
| return; | |
| } | |
| statusText.textContent = | |
| "Изображение получено. Отображаем..."; | |
| // -------------------------------- | |
| // Получаем изображение как Blob | |
| // -------------------------------- | |
| const blob = | |
| await response.blob(); | |
| // Проверяем MIME-тип | |
| if ( | |
| !blob.type || | |
| !blob.type.startsWith("image/") | |
| ) { | |
| const text = | |
| await blob.text(); | |
| showError( | |
| "API вернул не изображение", | |
| text | |
| ); | |
| return; | |
| } | |
| // -------------------------------- | |
| // Создаём URL изображения | |
| // -------------------------------- | |
| currentImageUrl = | |
| URL.createObjectURL( | |
| blob | |
| ); | |
| outputImage.src = | |
| currentImageUrl; | |
| outputImage.style.display = | |
| "block"; | |
| statusText.textContent = | |
| "✅ Изображение готово!"; | |
| } | |
| catch (error) { | |
| console.error( | |
| error | |
| ); | |
| showError( | |
| "Ошибка соединения", | |
| error.message || | |
| String(error) | |
| ); | |
| statusText.textContent = | |
| "Произошла ошибка."; | |
| } | |
| finally { | |
| btn.disabled = | |
| false; | |
| } | |
| } | |
| ); | |
| </script> | |
| </body> | |
| </html> | |