spaceai-all / index.html
VIPUZIS's picture
index.html
287e55f verified
Raw
History Blame Contribute Delete
18.3 kB
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🤗 AI Hub (Без ключей)</title>
<style>
:root { --bg: #0b0f1a; --card: #131828; --border: #1e2640; --accent: #7c3aed; --accent2: #06b6d4; --accent3: #f59e0b; --text: #e2e8f0; --muted: #64748b; --radius: 14px; }
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'Segoe UI', system-ui, sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; padding-bottom: 4rem; }
.header { background: linear-gradient(135deg, #1a1040 0%, #0b0f1a 50%, #0a1920 100%); padding: 2.5rem 1.5rem 2rem; text-align: center; border-bottom: 1px solid var(--border); }
.header h1 { font-size: 2rem; background: linear-gradient(135deg, var(--accent), var(--accent2), var(--accent3)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: .3rem; }
.header p { color: var(--muted); font-size: .9rem; max-width: 600px; margin: 0 auto; }
.tabs { display: flex; justify-content: center; gap: .5rem; padding: 1.5rem 1rem; flex-wrap: wrap; }
.tab-btn { padding: .7rem 1.8rem; border-radius: 50px; border: 1px solid var(--border); background: transparent; color: var(--muted); font-size: .95rem; font-weight: 600; cursor: pointer; transition: all .25s; }
.tab-btn:hover { color: var(--text); border-color: var(--muted); }
.tab-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; box-shadow: 0 0 20px rgba(124,58,237,.35); }
.tab-btn.active.img-tab { background: var(--accent2); border-color: var(--accent2); box-shadow: 0 0 20px rgba(6,182,212,.35); }
.tab-btn.active.aud-tab { background: var(--accent3); border-color: var(--accent3); box-shadow: 0 0 20px rgba(245,158,11,.35); }
.panel { display: none; max-width: 800px; margin: 0 auto; padding: 0 1.5rem; }
.panel.active { display: block; }
.panel-card { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); padding: 2rem; margin-bottom: 1.5rem; }
.panel-card h2 { font-size: 1.3rem; margin-bottom: .3rem; }
.panel-card .desc { color: var(--muted); font-size: .85rem; margin-bottom: 1.2rem; }
textarea, .input-field { width: 100%; padding: 1rem; border-radius: var(--radius); border: 1px solid var(--border); background: #0f1322; color: var(--text); font-size: .95rem; resize: vertical; outline: none; transition: border .2s; }
textarea:focus, .input-field:focus { border-color: var(--accent); }
.options-row { display: flex; gap: .8rem; margin-top: 1rem; flex-wrap: wrap; align-items: center; }
.options-row label { font-size: .85rem; color: var(--muted); }
.options-row select { padding: .5rem .8rem; border-radius: 8px; border: 1px solid var(--border); background: #0f1322; color: var(--text); font-size: .85rem; outline: none; }
.generate-btn { margin-top: 1rem; padding: .85rem 2.5rem; border-radius: var(--radius); border: none; font-size: 1rem; font-weight: 700; cursor: pointer; color: #fff; transition: transform .15s, box-shadow .25s; }
.generate-btn:hover { transform: translateY(-2px); }
.btn-text { background: linear-gradient(135deg, #7c3aed, #a855f7); }
.btn-img { background: linear-gradient(135deg, #0891b2, #06b6d4); }
.btn-aud { background: linear-gradient(135deg, #d97706, #f59e0b); }
.output-box { margin-top: 1.5rem; padding: 1.5rem; border-radius: var(--radius); background: #0f1322; border: 1px solid var(--border); min-height: 80px; white-space: pre-wrap; line-height: 1.7; font-size: .95rem; position: relative; }
.output-box img { max-width: 100%; border-radius: 10px; margin-top: .5rem; }
.output-placeholder { color: var(--muted); font-style: italic; }
.copy-btn { position: absolute; top: .8rem; right: .8rem; padding: .4rem .8rem; border-radius: 8px; border: 1px solid var(--border); background: var(--card); color: var(--muted); font-size: .75rem; cursor: pointer; }
.copy-btn:hover { color: var(--text); }
.spinner { display: inline-block; width: 18px; height: 18px; border: 3px solid rgba(255,255,255,.2); border-top-color: #fff; border-radius: 50%; animation: spin .6s linear infinite; vertical-align: middle; margin-right: .5rem; }
@keyframes spin { to { transform: rotate(360deg); } }
.status { margin-top: .8rem; font-size: .85rem; color: var(--muted); }
.status.error { color: #ef4444; }
.status.ok { color: #22c55e; }
.note { background: rgba(245,158,11,.1); border: 1px solid var(--accent3); color: var(--accent3); padding: .8rem; border-radius: 8px; font-size: .8rem; margin-top: 1rem; }
@media (max-width: 600px) { .header h1 { font-size: 1.6rem; } .panel-card { padding: 1.3rem; } }
</style>
</head>
<body>
<div class="header">
<h1>🤗 AI Hub</h1>
<p>Текст, изображения и аудио без API-ключей. Работает через бесплатные публичные сервисы и встроенные возможности браузера.</p>
</div>
<div class="tabs">
<button class="tab-btn active" onclick="switchTab('text', this)">✍️ Текст</button>
<button class="tab-btn img-tab" onclick="switchTab('img', this)">🎨 Изображение</button>
<button class="tab-btn aud-tab" onclick="switchTab('aud', this)">🎵 Аудио</button>
</div>
<!-- TEXT -->
<div class="panel active" id="panel-text">
<div class="panel-card">
<h2>✍️ Генерация текста</h2>
<p class="desc">Использует Pollinations.ai (бесплатно, без регистрации)</p>
<textarea id="text-prompt" placeholder="Введите запрос…&#10;Например: Напиши короткое стихотворение о космосе"></textarea>
<div class="options-row">
<label>Модель:</label>
<select id="text-model">
<option value="openai">OpenAI (похоже на GPT)</option>
<option value="mistral">Mistral</option>
<option value="llama">Llama 3</option>
</select>
</div>
<button class="generate-btn btn-text" onclick="generateText()">⚡ Сгенерировать</button>
<div class="status" id="text-status"></div>
</div>
<div class="output-box" id="text-output">
<span class="output-placeholder">Здесь появится текст…</span>
<button class="copy-btn" onclick="copyText()">📋 Копировать</button>
</div>
</div>
<!-- IMAGE -->
<div class="panel" id="panel-img">
<div class="panel-card">
<h2>🎨 Генерация изображений</h2>
<p class="desc">Pollinations Image Generator (бесплатно, без водяных знаков)</p>
<textarea id="img-prompt" placeholder="Опишите картинку…&#10;Например: Cyberpunk city at sunset, neon lights, rain reflections, cinematic style" style="min-height:80px"></textarea>
<div class="options-row">
<label>Размер:</label>
<select id="img-size">
<option value="1024x1024">1024×1024</option>
<option value="1280x720">1280×720 (широкий)</option>
<option value="720x1280">720×1280 (вертикальный)</option>
</select>
<label>Стиль:</label>
<select id="img-style">
<option value="">Авто</option>
<option value="photorealistic">Фотореализм</option>
<option value="anime">Аниме</option>
<option value="digital art">Цифровой арт</option>
<option value="oil painting">Масло</option>
</select>
</div>
<button class="generate-btn btn-img" onclick="generateImage()">🖼️ Создать</button>
<div class="status" id="img-status"></div>
</div>
<div class="output-box" id="img-output">
<span class="output-placeholder">Здесь появится изображение…</span>
</div>
</div>
<!-- AUDIO -->
<div class="panel" id="panel-aud">
<div class="panel-card">
<h2>🎵 Аудио (Без облака)</h2>
<p class="desc">Синтез речи через браузер + процедурная музыка через Web Audio API</p>
<select class="input-field" id="aud-mode" onchange="toggleAudMode()" style="margin-bottom:1rem">
<option value="tts">🗣️ Озвучка текста (TTS)</option>
<option value="music">🎹 Генерация музыки</option>
</select>
<div id="aud-tts-section">
<textarea id="aud-tts-prompt" placeholder="Введите текст для озвучки…"></textarea>
<div class="options-row">
<label>Язык:</label>
<select id="tts-lang">
<option value="ru-RU">Русский</option>
<option value="en-US">English</option>
<option value="de-DE">Deutsch</option>
<option value="fr-FR">Français</option>
</select>
<label>Скорость:</label>
<select id="tts-rate">
<option value="0.8">Медленно</option>
<option value="1" selected>Нормально</option>
<option value="1.2">Быстро</option>
</select>
</div>
<button class="generate-btn btn-aud" onclick="speakText()">🔊 Озвучить</button>
</div>
<div id="aud-music-section" style="display:none">
<textarea id="aud-music-prompt" placeholder="Настроение (влияет на гамму и темп):&#10;например: грустное, эпичное, танцевальное, расслабленное" style="min-height:80px"></textarea>
<div class="options-row">
<label>Длительность:</label>
<select id="music-duration">
<option value="5">5 сек</option>
<option value="10" selected>10 сек</option>
<option value="20">20 сек</option>
</select>
</div>
<button class="generate-btn btn-aud" onclick="generateMusic()">🎼 Играть</button>
<button class="generate-btn btn-aud" style="background:#ef4444" onclick="stopMusic()">⏹ Стоп</button>
</div>
<div class="note">⚠️ Генерация музыки работает локально через Web Audio API. Это не нейросеть, но создаёт уникальные мелодии алгоритмически.</div>
<div class="status" id="aud-status"></div>
</div>
<div class="output-box" id="aud-output">
<span class="output-placeholder">Результат появится здесь…</span>
</div>
</div>
<script>
// ── Tabs ──
function switchTab(name, btn) {
document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
document.getElementById('panel-' + name).classList.add('active');
btn.classList.add('active');
}
function setStatus(id, msg, cls = '') {
const el = document.getElementById(id);
el.textContent = msg;
el.className = 'status ' + cls;
}
function setLoading(btn, loading) {
if (loading) {
btn.disabled = true;
btn.dataset.orig = btn.innerHTML;
btn.innerHTML = '<span class="spinner"></span> Загрузка…';
} else {
btn.disabled = false;
btn.innerHTML = btn.dataset.orig;
}
}
function copyText() {
const text = document.getElementById('text-output').innerText.replace('📋 Копировать', '').trim();
navigator.clipboard.writeText(text);
}
// ═══════════════════ TEXT ═══════════════════
async function generateText() {
const prompt = document.getElementById('text-prompt').value.trim();
if (!prompt) return alert('Введите запрос!');
const btn = event.target;
const model = document.getElementById('text-model').value;
setLoading(btn, true);
setStatus('text-status', '⏳ Отправка запроса…');
try {
const res = await fetch(`https://text.pollinations.ai/${encodeURIComponent(prompt)}?model=${model}&seed=${Math.floor(Math.random()*999999)}`);
if (!res.ok) throw new Error('Ошибка сети');
const text = await res.text();
document.getElementById('text-output').innerHTML = text.replace(/\n/g, '<br>') + '<button class="copy-btn" onclick="copyText()">📋 Копировать</button>';
setStatus('text-status', '✅ Готово!', 'ok');
} catch (e) {
setStatus('text-status', '❌ ' + e.message, 'error');
} finally {
setLoading(btn, false);
}
}
// ═══════════════════ IMAGE ═══════════════════
function generateImage() {
const prompt = document.getElementById('img-prompt').value.trim();
if (!prompt) return alert('Введите описание!');
const btn = event.target;
const size = document.getElementById('img-size').value;
const style = document.getElementById('img-style').value;
const [w, h] = size.split('x');
setLoading(btn, true);
setStatus('img-status', '⏳ Генерация изображения…');
const fullPrompt = style ? `${prompt}, ${style}` : prompt;
const url = `https://image.pollinations.ai/prompt/${encodeURIComponent(fullPrompt)}?width=${w}&height=${h}&seed=${Math.floor(Math.random()*999999)}&nologo=true`;
const img = new Image();
img.onload = () => {
document.getElementById('img-output').innerHTML = `<img src="${url}" alt="AI Image"><br><a href="${url}" download="ai_image.png" style="color:var(--accent2);display:inline-block;margin-top:.8rem">⬇️ Скачать PNG</a>`;
setStatus('img-status', '✅ Готово!', 'ok');
setLoading(btn, false);
};
img.onerror = () => {
setStatus('img-status', '❌ Ошибка загрузки изображения', 'error');
setLoading(btn, false);
};
img.src = url;
}
// ═══════════════════ AUDIO ═══════════════════
function toggleAudMode() {
const mode = document.getElementById('aud-mode').value;
document.getElementById('aud-tts-section').style.display = mode === 'tts' ? 'block' : 'none';
document.getElementById('aud-music-section').style.display = mode === 'music' ? 'block' : 'none';
}
function speakText() {
const text = document.getElementById('aud-tts-prompt').value.trim();
if (!text) return alert('Введите текст!');
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = document.getElementById('tts-lang').value;
utterance.rate = parseFloat(document.getElementById('tts-rate').value);
window.speechSynthesis.speak(utterance);
setStatus('aud-status', '🔊 Воспроизведение…', 'ok');
}
let audioCtx, musicTimeout;
function generateMusic() {
const prompt = document.getElementById('aud-music-prompt').value.toLowerCase();
const duration = parseInt(document.getElementById('music-duration').value);
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const masterGain = audioCtx.createGain();
masterGain.gain.value = 0.3;
masterGain.connect(audioCtx.destination);
// Анализ настроения
let scale, tempo, waveType;
if (/груст|sad|minor/i.test(prompt)) { scale = [0,2,3,5,7,8,10]; tempo = 0.4; waveType = 'sine'; }
else if (/эпич|epic|hero/i.test(prompt)) { scale = [0,2,4,5,7,9,11]; tempo = 0.3; waveType = 'sawtooth'; }
else if (/танц|dance|happy|upbeat/i.test(prompt)) { scale = [0,4,5,7,9]; tempo = 0.2; waveType = 'triangle'; }
else { scale = [0,2,4,7,9]; tempo = 0.35; waveType = 'sine'; }
const baseFreq = 130.81; // C3
let noteIndex = 0;
let currentTime = 0;
function playNote() {
if (currentTime > duration) return;
const note = scale[noteIndex % scale.length];
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = waveType;
osc.frequency.value = baseFreq * Math.pow(2, (note + Math.floor(noteIndex/7)*12)/12);
gain.gain.setValueAtTime(0, audioCtx.currentTime);
gain.gain.linearRampToValueAtTime(0.5, audioCtx.currentTime + 0.05);
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + tempo * 1.5);
osc.connect(gain).connect(masterGain);
osc.start(audioCtx.currentTime);
osc.stop(audioCtx.currentTime + tempo * 2);
noteIndex++;
currentTime += tempo;
musicTimeout = setTimeout(playNote, tempo * 1000);
}
playNote();
setStatus('aud-status', '🎼 Играет…', 'ok');
}
function stopMusic() {
if (audioCtx) audioCtx.close();
if (musicTimeout) clearTimeout(musicTimeout);
setStatus('aud-status', '⏹ Остановлено');
}
</script>
</body>
</html>