THEMES.md: несколько тем, цвета только из схемы (не числом в коде)
This commit is contained in:
@@ -223,7 +223,55 @@ clipboardManager.setText(AnnotatedString(segment.code))
|
|||||||
`SelectionContainer` вокруг сообщения — иначе текст не выделить, а для агента это
|
`SelectionContainer` вокруг сообщения — иначе текст не выделить, а для агента это
|
||||||
нужно постоянно.
|
нужно постоянно.
|
||||||
|
|
||||||
### 3.10. Сборка стилей текста
|
### 3.10. Образец темы: ценный, но повторять НЕ всё
|
||||||
|
|
||||||
|
**Где:** `client-shared/src/main/kotlin/pw/binom/client/shared/ui/FolderTheme.kt`.
|
||||||
|
|
||||||
|
Там есть готовый образец, **очень близкий к тому, что нужно нам**:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
data class ClientTheme(
|
||||||
|
val name: String,
|
||||||
|
val colorScheme: ColorScheme,
|
||||||
|
val folderIconColor: Color,
|
||||||
|
val unreadBadgeColor: Color,
|
||||||
|
// ...
|
||||||
|
)
|
||||||
|
|
||||||
|
val DarkClientTheme = ClientTheme(
|
||||||
|
name = "dark",
|
||||||
|
colorScheme = darkColorScheme(
|
||||||
|
primary = Color(0xFF6AB2F2), onPrimary = Color.White,
|
||||||
|
surface = Color(0xFF1A1A2E), onSurface = Color(0xFFEBEBEB),
|
||||||
|
background = Color(0xFF121218), onBackground = Color(0xFFE0E0E0),
|
||||||
|
error = Color(0xFFCF6679),
|
||||||
|
// ...
|
||||||
|
),
|
||||||
|
// ...
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Что тут ценно — и почти совпадает с нашим решением:**
|
||||||
|
|
||||||
|
- тема **уже описана отдельным объектом**, а не размазана по экранам;
|
||||||
|
- в комментарии к коду прямо сказано: «Сейчас — одна тёмная тема; **в будущем —
|
||||||
|
переключатель тем (светлая/тёмная)**». То есть к тому же и шли;
|
||||||
|
- значения цветов объявлены **единым источником правды для всех платформ**
|
||||||
|
(десктоп и Android) — ровно наш случай, десктоп как образец для Android;
|
||||||
|
- кроме цветов схемы в теме есть **именованные значения для смыслов**
|
||||||
|
(`unreadBadgeColor`, `folderIconColor`) — то, к чему мы и хотим прийти.
|
||||||
|
|
||||||
|
**Что повторять НЕ надо:** в остальном коде assistent цвета всё равно вписаны
|
||||||
|
числами — **36 штук** (`Color(0xFF4CAF50)`, `Color(0xFFF131327)` и подобные) прямо
|
||||||
|
в экранах. То есть theme есть, а половина цветов мимо неё. Плюс `folderIconColor`
|
||||||
|
и подобные значения заданы как `Color(0xFF768C9E)` **внутри** объекта темы — это
|
||||||
|
правильное место, но при второй теме их придётся задавать заново руками.
|
||||||
|
|
||||||
|
**Наш вывод:** берём **идею** (тема = объект, имена по смыслу, единый источник для
|
||||||
|
платформ), но **делаем строже**: ни одного цвета числом вне самой схемы, все
|
||||||
|
именованные значения — в теме, и всё проверяется на второй теме. Подробно — `THEMES.md`.
|
||||||
|
|
||||||
|
### 3.11. Сборка стилей текста
|
||||||
|
|
||||||
**Где:** там же, `MarkdownSegment.Text.toAnnotatedString`, строки ~341–377.
|
**Где:** там же, `MarkdownSegment.Text.toAnnotatedString`, строки ~341–377.
|
||||||
|
|
||||||
@@ -267,6 +315,9 @@ clipboardManager.setText(AnnotatedString(segment.code))
|
|||||||
(`sketches/004-settings-agents`).
|
(`sketches/004-settings-agents`).
|
||||||
- **`ui/NewChatDialog.kt`** (616), **`ui/ParticipantsDialog.kt`** (223) — чужое.
|
- **`ui/NewChatDialog.kt`** (616), **`ui/ParticipantsDialog.kt`** (223) — чужое.
|
||||||
- **`ui/MessageBubble.kt`** (789) — пузырь сообщения; тянет `chat-common` и чужие модели.
|
- **`ui/MessageBubble.kt`** (789) — пузырь сообщения; тянет `chat-common` и чужие модели.
|
||||||
|
- **Цвета числами в экранах** — в assistent их 36 штук `Color(0xFF…)` прямо в
|
||||||
|
`ChatScreen` и соседних файлах. Тема там есть, но обходится мимо. Нам нужны
|
||||||
|
цвета строго из схемы, см. `THEMES.md`.
|
||||||
- **Модули `chat-*`** (`chat-common`, `chat-client`, `chat-cache`, `chat-adapter`,
|
- **Модули `chat-*`** (`chat-common`, `chat-client`, `chat-cache`, `chat-adapter`,
|
||||||
`chat-server-client`) — чужая система чатов. У нас связь с агентом идёт через
|
`chat-server-client`) — чужая система чатов. У нас связь с агентом идёт через
|
||||||
**`pw.binom.agentik:client`**, совсем другой протокол.
|
**`pw.binom.agentik:client`**, совсем другой протокол.
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ sketches/005-new-chat-picker/index.html # новый диалог: выбо
|
|||||||
(`mic-kmp`, `asr-kmp`, `vad-kmp`), вместо копирования кода из assistent.
|
(`mic-kmp`, `asr-kmp`, `vad-kmp`), вместо копирования кода из assistent.
|
||||||
`CACHE.md` — кэш сообщений на клиенте: что для него есть в библиотеке, схема
|
`CACHE.md` — кэш сообщений на клиенте: что для него есть в библиотеке, схема
|
||||||
загрузки «только новое», где может порваться.
|
загрузки «только новое», где может порваться.
|
||||||
|
`THEMES.md` — цветовые темы: цвета не пишем в коде, берём из схемы. Тем будет
|
||||||
|
несколько, поэтому ни одного цвета числом.
|
||||||
`MARKDOWN-SOURCE.md` — откуда брать готовую отрисовку Markdown (файлы и адреса).
|
`MARKDOWN-SOURCE.md` — откуда брать готовую отрисовку Markdown (файлы и адреса).
|
||||||
`REQUIREMENTS.md` — требования. Статус: накидываем, ни один пункт не обязателен
|
`REQUIREMENTS.md` — требования. Статус: накидываем, ни один пункт не обязателен
|
||||||
к исполнению в том виде, как записан.
|
к исполнению в том виде, как записан.
|
||||||
@@ -49,6 +51,9 @@ sketches/005-new-chat-picker/index.html # новый диалог: выбо
|
|||||||
источник идей.
|
источник идей.
|
||||||
- Стиль — тёмный, из уже принятой темы клиента assistent: фон `#121218`,
|
- Стиль — тёмный, из уже принятой темы клиента assistent: фон `#121218`,
|
||||||
панели `#17212B`, акцент `#6AB2F2`, текст `#EBEBEB`.
|
панели `#17212B`, акцент `#6AB2F2`, текст `#EBEBEB`.
|
||||||
|
- **Тем будет несколько — цвета берём из цветовой схемы, не пишем числом в коде.**
|
||||||
|
Имена по смыслу («фон», «панель», «акцент»), чтобы смена темы ничего не ломала.
|
||||||
|
В макетах числа — это нормально. Разбор и список того, что забывают: `THEMES.md`.
|
||||||
- **Узкое окно** — на экране что-то одно: либо список диалогов, либо чат.
|
- **Узкое окно** — на экране что-то одно: либо список диалогов, либо чат.
|
||||||
Переключение кнопкой «Назад», как в Телеграме. Широкое — обе части сразу.
|
Переключение кнопкой «Назад», как в Телеграме. Широкое — обе части сразу.
|
||||||
- **Подсказка о горячих клавишах** под полем ввода убрана.
|
- **Подсказка о горячих клавишах** под полем ввода убрана.
|
||||||
|
|||||||
+21
-1
@@ -12,7 +12,27 @@
|
|||||||
## 1. Общее
|
## 1. Общее
|
||||||
|
|
||||||
- **R1.** Стиль — тёмная тема, уже принятая в клиентах пользователя
|
- **R1.** Стиль — тёмная тема, уже принятая в клиентах пользователя
|
||||||
(фон `#121218`, панели `#17212B`, текст `#EBEBEB`).
|
(фон `#121218`, панели `#17212B`, текст `#EBEBEB`). **Это текущая тема, а не
|
||||||
|
единственная** — см. R1.1.
|
||||||
|
- **R1.1.** **Цвета берём из цветовой схемы, а не пишем в коде числом.**
|
||||||
|
Предусматриваем, что тем будет **несколько** (тёмная, светлая и другие).
|
||||||
|
В коде не должно быть цвета вида `#121218` — вместо этого берём цвет из
|
||||||
|
текущей схемы по смыслу: «фон», «панель», «текст», «акцент», «ошибка»,
|
||||||
|
«успех». Смена темы тогда ничего не ломает — меняется схема, код остаётся.
|
||||||
|
- **R1.2.** **Имена берём по смыслу, а не по виду.** Не «тёмно-серый» и не
|
||||||
|
«синий», а «фон», «панель», «акцент». Смысл не меняется от смены темы, а
|
||||||
|
название цвета — меняется.
|
||||||
|
- **R1.3.** В макетах (`sketches/`, `approved/`) цвета стоят числами — это
|
||||||
|
нормально, макет должен выглядеть как задумано. **На этом основан и способ
|
||||||
|
проверки темы:** имена в коде — из R1.1, числа — только в макетах.
|
||||||
|
- **R1.4.** Отдельно проверяем, что при смене темы **ничего не осталось
|
||||||
|
вписанным числом**: ни текст, ни фон, ни границы, ни тени, ни цвет
|
||||||
|
«наведения» курсора. Если цвет не из схемы — он не переключится и будет
|
||||||
|
выбиваться.
|
||||||
|
- **R1.5.** Особый случай — **цвета для различения ассистентов** (цвет агента из
|
||||||
|
настроек). Они **не из схемы**: это пользовательские цвета, они заданы
|
||||||
|
сознательно и в другой теме остаются собой. Их надо отличать от цветов темы,
|
||||||
|
чтобы не перепутать при смене.
|
||||||
- **R2.** Что видно на экране в любой момент: список диалогов, выбранный диалог
|
- **R2.** Что видно на экране в любой момент: список диалогов, выбранный диалог
|
||||||
и поле ввода. Больше ничего обязательного нет.
|
и поле ввода. Больше ничего обязательного нет.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
# Цветовые темы: цвета не пишем в коде
|
||||||
|
|
||||||
|
**Правило:** в коде **не должно быть цветов числами**. Цвет берётся из текущей
|
||||||
|
цветовой схемы по смыслу. Тем будет **несколько** (тёмная, светлая и другие),
|
||||||
|
и одна и та же деталь интерфейса должна выглядеть правильно в каждой.
|
||||||
|
|
||||||
|
**Почему так:** если цвет вписан числом, при смене темы он останется прежним —
|
||||||
|
и на светлом фоне окажется тёмно-серый текст, тёмная тень или невидимая граница.
|
||||||
|
Такие места обычно находят уже у пользователя.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Как это выглядит в коде
|
||||||
|
|
||||||
|
### Неправильно
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
Text(
|
||||||
|
text = "Диалоги",
|
||||||
|
color = Color(0xFFEBEBEB), // цвет вписан числом
|
||||||
|
modifier = Modifier.background(Color(0xFF17212B)) // и здесь тоже
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
При смене темы этот текст останется белым, а панель — тёмно-синей. В светлой
|
||||||
|
теме получится белое на белом.
|
||||||
|
|
||||||
|
### Правильно
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
Text(
|
||||||
|
text = "Диалоги",
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
modifier = Modifier.background(MaterialTheme.colorScheme.surface)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Здесь сказано **что это по смыслу**: текст «на поверхности» и сама «поверхность».
|
||||||
|
Какие именно цвета — решает схема, а не код.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Имена по смыслу, а не по виду
|
||||||
|
|
||||||
|
| Хорошо (по смыслу) | Плохо (по виду) |
|
||||||
|
|---|---|
|
||||||
|
| `surface` — поверхность | `darkBlue` — тёмно-синий |
|
||||||
|
| `onSurface` — текст на поверхности | `almostWhite` — почти белый |
|
||||||
|
| `primary` — главный акцент | `blue` — синий |
|
||||||
|
| `error` — ошибка | `red` — красный |
|
||||||
|
| `outline` — границы | `greyLine` — серая линия |
|
||||||
|
|
||||||
|
Смысл от смены темы не меняется, а название цвета — меняется. `darkBlue`
|
||||||
|
в светлой теме станет ложью в самом названии.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что именно берётся из схемы
|
||||||
|
|
||||||
|
Проверять надо **все** цвета, а не только текст с фоном. Чаще всего забывают:
|
||||||
|
|
||||||
|
- **границы** — `outline`, `outlineVariant`;
|
||||||
|
- **фон «наведения» курсора** и **выбранной строки**;
|
||||||
|
- **тени** вокруг окон;
|
||||||
|
- **затемнение** под окном (полупрозрачное чёрное — тоже цвет);
|
||||||
|
- **цвет неактивного текста** — подписи, время, пояснения;
|
||||||
|
- **цвет состояния** — «на связи» зелёным, «не отвечает» красным;
|
||||||
|
- **фон поля ввода** и его рамку при фокусе;
|
||||||
|
- **цвет значков** и кружка непрочитанных;
|
||||||
|
- **фон блока кода** и **цвет ссылок** в ответах ассистента;
|
||||||
|
- **полосы прокрутки**;
|
||||||
|
- **выделение текста мышью**.
|
||||||
|
|
||||||
|
Если деталь где-то не переключилась — она выбивается и её видно сразу.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Особый случай: цвета для различения ассистентов
|
||||||
|
|
||||||
|
**Эти цвета из схемы не берутся.** Пользователь задаёт их сам в настройках, чтобы
|
||||||
|
отличать ассистентов друг от друга. Они заданы сознательно и в другой теме
|
||||||
|
остаются собой — иначе различие пропадёт.
|
||||||
|
|
||||||
|
То есть в клиенте **два разных типа цвета**, и путать их нельзя:
|
||||||
|
|
||||||
|
| Тип | Откуда | Меняется ли с темой |
|
||||||
|
|---|---|---|
|
||||||
|
| Цвета интерфейса | Из цветовой схемы | **Да** |
|
||||||
|
| Цвет или картинка ассистента | Заданы пользователем | **Нет** |
|
||||||
|
|
||||||
|
**Практический вывод:** там, где подставляется цвет ассистента, нельзя брать
|
||||||
|
`MaterialTheme.colorScheme.*` — нужен именно пользовательский цвет. И наоборот:
|
||||||
|
фон строки диалога берётся из схемы, даже если рядом стоит цветная полоска агента.
|
||||||
|
|
||||||
|
**Ещё следствие:** пользовательский цвет должен **читаться** на фоне любой темы.
|
||||||
|
Пять предложенных цветов подобраны под тёмную тему; в светлой какие-то могут
|
||||||
|
оказаться бледными. Это надо проверить отдельно — либо ограничить набор
|
||||||
|
безопасными цветами, либо подстраивать яркость под тему.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что писать в макетах
|
||||||
|
|
||||||
|
В `sketches/` и `approved/` цвета стоят числами — **так и надо**, иначе макет
|
||||||
|
не будет выглядеть задуманным. Это не код.
|
||||||
|
|
||||||
|
Правило простое:
|
||||||
|
- **макеты** — числа, чтобы было видно замысел;
|
||||||
|
- **код** — имена из схемы, чтобы переключалось.
|
||||||
|
|
||||||
|
Проверка при реализации: **поиск по коду на цвета числами и на «#»**. Что нашлось —
|
||||||
|
то и надо заменить на схему. Исключение — цвета ассистентов из настроек
|
||||||
|
(см. выше) и, возможно, значения внутри самой схемы, где цвета как раз и задаются.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Готовый образец у нас уже есть
|
||||||
|
|
||||||
|
В `ai/assistent`, файл `client-shared/.../ui/FolderTheme.kt` — тема описана
|
||||||
|
отдельным объектом, с именем, и в комментарии сказано: «Сейчас — одна тёмная тема;
|
||||||
|
в будущем — переключатель тем (светлая/тёмная)». Идею **берём**, детали —
|
||||||
|
|
||||||
|
**Чего в образце нет:** он соблюдается не везде. В том же клиенте **36 цветов
|
||||||
|
вписаны числами** прямо в экранах (`Color(0xFF4CAF50)` и подобные). Тема есть,
|
||||||
|
а половина цветов идёт мимо неё. Поэтому наш подход строже:
|
||||||
|
|
||||||
|
- **ни одного цвета числом вне самой схемы** — все цвета живут в одном месте;
|
||||||
|
- **именованные значения для смыслов** — тоже в теме, а не в экране;
|
||||||
|
- **проверяем на второй теме** — пока не переключили тему и не посмотрели, считать
|
||||||
|
готовым нельзя.
|
||||||
|
|
||||||
|
Разбор — `BORROW-FROM-ASSISTENT.md`, п. 3.10.
|
||||||
|
|
||||||
|
## Что осталось решить
|
||||||
|
|
||||||
|
- **Какие темы будут кроме тёмной.** Пока известно только: тёмная есть, светлая
|
||||||
|
предполагается.
|
||||||
|
- **Переключается ли тема в клиенте или берётся системная** (как в системе —
|
||||||
|
тёмная/светлая). От этого зависит, нужен ли в настройках отдельный переключатель.
|
||||||
|
- **Что делать с цветами ассистентов в светлой теме** (см. выше) — проверять
|
||||||
|
читаемость или ограничить набор.
|
||||||
@@ -36,6 +36,12 @@
|
|||||||
попала в макет случайно, это была заметка для проверки, а не часть интерфейса.
|
попала в макет случайно, это была заметка для проверки, а не часть интерфейса.
|
||||||
В одобренном файле её нет и быть не должно.
|
В одобренном файле её нет и быть не должно.
|
||||||
|
|
||||||
|
## Про цвета в этом файле
|
||||||
|
|
||||||
|
Цвета в макете стоят числами — так и надо, иначе макет не будет выглядеть
|
||||||
|
задуманным. **В коде так нельзя:** цвет берётся из цветовой схемы по смыслу,
|
||||||
|
потому что тем будет несколько. Разбор — `THEMES.md`.
|
||||||
|
|
||||||
## Как смотреть
|
## Как смотреть
|
||||||
|
|
||||||
Открыть файл в браузере — самодостаточный, без сборки. Кликом по карточке
|
Открыть файл в браузере — самодостаточный, без сборки. Кликом по карточке
|
||||||
|
|||||||
Reference in New Issue
Block a user