61 lines
7.4 KiB
Markdown
61 lines
7.4 KiB
Markdown
# Жесты и режимы очков (design, согласовано 2026-08-22)
|
||
|
||
> Согласовано с пользователем. Реализация — opencode по TASK (см. ниже).
|
||
> Это документ проекта: сначала думаем, потом хуячим, потом тесты, потом ОБЯЗАТЕЛЬНО мануальные тесты на железе.
|
||
|
||
## Ограничения системы (факты)
|
||
|
||
- Жестов всего шесть: Click, DoubleClick, TripleClick, SlideForward, SlideBackward, LongClick (+ SlideContinuous с delta).
|
||
- **LongClick НЕ работает** — перехвачен системным ассистентом RayNeo. Не рассчитывать, не использовать.
|
||
- **SlideContinuous существует** (SDK: `TempleAction.SlideContinuous(delta: Float, ...)` — подтверждено javap) — плавный скролл возможен, delta = дельту движения пальца. Частоту событий и амплитуду дельт проверить на железе.
|
||
- Экран очков — широкий 1280×480 (640×480 на глаз). Текста влезает мало → скролл ответа обязателен для длинных ответов.
|
||
- Оверлей общения затемняет кино (72% чёрного) → режим общения ЯВНЫЙ: пользователь видит, что он в чате, а не в кино. Это позволяет переназначать свайпы в режиме общения без конфликта.
|
||
- Нет клавиатуры — только жесты и голос. Выход из режима — только жестом.
|
||
- LLM думает 5-15с — нужен индикатор «думаю...», иначе экран замирает.
|
||
|
||
## Общий паттерн жестов (единая семантика через все режимы)
|
||
|
||
| Жест | Смысл везде |
|
||
|---|---|
|
||
| **TripleClick** | вызов локального ассистента (переход в режим общения) |
|
||
| **DoubleClick** | отмена / назад |
|
||
| **Click** | основное действие текущего контекста |
|
||
|
||
## Матрица жестов по состояниям (ЗАКРЫТА)
|
||
|
||
| Состояние | Click | DoubleClick | TripleClick |
|
||
|---|---|---|---|
|
||
| **Кино** | плей/пауза | игнор (назад некуда) | вызов ассистента (пауза + «слушаю...») |
|
||
| **Прослушивание** | отправить фразу | отмена, **остаёмся в диалоге** | игнор |
|
||
| **Ответ виден** | новая фраза (микрофон открывается) | **выход из режима** | игнор (уже в режиме) |
|
||
|
||
Ключевой принцип: **клик = «главное действие здесь»**. В кино — плей/пауза, в диалоге — сказать новую фразу, при прослушивании — отправить. Один жест, контекст решает. Двойной — всегда «не хочу/назад». Тройной — всегда «хочу говорить с ассистентом».
|
||
|
||
Конфликтов нет: подрежимы различимы по состоянию (listening = микрофон открыт / assistantText непуст = ответ виден).
|
||
|
||
## Решения по деталям
|
||
|
||
1. **Отмена диктовки (DoubleClick в прослушивании)** — просто отмена, остаёмся в диалоге (назад к «ответу виден», ничего не теряется).
|
||
2. **Фильм после выхода (DoubleClick при ответе)** — ВСЕГДА на паузе, независимо от того, был ли он на паузе до входа в режим ассистента.
|
||
3. **Скролл ответа** — плавный: SlideContinuous, отслеживать движение пальца (delta), а не факт свайпа с шагом N. Свайпы SlideForward/SlideBackward в режиме общения — тоже скролл (запасной вариант, если SlideContinuous окажется нестабильным на железе).
|
||
4. **Новая фраза в режиме «ответ виден»** — Click: микрофон открывается, «слушаю...». Старый ответ с экрана — стереть (история живёт в SQLite на телефоне, не теряется).
|
||
5. **Индикатор «думаю...»** — после отправки фразы (между прослушиванием и ответом) показывать «думаю...» вместо замирания на тексте фразы.
|
||
|
||
## Что делать (TASK для opencode)
|
||
|
||
В `app-glasses` (MainActivity.kt + оверлей):
|
||
|
||
1. **Переразметка жестов по матрице**:
|
||
- Кино: как сейчас (Click=TOGGLE, TripleClick=вызов, свайпы=±30с, DoubleClick=игнор)
|
||
- Прослушивание: как сейчас (Click=отправить, DoubleClick=отмена, TripleClick=игнор)
|
||
- Ответ виден (listening=false, assistantText непуст): **Click → новая фраза** (открыть микрофон, стереть assistantText со старта), **DoubleClick → выход из режима** (закрыть оверлей, фильм на паузе, assistantText стереть), **TripleClick → игнор**, свайпы/SlideContinuous → скролл
|
||
2. **Скролл ответа**: плавный через SlideContinuous (delta), scrollState для текста ответа; запасной вариант — дискретные свайпы. Ограничить скролл границами текста.
|
||
3. **Индикатор «думаю...»**: состояние между отправкой фразы и получением ShowText; показывать в оверлее.
|
||
4. **Выход**: DoubleClick при «ответ виден» — закрыть оверлей, фильм остаётся на паузе (не трогать play/pause при выходе, только при входе ставим паузу).
|
||
|
||
Тесты: юнит-тесты на чистую логику разметки жестов (переходы состояний, скролл-дельта → смещение), в духе GestureCommandsTest. Мануальные тесты — ОБЯЗАТЕЛЬНО на железе (142: очки 7687f645 + телефон AXGL024B05001337): полный цикл кино → тройной тап → фраза → «думаю...» → ответ → скролл → выход → фильм на паузе.
|
||
|
||
## Смежные договорённости (архитектура агента)
|
||
|
||
Полная архитектура скиллов/тулсетов — в `TASK-agent-tools.md`. Ключевое для очков: ассистент — локальный, на телефоне; очки — тонкий клиент (микрофон → STT → телефон → LLM → ShowText на очки). Режим общения — поверх любого режима (кино/чат), активируется тройным тапом из любого места.
|