From 6f83766048128b17effb43bc54270197ff8dcc7c Mon Sep 17 00:00:00 2001 From: Porfiry Date: Sat, 19 Sep 2026 13:49:49 +0300 Subject: [PATCH] =?UTF-8?q?THEMES.md:=20=D0=BD=D0=B5=D1=81=D0=BA=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D0=BA=D0=BE=20=D1=82=D0=B5=D0=BC,=20=D1=86=D0=B2?= =?UTF-8?q?=D0=B5=D1=82=D0=B0=20=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20?= =?UTF-8?q?=D0=B8=D0=B7=20=D1=81=D1=85=D0=B5=D0=BC=D1=8B=20(=D0=BD=D0=B5?= =?UTF-8?q?=20=D1=87=D0=B8=D1=81=D0=BB=D0=BE=D0=BC=20=D0=B2=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BORROW-FROM-ASSISTENT.md | 53 ++++++++++++++- README.md | 5 ++ REQUIREMENTS.md | 22 +++++- THEMES.md | 141 +++++++++++++++++++++++++++++++++++++++ approved/README.md | 6 ++ 5 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 THEMES.md diff --git a/BORROW-FROM-ASSISTENT.md b/BORROW-FROM-ASSISTENT.md index e9fa175..1212429 100644 --- a/BORROW-FROM-ASSISTENT.md +++ b/BORROW-FROM-ASSISTENT.md @@ -223,7 +223,55 @@ clipboardManager.setText(AnnotatedString(segment.code)) `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. @@ -267,6 +315,9 @@ clipboardManager.setText(AnnotatedString(segment.code)) (`sketches/004-settings-agents`). - **`ui/NewChatDialog.kt`** (616), **`ui/ParticipantsDialog.kt`** (223) — чужое. - **`ui/MessageBubble.kt`** (789) — пузырь сообщения; тянет `chat-common` и чужие модели. +- **Цвета числами в экранах** — в assistent их 36 штук `Color(0xFF…)` прямо в + `ChatScreen` и соседних файлах. Тема там есть, но обходится мимо. Нам нужны + цвета строго из схемы, см. `THEMES.md`. - **Модули `chat-*`** (`chat-common`, `chat-client`, `chat-cache`, `chat-adapter`, `chat-server-client`) — чужая система чатов. У нас связь с агентом идёт через **`pw.binom.agentik:client`**, совсем другой протокол. diff --git a/README.md b/README.md index bd9688f..56ff69c 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ sketches/005-new-chat-picker/index.html # новый диалог: выбо (`mic-kmp`, `asr-kmp`, `vad-kmp`), вместо копирования кода из assistent. `CACHE.md` — кэш сообщений на клиенте: что для него есть в библиотеке, схема загрузки «только новое», где может порваться. +`THEMES.md` — цветовые темы: цвета не пишем в коде, берём из схемы. Тем будет +несколько, поэтому ни одного цвета числом. `MARKDOWN-SOURCE.md` — откуда брать готовую отрисовку Markdown (файлы и адреса). `REQUIREMENTS.md` — требования. Статус: накидываем, ни один пункт не обязателен к исполнению в том виде, как записан. @@ -49,6 +51,9 @@ sketches/005-new-chat-picker/index.html # новый диалог: выбо источник идей. - Стиль — тёмный, из уже принятой темы клиента assistent: фон `#121218`, панели `#17212B`, акцент `#6AB2F2`, текст `#EBEBEB`. +- **Тем будет несколько — цвета берём из цветовой схемы, не пишем числом в коде.** + Имена по смыслу («фон», «панель», «акцент»), чтобы смена темы ничего не ломала. + В макетах числа — это нормально. Разбор и список того, что забывают: `THEMES.md`. - **Узкое окно** — на экране что-то одно: либо список диалогов, либо чат. Переключение кнопкой «Назад», как в Телеграме. Широкое — обе части сразу. - **Подсказка о горячих клавишах** под полем ввода убрана. diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md index b68fcfb..51f7565 100644 --- a/REQUIREMENTS.md +++ b/REQUIREMENTS.md @@ -12,7 +12,27 @@ ## 1. Общее - **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.** Что видно на экране в любой момент: список диалогов, выбранный диалог и поле ввода. Больше ничего обязательного нет. diff --git a/THEMES.md b/THEMES.md new file mode 100644 index 0000000..5bc88d3 --- /dev/null +++ b/THEMES.md @@ -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. + +## Что осталось решить + +- **Какие темы будут кроме тёмной.** Пока известно только: тёмная есть, светлая + предполагается. +- **Переключается ли тема в клиенте или берётся системная** (как в системе — + тёмная/светлая). От этого зависит, нужен ли в настройках отдельный переключатель. +- **Что делать с цветами ассистентов в светлой теме** (см. выше) — проверять + читаемость или ограничить набор. diff --git a/approved/README.md b/approved/README.md index 4096832..669de19 100644 --- a/approved/README.md +++ b/approved/README.md @@ -36,6 +36,12 @@ попала в макет случайно, это была заметка для проверки, а не часть интерфейса. В одобренном файле её нет и быть не должно. +## Про цвета в этом файле + +Цвета в макете стоят числами — так и надо, иначе макет не будет выглядеть +задуманным. **В коде так нельзя:** цвет берётся из цветовой схемы по смыслу, +потому что тем будет несколько. Разбор — `THEMES.md`. + ## Как смотреть Открыть файл в браузере — самодостаточный, без сборки. Кликом по карточке