cd /home/puh/scud_context_api # 1. Создаем необходимые директории для CSS и JS mkdir -p static/css static/js # 2. Выносим CSS в static/css/styles.css cat << 'EOF' > static/css/styles.css /* Плавное (градиентное) исчезновение текста сверху при скролле */ .fade-scroll-top { mask-image: linear-gradient(to bottom, transparent 0%, black 14px); -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 14px); } /* Скрытие стандартного скроллбара */ .no-scrollbar::-webkit-scrollbar { display: none; } .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; } EOF # 3. Выносим JavaScript в static/js/app.js cat << 'EOF' > static/js/app.js const API_TOKEN = "scud_secret_token_2026"; const SESSION_ID = "web_session_main"; const STORAGE_KEY = 'scud_chat_input_history'; let currentFilter = 'ALL'; let allTasks = []; // === ИСТОРИЯ КОМАНД (LOCALSTORAGE + СТРЕЛКИ ВВЕРХ/ВНИЗ) === let inputHistory = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'); let historyIndex = -1; document.addEventListener('DOMContentLoaded', () => { const userInputEl = document.getElementById('user-input'); if (userInputEl) { // Динамическое расширение высоты поля до 4 строк (~96px) userInputEl.addEventListener('input', function() { this.style.height = 'auto'; this.style.height = Math.min(this.scrollHeight, 96) + 'px'; }); userInputEl.addEventListener('keydown', function(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); document.getElementById('chat-form').requestSubmit(); } else if (e.key === 'ArrowUp') { if (inputHistory.length > 0 && historyIndex < inputHistory.length - 1) { e.preventDefault(); if (historyIndex === -1) { this.dataset.draft = this.value; } historyIndex++; this.value = inputHistory[inputHistory.length - 1 - historyIndex]; this.dispatchEvent(new Event('input')); setTimeout(() => this.setSelectionRange(this.value.length, this.value.length), 0); } } else if (e.key === 'ArrowDown') { if (historyIndex !== -1) { e.preventDefault(); if (historyIndex > 0) { historyIndex--; this.value = inputHistory[inputHistory.length - 1 - historyIndex]; } else { historyIndex = -1; this.value = this.dataset.draft || ''; } this.dispatchEvent(new Event('input')); setTimeout(() => this.setSelectionRange(this.value.length, this.value.length), 0); } } }); } loadTasks(); }); function toggleDrawer() { const drawer = document.getElementById('task-drawer'); const backdrop = document.getElementById('drawer-backdrop'); const isHidden = drawer.classList.contains('translate-x-full'); if (isHidden) { drawer.classList.remove('translate-x-full'); backdrop.classList.remove('hidden'); } else { drawer.classList.add('translate-x-full'); backdrop.classList.add('hidden'); } } function setFilter(status) { currentFilter = status; ['ALL', 'IN_PROGRESS', 'BACKLOG', 'COMPLETED'].forEach(f => { const btn = document.getElementById(`filter-${f}`); if (f === status) { btn.className = "px-3 py-1.5 rounded-t-lg border-b-2 border-indigo-600 text-indigo-600 font-bold"; } else { btn.className = "px-3 py-1.5 rounded-t-lg border-b-2 border-transparent hover:text-slate-700"; } }); renderTasks(); } async function loadTasks() { try { const res = await fetch('/api/v1/tasks', { headers: { 'Authorization': `Bearer ${API_TOKEN}` } }); allTasks = await res.json(); document.getElementById('task-count-badge').innerText = allTasks.length; renderTasks(); } catch (err) { document.getElementById('tasks-container').innerHTML = `
Ошибка загрузки задач
`; } } function renderTasks() { const container = document.getElementById('tasks-container'); const filtered = allTasks.filter(t => currentFilter === 'ALL' || t.status === currentFilter); if (filtered.length === 0) { container.innerHTML = `
Нет задач с выбранным фильтром
`; return; } container.innerHTML = filtered.map(t => { let statusBadge = 'bg-slate-100 text-slate-600 border-slate-200'; let cardBg = 'bg-white'; if (t.status === 'COMPLETED') { statusBadge = 'bg-emerald-50 text-emerald-700 border-emerald-300 font-semibold'; cardBg = 'bg-emerald-50/20'; } else if (t.status === 'IN_PROGRESS') { statusBadge = 'bg-amber-50 text-amber-700 border-amber-300 font-bold'; cardBg = 'bg-amber-50/20 border-amber-200'; } let priorityBadge = 'text-slate-500 bg-slate-100 border-slate-200'; if (t.priority === 'HIGH') priorityBadge = 'text-red-700 bg-red-50 border-red-200 font-bold'; let dueDateHtml = ''; if (t.due_date) { dueDateHtml = `
Срок: ${t.due_date}
`; } return `
${t.task_id} ${t.priority || 'HIGH'}
${t.status}

${t.title}

${t.module}
${dueDateHtml}
`; }).join(''); } async function sendMessage(e) { e.preventDefault(); const input = document.getElementById('user-input'); const chatWindow = document.getElementById('chat-window'); const sendBtn = document.getElementById('send-btn'); const text = input.value.trim(); if (!text) return; if (inputHistory[inputHistory.length - 1] !== text) { inputHistory.push(text); if (inputHistory.length > 50) inputHistory.shift(); localStorage.setItem(STORAGE_KEY, JSON.stringify(inputHistory)); } historyIndex = -1; chatWindow.innerHTML += `
${text}
`; input.value = ''; input.style.height = 'auto'; chatWindow.scrollTop = chatWindow.scrollHeight; sendBtn.disabled = true; sendBtn.classList.add('opacity-50'); try { const res = await fetch('/api/v1/chat', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${API_TOKEN}` }, body: JSON.stringify({ session_id: SESSION_ID, message: text }) }); const data = await res.json(); chatWindow.innerHTML += `

ИИ-Ассистент

${data.reply}

`; chatWindow.scrollTop = chatWindow.scrollHeight; loadTasks(); } catch (err) { chatWindow.innerHTML += `
Ошибка связи с сервером API.
`; } finally { sendBtn.disabled = false; sendBtn.classList.remove('opacity-50'); } } EOF # 4. Обновляем чистый static/index.html cat << 'EOF' > static/index.html SCUD Orion AI — Context Manager

SCUD Orion AI

Context & Task Manager
API Online

ИИ-Ассистент

Привет! Я подключен к вашей базе задач и системным промптам. Вы можете писать команды прямо в чат или управлять задачами через панель справа.

EOF # 5. Фиксируем изменения фронтенда в Git git add static/ git commit -m "refactor: декомпозиция фронтенда index.html (вынос static/css/styles.css и static/js/app.js)" git push origin feature/llm-refactoring