diff --git a/README.md b/README.md index 5da7045..99cf5cf 100644 --- a/README.md +++ b/README.md @@ -50,3 +50,45 @@ export ANDROID_HOME=/root/Android/Sdk Экраны должны читать `MaterialTheme.colorScheme.*`, а не палитру напрямую. + +**Макеты экранов — в `design/`**: пятнадцать экранов, по одному состоянию на +файл, плюс те же экраны картинками. Смотреть — открыв `design/mockups/*.html` +в браузере. Разбор — `design/README.md`. + +## Принципы + +**Что куда и как переходить — принципы описаны в десктопном клиенте**, он +эталон требований и макетов: +[`subochev/agentik-desktop`](https://git.binom.pw/subochev/agentik-desktop) — +`REQUIREMENTS.md`, `approved/README.md`, `THEMES.md`, `STORAGE.md`, `CACHE.md`. + +Копий этих документов здесь нет намеренно: два источника правды разъедутся. + +**Чем Android отличается от десктопа** — отдельный документ: +`design/PRINCIPLES.md`. Коротко: принципы те же, меняется обёртка — телефон +всегда узкий (десктопные требования про широкое и узкое окно к нам не +относятся), системная кнопка и жест «Назад» вместо кнопки внутри узкого окна, +шторки снизу вместо модальных окон, нет наведения курсора, есть клавиатура +и системные разрешения. + +## Откуда код + +Что берём и откуда — `design/BORROW.md`. Коротко: + +| Что | Откуда | Состояние | +|---|---|---| +| Ядро агента | `subochev/agentik` → Nexus `caffeine`, `pw.binom.agentik:client` | подключено | +| Скелет и тема | свой, этот репозиторий | есть | +| **Отрисовка Markdown** | **[`ai/assistent`](https://git.binom.pw/ai/assistent)**, коммит `2c5b3f9` | **ещё не перенесена** | +| Макеты экранов | свои, из одобренного десктопного дизайна | в `design/` | + +**Отрисовку Markdown писать не надо — её надо взять** из `ai/assistent`: четыре +файла, разобраны в `design/BORROW.md` (что копировать, что нет, как переносить +на Android). Модуль оттуда собран под Compose Desktop — целиком как зависимость +не подключается, файлы копируются к нам. Единственное, что придётся переделать — +открытие ссылок: `java.awt.Desktop` на телефоне нет, вместо него системный +`Intent`. + +Цвета темы пришли из одобренного десктопного макета +`agentik-desktop/approved/001-sidebar-utility.html` — согласованы с десктопом, +не выдуманы заново. diff --git a/design/BORROW.md b/design/BORROW.md new file mode 100644 index 0000000..7512aba --- /dev/null +++ b/design/BORROW.md @@ -0,0 +1,147 @@ +# Что откуда взято + +**Правило простое:** чужое не переписываем заново, а берём и говорим, откуда +взяли. Ниже по каждому куску: откуда, что именно, и что с ним делать на телефоне. + +| Куски кода | Откуда | Состояние | +|---|---|---| +| Ядро агента | `subochev/agentik` → Nexus `caffeine` | **подключено** | +| Скелет приложения, тема | свой, в этом репозитории | **есть** | +| Отрисовка Markdown | `ai/assistent` | **ещё не перенесена** | +| Макеты экранов | свои, из десктопного дизайна | **лежат в `design/`** | + +--- + +## 1. Ядро агента — берём из Nexus + +**Откуда:** репозиторий `https://git.binom.pw/subochev/agentik`, модуль `client`. +Библиотека публикуется в Nexus (репозиторий `caffeine`) — **руками не публикуем**, +публикует CI/CD по релизу. + +**Что берём:** `pw.binom.agentik:client`, версия задана в +`gradle/libs.versions.toml` (`agentik = "5"`). + +**Что важно знать про неё:** движок библиотека **не задаёт** — он передаётся +снаружи. На Android это OkHttp: + +```kotlin +agentikHttpClient(OkHttp, token) { ... } // ktor-client-okhttp уже подключён +``` + +Это не наша выдумка, а условие задачи: библиотека не должна тянуть на телефон +чужой движок. Своя авторизация у agentik — **отдельная**, не копия A2A. + +## 2. Скелет приложения и тема — своё + +`app/src/main/kotlin/pw/binom/agentik/android/` — написано в этом репозитории, +коммит `ef4804a` («Скелет Android-клиента agentik»). + +**Тема** (`ui/theme/Color.kt`, `Theme.kt`) — цвета **согласованы с десктопным +клиентом**: они пришли из одобренного десктопного макета +`agentik-desktop/approved/001-sidebar-utility.html`. Значения: фон `#121218`, +панель `#17212B`, акцент `#6AB2F2`, текст `#EBEBEB`, приглушённый `#768C9E`, +ошибка `#EC3942`. + +Экраны обязаны читать `MaterialTheme.colorScheme.*`, а **не** палитру напрямую — +чтобы позже появились другие темы без правки экранов (требование `R1.1` +десктопных требований). + +## 3. Отрисовка Markdown — берём из `ai/assistent` + +**Откуда:** https://git.binom.pw/ai/assistent (ветка `main`, +проверено на коммите `2c5b3f9`). Репозиторий приватный — скачивать по RAW-адресу +с логином: `https://git.binom.pw/ai/assistent/raw/branch/main/<путь>`. + +**Писать своё не надо — надо взять это.** Четыре файла: + +| Файл | Строк | Что внутри | +|---|---|---| +| `client-shared/src/main/kotlin/pw/binom/client/shared/markdown/MarkdownSegment.kt` | 59 | куски разметки: текст, блок кода, таблица, картинка, разделитель + стилевые участки | +| `client-shared/src/main/kotlin/pw/binom/client/shared/markdown/MarkdownParserImpl.kt` | 353 | `parseMarkdown(markdown): List` | +| `client-shared/src/main/kotlin/pw/binom/client/shared/ui/MarkdownRendering.kt` | 377 | сама отрисовка: inline-стили, ссылки, код с копированием, таблицы, картинки | +| `client/src/main/kotlin/pw/binom/client/ui/MarkdownRenderer.kt` | 25 | обёртка: открытие ссылки в браузере, `parseMultiline` — **образец использования** | + +### Как переносить на Android + +Модуль `client-shared` собран под **Compose Desktop** (JVM) — целиком как +зависимость его взять нельзя, на телефоне не соберётся. Поэтому **файлы +копируются в наш модуль**, а не подключаются. + +Проверено по коду — переносятся почти дословно: + +- `MarkdownSegment.kt`, `MarkdownParserImpl.kt` — **ни одной строки** под JVM, + Swing, AWT или Skiko. Чистая логика; +- `MarkdownRendering.kt` — только `androidx.compose.*` и **ни одного диалога** + и ни одного оконного примитива. Единственная платформенная мелочь — + `LocalClipboardManager` (для копирования кода), на Android он есть. + +**Переделать придётся только одно:** открытие ссылок. В четвёртом файле это +`java.awt.Desktop.getDesktop().browse(...)` — **на телефоне AWT нет**. Вместо +этого системный переход: + +```kotlin +context.startActivity(Intent(Intent.ACTION_VIEW, url.toUri())) +``` + +### Зависимость + +Одна, и она уже есть в нашем Nexus: + +```kotlin +implementation("ru.otpbank.ai:markdown:0.51.0") +``` + +Плюс репозиторий в `build.gradle.kts` — без него не соберётся: + +```kotlin +maven { + url = uri("http://192.168.76.117/repository/developerspace-prod-mvn-hosted/") + isAllowInsecureProtocol = true +} +``` + +### Что из assistent НЕ берём + +`MessageBubble.kt` (789 строк, пузырь сообщения целиком) — тянет за собой модуль +`chat-common`. Нам он не нужен: пузырь у нас свой, из макетов. Архитектуру, +слои, готовые экраны (`ChatScreen`, `SettingsDialog`) и модули `chat-*` — +**тоже не берём**, у нас своё (границы заимствования разобраны в десктопном +репозитории: `BORROW-FROM-ASSISTENT.md`). + +### Приёмы отрисовки — самое ценное + +В десктопном репозитории разобраны чужие грабли с указанием мест в коде: +`BORROW-FROM-ASSISTENT.md`, раздел «Приёмы отрисовки». Читать **до** переноса. +Главная из них: `Box(horizontalScroll)` внутри контейнера с бесконечной +максимальной шириной (`wrapContentWidth`) роняет Compose с ошибкой +`measured with an infinity maximum width` — таблицы размечаются парой разных +режимов. На телефоне экран узкий, и это особенно чувствительно. + +**Состояние:** файлы ещё не перенесены — это отдельная задача. Переносить из +`ai/assistent` коммита `2c5b3f9` (свежее проверять там же). + +## 4. Макеты экранов — свои, но выросли из десктопных + +`design/mockups/` — пятнадцать экранов, по одному состоянию на файл. Рисовались +как продолжение одобренного десктопного дизайна +(`agentik-desktop/approved/001-sidebar-utility.html` — «привычный мессенджер»), +поэтому палитра, значки и порядок экранов совпадают с десктопом. + +Чем отличаются — `PRINCIPLES.md`. Кратко: телефон всегда узкий, вместо +«Назад внутри узкого окна» — системная кнопка и жест, вместо модальных окон — +шторки снизу. + +## 5. Принципы — не копируем, ссылаемся + +Принципы (что видно на экране, группы, агенты, непрочитанные, «нет данных — +нет цифры», сообщения как источник правды) описаны в десктопном репозитории: +`REQUIREMENTS.md`, `approved/README.md`, `THEMES.md`, `STORAGE.md`, `CACHE.md`. +**Копий не делаем** — один источник правды лучше двух, которые разъедутся. +Отличия Android — в `PRINCIPLES.md`. + +--- + +## Про доступы + +Адрес и токен агента — **настройки приложения**, вводятся человеком, в коде +репозитория их нет и быть не должно. В историю коммитов секреты не попадают. diff --git a/design/PRINCIPLES.md b/design/PRINCIPLES.md new file mode 100644 index 0000000..4f50308 --- /dev/null +++ b/design/PRINCIPLES.md @@ -0,0 +1,81 @@ +# Принципы Android-клиента + +**Коротко:** принципы те же, что у десктопного клиента. Меняется не смысл, +а обёртка — телефон всегда узкий, у него своя кнопка «Назад», нет курсора и +есть клавиатура со шторками. Ниже: что берём как есть, что своё. + +**Обговорённые принципы лежат в репозитории десктопного клиента** (он — эталон +требований и макетов): + +- https://git.binom.pw/subochev/agentik-desktop — файл `REQUIREMENTS.md` + (требования R1…, разделы «Общее», «Раскладка и адаптивность», «Список + диалогов» и далее), плюс `approved/README.md` (что одобрено и почему), + `THEMES.md` (тема и имена цветов), `STORAGE.md`, `CACHE.md`. + Ссылки даны на `main`; для точного среза — коммит `2c5b3f9`. + +Мы **не копируем** эти документы себе: один источник правды лучше двух копий, +которые разъедутся. Здесь только список того, что для телефона иначе. + +--- + +## ЧАСТЬ 1. Берём как есть + +1. **Состав экрана.** Что видно в любой момент: список диалогов и открытый + диалог с полем ввода. Больше обязательного нет (`R2`). +2. **Список диалогов.** Лента папок («Все чаты», группы, «+»), поиск, список + со значком, названием, последней строкой, временем, кружком непрочитанных. +3. **Группы.** «Все чаты» — встроенная: не удаляется и не переименовывается. + Удаление группы диалоги не удаляет — они возвращаются в «Все чаты». + У группы только название. +4. **Ассистенты.** Имя придумывает человек (сервер своего имени не сообщает), + адрес целиком вместе с путём `/agentik`, отличие в списке — цветом **или** + картинкой, что-то одно. Картинка **копируется внутрь клиента**, не берётся + по исходному адресу. +5. **Проверка связи.** Отдельным экраном поверх: идёт → отвечает → не отвечает, + внизу «Проверить снова»; добавление доступно только при успехе. + Запросы — из исходников агента, оба ничего не меняют. +6. **Цвета.** Берём из схемы по смыслу, **не числом в коде** (`R1.1`). Имена + по смыслу, а не по виду: «фон», «панель», «акцент», «ошибка», «успех» + (`R1.2`). Цвета различения ассистентов — **не из схемы**: они + пользовательские и в другой теме остаются собой (`R1.5`). +7. **«Нет данных — нет цифры».** Не показываем счётчик, которого нет в + источниках. +8. **Сообщения — единственный источник правды.** События от агента — только + «будильник»; после сигнала догоняем сообщения запросом «что после такого-то». + +## ЧАСТЬ 2. На телефоне иначе + +9. **Узких и широких окон нет.** Десктопные `R3`–`R6` (две панели рядом, + порог ширины, кнопка «Назад» внутри узкого окна) **к нам не относятся**: + телефон всегда узкий. Вместо них — системная кнопка и жест «Назад» + (предсказуемый переход), и он же закрывает открытый диалог. +10. **Модальные окна → шторки снизу.** Где на десктопе два окна друг над другом + (настройки + проверка связи) — на телефоне полноэкранный экран или шторка + снизу. Одно действие — один экран. +11. **Курсора нет** — нет «наведения». Вместо него отклик на нажатие + (подсветка/рябь). Десктопное требование про цвета наведения (`R1.4`) + на телефоне превращается в проверку цвета нажатия. +12. **Клавиатура.** Поле ввода поднимается над клавиатурой, список сообщений + сдвигается, а не перекрывается. Открытая клавиатура — обычное состояние, + а не край. +13. **Запись голоса — с разрешением.** На десктопе микрофон доступен без + спроса, на телефоне нужно системное разрешение; отказ — состояние экрана, + а не ошибка. +14. **Файлы.** Картинки ассистентов и вложения — во внутреннем каталоге + приложения (на телефоне свои правила доступа к файлам). Пользователь + выбирает файл системным диалогом. +15. **Экран и поворот.** Края экрана (строка состояния, «подбородок» с + жестами) учитываем; при повороте и смене размера ничего не теряется. +16. **Тёмная тема — стартовая, не единственная.** Как и на десктопе, + схем несколько (`R1.1`). + +## ЧАСТЬ 3. Что решаем позже + +- Порог, с которого телефон считать «широким» (планшет, разворот) — пока не + нужен: рисуем узкий вид. +- Системная цветовая схема телефона: следовать за ней или держать свою. + +--- + +**Кода в этих документах нет намеренно** — здесь поведение. Реализация — по +задачам, через правки в этом репозитории. diff --git a/design/README.md b/design/README.md new file mode 100644 index 0000000..3af7c28 --- /dev/null +++ b/design/README.md @@ -0,0 +1,47 @@ +# Макеты Android-клиента agentik + +**Здесь принятое.** Пятнадцать экранов, по одному состоянию на файл — рисовались +и одобрялись 2026-09-20. Это решение, на которое опираемся в реализации, а не +предложение к обсуждению. + +Интерактивности в макетах **нет намеренно**: вместо переключений — отдельный файл +на каждое состояние. Так видно каждое состояние по отдельности и не надо ничего +нажимать, чтобы его показать. + +## Что где + +| Файл | Что показывает | +|---|---| +| `mockups/01-chats.html` | Диалоги — список | +| `mockups/02-chat.html` | Чат — открытый диалог | +| `mockups/03-streaming.html` | Чат — ответ идёт, кнопка «Стоп» | +| `mockups/04-recording.html` | Чат — запись голоса | +| `mockups/05-empty.html` | Диалоги — ни одного ассистента | +| `mockups/06-agents.html` | Ассистенты — список | +| `mockups/07-agent-color.html` | Ассистент — форма, отличие цветом | +| `mockups/08-agent-pic.html` | Ассистент — форма, отличие картинкой | +| `mockups/09-check-wait.html` | Проверка связи — идёт | +| `mockups/10-check-ok.html` | Проверка связи — отвечает | +| `mockups/11-check-fail.html` | Проверка связи — не отвечает | +| `mockups/12-new-chat.html` | Новый диалог — выбор ассистента | +| `mockups/13-groups.html` | Группы — список | +| `mockups/14-group-edit.html` | Группы — изменение | +| `mockups/15-group-delete.html` | Группы — удаление | + +`screenshots/` — те же экраны картинками (PNG), чтобы посмотреть без браузера +и вставлять в переписку. `00-all.png` — общий лист, остальные по одному экрану. + +`gen_mockups.py` — генератор макетов: CSS и оболочка телефона заданы один раз, +состояние — фрагментом. Экран телефона 390×844. **Цвета в макетах стоят числами +и так и надо** — иначе макет не будет выглядеть задуманным; в коде приложения +цвет берётся из схемы по смыслу (см. `../README.md`, раздел «Дизайн»). + +## Как смотреть + +Открыть файл `mockups/*.html` в браузере — самодостаточный, без сборки и без +зависимостей. + +## Принципы и откуда код + +- `PRINCIPLES.md` — как это должно работать и чем Android отличается от десктопа; +- `BORROW.md` — что откуда взято, в том числе отрисовка Markdown. diff --git a/design/gen_mockups.py b/design/gen_mockups.py new file mode 100644 index 0000000..6544ffa --- /dev/null +++ b/design/gen_mockups.py @@ -0,0 +1,863 @@ +#!/usr/bin/env python3 +"""Генератор статичных макетов Android-клиента agentik (одно состояние = один файл). + +Правило пользователя: интерактивность не нужна, вместо неё много html-файлов. +CSS и оболочка заданы ОДИН раз, состояние — фрагментом. + +Телефон 390x844. Цвета в макете числами — так и надо; в коде приложения берутся +из цветовой схемы по смыслу (THEMES.md). +""" + +import os + +OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "mockups") +os.makedirs(OUT, exist_ok=True) + +CSS = """ + * { box-sizing: border-box; margin: 0; padding: 0; } + :root { + --bg: #121218; --panel: #17212B; --panel2: #1C2733; --line: #232B36; + --fg: #EBEBEB; --dim: #768C9E; --accent: #6AB2F2; --badge: #5EB5F7; + --err: #EC3942; --ok: #63C88A; --user-bubble: #2B5278; --bot-bubble: #1C2733; + --a1: #6AB2F2; --a2: #86C99A; --a3: #C9A886; --a4: #B79AE0; --a5: #E28C8C; + } + body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + -webkit-font-smoothing: antialiased; + background: #0A0A0F; color: var(--fg); line-height: 1.45; + display: flex; flex-direction: column; align-items: center; justify-content: center; + min-height: 100vh; padding: 20px; gap: 12px; + } + .caption { font-size: 12.5px; color: var(--dim); letter-spacing: .2px; } + + /* ── корпус телефона ── */ + .phone { + width: 390px; height: 800px; flex: 0 0 auto; background: var(--bg); + border-radius: 38px; overflow: hidden; position: relative; + border: 1px solid #2A3340; display: flex; flex-direction: column; + box-shadow: 0 30px 90px rgba(0,0,0,.75); + } + .status { height: 38px; flex: 0 0 38px; display: flex; align-items: center; + justify-content: space-between; padding: 0 24px; font-size: 12px; + font-variant-numeric: tabular-nums; color: var(--fg); } + .status .r { display: flex; gap: 6px; align-items: center; font-size: 11px; } + + /* ── шапки ── */ + .appbar { height: 56px; flex: 0 0 56px; display: flex; align-items: center; + gap: 10px; padding: 0 12px; border-bottom: 1px solid var(--line); + background: var(--panel); } + .appbar.plain { background: var(--bg); } + .appbar .nm { font-size: 16px; font-weight: 600; } + .grow { flex: 1; min-width: 0; } + .ic { width: 40px; height: 40px; flex: 0 0 40px; border-radius: 12px; border: none; + background: transparent; color: var(--dim); font-size: 17px; cursor: pointer; + display: grid; place-items: center; font-family: inherit; } + .ic:hover { background: var(--panel2); color: var(--fg); } + .ava { width: 38px; height: 38px; flex: 0 0 38px; border-radius: 50%; + display: grid; place-items: center; font-size: 15px; font-weight: 700; + color: #0F1922; } + .hd { flex: 1; min-width: 0; } + .hd .t1 { font-size: 15px; font-weight: 600; white-space: nowrap; + overflow: hidden; text-overflow: ellipsis; } + .hd .t2 { font-size: 11.5px; color: var(--dim); } + .ok { color: var(--ok); } + .bad { color: var(--err); } + + /* ── поиск и лента групп ── */ + .search { margin: 10px 14px 8px; height: 40px; border-radius: 12px; + border: 1px solid var(--line); background: var(--panel); color: var(--dim); + font-size: 13.5px; display: flex; align-items: center; gap: 9px; padding: 0 13px; } + .chips { display: flex; gap: 7px; padding: 2px 14px 10px; align-items: center; } + .chip { padding: 7px 12px; border-radius: 16px; font-size: 12.5px; color: var(--dim); + background: var(--panel); white-space: nowrap; border: 1px solid transparent; } + .chip.on { color: var(--fg); border-color: var(--accent); + background: rgba(106,178,242,.13); } + .chip b { color: var(--badge); font-weight: 700; margin-left: 3px; } + .chip.act { margin-left: auto; background: transparent; padding: 7px 9px; } + + /* ── список диалогов ── */ + .list { flex: 1; overflow: hidden; padding: 2px 10px 12px; position: relative; } + .row { display: flex; align-items: center; gap: 11px; padding: 11px 10px; + border-radius: 13px; position: relative; } + .row.on { background: #233040; } + .row .strip { position: absolute; left: 0; top: 12px; bottom: 12px; width: 3px; + border-radius: 3px; } + .row .rava { width: 44px; height: 44px; flex: 0 0 44px; border-radius: 50%; + display: grid; place-items: center; font-size: 16px; font-weight: 700; color: #0F1922; } + .rmid { flex: 1; min-width: 0; } + .l1 { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; } + .l1 .n { font-size: 14.5px; font-weight: 500; white-space: nowrap; overflow: hidden; + text-overflow: ellipsis; } + .l1 .t { font-size: 11px; color: var(--dim); flex: 0 0 auto; } + .l2 { display: flex; justify-content: space-between; gap: 8px; align-items: center; + margin-top: 2px; } + .l2 .p { font-size: 12.5px; color: var(--dim); white-space: nowrap; overflow: hidden; + text-overflow: ellipsis; } + .bg { background: var(--badge); color: #0F1922; border-radius: 11px; font-size: 11px; + font-weight: 700; padding: 1px 7px; flex: 0 0 auto; } + + .fab { position: absolute; right: 20px; bottom: 22px; width: 56px; height: 56px; + border-radius: 18px; border: none; background: var(--accent); color: #0F1922; + font-size: 26px; font-weight: 600; display: grid; place-items: center; + box-shadow: 0 10px 26px rgba(106,178,242,.35); font-family: inherit; } + + /* ── чат: сообщения ── */ + .msgs { flex: 1; overflow: hidden; padding: 14px 14px 4px; display: flex; + flex-direction: column; gap: 10px; } + .msg { max-width: 84%; padding: 9px 13px; border-radius: 15px; font-size: 13.5px; } + .msg.user { align-self: flex-end; background: var(--user-bubble); + border-bottom-right-radius: 5px; } + .msg.bot { align-self: flex-start; background: var(--bot-bubble); + border-bottom-left-radius: 5px; } + .msg .meta { font-size: 10.5px; color: var(--dim); margin-top: 6px; } + .msg b { font-weight: 650; } + .msg code { background: rgba(255,255,255,.08); border-radius: 4px; padding: 1px 5px; + font-size: 12px; font-family: "DejaVu Sans Mono", monospace; } + .msg ul { margin: 6px 0 2px 16px; } + .msg li { margin-bottom: 3px; } + + /* ход работы агента */ + .act-line { align-self: stretch; display: flex; align-items: center; gap: 9px; + background: var(--panel); border: 1px solid var(--line); border-radius: 11px; + padding: 8px 12px; font-size: 12.5px; color: var(--dim); } + .act-line .g { color: var(--accent); font-size: 13px; } + .act-line .nm { color: var(--fg); } + .act-line .ch { margin-left: auto; font-size: 11px; } + .act-line.think { border-left: 2px solid var(--accent); border-radius: 0 11px 11px 0; + background: rgba(106,178,242,.07); } + .act-open { align-self: stretch; background: var(--panel); border: 1px solid var(--line); + border-radius: 11px; padding: 10px 12px; font-size: 12px; color: var(--dim); } + .act-open .h { display: flex; align-items: center; gap: 9px; color: var(--dim); + margin-bottom: 7px; } + .act-open .h .nm { color: var(--fg); } + .act-open .kv { display: flex; gap: 8px; margin-top: 4px; } + .act-open .kv .k { color: var(--dim); flex: 0 0 78px; } + .act-open .kv .v { color: var(--fg); } + .cur { display: inline-block; width: 7px; height: 14px; background: var(--accent); + vertical-align: -2px; margin-left: 2px; } + + /* ── панель ввода ── */ + .composer { border-top: 1px solid var(--line); background: var(--panel); + padding: 10px 12px 14px; } + .irow { display: flex; align-items: center; gap: 9px; } + .field { flex: 1; min-width: 0; height: 44px; border-radius: 14px; background: var(--bg); + border: 1px solid var(--line); display: flex; align-items: center; padding: 0 14px; + font-size: 13.5px; color: var(--dim); } + .mic { width: 44px; height: 44px; flex: 0 0 44px; border-radius: 50%; + border: 1px solid var(--line); background: var(--bg); color: var(--dim); + font-size: 17px; display: grid; place-items: center; font-family: inherit; } + .mic.rec { background: var(--err); border-color: var(--err); color: #fff; } + .send { width: 44px; height: 44px; flex: 0 0 44px; border-radius: 14px; border: none; + background: var(--accent); color: #0F1922; font-size: 17px; + display: grid; place-items: center; font-family: inherit; } + .stop { height: 44px; flex: 1; border-radius: 14px; border: 1px solid rgba(236,57,66,.45); + background: rgba(236,57,66,.10); color: #F0A6AA; font-size: 13.5px; font-weight: 600; + display: flex; align-items: center; justify-content: center; gap: 9px; + font-family: inherit; } + + /* запись */ + .recpanel { display: flex; align-items: center; gap: 11px; background: var(--bg); + border: 1px solid var(--err); border-radius: 14px; padding: 10px 13px; + margin-bottom: 10px; } + .wave { display: flex; align-items: center; gap: 3px; height: 22px; flex: 1; } + .wave i { width: 3px; background: var(--err); border-radius: 2px; } + .wave i:nth-child(1) { height: 30%; } .wave i:nth-child(2) { height: 70%; } + .wave i:nth-child(3) { height: 45%; } .wave i:nth-child(4) { height: 100%; } + .wave i:nth-child(5) { height: 55%; } .wave i:nth-child(6) { height: 80%; } + .wave i:nth-child(7) { height: 35%; } .wave i:nth-child(8) { height: 65%; } + .wave i:nth-child(9) { height: 25%; } + .rectime { font-variant-numeric: tabular-nums; font-size: 13.5px; color: var(--err); + font-weight: 700; } + .recnote { font-size: 11.5px; color: var(--dim); margin-top: 8px; text-align: center; } + + /* ── модалки: нижний лист ── */ + .scrim { position: absolute; inset: 0; background: rgba(0,0,0,.6); } + .sheet { position: absolute; left: 0; right: 0; bottom: 0; background: var(--panel); + border-radius: 22px 22px 0 0; border-top: 1px solid var(--line); + max-height: 78%; display: flex; flex-direction: column; overflow: hidden; } + .sheet .grab { width: 40px; height: 4px; border-radius: 3px; background: var(--line); + margin: 10px auto 4px; flex: 0 0 auto; } + .sheet .hd2 { padding: 8px 18px 12px; } + .sheet .hd2 h2 { font-size: 17px; font-weight: 650; } + .sheet .hd2 p { font-size: 12.5px; color: var(--dim); margin-top: 4px; } + .sheet .body { flex: 1; overflow: hidden; padding: 0 14px 8px; } + .sheet .foot { padding: 12px 16px 18px; display: flex; gap: 10px; + border-top: 1px solid var(--line); } + + /* ── карточка по центру ── */ + .center { position: absolute; inset: 0; display: flex; align-items: center; + justify-content: center; padding: 22px; } + .card { width: 100%; max-height: 84%; background: var(--panel); + border: 1px solid var(--line); border-radius: 20px; display: flex; + flex-direction: column; overflow: hidden; } + + /* ── кнопки ── */ + .btn { border-radius: 13px; padding: 13px 16px; font-size: 14px; cursor: pointer; + border: 1px solid var(--line); background: transparent; color: var(--fg); + font-family: inherit; white-space: nowrap; } + .btn.prim { background: var(--accent); border-color: var(--accent); color: #0F1922; + font-weight: 650; } + .btn.dang { color: var(--err); border-color: rgba(236,57,66,.4); } + .btn.dang.solid { background: var(--err); border-color: var(--err); color: #FFF; + font-weight: 650; } + .btn.off { background: var(--panel2); border-color: var(--panel2); color: var(--dim); } + .btn.wide { width: 100%; display: flex; align-items: center; justify-content: center; + gap: 9px; border-style: dashed; color: var(--dim); padding: 15px; } + .btn.grow { flex: 1; } + .mini-ic { width: 38px; height: 38px; flex: 0 0 38px; border-radius: 11px; border: none; + background: transparent; color: var(--dim); font-size: 15px; display: grid; + place-items: center; font-family: inherit; } + .mini-ic:hover { background: var(--panel2); color: var(--fg); } + .mini-ic.del { color: #B4666B; } + + /* ── строки-объекты (агенты, группы) ── */ + .obj { display: flex; align-items: center; gap: 12px; padding: 13px 12px; + border-bottom: 1px solid var(--line); } + .obj .sign { width: 42px; height: 42px; flex: 0 0 42px; border-radius: 50%; + display: grid; place-items: center; font-size: 15px; font-weight: 700; color: #0F1922; } + .obj .mid { flex: 1; min-width: 0; } + .obj .nm { font-size: 14.5px; font-weight: 550; } + .obj .sb { font-size: 12px; color: var(--dim); white-space: nowrap; overflow: hidden; + text-overflow: ellipsis; } + .obj .acts { display: flex; gap: 2px; flex: 0 0 auto; } + .obj .lock { font-size: 10.5px; color: var(--dim); border: 1px dashed var(--line); + border-radius: 8px; padding: 3px 8px; } + .sys .nm { color: var(--dim); } + + /* ── формы ── */ + .form { padding: 6px 16px 12px; overflow: hidden; } + .lbl { font-size: 12px; color: var(--dim); margin: 14px 0 6px; } + .inp { height: 46px; border-radius: 13px; background: var(--bg); + border: 1px solid var(--line); display: flex; align-items: center; padding: 0 14px; + font-size: 13.5px; } + .inp.ph { color: var(--dim); } + .inp.focus { border-color: var(--accent); } + .inp .tail { margin-left: auto; font-size: 12px; color: var(--accent); } + .note { font-size: 11.5px; color: var(--dim); margin-top: 6px; line-height: 1.5; } + .tabs { display: flex; gap: 8px; margin: 14px 0 12px; } + .tab { flex: 1; text-align: center; padding: 11px; border-radius: 12px; + font-size: 13.5px; color: var(--dim); background: var(--panel2); } + .tab.on { color: var(--fg); background: rgba(106,178,242,.14); + border: 1px solid var(--accent); padding: 10px; } + .swatches { display: flex; gap: 13px; padding: 4px 2px; } + .sw { width: 34px; height: 34px; border-radius: 50%; border: 2px solid transparent; } + .sw.on { border-color: var(--fg); } + + /* ── проверка связи ── */ + .chk { display: flex; flex-direction: column; align-items: center; text-align: center; + padding: 24px 18px 18px; } + .ring { width: 62px; height: 62px; border-radius: 50%; border: 3px solid var(--panel2); + border-top-color: var(--accent); margin-bottom: 14px; } + .tick { width: 62px; height: 62px; border-radius: 50%; background: rgba(99,200,138,.14); + border: 2px solid var(--ok); color: var(--ok); font-size: 28px; display: grid; + place-items: center; margin-bottom: 14px; } + .cross { width: 62px; height: 62px; border-radius: 50%; background: rgba(236,57,66,.12); + border: 2px solid var(--err); color: var(--err); font-size: 26px; display: grid; + place-items: center; margin-bottom: 14px; } + .chk .st { font-size: 16px; font-weight: 600; } + .chk .ds { font-size: 12.5px; color: var(--dim); margin-top: 5px; } + .kv2 { width: 100%; margin-top: 16px; border-top: 1px solid var(--line); + border-bottom: 1px solid var(--line); padding: 10px 0; } + .kv2 .r { display: flex; justify-content: space-between; gap: 10px; font-size: 12.5px; + padding: 4px 0; } + .kv2 .r .k { color: var(--dim); } + .kv2 .r .v { color: var(--fg); text-align: right; } + .kv2 .r .v.ok { color: var(--ok); } .kv2 .r .v.bad { color: var(--err); } + + /* ── предупреждение ── */ + .warn { display: flex; gap: 10px; align-items: flex-start; padding: 12px 13px; + border-radius: 12px; background: rgba(236,57,66,.09); + border: 1px solid rgba(236,57,66,.32); font-size: 12px; color: #F0A6AA; + line-height: 1.5; } + .warn b { color: #FFC9CB; } + .danger-blk { margin-top: 16px; border-top: 1px solid var(--line); padding-top: 14px; } + .danger-blk .cap { font-size: 11px; letter-spacing: .8px; color: var(--err); + font-weight: 700; margin-bottom: 8px; } + + /* ── пустое состояние ── */ + .empty { flex: 1; display: flex; flex-direction: column; align-items: center; + justify-content: center; text-align: center; padding: 30px 34px; gap: 12px; } + .empty .big { width: 74px; height: 74px; border-radius: 22px; background: var(--panel); + border: 1px solid var(--line); display: grid; place-items: center; font-size: 30px; + color: var(--dim); } + .empty h3 { font-size: 16.5px; font-weight: 600; } + .empty p { font-size: 13px; color: var(--dim); line-height: 1.55; } + + /* ── радио в выборе ассистента ── */ + .radio { width: 22px; height: 22px; flex: 0 0 22px; border-radius: 50%; + border: 2px solid var(--line); } + .radio.on { border-color: var(--accent); background: radial-gradient(circle, + var(--accent) 0 6px, transparent 7px); } +""" + + +def statusbar(): + return ('
9:41' + '▮▮▮ ⏸ 82%
') + + +def chips(active="Все чаты"): + """Лента групп над списком. Карандаш — управление группами, прижат вправо.""" + items = [("Все чаты", "3"), ("Работа", ""), ("Дом", "")] + out = [] + for n, c in items: + cls = " on" if n == active else "" + cnt = f' {c}' if c else "" + out.append(f'{n}{cnt}') + out.append('✎') + return '
' + "".join(out) + "
" + + +def row(name, prev, time, color, letter, badge="", active=False): + cls = " on" if active else "" + b = f'{badge}' if badge else "" + return f'''
+
+
{letter}
+
+
{name}{time}
+
{prev}{b}
+
+
''' + + +# ───────────────────────────────────────────────────────────── +# СОСТОЯНИЯ +# ───────────────────────────────────────────────────────────── + +CHATS = f'''
+ {statusbar()} +
+
Диалоги
+
+ +
+ + {chips("Все чаты")} +
+ {row("Ремонт на кухне", "Плитку лучше матовую — бликов не будет", "14:32", "var(--a1)", "С", "3", True)} + {row("Отчёт за квартал", "Готово. Таблицу собрал, отправил в файл", "13:05", "var(--a2)", "А")} + {row("Ремонт на кухне", "Смеситель взял в расчёт, 12 400 ₽", "Вчера", "var(--a1)", "С")} + {row("Список покупок", "Молоко, хлеб, кофе — записал", "Вчера", "var(--a3)", "Д")} + {row("Отчёт за квартал", "Уточни, за какой период считать", "12.09", "var(--a2)", "А", "1")} + {row("Разбор документации", "Нашёл три противоречия в разделах", "11.09", "var(--a4)", "У")} +
+ +
''' + +CHAT = f'''
+ {statusbar()} +
+ +
С
+
+
Ремонт на кухне
+
Строитель · на связи
+
+ +
+
+
Чем закончить стык плитки и столешницы?
+
✦ + рассуждаю: нужен герметик или профиль + ▾
+
⚙ + вызов инструмента: web_search + ▾
+
Проверил варианты. Практичнее всего силиконовый профиль: +
  • не боится воды и пара;
  • снимается без следа, если понадобится переклеить.
+ Герметик дешевле примерно на 15%, но через год темнеет. +
14:32
+
А если столешница из массива?
+
+
⚙вызов инструмента: web_search + ▴
+
запроссиликоновый профиль массив дуба
+
результат8 источников, 0,4 с
+
+
Тогда профиль лучше не ставить — массив «дышит». Возьмите + герметик для дерева. +
14:33
+
+
+
+
Сообщение…
+ + +
+
+
''' + +STREAMING = f'''
+ {statusbar()} +
+ +
С
+
+
Ремонт на кухне
+
Строитель · на связи
+
+ +
+
+
А если столешница из массива?
+
⚙ + вызов инструмента: web_search + ▾
+
Тогда профиль лучше не ставить — массив «дышит», + а жёсткая планка не даст ему двигаться. Возьмите герметик для дерева: + он остаётся эластичным +
14:33
+
+
+
+ +
+
Агент отвечает — можно прервать
+
+
''' + +RECORDING = f'''
+ {statusbar()} +
+ +
С
+
+
Ремонт на кухне
+
Строитель · на связи
+
+ +
+
+
Тогда профиль лучше не ставить — массив «дышит». +
14:33
+
Скинь, что брать по смесителю
+
Смеситель с выдвижным изливом, GROHE или + Hansgrohe.
14:34
+
+
+
+
+
0:07
+ + +
+
+
Идёт запись…
+ +
+
Говорите — текст появится в поле. ✕ отменяет запись целиком.
+
+
''' + +EMPTY = f'''
+ {statusbar()} +
+
Диалоги
+
+ +
+
+
◇
+

Пока не добавлен ни один ассистент

+

Диалог живёт внутри ассистента — создать его отдельно нельзя. + Сначала добавьте ассистента: укажите название и адрес сервера.

+ +
+
''' + +AGENTS = f'''
+ {statusbar()} +
+ +
Ассистенты
+
+
+
+
+
С
+
Строитель
+
http://192.168.88.21:8080/agentik
+
● на связи
+
+
+
+
+
А
+
Аналитик
+
http://192.168.88.19:8080/agentik
+
● не отвечает
+
+
+
+
+
Д
+
Домовой
+
http://192.168.76.153:8080/agentik
+
● не проверялся
+
+
+
+
+
+
''' + +FORM_COLOR = f'''
+ {statusbar()} +
+ +
Новый ассистент
+
+
+
+
Название
+
Строитель
+
Имя придумываете вы. Сервер своего имени не сообщает.
+ +
Адрес сервера
+
http://192.168.88.21:8080/agentik⇄
+
Адрес целиком, вместе с путём /agentik. + Значок ⇄ проверяет связь.
+ +
Как отличать в списке
+
+
Цвет
+
Картинка
+
+
+
+
+
+
+
+
+
Этим цветом помечаются диалоги ассистента в списке.
+
+
+ + +
+
''' + +FORM_PIC = f'''
+ {statusbar()} +
+ +
Новый ассистент
+
+
+
+
Название
+
Строитель
+ +
Адрес сервера
+
http://192.168.88.21:8080/agentik⇄
+ +
Как отличать в списке
+
+
Цвет
+
Картинка
+
+
+
🏠
+ + +
+
Картинка копируется внутрь клиента — + по исходному адресу не берётся. Файл с флешки могут убрать, и значок пропадёт.
+
+
+ + +
+
''' + +CHECK_OK = f'''
+ {statusbar()} +
+ +
Ассистенты
+
+
+
+
С
+
Строитель
+
http://192.168.88.21:8080/agentik
+
А
+
Аналитик
+
http://192.168.88.19:8080/agentik
+
+
+
+
+
+
✓
+
Ассистент отвечает
+
Можно сохранять
+
+
Ответ на запросok
+
Адрес + 192.168.88.21:8080
+
Диалогов7
+
Отклик34 мс
+
+
+
+ + +
+
+
+
''' + +CHECK_WAIT = f'''
+ {statusbar()} +
+ +
Ассистенты
+
+
+
+
С
+
Строитель
+
http://192.168.88.21:8080/agentik
+
+
+
+
+
+
+
Спрашиваю ассистента…
+
Отправлен запрос состояния и запрос списка диалогов. + Оба ничего не меняют.
+
+
+ +
+
+
+
''' + +CHECK_FAIL = f'''
+ {statusbar()} +
+ +
Ассистенты
+
+
+
+
А
+
Аналитик
+
http://192.168.88.19:8080/agentik
+
+
+
+
+
+
✕
+
Ассистент не отвечает
+
Проверьте адрес и что сервер запущен
+
+
Ответ на запрос + соединение не установлено
+
Адрес192.168.88.19:8080
+
Отклик5 000 мс
+
+
+
+ + +
+
+
+
''' + +NEW_CHAT = f'''
+ {statusbar()} +
+
Диалоги
+
+ +
+ + {chips("Все чаты")} +
+ {row("Ремонт на кухне", "Плитку лучше матовую", "14:32", "var(--a1)", "С", "3")} + {row("Отчёт за квартал", "Готово. Таблицу собрал", "13:05", "var(--a2)", "А")} + {row("Список покупок", "Молоко, хлеб, кофе", "Вчера", "var(--a3)", "Д")} +
+
+
+
+

Новый диалог

+

В каком ассистенте создать? Диалог живёт внутри ассистента.

+
+
+
С
+
Строитель
+
http://192.168.88.21:8080/agentik
+
● на связи
+
+
+
+
А
+
Аналитик
+
http://192.168.88.19:8080/agentik
+
● не отвечает
+
+
+
+
+ + +
+
+
''' + +GROUPS = f'''
+ {statusbar()} +
+ +
Группы
+
+
+
+
+
Все чаты
+
Показывает все диалоги, из любой группы
+
встроенная
+
+
+
Работа
12 диалогов
+
+
+
+
+
Дом
4 диалога
+
+
+
+
+
Учёба
2 диалога
+
+
+
+
+
Пустая
+
Ни одного диалога
+
+
+
+
+
+
''' + +GROUP_EDIT = f'''
+ {statusbar()} +
+ +
Группы
+
+
+
+
Все чаты
+
Работа
+
12 диалогов
+
Дом
+
4 диалога
+
+
+
+
+
Изменить группу
+
+
Название
+
Работа
+
+
ОПАСНОЕ
+ +
Диалоги останутся — они вернутся в «Все чаты».
+
+
+
+ + +
+
+
+
''' + +GROUP_DELETE = f'''
+ {statusbar()} +
+ +
Группы
+
+
+
+
Все чаты
+
Работа
+
12 диалогов
+
+
+
+
+
Удалить группу «Работа»?
+
+
⚠Диалоги не удаляются. + 12 диалогов останутся и будут видны в «Все чаты». Сама группа исчезнет, + и диалоги потеряют к ней привязку.
+
+
+ + +
+
+
+
''' + + +FILES = { + "01-chats.html": ("Диалоги — список", CHATS), + "02-chat.html": ("Чат — открытый диалог", CHAT), + "03-streaming.html": ("Чат — ответ идёт, кнопка «Стоп»", STREAMING), + "04-recording.html": ("Чат — запись голоса", RECORDING), + "05-empty.html": ("Диалоги — ни одного ассистента", EMPTY), + "06-agents.html": ("Ассистенты — список", AGENTS), + "07-agent-color.html": ("Ассистент — форма, цвет", FORM_COLOR), + "08-agent-pic.html": ("Ассистент — форма, картинка", FORM_PIC), + "09-check-wait.html": ("Проверка связи — идёт", CHECK_WAIT), + "10-check-ok.html": ("Проверка связи — отвечает", CHECK_OK), + "11-check-fail.html": ("Проверка связи — не отвечает", CHECK_FAIL), + "12-new-chat.html": ("Новый диалог — выбор ассистента", NEW_CHAT), + "13-groups.html": ("Группы — список", GROUPS), + "14-group-edit.html": ("Группы — изменение", GROUP_EDIT), + "15-group-delete.html": ("Группы — удаление", GROUP_DELETE), +} + +PAGE = ''' + + + +{title} + + + +
{title}
+{body} + + +''' + +if __name__ == "__main__": + for name, (title, body) in FILES.items(): + html = PAGE.format(title=title, css=CSS, body=body) + path = os.path.join(OUT, name) + with open(path, "w", encoding="utf-8") as f: + f.write(html) + print(f"{name:22} {os.path.getsize(path):>7} байт") + print(f"\n→ {OUT} ({len(FILES)} состояний)") diff --git a/design/mockups/01-chats.html b/design/mockups/01-chats.html new file mode 100644 index 0000000..2507868 --- /dev/null +++ b/design/mockups/01-chats.html @@ -0,0 +1,347 @@ + + + + +Диалоги — список + + + +
Диалоги — список
+
+
9:41▮▮▮ ⏸ 82%
+
+
Диалоги
+
+ +
+ +
Все чаты 3РаботаДом✎
+
+
+
+
С
+
+
Ремонт на кухне14:32
+
Плитку лучше матовую — бликов не будет3
+
+
+
+
+
А
+
+
Отчёт за квартал13:05
+
Готово. Таблицу собрал, отправил в файл
+
+
+
+
+
С
+
+
Ремонт на кухнеВчера
+
Смеситель взял в расчёт, 12 400 ₽
+
+
+
+
+
Д
+
+
Список покупокВчера
+
Молоко, хлеб, кофе — записал
+
+
+
+
+
А
+
+
Отчёт за квартал12.09
+
Уточни, за какой период считать1
+
+
+
+
+
У
+
+
Разбор документации11.09
+
Нашёл три противоречия в разделах
+
+
+
+ +
+ + diff --git a/design/mockups/02-chat.html b/design/mockups/02-chat.html new file mode 100644 index 0000000..136006b --- /dev/null +++ b/design/mockups/02-chat.html @@ -0,0 +1,328 @@ + + + + +Чат — открытый диалог + + + +
Чат — открытый диалог
+
+
9:41▮▮▮ ⏸ 82%
+
+ +
С
+
+
Ремонт на кухне
+
Строитель · на связи
+
+ +
+
+
Чем закончить стык плитки и столешницы?
+
✦ + рассуждаю: нужен герметик или профиль + ▾
+
⚙ + вызов инструмента: web_search + ▾
+
Проверил варианты. Практичнее всего силиконовый профиль: +
  • не боится воды и пара;
  • снимается без следа, если понадобится переклеить.
+ Герметик дешевле примерно на 15%, но через год темнеет. +
14:32
+
А если столешница из массива?
+
+
⚙вызов инструмента: web_search + ▴
+
запроссиликоновый профиль массив дуба
+
результат8 источников, 0,4 с
+
+
Тогда профиль лучше не ставить — массив «дышит». Возьмите + герметик для дерева. +
14:33
+
+
+
+
Сообщение…
+ + +
+
+
+ + diff --git a/design/mockups/03-streaming.html b/design/mockups/03-streaming.html new file mode 100644 index 0000000..d8c05ef --- /dev/null +++ b/design/mockups/03-streaming.html @@ -0,0 +1,314 @@ + + + + +Чат — ответ идёт, кнопка «Стоп» + + + +
Чат — ответ идёт, кнопка «Стоп»
+
+
9:41▮▮▮ ⏸ 82%
+
+ +
С
+
+
Ремонт на кухне
+
Строитель · на связи
+
+ +
+
+
А если столешница из массива?
+
⚙ + вызов инструмента: web_search + ▾
+
Тогда профиль лучше не ставить — массив «дышит», + а жёсткая планка не даст ему двигаться. Возьмите герметик для дерева: + он остаётся эластичным +
14:33
+
+
+
+ +
+
Агент отвечает — можно прервать
+
+
+ + diff --git a/design/mockups/04-recording.html b/design/mockups/04-recording.html new file mode 100644 index 0000000..4b8670b --- /dev/null +++ b/design/mockups/04-recording.html @@ -0,0 +1,318 @@ + + + + +Чат — запись голоса + + + +
Чат — запись голоса
+
+
9:41▮▮▮ ⏸ 82%
+
+ +
С
+
+
Ремонт на кухне
+
Строитель · на связи
+
+ +
+
+
Тогда профиль лучше не ставить — массив «дышит». +
14:33
+
Скинь, что брать по смесителю
+
Смеситель с выдвижным изливом, GROHE или + Hansgrohe.
14:34
+
+
+
+
+
0:07
+ + +
+
+
Идёт запись…
+ +
+
Говорите — текст появится в поле. ✕ отменяет запись целиком.
+
+
+ + diff --git a/design/mockups/05-empty.html b/design/mockups/05-empty.html new file mode 100644 index 0000000..d8e757c --- /dev/null +++ b/design/mockups/05-empty.html @@ -0,0 +1,301 @@ + + + + +Диалоги — ни одного ассистента + + + +
Диалоги — ни одного ассистента
+
+
9:41▮▮▮ ⏸ 82%
+
+
Диалоги
+
+ +
+
+
◇
+

Пока не добавлен ни один ассистент

+

Диалог живёт внутри ассистента — создать его отдельно нельзя. + Сначала добавьте ассистента: укажите название и адрес сервера.

+ +
+
+ + diff --git a/design/mockups/06-agents.html b/design/mockups/06-agents.html new file mode 100644 index 0000000..64c1cfe --- /dev/null +++ b/design/mockups/06-agents.html @@ -0,0 +1,321 @@ + + + + +Ассистенты — список + + + +
Ассистенты — список
+
+
9:41▮▮▮ ⏸ 82%
+
+ +
Ассистенты
+
+
+
+
+
С
+
Строитель
+
http://192.168.88.21:8080/agentik
+
● на связи
+
+
+
+
+
А
+
Аналитик
+
http://192.168.88.19:8080/agentik
+
● не отвечает
+
+
+
+
+
Д
+
Домовой
+
http://192.168.76.153:8080/agentik
+
● не проверялся
+
+
+
+
+
+
+ + diff --git a/design/mockups/07-agent-color.html b/design/mockups/07-agent-color.html new file mode 100644 index 0000000..c76e647 --- /dev/null +++ b/design/mockups/07-agent-color.html @@ -0,0 +1,322 @@ + + + + +Ассистент — форма, цвет + + + +
Ассистент — форма, цвет
+
+
9:41▮▮▮ ⏸ 82%
+
+ +
Новый ассистент
+
+
+
+
Название
+
Строитель
+
Имя придумываете вы. Сервер своего имени не сообщает.
+ +
Адрес сервера
+
http://192.168.88.21:8080/agentik⇄
+
Адрес целиком, вместе с путём /agentik. + Значок ⇄ проверяет связь.
+ +
Как отличать в списке
+
+
Цвет
+
Картинка
+
+
+
+
+
+
+
+
+
Этим цветом помечаются диалоги ассистента в списке.
+
+
+ + +
+
+ + diff --git a/design/mockups/08-agent-pic.html b/design/mockups/08-agent-pic.html new file mode 100644 index 0000000..5879cc6 --- /dev/null +++ b/design/mockups/08-agent-pic.html @@ -0,0 +1,320 @@ + + + + +Ассистент — форма, картинка + + + +
Ассистент — форма, картинка
+
+
9:41▮▮▮ ⏸ 82%
+
+ +
Новый ассистент
+
+
+
+
Название
+
Строитель
+ +
Адрес сервера
+
http://192.168.88.21:8080/agentik⇄
+ +
Как отличать в списке
+
+
Цвет
+
Картинка
+
+
+
🏠
+ + +
+
Картинка копируется внутрь клиента — + по исходному адресу не берётся. Файл с флешки могут убрать, и значок пропадёт.
+
+
+ + +
+
+ + diff --git a/design/mockups/09-check-wait.html b/design/mockups/09-check-wait.html new file mode 100644 index 0000000..35f911a --- /dev/null +++ b/design/mockups/09-check-wait.html @@ -0,0 +1,313 @@ + + + + +Проверка связи — идёт + + + +
Проверка связи — идёт
+
+
9:41▮▮▮ ⏸ 82%
+
+ +
Ассистенты
+
+
+
+
С
+
Строитель
+
http://192.168.88.21:8080/agentik
+
+
+
+
+
+
+
Спрашиваю ассистента…
+
Отправлен запрос состояния и запрос списка диалогов. + Оба ничего не меняют.
+
+
+ +
+
+
+
+ + diff --git a/design/mockups/10-check-ok.html b/design/mockups/10-check-ok.html new file mode 100644 index 0000000..5fdeecd --- /dev/null +++ b/design/mockups/10-check-ok.html @@ -0,0 +1,323 @@ + + + + +Проверка связи — отвечает + + + +
Проверка связи — отвечает
+
+
9:41▮▮▮ ⏸ 82%
+
+ +
Ассистенты
+
+
+
+
С
+
Строитель
+
http://192.168.88.21:8080/agentik
+
А
+
Аналитик
+
http://192.168.88.19:8080/agentik
+
+
+
+
+
+
✓
+
Ассистент отвечает
+
Можно сохранять
+
+
Ответ на запросok
+
Адрес + 192.168.88.21:8080
+
Диалогов7
+
Отклик34 мс
+
+
+
+ + +
+
+
+
+ + diff --git a/design/mockups/11-check-fail.html b/design/mockups/11-check-fail.html new file mode 100644 index 0000000..dc46cb8 --- /dev/null +++ b/design/mockups/11-check-fail.html @@ -0,0 +1,319 @@ + + + + +Проверка связи — не отвечает + + + +
Проверка связи — не отвечает
+
+
9:41▮▮▮ ⏸ 82%
+
+ +
Ассистенты
+
+
+
+
А
+
Аналитик
+
http://192.168.88.19:8080/agentik
+
+
+
+
+
+
✕
+
Ассистент не отвечает
+
Проверьте адрес и что сервер запущен
+
+
Ответ на запрос + соединение не установлено
+
Адрес192.168.88.19:8080
+
Отклик5 000 мс
+
+
+
+ + +
+
+
+
+ + diff --git a/design/mockups/12-new-chat.html b/design/mockups/12-new-chat.html new file mode 100644 index 0000000..384bca7 --- /dev/null +++ b/design/mockups/12-new-chat.html @@ -0,0 +1,348 @@ + + + + +Новый диалог — выбор ассистента + + + +
Новый диалог — выбор ассистента
+
+
9:41▮▮▮ ⏸ 82%
+
+
Диалоги
+
+ +
+ +
Все чаты 3РаботаДом✎
+
+
+
+
С
+
+
Ремонт на кухне14:32
+
Плитку лучше матовую3
+
+
+
+
+
А
+
+
Отчёт за квартал13:05
+
Готово. Таблицу собрал
+
+
+
+
+
Д
+
+
Список покупокВчера
+
Молоко, хлеб, кофе
+
+
+
+
+
+
+

Новый диалог

+

В каком ассистенте создать? Диалог живёт внутри ассистента.

+
+
+
С
+
Строитель
+
http://192.168.88.21:8080/agentik
+
● на связи
+
+
+
+
А
+
Аналитик
+
http://192.168.88.19:8080/agentik
+
● не отвечает
+
+
+
+
+ + +
+
+
+ + diff --git a/design/mockups/13-groups.html b/design/mockups/13-groups.html new file mode 100644 index 0000000..f17cf2c --- /dev/null +++ b/design/mockups/13-groups.html @@ -0,0 +1,323 @@ + + + + +Группы — список + + + +
Группы — список
+
+
9:41▮▮▮ ⏸ 82%
+
+ +
Группы
+
+
+
+
+
Все чаты
+
Показывает все диалоги, из любой группы
+
встроенная
+
+
+
Работа
12 диалогов
+
+
+
+
+
Дом
4 диалога
+
+
+
+
+
Учёба
2 диалога
+
+
+
+
+
Пустая
+
Ни одного диалога
+
+
+
+
+
+
+ + diff --git a/design/mockups/14-group-edit.html b/design/mockups/14-group-edit.html new file mode 100644 index 0000000..4d414a7 --- /dev/null +++ b/design/mockups/14-group-edit.html @@ -0,0 +1,321 @@ + + + + +Группы — изменение + + + +
Группы — изменение
+
+
9:41▮▮▮ ⏸ 82%
+
+ +
Группы
+
+
+
+
Все чаты
+
Работа
+
12 диалогов
+
Дом
+
4 диалога
+
+
+
+
+
Изменить группу
+
+
Название
+
Работа
+
+
ОПАСНОЕ
+ +
Диалоги останутся — они вернутся в «Все чаты».
+
+
+
+ + +
+
+
+
+ + diff --git a/design/mockups/15-group-delete.html b/design/mockups/15-group-delete.html new file mode 100644 index 0000000..38bae30 --- /dev/null +++ b/design/mockups/15-group-delete.html @@ -0,0 +1,315 @@ + + + + +Группы — удаление + + + +
Группы — удаление
+
+
9:41▮▮▮ ⏸ 82%
+
+ +
Группы
+
+
+
+
Все чаты
+
Работа
+
12 диалогов
+
+
+
+
+
Удалить группу «Работа»?
+
+
⚠Диалоги не удаляются. + 12 диалогов останутся и будут видны в «Все чаты». Сама группа исчезнет, + и диалоги потеряют к ней привязку.
+
+
+ + +
+
+
+
+ + diff --git a/design/screenshots/00-all.png b/design/screenshots/00-all.png new file mode 100644 index 0000000..f1dd215 Binary files /dev/null and b/design/screenshots/00-all.png differ diff --git a/design/screenshots/01-chats.png b/design/screenshots/01-chats.png new file mode 100644 index 0000000..4dfc21d Binary files /dev/null and b/design/screenshots/01-chats.png differ diff --git a/design/screenshots/02-chat.png b/design/screenshots/02-chat.png new file mode 100644 index 0000000..74b745c Binary files /dev/null and b/design/screenshots/02-chat.png differ diff --git a/design/screenshots/03-streaming.png b/design/screenshots/03-streaming.png new file mode 100644 index 0000000..ad1db92 Binary files /dev/null and b/design/screenshots/03-streaming.png differ diff --git a/design/screenshots/04-recording.png b/design/screenshots/04-recording.png new file mode 100644 index 0000000..bcf4b30 Binary files /dev/null and b/design/screenshots/04-recording.png differ diff --git a/design/screenshots/05-empty.png b/design/screenshots/05-empty.png new file mode 100644 index 0000000..83251fe Binary files /dev/null and b/design/screenshots/05-empty.png differ diff --git a/design/screenshots/06-agents.png b/design/screenshots/06-agents.png new file mode 100644 index 0000000..70a1f6f Binary files /dev/null and b/design/screenshots/06-agents.png differ diff --git a/design/screenshots/07-agent-color.png b/design/screenshots/07-agent-color.png new file mode 100644 index 0000000..39c7f86 Binary files /dev/null and b/design/screenshots/07-agent-color.png differ diff --git a/design/screenshots/08-agent-pic.png b/design/screenshots/08-agent-pic.png new file mode 100644 index 0000000..2fac379 Binary files /dev/null and b/design/screenshots/08-agent-pic.png differ diff --git a/design/screenshots/09-check-wait.png b/design/screenshots/09-check-wait.png new file mode 100644 index 0000000..de57eb6 Binary files /dev/null and b/design/screenshots/09-check-wait.png differ diff --git a/design/screenshots/10-check-ok.png b/design/screenshots/10-check-ok.png new file mode 100644 index 0000000..a394ad7 Binary files /dev/null and b/design/screenshots/10-check-ok.png differ diff --git a/design/screenshots/11-check-fail.png b/design/screenshots/11-check-fail.png new file mode 100644 index 0000000..9e9ca8c Binary files /dev/null and b/design/screenshots/11-check-fail.png differ diff --git a/design/screenshots/12-new-chat.png b/design/screenshots/12-new-chat.png new file mode 100644 index 0000000..56b5838 Binary files /dev/null and b/design/screenshots/12-new-chat.png differ diff --git a/design/screenshots/13-groups.png b/design/screenshots/13-groups.png new file mode 100644 index 0000000..435d511 Binary files /dev/null and b/design/screenshots/13-groups.png differ diff --git a/design/screenshots/14-group-edit.png b/design/screenshots/14-group-edit.png new file mode 100644 index 0000000..880c1b5 Binary files /dev/null and b/design/screenshots/14-group-edit.png differ diff --git a/design/screenshots/15-group-delete.png b/design/screenshots/15-group-delete.png new file mode 100644 index 0000000..ffa33da Binary files /dev/null and b/design/screenshots/15-group-delete.png differ