From 8cf5863de4c314eae05c9fb342cc5738b8bbba35 Mon Sep 17 00:00:00 2001 From: Porfiry Date: Sat, 19 Sep 2026 00:14:45 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A2=D1=80=D0=B5=D0=B1=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20(=D1=87=D0=B5=D1=80=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=B8=D0=BA)=20+=20=D0=B2=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82?= =?UTF-8?q?=201:=20=D1=83=D0=B1=D1=80=D0=B0=D0=BD=D0=B0=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=B4=D1=81=D0=BA=D0=B0=D0=B7=D0=BA=D0=B0,=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D1=86=D0=BE=20=D1=82=D0=BE=D0=BA=D0=B5=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2,=20=D1=83=D0=B7=D0=BA=D0=BE=D0=B5=20=D0=BE=D0=BA=D0=BD?= =?UTF-8?q?=D0=BE=20=D1=81=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=BE=D0=B9=20?= =?UTF-8?q?=D0=BD=D0=B0=D0=B7=D0=B0=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- REQUIREMENTS.md | 105 +++++++++++++++++++++ sketches/001-sidebar-utility/index.html | 120 +++++++++++++++++++++++- 2 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 REQUIREMENTS.md diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md new file mode 100644 index 0000000..b295087 --- /dev/null +++ b/REQUIREMENTS.md @@ -0,0 +1,105 @@ +# Требования к десктопному клиенту agentik + +**Статус:** накидываем. Это черновик — ни один пункт не является обязательным к +исполнению в том виде, как записан. Позже пересматриваем. + +**Основа дизайна:** вариант 1 (`sketches/001-sidebar-utility`). Варианты 2 и 3 +остаются рядом как источник идей. + +**Сейчас описываем только поведение.** Кода нет и не пишем, пока требования не устоятся. + +--- + +## 1. Общее + +- **R1.** Клиент — только для одного ассистента, не для переписки с людьми. + Никаких «пользователей», «контактов», «групп» — их нет и не должно быть. +- **R2.** Стиль — тёмная тема, уже принятая в клиентах пользователя + (фон `#121218`, панели `#17212B`, текст `#EBEBEB`). +- **R3.** Что видно на экране в любой момент: список диалогов, выбранный диалог + и поле ввода. Больше ничего обязательного нет. + +## 2. Раскладка и адаптивность + +- **R4.** **Широкое окно** — две части: список диалогов слева, открытый диалог справа. +- **R5.** **Узкое окно** — на экране что-то одно: либо список диалогов, либо + конкретный диалог. Переключение — как в Телеграме. +- **R6.** В узком окне у открытого диалога сверху слева — кнопка «Назад». + Нажатие возвращает к списку диалогов на всю ширину. +- **R7.** Порог «широкое/узкое» — по ширине окна, одна величина. Какой именно + размер считать порогом — решим позже. + +## 3. Список диалогов + +- **R8.** Список диалогов — это список бесед с одним и тем же ассистентом, + различающихся темой (задача, контекст, история). +- **R9.** Группировка диалогов остаётся: «Работа», «Дом» и подобное. Подпись и + состав групп — настраиваемые. +- **R10.** В строке диалога: название, последнее сообщение, время, счётчик + непрочитанных. +- **R11.** Есть поиск по диалогам. + +## 4. Открытый диалог + +- **R12.** Показ сообщений: мои и ассистента, с временем. +- **R13.** Ответ ассистента можно прервать — на видном месте кнопка «Стоп». +- **R14.** Сообщения приходят вживую, по мере генерации (текст дотекает). + +## 5. Панель ввода + +- **R15.** Поле ввода, кнопка «Отправить» и **отдельная кнопка записи микрофона**. +- **R16.** Подсказка о горячих клавишах под полем **убирается** — её не должно быть. + (Убрать «Ctrl+R — запись · Enter — отправить · Shift+Enter — новая строка».) +- **R17.** Пока идёт запись, ввод текста не должен вводить в заблуждение: + должно быть понятно, что сейчас пишется голос. +- **R18.** Отмена записи — явная. + +## 6. Голос + +- **R19.** Кнопка записи включает микрофон и останавливает его. Точная механика + (по нажатию или на удержание) — см. раздел 10. +- **R20.** Распознавание речи — **отдельная тема, здесь не решается.** В интерфейсе + важно только: где кнопка, что видно во время записи, что происходит после. + +## 7. Состояние агента + +- **R21.** Показывать, **сколько токенов израсходовано** за диалог целиком. +- **R22.** Показывать, **сколько занято сейчас** и **сколько всего доступно**. +- **R23.** Показывать это **кольцом заполнения** — как сильно заполнено. +- **R24.** По умолчанию состояние **свёрнуто**; по нажатию — разворачивается + с подробностями. +- **R25.** Место и точный вид — решим позже. + +## 8. Ход работы агента + +- **R26.** Показывать не только ответы ассистента, но и происходящее вокруг них: + вызовы инструментов, рассуждения, ошибки. +- **R27.** Свёрнутый вид — одна строка: кратко, что происходит + (например, «вызов инструмента: такой-то»). +- **R28.** Развёрнутый вид — подробности этого шага: аргументы, результат, время. +- **R29.** Сворачивание/разворачивание — по нажатию на строку. +- **R30.** Что именно показывать в строке — решим позже. + +## 9. Отдельным списком: чего в библиотеке agentik СЕЙЧАС нет + +Проверено по коду `pw.binom.agentik:client` (версия 3): + +- **Токенов нет вообще.** Ни в сообщениях, ни в событиях, ни в диалоге. То есть + R21–R23 сейчас не из чего взять — потребуется доработка на стороне агента + и протокола. Это не задача дизайна, но без неё кольцо нарисовать нечем. +- **Есть события** (их и показываем в R26–R30): начало ответа, рассуждение, + текст по мере генерации, картинка, вызов инструмента, результат инструмента, + конец хода, прерывание, ошибка. +- **Есть события о диалогах целиком:** создан, удалён, переименован. +- **Есть:** прерывание ответа, история сообщений, список диалогов. + +## 10. Решения, которые ещё не приняты + +- Запись: по нажатию (нажал — говоришь — нажал) или на удержание. +- Нужны ли вложения (картинки) в этом клиенте. +- Как именно выглядит кольцо токенов и где живёт. + +## 11. Как проверяем + +Проверка — **живой прогон**, а не «собралось». Правило пользователя: перед показом +гонять сценарий руками. Сценарии по этим требованиям будут отдельно. diff --git a/sketches/001-sidebar-utility/index.html b/sketches/001-sidebar-utility/index.html index ae7a2d9..5add8e4 100644 --- a/sketches/001-sidebar-utility/index.html +++ b/sketches/001-sidebar-utility/index.html @@ -71,8 +71,9 @@ /* ---------- правая часть: чат ---------- */ .main { flex: 1; display: flex; flex-direction: column; } .chat-head { + position: relative; height: 58px; flex: 0 0 58px; border-bottom: 1px solid var(--line); display: flex; align-items: center; - gap: 11px; padding: 0 16px; + gap: 8px; padding: 0 16px; } .chat-head .avatar { width: 36px; height: 36px; flex: 0 0 36px; font-size: 13px; } .head-meta { flex: 1; } @@ -80,6 +81,46 @@ .head-sub { font-size: 11.5px; color: var(--dim); } .head-sub .dot { color: #63C88A; } + /* кнопка «назад» — нужна только в узком окне */ + .back-btn { + display: none; width: 32px; height: 32px; flex: 0 0 32px; border-radius: 8px; + border: none; background: transparent; color: var(--fg); cursor: pointer; + font-size: 17px; place-items: center; + } + .back-btn:hover { background: var(--panel); } + + /* кольцо израсходованного контекста */ + .tok-btn { + display: flex; align-items: center; gap: 8px; background: transparent; border: none; + cursor: pointer; color: var(--dim); padding: 4px 6px; border-radius: 9px; font-size: 11.5px; + } + .tok-btn:hover { background: var(--panel); color: var(--fg); } + .tok-ring { position: relative; width: 27px; height: 27px; flex: 0 0 27px; } + .tok-ring svg { transform: rotate(-90deg); display: block; } + .tok-ring .pct { + position: absolute; inset: 0; display: grid; place-items: center; + font-size: 8.5px; font-weight: 700; color: var(--fg); + } + + /* развёрнутая карточка состояния */ + .tok-panel { + display: none; position: absolute; top: 62px; right: 16px; width: 300px; min-width: 300px; + flex: 0 0 auto; z-index: 40; + background: var(--panel); border: 1px solid var(--line); border-radius: 12px; + box-shadow: 0 16px 48px rgba(0,0,0,.55); padding: 14px 16px; + } + .tok-panel.on { display: block; } + .tok-panel h4 { font-size: 10.5px; letter-spacing: 1.1px; text-transform: uppercase; + color: var(--dim); margin-bottom: 11px; font-weight: 700; } + .tok-row { display: flex; justify-content: space-between; font-size: 12.5px; margin-bottom: 8px; } + .tok-row span:first-child { color: var(--dim); } + .tok-row span:last-child { font-weight: 600; } + .tok-bar { height: 6px; border-radius: 3px; background: var(--panel2); overflow: hidden; margin: 3px 0 12px; } + .tok-bar i { display: block; height: 100%; background: var(--accent); } + .tok-sep { height: 1px; background: var(--line); margin: 12px 0; } + .tok-kv { font-size: 11.5px; color: var(--dim); display: flex; justify-content: space-between; margin-bottom: 6px; } + .tok-kv b { color: var(--fg); font-weight: 600; } + .messages { flex: 1; overflow-y: auto; padding: 18px 24px; display: flex; flex-direction: column; gap: 12px; } .msg { max-width: 74%; padding: 9px 13px; border-radius: 13px; font-size: 13.5px; } .msg.user { align-self: flex-end; background: var(--user-bubble); border-bottom-right-radius: 4px; } @@ -164,6 +205,20 @@ .asr-note.on { display: flex; } .asr-note .live { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); } + /* ---------- узкое окно: либо список, либо чат ---------- */ + .backdrop { + display: none; position: fixed; inset: 0; background: rgba(0,0,0,.55); z-index: 90; + } + .backdrop.on { display: block; } + @media (max-width: 880px) { + body { padding: 0; } + .window { width: 100vw; height: 100vh; border-radius: 0; border: none; transform: none !important; } + .window.mode-chat .sidebar { display: none; } + .window.mode-list .main { display: none; } + .sidebar { width: 100%; flex: 1 1 auto; border-right: none; } + .back-btn { display: grid; } + } + /* окно целиком влезает на любой экран — не режем картинку */ @media (max-height: 820px) { body { padding: 8px; } .window, .win { transform: scale(.78); } } @media (max-height: 700px) { .window, .win { transform: scale(.66); } } @@ -225,12 +280,41 @@
+
АГ
agentik · рабочий
-
● на связи · модель codding-big
+
● на связи
+ + + + +
+

Контекст диалога

+
Занято сейчас34 300
+
+
Всего доступно100 000
+
Осталось65 700
+
+

За диалог целиком

+
Отправлено в модель412 800
+
Получено от модели58 240
+
Ходов18
+
+
Модельcodding-big
+
@@ -277,8 +361,7 @@
- Ctrl+R — запись голоса · Enter — отправить · Shift+Enter — новая строка - готов +
@@ -357,6 +440,35 @@ document.addEventListener('keydown', e => { if (e.ctrlKey && e.key.toLowerCase() === 'r') { e.preventDefault(); recBtn.click(); } }); + + // ---------- широкое / узкое окно ---------- + const win = document.querySelector('.window'); + const NARROW = 880; + function applyMode() { + if (innerWidth > NARROW) { // широкое: видно и то, и другое + win.classList.remove('mode-chat', 'mode-list'); + return; + } + // узкое: показываем что-то одно, по умолчанию список + if (!win.classList.contains('mode-chat') && !win.classList.contains('mode-list')) { + win.classList.add('mode-list'); + } + } + document.querySelectorAll('.chat-row').forEach(r => r.addEventListener('click', () => { + document.querySelectorAll('.chat-row').forEach(x => x.classList.remove('active')); + r.classList.add('active'); + if (innerWidth <= NARROW) win.classList.remove('mode-list'), win.classList.add('mode-chat'); + })); + document.getElementById('backBtn').onclick = () => win.classList.remove('mode-chat'), win.classList.add('mode-list'); + addEventListener('resize', applyMode); + applyMode(); + + // ---------- кольцо токенов: свёрнуто -> развёрнуто ---------- + const tokPanel = document.getElementById('tokPanel'); + document.getElementById('tokBtn').onclick = e => { e.stopPropagation(); tokPanel.classList.toggle('on'); }; + document.addEventListener('click', e => { + if (tokPanel.classList.contains('on') && !tokPanel.contains(e.target)) tokPanel.classList.remove('on'); + });