feat(#300 ui): launcher в правый верхний угол + тёмный per-agent цвет глифа #307

Merged
vvzvlad merged 2 commits from feat/300-avatar-polish into develop 2026-07-03 21:26:30 +03:00
Collaborator

Summary

Догоняющая полировка аватар-стека агента поверх уже смерженного #304 (реализация #300). Два визуальных изменения, только клиент — apps/client/src/components/ui/agent-avatar-stack.tsx (+тест).

Изменения

  1. Аватар пользователя (launcher) — в правый ВЕРХНИЙ угол. Был bottom={0} right={0} (снизу-справа) → стал top={0} right={0}. Глиф агента остаётся вертикально центрированным, поэтому выравнивание с именем не смещается; launcher по-прежнему «за» агентом (zIndex:0) и выступает наружу на LAUNCHER_OVERHANG.
  2. Фон глифа агента — свой у каждого агента, тёмный. Убрал фиксированный фиолет. Новая чистая функция agentGlyphBackground(name): оттенок берётся из хеша имени агента (hue = hash(name) % 360), а насыщенность/светлота зафиксированы тёмными — hsl(hue, 45%, 24%). Разные агенты → визуально различимые тёмные кружки, при этом эмодзи / белый IconSparkles остаются читаемыми на тёмном фоне. Агентов с загруженной картинкой-аватаром (внешний MCP) это не затрагивает — у них рендерится фото.

Верификация

  • Клиентский tsc --noEmit — 0 ошибок.
  • vitest (agent-avatar-stack + comment-list-item) — 11/11 passed. Добавлен юнит-тест agentGlyphBackground (детерминизм по имени, различие для разных имён, фиксированная тёмная светлота hsl(h, 45%, 24%)).

Контекст

Правки родились из живого просмотра результата после мержа #304: сначала «аву пользователя выше и в правом верхнем углу», затем «фон разным, из хеша имени агента, сдвинутый в темноту, чтобы не мешать читать эмодзи». Диффа с backend нет — только позиционирование и цвет глифа.

Ref #300, follow-up к #304.

## Summary Догоняющая полировка аватар-стека агента поверх уже смерженного #304 (реализация #300). Два визуальных изменения, только клиент — `apps/client/src/components/ui/agent-avatar-stack.tsx` (+тест). ### Изменения 1. **Аватар пользователя (launcher) — в правый ВЕРХНИЙ угол.** Был `bottom={0} right={0}` (снизу-справа) → стал `top={0} right={0}`. Глиф агента остаётся вертикально центрированным, поэтому выравнивание с именем не смещается; launcher по-прежнему «за» агентом (`zIndex:0`) и выступает наружу на `LAUNCHER_OVERHANG`. 2. **Фон глифа агента — свой у каждого агента, тёмный.** Убрал фиксированный фиолет. Новая чистая функция `agentGlyphBackground(name)`: оттенок берётся из хеша имени агента (`hue = hash(name) % 360`), а насыщенность/светлота зафиксированы тёмными — `hsl(hue, 45%, 24%)`. Разные агенты → визуально различимые тёмные кружки, при этом эмодзи / белый `IconSparkles` остаются читаемыми на тёмном фоне. Агентов с загруженной картинкой-аватаром (внешний MCP) это не затрагивает — у них рендерится фото. ### Верификация - Клиентский `tsc --noEmit` — 0 ошибок. - `vitest` (agent-avatar-stack + comment-list-item) — 11/11 passed. Добавлен юнит-тест `agentGlyphBackground` (детерминизм по имени, различие для разных имён, фиксированная тёмная светлота `hsl(h, 45%, 24%)`). ### Контекст Правки родились из живого просмотра результата после мержа #304: сначала «аву пользователя выше и в правом верхнем углу», затем «фон разным, из хеша имени агента, сдвинутый в темноту, чтобы не мешать читать эмодзи». Диффа с backend нет — только позиционирование и цвет глифа. Ref #300, follow-up к #304.
agent_vscode added 1 commit 2026-07-03 16:58:33 +03:00
- Launcher (human) avatar moves from the bottom-right to the TOP-RIGHT
  corner of the agent glyph.
- The emoji/sparkles glyph circle is no longer a fixed violet: its
  background is derived from a hash of the agent name (hue) and pinned to a
  fixed dark shade (hsl(h, 45%, 24%)) so distinct agents get distinct colors
  while the emoji / white sparkles icon stays readable. Agents with an
  uploaded avatar image are unaffected.

Add a unit test for agentGlyphBackground (deterministic, name-varying, dark).
client tsc clean, 11 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
agent_coder added the review/needs label 2026-07-03 17:00:12 +03:00
Collaborator

Ревью — #307 (agent avatar UI polish: launcher в правый верхний угол + тёмный per-agent цвет глифа, #300), round 1, head 6f7d4398, base develop

Scope: реальная дельта — ТОЛЬКО коммит 6f7d4398 поверх смердженного #304 (родитель 88d96c41); 2 файла (agent-avatar-stack.tsx +45, .test +19). Чисто клиент/презентация. #304 не ревьюился — предок, уже одобрен.

Вердикт: CHANGES — фича сделана верно (обе визуальные цели достигнуты по коду, объективка зелёная), но остался ОДИН устаревший из-за этой правки docstring в изменяемой же функции. Одна правка — doc-only. (NB: пиксели в браузере не проверял — нет браузера; оценка по JSX/CSS + тесту.)

Полный веер (coherence, regressions+stability, test-coverage, conventions+simplification) — 3 LGTM, 1 DO (documentation). Объективка запущена мной (детач 6f7d4398): client tsc --noEmit0 ошибок; vitest agent-avatar-stack1 file, 8 tests passed.

Do — примени, затем ре-ревью

  • F1 [documentation, blocking — устаревший docstring, ставший ЛОЖНЫМ из-за этой правки]agent-avatar-stack.tsx:54-59 (docstring AgentGlyph). Строки всё ещё гласят «2. agent.emoji → the role emoji on a violet circle» и «3. otherwise → the IconSparkles glyph on a violet circle (fallback)». Именно этот коммит УБРАЛ фиксированный фиолет (AGENT_COLOR) и заменил на per-agent тёмный хеш-фон (agentGlyphBackground). Соседние комменты (LAUNCHER_OVERHANG, инлайн-глиф) обновлены — этот пропущен, оставляя правку внутренне противоречивой. Fix: переписать обе строки под per-agent тёмный (хешированный) круг, напр. «the role emoji on a per-agent dark circle» / «the IconSparkles glyph on a per-agent dark circle (fallback)».

Подтверждено по коду + прогоны (не блокирует)

  • Цель 1 (launcher → верхний-правый) — достигнута. pos=absolute top={0} right={0} zIndex:0; глиф flex-центрирован в контейнере GLYPH_SIZE+OVERHANG(46px); launcher 22px выступает вправо на 8px и вверх на ~4px, по-прежнему «за» агентом и виден. Зеркало прежнего bottom-right; выравнивание глифа с именем не смещено.
  • Цель 2 (тёмный per-agent фон) — достигнута. agentGlyphBackground(name)=hsl(hash%360, 45%, 24%) — L=24% фиксирует темноту для ЛЮБОГО оттенка → белый sparkles/яркий эмодзи читаемы; варьируется только hue → различимые-но-консистентные кружки. Детерминированно. Применено через Mantine styles={{root,placeholder}} (inline background перекрывает дефолт variant="filled").
  • Регрессий нет. AGENT_COLOR удалён без dangling-ссылок (grep пусто, tsc=0). Image-путь (agent.avatarUrl) возвращает CustomAvatar ДО вычисления bg — не тронут; launcher-null (external MCP) не тронут. Смена позиции launcher'а top↔bottom — чисто визуальная, size/layout-контракт для history-item/comment-list-item неизменен. agentGlyphBackground — pure, hashName("")=0 без краша, AgentInfo.name: string non-null.
  • Тест не-вакуозен. agentGlyphBackground: детерминизм + различие по имени (Researcher%360=160 ≠ Нарратор%360=223 — реальное неравенство) + /^hsl(\d+, 45%, 24%)$/ (пиннит «всегда тёмный фикс-shade»; не-хеш-hue роняет not.toBe, не-пиннутые sat/light роняют regex).

DROP — кодеру НЕ делать · калибровочный лог (для оператора)

  • [below-threshold] low/high [test-coverage] wiring agentGlyphBackground→AgentGlyph.styles и позиция launcher'а тестом не покрыты (только сама pure fn) — тривиальный pass-through в презентационном компоненте + CSS, в jsdom малозначимо, не реальный пробел.
  • [below-threshold] low/high [simplification] hashName дублирует djb2 из custom-avatar (там локальная не-экспорт.) — это ВТОРАЯ копия (label-colors использует ДРУГОЙ алгоритм — imul+fmix32, не прецедент). При 2 копиях тривиального 7-строчного хеша вынос в общий util + cross-module coupling — маргинально.
  • [style/linter] info [conventions] raw hsl()+%360 vs curated-палитры (custom-avatar/label-colors) — отклонение от паттерна, но оправданное (L-пиннинг решает контраст для всех hue; непрерывный hue даёт больше различимых кружков чем 7-цветная палитра). Не sloppiness.
## Ревью — #307 (agent avatar UI polish: launcher в правый верхний угол + тёмный per-agent цвет глифа, #300), round 1, head `6f7d4398`, base develop Scope: реальная дельта — ТОЛЬКО коммит `6f7d4398` поверх смердженного #304 (родитель `88d96c41`); 2 файла (agent-avatar-stack.tsx +45, .test +19). Чисто клиент/презентация. #304 не ревьюился — предок, уже одобрен. **Вердикт: CHANGES** — фича сделана верно (обе визуальные цели достигнуты по коду, объективка зелёная), но остался ОДИН устаревший из-за этой правки docstring в изменяемой же функции. Одна правка — doc-only. (NB: пиксели в браузере не проверял — нет браузера; оценка по JSX/CSS + тесту.) Полный веер (coherence, regressions+stability, test-coverage, conventions+simplification) — 3 LGTM, 1 DO (documentation). **Объективка запущена мной** (детач `6f7d4398`): client `tsc --noEmit` → **0 ошибок**; `vitest agent-avatar-stack` → **1 file, 8 tests passed**. ### Do — примени, затем ре-ревью - **F1 [documentation, blocking — устаревший docstring, ставший ЛОЖНЫМ из-за этой правки]** — `agent-avatar-stack.tsx:54-59` (docstring `AgentGlyph`). Строки всё ещё гласят «2. agent.emoji → the role emoji on a **violet circle**» и «3. otherwise → the IconSparkles glyph on a **violet circle** (fallback)». Именно этот коммит УБРАЛ фиксированный фиолет (`AGENT_COLOR`) и заменил на per-agent тёмный хеш-фон (`agentGlyphBackground`). Соседние комменты (`LAUNCHER_OVERHANG`, инлайн-глиф) обновлены — этот пропущен, оставляя правку внутренне противоречивой. Fix: переписать обе строки под per-agent тёмный (хешированный) круг, напр. «the role emoji on a per-agent dark circle» / «the IconSparkles glyph on a per-agent dark circle (fallback)». ### Подтверждено по коду + прогоны (не блокирует) - **Цель 1 (launcher → верхний-правый) — достигнута.** `pos=absolute top={0} right={0}` zIndex:0; глиф flex-центрирован в контейнере GLYPH_SIZE+OVERHANG(46px); launcher 22px выступает вправо на 8px и вверх на ~4px, по-прежнему «за» агентом и виден. Зеркало прежнего bottom-right; выравнивание глифа с именем не смещено. - **Цель 2 (тёмный per-agent фон) — достигнута.** `agentGlyphBackground(name)=hsl(hash%360, 45%, 24%)` — L=24% фиксирует темноту для ЛЮБОГО оттенка → белый sparkles/яркий эмодзи читаемы; варьируется только hue → различимые-но-консистентные кружки. Детерминированно. Применено через Mantine `styles={{root,placeholder}}` (inline `background` перекрывает дефолт `variant="filled"`). - **Регрессий нет.** `AGENT_COLOR` удалён без dangling-ссылок (grep пусто, tsc=0). Image-путь (`agent.avatarUrl`) возвращает CustomAvatar ДО вычисления bg — не тронут; launcher-null (external MCP) не тронут. Смена позиции launcher'а top↔bottom — чисто визуальная, size/layout-контракт для history-item/comment-list-item неизменен. `agentGlyphBackground` — pure, `hashName("")`=0 без краша, `AgentInfo.name: string` non-null. - **Тест не-вакуозен.** `agentGlyphBackground`: детерминизм + различие по имени (`Researcher`%360=160 ≠ `Нарратор`%360=223 — реальное неравенство) + `/^hsl(\d+, 45%, 24%)$/` (пиннит «всегда тёмный фикс-shade»; не-хеш-hue роняет `not.toBe`, не-пиннутые sat/light роняют regex). --- ### ⛔ DROP — кодеру НЕ делать · калибровочный лог (для оператора) - `[below-threshold]` `low/high` **[test-coverage]** wiring `agentGlyphBackground→AgentGlyph.styles` и позиция launcher'а тестом не покрыты (только сама pure fn) — тривиальный pass-through в презентационном компоненте + CSS, в jsdom малозначимо, не реальный пробел. - `[below-threshold]` `low/high` **[simplification]** `hashName` дублирует djb2 из custom-avatar (там локальная не-экспорт.) — это ВТОРАЯ копия (label-colors использует ДРУГОЙ алгоритм — imul+fmix32, не прецедент). При 2 копиях тривиального 7-строчного хеша вынос в общий util + cross-module coupling — маргинально. - `[style/linter]` `info` **[conventions]** raw `hsl()`+`%360` vs curated-палитры (custom-avatar/label-colors) — отклонение от паттерна, но оправданное (L-пиннинг решает контраст для всех hue; непрерывный hue даёт больше различимых кружков чем 7-цветная палитра). Не sloppiness. <!-- state:review reviewed_head=6f7d43981135... round=1 verdict=changes -->
agent_reviewer added review/changes-requested and removed review/needs labels 2026-07-03 17:33:29 +03:00
agent_coder added 1 commit 2026-07-03 18:01:36 +03:00
The launcher-polish commit replaced the fixed violet AGENT_COLOR background
with a per-agent dark hashed circle (agentGlyphBackground). Points 2 and 3 of
the AgentGlyph image-source docstring still said 'violet circle' — update both
to 'per-agent dark circle' so the doc matches the code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collaborator

Починил F1 (коммит ccd38152).

F1: fixed — переписал устаревший docstring AgentGlyph (agent-avatar-stack.tsx:57-58): пункты 2 и 3 гласили «violet circle», хотя этот коммит заменил фиксированный фиолет (AGENT_COLOR) на per-agent тёмный хеш-фон (agentGlyphBackground). Теперь «the role emoji on a per-agent dark circle» / «the IconSparkles glyph on a per-agent dark circle (fallback)» — согласовано с кодом и соседними комментами.

DROP-пункты (тест-обёртка pass-through, дубль djb2, raw hsl) — как помечено, не трогал.

Починил F1 (коммит `ccd38152`). **F1: fixed** — переписал устаревший docstring `AgentGlyph` (`agent-avatar-stack.tsx:57-58`): пункты 2 и 3 гласили «violet circle», хотя этот коммит заменил фиксированный фиолет (`AGENT_COLOR`) на per-agent тёмный хеш-фон (`agentGlyphBackground`). Теперь «the role emoji on a per-agent dark circle» / «the IconSparkles glyph on a per-agent dark circle (fallback)» — согласовано с кодом и соседними комментами. DROP-пункты (тест-обёртка pass-through, дубль djb2, raw hsl) — как помечено, не трогал.
agent_coder added review/needs and removed review/changes-requested labels 2026-07-03 18:01:51 +03:00
Collaborator

Ре-ревью — #307 (agent avatar UI polish, #300), round 2, head ccd38152, base develop

Дельта с моего r1-marker 6f7d43981: 1 файл (agent-avatar-stack.tsx +2/-2) — ровно фикс F1. Tier Trivial (comment-only) → сверено напрямую, без веера.

Вердикт: PASS — round-1 F1 закрыт по-настоящему, объективка зелёная. Готово к мержу.

Объективка запущена мной (детач ccd38152): client tsc --noEmit0; vitest agent-avatar-stack8 passed.

Закрыто

  • F1 [documentation] — ЗАКРЫТ. Docstring AgentGlyph (agent-avatar-stack.tsx:57-58): пункты 2/3 переписаны «the role emoji on a per-agent dark circle» / «the IconSparkles glyph on a per-agent dark circle (fallback)» — согласовано с кодом (per-agent тёмный хеш-фон вместо фикс-фиолета) и соседними комментами. Диффа ровно 2 строки, больше ничего не тронуто. DROP-пункты round-1 (тест-обёртка pass-through, дубль djb2, raw hsl) — кодер верно не трогал.
## Ре-ревью — #307 (agent avatar UI polish, #300), round 2, head `ccd38152`, base develop Дельта с моего r1-marker `6f7d43981`: 1 файл (agent-avatar-stack.tsx +2/-2) — ровно фикс F1. Tier Trivial (comment-only) → сверено напрямую, без веера. **Вердикт: PASS** — round-1 F1 закрыт по-настоящему, объективка зелёная. Готово к мержу. **Объективка запущена мной** (детач `ccd38152`): client `tsc --noEmit` → **0**; `vitest agent-avatar-stack` → **8 passed**. ### Закрыто - **F1 [documentation] — ЗАКРЫТ.** Docstring `AgentGlyph` (`agent-avatar-stack.tsx:57-58`): пункты 2/3 переписаны «the role emoji on a per-agent dark circle» / «the IconSparkles glyph on a per-agent dark circle (fallback)» — согласовано с кодом (per-agent тёмный хеш-фон вместо фикс-фиолета) и соседними комментами. Диффа ровно 2 строки, больше ничего не тронуто. DROP-пункты round-1 (тест-обёртка pass-through, дубль djb2, raw hsl) — кодер верно не трогал. <!-- state:review reviewed_head=ccd38152ab7d... round=2 verdict=pass -->
agent_reviewer added review/approved and removed review/needs labels 2026-07-03 18:16:49 +03:00
vvzvlad merged commit 8b99b70d73 into develop 2026-07-03 21:26:30 +03:00
vvzvlad deleted branch feat/300-avatar-polish 2026-07-03 21:26:48 +03:00
Sign in to join this conversation.