ИИ ассистент
👁 4
var GROQ_KEY = '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';
var HF_KEY = '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';
var MODEL = 'llama-3.3-70b-versatile';
var HF_MODEL = 'stabilityai/stable-diffusion-xl-base-1.0';
var history = [];
var style = document.createElement('style');
style.textContent = '.gc-wrap{font-family:"Segoe UI",Arial,sans-serif;background:rgba(5,12,35,0.98);border:1px solid rgba(0,150,255,0.25);border-radius:20px;overflow:hidden;box-shadow:0 0 50px rgba(0,80,255,0.15),0 24px 60px rgba(0,0,0,0.7);display:flex;flex-direction:column;height:560px;max-width:420px;margin:0 auto}'
+ '.gc-header{padding:14px 18px;background:rgba(0,10,30,0.8);border-bottom:1px solid rgba(0,150,255,0.15);display:flex;align-items:center;gap:10px;flex-shrink:0}'
+ '.gc-avatar{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,#3b82f6,#8b5cf6);display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:bold;color:#fff;flex-shrink:0;position:relative;box-shadow:0 0 12px rgba(59,130,246,0.4)}'
+ '.gc-avatar::after{content:"";position:absolute;bottom:1px;right:1px;width:8px;height:8px;background:#00ff9c;border-radius:50%;border:2px solid rgba(5,12,35,0.98);box-shadow:0 0 6px rgba(0,255,156,0.6)}'
+ '.gc-info{flex:1}'
+ '.gc-name{font-size:13px;font-weight:600;color:#fff}'
+ '.gc-status{font-size:10px;color:#00ff9c;margin-top:2px}'
+ '.gc-caps{display:flex;gap:5px;align-items:center}'
+ '.gc-cap{font-size:9px;padding:2px 6px;border-radius:4px;background:rgba(0,255,156,0.1);border:1px solid rgba(0,255,156,0.2);color:#00ff9c;letter-spacing:0.04em}'
+ '.gc-cap.img{background:rgba(168,85,247,0.1);border-color:rgba(168,85,247,0.2);color:#a855f7}'
+ '.gc-messages{flex:1;overflow-y:auto;padding:14px;display:flex;flex-direction:column;gap:12px;scrollbar-width:thin;scrollbar-color:#30363d transparent}'
+ '.gc-date{text-align:center;font-size:10px;color:rgba(255,255,255,0.2);letter-spacing:0.08em}'
+ '.gc-msg-ai{display:flex;gap:8px;align-items:flex-end}'
+ '.gc-msg-ai-av{width:24px;height:24px;border-radius:50%;background:linear-gradient(135deg,#3b82f6,#8b5cf6);display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:bold;color:#fff;flex-shrink:0}'
+ '.gc-bubble-ai{max-width:82%;background:rgba(59,130,246,0.1);border:1px solid rgba(59,130,246,0.2);border-radius:14px 14px 14px 3px;padding:9px 12px;font-size:13px;line-height:1.6;color:#e2e8f0;word-break:break-word;white-space:pre-wrap}'
+ '.gc-time-ai{font-size:10px;color:rgba(255,255,255,0.2);margin-top:3px;padding-left:32px}'
+ '.gc-msg-user{display:flex;flex-direction:column;align-items:flex-end;gap:3px}'
+ '.gc-bubble-user{max-width:82%;background:linear-gradient(135deg,rgba(99,102,241,0.3),rgba(139,92,246,0.3));border:1px solid rgba(139,92,246,0.3);border-radius:14px 14px 3px 14px;padding:9px 12px;font-size:13px;line-height:1.6;color:#e2e8f0;word-break:break-word}'
+ '.gc-time-user{font-size:10px;color:rgba(255,255,255,0.2);display:flex;align-items:center;gap:3px}'
+ '.gc-check{color:#60a5fa;font-size:10px}'
+ '.gc-typing{display:flex;gap:8px;align-items:center}'
+ '.gc-typing-av{width:24px;height:24px;border-radius:50%;background:linear-gradient(135deg,#3b82f6,#8b5cf6);display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:bold;color:#fff;flex-shrink:0}'
+ '.gc-typing-bubble{background:rgba(59,130,246,0.1);border:1px solid rgba(59,130,246,0.2);border-radius:14px 14px 14px 3px;padding:10px 14px;display:flex;gap:4px;align-items:center}'
+ '.gc-dot{width:5px;height:5px;border-radius:50%;background:rgba(96,165,250,0.7);animation:gc-bounce 1.2s ease-in-out infinite}'
+ '.gc-dot:nth-child(2){animation-delay:0.2s}'
+ '.gc-dot:nth-child(3){animation-delay:0.4s}'
+ '@keyframes gc-bounce{0%,60%,100%{transform:translateY(0);opacity:0.5}30%{transform:translateY(-4px);opacity:1}}'
+ '.gc-bubble-err{background:rgba(239,68,68,0.08);border-color:rgba(239,68,68,0.2);color:#fca5a5}'
+ '.gc-img-wrap{max-width:82%;margin-top:4px}'
+ '.gc-img-label{font-size:10px;color:rgba(168,85,247,0.7);margin-bottom:4px;padding-left:32px;letter-spacing:0.05em}'
+ '.gc-img{width:100%;border-radius:12px;border:1px solid rgba(168,85,247,0.3);display:block}'
+ '.gc-img-loading{width:100%;height:160px;border-radius:12px;border:1px solid rgba(168,85,247,0.3);background:rgba(168,85,247,0.05);display:flex;align-items:center;justify-content:center;font-size:12px;color:rgba(168,85,247,0.6);letter-spacing:0.05em}'
+ '.gc-divider{height:1px;background:linear-gradient(90deg,transparent,rgba(0,150,255,0.12),transparent);flex-shrink:0}'
+ '.gc-input-wrap{padding:10px 12px;display:flex;gap:8px;align-items:flex-end;background:rgba(0,5,20,0.5);flex-shrink:0}'
+ '.gc-input{flex:1;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);border-radius:10px;padding:9px 12px;font-size:13px;color:#fff;font-family:inherit;resize:none;min-height:38px;max-height:90px;line-height:1.5;outline:none;transition:border-color 0.2s}'
+ '.gc-input:focus{border-color:rgba(59,130,246,0.4)}'
+ '.gc-input::placeholder{color:rgba(255,255,255,0.25)}'
+ '.gc-send{width:38px;height:38px;border-radius:10px;background:linear-gradient(135deg,#3b82f6,#8b5cf6);border:none;display:flex;align-items:center;justify-content:center;font-size:14px;color:#fff;cursor:pointer;flex-shrink:0;box-shadow:0 4px 12px rgba(59,130,246,0.3);transition:0.2s}'
+ '.gc-send:hover{transform:translateY(-1px);box-shadow:0 6px 18px rgba(59,130,246,0.5)}'
+ '.gc-send:disabled{opacity:0.4;cursor:default;transform:none}'
+ '.gc-toolbar{display:flex;gap:6px;padding:0 12px 10px;flex-shrink:0}'
+ '.gc-tbtn{padding:4px 10px;border-radius:8px;background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.07);font-size:11px;color:rgba(255,255,255,0.3);cursor:pointer;font-family:inherit;transition:0.2s}'
+ '.gc-tbtn:hover{background:rgba(255,255,255,0.08);color:rgba(255,255,255,0.6)}';
document.head.appendChild(style);
var wrap = document.createElement('div');
wrap.className = 'gc-wrap';
// Шапка
var header = document.createElement('div');
header.className = 'gc-header';
header.innerHTML = '<div class="gc-avatar">A</div>'
+ '<div class="gc-info"><div class="gc-name">AI Assistant</div><div class="gc-status">Online</div></div>'
+ '<div class="gc-caps"><span class="gc-cap">TEXT</span><span class="gc-cap img">IMAGE</span></div>';
// Сообщения
var msgsEl = document.createElement('div');
msgsEl.className = 'gc-messages';
var firstMsg = document.createElement('div');
firstMsg.innerHTML = '<div class="gc-date">Сегодня</div>';
msgsEl.appendChild(firstMsg);
addAiMsg('Привет! Я умею отвечать на вопросы и генерировать изображения. Попробуй написать "нарисуй закат над горами" или любой другой запрос!');
var divider = document.createElement('div');
divider.className = 'gc-divider';
// Инпут
var inputWrap = document.createElement('div');
inputWrap.className = 'gc-input-wrap';
var inputEl = document.createElement('textarea');
inputEl.className = 'gc-input';
inputEl.placeholder = 'Напишите или попросите нарисовать...';
inputEl.rows = 1;
var sendBtn = document.createElement('button');
sendBtn.className = 'gc-send';
sendBtn.innerHTML = '▶';
inputWrap.appendChild(inputEl);
inputWrap.appendChild(sendBtn);
// Тулбар
var toolbar = document.createElement('div');
toolbar.className = 'gc-toolbar';
var clearBtn = document.createElement('button');
clearBtn.className = 'gc-tbtn';
clearBtn.textContent = 'Очистить';
var hintBtn = document.createElement('button');
hintBtn.className = 'gc-tbtn';
hintBtn.textContent = 'Нарисуй закат';
hintBtn.addEventListener('click', function() {
inputEl.value = 'Нарисуй закат над горами';
inputEl.focus();
});
toolbar.appendChild(clearBtn);
toolbar.appendChild(hintBtn);
wrap.appendChild(header);
wrap.appendChild(msgsEl);
wrap.appendChild(divider);
wrap.appendChild(inputWrap);
wrap.appendChild(toolbar);
container.appendChild(wrap);
// Авторесайз
inputEl.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = Math.min(this.scrollHeight, 90) + 'px';
});
inputEl.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
sendBtn.addEventListener('click', sendMessage);
clearBtn.addEventListener('click', clearChat);
function getTime() {
var d = new Date();
return d.getHours() + ':' + String(d.getMinutes()).padStart(2, '0');
}
function escHtml(t) {
return t.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\n/g,'<br>');
}
function scrollBottom() {
msgsEl.scrollTop = msgsEl.scrollHeight;
}
function addAiMsg(text, isErr) {
var w = document.createElement('div');
var cls = 'gc-bubble-ai' + (isErr ? ' gc-bubble-err' : '');
w.innerHTML = '<div class="gc-msg-ai"><div class="gc-msg-ai-av">A</div>'
+ '<div class="' + cls + '">' + escHtml(text) + '</div></div>'
+ '<div class="gc-time-ai">' + getTime() + '</div>';
msgsEl.appendChild(w);
scrollBottom();
}
function addUserMsg(text) {
var w = document.createElement('div');
w.className = 'gc-msg-user';
w.innerHTML = '<div class="gc-bubble-user">' + escHtml(text) + '</div>'
+ '<div class="gc-time-user">' + getTime() + ' <span class="gc-check">checkcheck</span></div>';
msgsEl.appendChild(w);
scrollBottom();
}
function addTyping(id) {
var el = document.createElement('div');
el.className = 'gc-typing';
el.id = id || 'gc-typing';
el.innerHTML = '<div class="gc-typing-av">A</div>'
+ '<div class="gc-typing-bubble"><div class="gc-dot"></div><div class="gc-dot"></div><div class="gc-dot"></div></div>';
msgsEl.appendChild(el);
scrollBottom();
return el;
}
function addImageLoading() {
var w = document.createElement('div');
w.id = 'gc-img-loading';
w.innerHTML = '<div class="gc-img-label">Генерирую изображение...</div>'
+ '<div class="gc-img-loading">Подождите ~10-20 сек...</div>';
msgsEl.appendChild(w);
scrollBottom();
return w;
}
function removeEl(id) {
var el = document.getElementById(id);
if (el) el.remove();
}
function addImage(blob) {
var url = URL.createObjectURL(blob);
var w = document.createElement('div');
w.innerHTML = '<div class="gc-img-label">Сгенерировано AI</div>'
+ '<div class="gc-msg-ai"><div class="gc-msg-ai-av">A</div>'
+ '<div class="gc-img-wrap"><img class="gc-img" src="' + url + '"></div></div>'
+ '<div class="gc-time-ai">' + getTime() + '</div>';
msgsEl.appendChild(w);
scrollBottom();
}
function setLoading(on) {
sendBtn.disabled = on;
inputEl.disabled = on;
}
// Определяем — запрос на картинку или текст
async function detectIntent(text) {
var res = await fetch('https://hf-proxy.kennyshwenny.workers.dev', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + GROQ_KEY
},
body: JSON.stringify({
model: MODEL,
max_tokens: 200,
messages: [
{
role: 'system',
content: 'Ты определяешь намерение пользователя. Если пользователь хочет сгенерировать, нарисовать, создать изображение/картинку/фото — ответь JSON: {"type":"image","prompt":"описание на английском для image generation"}. Иначе — {"type":"text"}. Только JSON, без пояснений.'
},
{ role: 'user', content: text }
]
})
});
var data = await res.json();
var raw = data.choices[0].message.content.trim();
console.log('GROQ RESPONSE:', raw);
// Ищем JSON внутри ответа
var match = raw.match(/\{[\s\S]*?\}/);
if (match) {
try { return JSON.parse(match[0]); } catch(e) {}
}
// Если модель написала про картинку но не вернула JSON
var isImage = /нарисуй|картинк|изображени|draw|generate|image|paint|photo/i.test(raw);
if (isImage) return { type: 'image', prompt: raw };
return { type: 'text' };
}
// Генерация изображения через PHP прокси
async function generateImage(prompt) {
var formData = new FormData();
formData.append('prompt', prompt);
var res = await fetch('/hf-proxy.php', {
method: 'POST',
body: formData
});
if (!res.ok) throw new Error('Proxy error: ' + res.status);
return await res.blob();
}
// Текстовый ответ через Groq
async function getTextReply(text) {
history.push({ role: 'user', content: text });
var res = await fetch('https://api.groq.com/openai/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + GROQ_KEY
},
body: JSON.stringify({
model: MODEL,
messages: history,
max_tokens: 1024,
temperature: 0.7
})
});
var data = await res.json();
if (data.choices && data.choices[0]) {
var reply = data.choices[0].message.content;
history.push({ role: 'assistant', content: reply });
return reply;
}
throw new Error(data.error ? data.error.message : 'Ошибка');
}
async function sendMessage() {
var text = inputEl.value.trim();
if (!text) return;
inputEl.value = '';
inputEl.style.height = 'auto';
addUserMsg(text);
setLoading(true);
try {
// Определяем намерение
var typing = addTyping('gc-typing');
var intent = await detectIntent(text);
removeEl('gc-typing');
if (intent.type === 'image') {
// Генерация картинки
addImageLoading();
var res2 = await fetch('/hf-proxy.php', {
method: 'POST',
body: (() => { var f = new FormData(); f.append('prompt', intent.prompt || text); return f; })()
});
console.log('HF STATUS:', res2.status);
console.log('HF CONTENT TYPE:', res2.headers.get('content-type'));
var text2 = await res2.clone().text();
console.log('HF RESPONSE:', text2.substring(0, 200));
var blob = await res2.blob();
removeEl('gc-img-loading');
addImage(blob);
addAiMsg('Вот что получилось! Промт: ' + intent.prompt, false);
} else {
// Текстовый ответ
addTyping('gc-typing');
var reply = await getTextReply(text);
removeEl('gc-typing');
addAiMsg(reply, false);
}
} catch(e) {
removeEl('gc-typing');
removeEl('gc-img-loading');
addAiMsg('Ошибка: ' + e.message, true);
}
setLoading(false);
inputEl.focus();
}
function clearChat() {
history = [];
msgsEl.innerHTML = '<div class="gc-date">Сегодня</div>';
addAiMsg('Чат очищен. Спроси что-нибудь или попроси нарисовать!');
}