Чат ИИ-агента: закрепление в боковом меню (dock) #276
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Задача
Дать возможность перетащить плавающее окно чата ИИ‑агента на боковое меню (навбар с деревом страниц). При отпускании над меню окно закрепляется, занимая область навбара и перекрывая дерево. При закрытии чата дерево снова показывается. Открепление — кнопкой в шапке окна и вытягиванием мышью обратно на контент.
Поведение (acceptance criteria)
Как устроено сейчас
apps/client/src/features/ai-chat/components/ai-chat-window.tsx, рендерится один раз вglobal-app-shell.tsx. Оноposition: fixed, геометрия хранится вaiChatWindowGeomAtom(localStorage), z-index = 105.ChatThreadсо стримом (useChat+AbortController). Тело не размонтируется даже при сворачивании → закрепление нельзя делать переносом компонента внутрь навбара (это вызовет remount и обрыв стрима). Режим dock обязан быть переключением CSS/геометрии на том же инстансе.AppShell.Navbar. Z-index навбара = 101 (Mantine база 100 +1), шапки = 100 → окно (105) корректно перекроет дерево и не заденет шапку.План реализации
Добавить персистентный флаг
docked. Тот же инстанс окна в режиме dock переключается со стиля «плавающее по geom» на «прибитое к живомуgetBoundingClientRect()навбара» (по стабильномуid), с синхронизацией черезResizeObserver+resize.Файлы:
ai-chat/atoms/ai-chat-atom.ts— новыйaiChatWindowDockedAtom(atomWithStorage<boolean>, defaultfalse).layouts/global/hooks/atoms/sidebar-atom.ts— экспорт константыAPP_NAVBAR_ID = "app-shell-navbar"(кладём сюда во избежание import‑цикла shell ↔ chat window).layouts/global/global-app-shell.tsx— повеситьid={APP_NAVBAR_ID}на<AppShell.Navbar>.ai-chat/components/ai-chat-window.tsx— основная логика:docked/dockRect/dockHint, refsdockedRef/geomRef;getNavbarRect()/isPointerOverNavbar();useLayoutEffectсинхронизацииdockRect(before paint,ResizeObserverна навбаре +window resize, повтор поlocation.pathname);.docked), fallback на плавающий вид если навбар недоступен;startDrag: решение dock/undock наmouseup(над навбаром → dock; для закреплённого — отпускание вне навбара → undock у точки броса черезclampGeom);IconLayoutSidebarLeftCollapse/IconLayoutSidebarLeftExpand), скрытие «Свернуть» в режиме dock;dockHint;ResizeObserver‑персист размеров.ai-chat/components/ai-chat-window.module.css— классы.docked(без border-radius/тени/resize, снятые min/max) и.dockHighlight(z-index 106, dashed border, полупрозрачная заливка).public/locales/en-USиru-RU— ключи"Dock to sidebar"/"Undock".Крайние случаи
getNavbarRect()возвращаетnull, окно откатывается в плавающий вид (не «исчезает»).ResizeObserver+ повторный запуск эффекта поlocation.pathname..dockedснимаемmin/max, размеры целиком из inline‑стиля отdockRect.Вне рамок
Логика стрима/сессии (
useChatSession,ChatThread, адаптация id чата),conversation-list, серверная часть — не трогаем. Только фронт.