From 99b32720b64f1577a564a1cfcd3ca54c76f3c693 Mon Sep 17 00:00:00 2001 From: manoraga Date: Sat, 8 Aug 2026 14:32:13 +0300 Subject: [PATCH] =?UTF-8?q?refactor:=20=D0=B4=D0=B5=D0=BA=D0=BE=D0=BC?= =?UTF-8?q?=D0=BF=D0=BE=D0=B7=D0=B8=D1=86=D0=B8=D1=8F=20=D1=84=D1=80=D0=BE?= =?UTF-8?q?=D0=BD=D1=82=D0=B5=D0=BD=D0=B4=D0=B0=20index.html=20(=D0=B2?= =?UTF-8?q?=D1=8B=D0=BD=D0=BE=D1=81=20static/css/styles.css=20=D0=B8=20sta?= =?UTF-8?q?tic/js/app.js)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/css/styles.css | 15 +++ static/index.html | 243 +----------------------------------------- static/js/app.js | 215 +++++++++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+), 238 deletions(-) create mode 100644 static/css/styles.css create mode 100644 static/js/app.js diff --git a/static/css/styles.css b/static/css/styles.css new file mode 100644 index 0000000..d2dc205 --- /dev/null +++ b/static/css/styles.css @@ -0,0 +1,15 @@ +/* Плавное (градиентное) исчезновение текста сверху при скролле */ +.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; +} diff --git a/static/index.html b/static/index.html index c324ede..c386b6e 100644 --- a/static/index.html +++ b/static/index.html @@ -6,21 +6,7 @@ SCUD Orion AI — Context Manager - + @@ -61,7 +47,7 @@ - +
@@ -77,11 +63,10 @@
- + - + - \ No newline at end of file + diff --git a/static/js/app.js b/static/js/app.js new file mode 100644 index 0000000..ef697d0 --- /dev/null +++ b/static/js/app.js @@ -0,0 +1,215 @@ +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'); + } +}