- Refactored cross-team message formatting to use a canonical XML-like tag structure for improved clarity and consistency. - Introduced new attributes for cross-team messages, including 'from', 'depth', 'conversationId', and 'replyToConversationId'. - Enhanced TeamProvisioningService to manage pending cross-team reply expectations, ensuring proper message handling and relay. - Updated UI components to reflect changes in cross-team messaging, including stripping metadata from displayed messages. - Added tests to validate new cross-team message handling and ensure proper functionality across services.
8.6 KiB
Findings: реверс порядка lead thoughts (свежие вверху)
Затронутые файлы
| Файл | Что менять |
|---|---|
LeadThoughtsGroup.tsx |
Основной компонент. Строка 471: chronologicalThoughts = [...thoughts].reverse() — убрать reverse (thoughts уже newest-first). Логика автоскрола внутри LeadThoughtsGroupRow (строки 570-658). |
ActivityTimeline.tsx |
Pinned thought group (строка 322). Порядок messages — newest-first (desc), thoughts группируются через groupTimelineItems(). |
collapseState.ts |
findNewestMessageIndex() ищет первый type: 'message' — при реверсе внутри группы не затронут. |
AnimatedHeightReveal.tsx |
Не требует изменений — анимирует высоту, а не направление. |
Архитектура текущего скрола thoughts
LeadThoughtsGroupRow имеет свой собственный скрол-контейнер (строки 780-807):
scrollRef— div сmaxHeight: 200px,overflowY: autocontentRef— внутренний div с thoughts- Автоскрол к низу (
queueScrollSync('bottom')) — потому что newest внизу isUserScrolledUpRef— трекает, отскроллил ли юзер вверхdistanceFromBottomRef— сохраняет позицию дляpreservemodehandleScroll(строка 652) — обновляетisUserScrolledUpRefчерезAUTO_SCROLL_THRESHOLD = 30pxhandleCollapse(строка 661) — при Show Less скроллит кscrollHeight(к низу)syncScrollableBody(строка 598) — оркестрирует: force bottom / preserve / noop
Рендеринг (строка 795): chronologicalThoughts.map() — oldest-first, newest в конце. Анимация shouldAnimate только для последнего (newest).
Что нужно изменить для реверса
- Убрать
.reverse()на строке 471 — thoughts уже newest-first, рендерить как есть - Перевернуть автоскрол: вместо scroll-to-bottom → scroll-to-top (
scrollTop = 0) - Анимация нового thought:
shouldAnimateдляidx === 0(вместоidx === chronologicalThoughts.length - 1) isUserScrolledUpRef→ переименовать вisUserScrolledDownRef, проверять расстояние от верха (scrollTop > threshold)queueScrollSync—mode: 'top'вместо'bottom'(scrollTop = 0), preserve mode:scrollTop = distanceFromTopRef.current- Show More кнопка — сейчас внизу (ChevronDown). При реверсе: newest вверху, кнопка "Show more" нужна внизу для загрузки старых thoughts — по сути остаётся на месте
- Show Less (
handleCollapse) — скроллит к верху (scrollTop = 0) вместоscrollHeight - Divider timestamps (строка 355-360):
showDividerприidx > 0— работает корректно и при реверсе
Edge Cases
-
Новый thought приходит во время скрола вниз к старым — если юзер прокрутил вниз (к старым), новый thought добавляется вверху. Нужно сохранить
scrollTopпозицию. Решение:preservemode отслеживаетdistanceFromTopвместоdistanceFromBottom. -
Первый thought в пустой группе — скролл не нужен (нет overflow), анимация fade-in для первого элемента.
-
Переход collapsed → expanded —
setExpanded(true)убирает maxHeight. При реверсе scrollTop=0 уже наверху, без скачков. Проблем нет. -
Expanded → collapsed (Show Less) — сейчас
handleCollapseскроллит к scrollHeight. При реверсе → скроллить к 0. ПлюсscrollIntoViewна контейнер — остаётся как есть. -
Real-time streaming — thoughts приходят через InboxMessage live updates. Каждый новый thought prepend-ится в начало массива
thoughts[](newest-first). При реверсе рендера: новый появляется вверху, анимируется LeadThoughtItem сshouldAnimate. Ключевой риск:AnimatedHeightRevealwrapper расширяется вниз (grid-template-rows: 0fr→1fr). При insert-е вверху контейнер сдвигает всё вниз → layout shift. Mitigation: CSSoverflow-anchor: noneуже стоит (строка 790). Но для items внутри scroll-контейнера нужноoverflow-anchor: autoна последнем видимом элементе. Это главный технический риск. -
getThoughtGroupKey(строка 67-70) — использует oldest thought для стабильного ключа. При реверсе rendering порядок меняется, но key остаётся тот же. Проблем нет. -
ResizeObserver (строка 632-637) — наблюдает за contentRef. При реверсе content растёт вверху → ResizeObserver сработает, вызовет syncScrollableBody. Нужно убедиться что
preservemode корректно считает offset от верха. -
isBodyVisibletoggle (collapse mode из ActivityTimeline) — скрывает/показывает body. При реверсе: после re-show нужно скроллить к top (не bottom). Затронуто useLayoutEffect на строке 625-638.
Аналоги в проекте для референса
DisplayItemList.tsx:107— используетflex-col-reverseдля newest-first. Простой подход, но не подходит для нашего случая (у нас свой scroll container с автоскролом).CliLogsRichView.tsx:376—[...entries].reverse()для newest-first порядка.
Предложенный план реализации
- В
LeadThoughtsGroupRow: убратьchronologicalThoughtsreverse, рендеритьthoughtsнапрямую (newest-first) - Перевернуть scroll-sync логику: auto-scroll к
scrollTop=0, preserve черезdistanceFromTop - Обновить
shouldAnimateдляidx === 0 - Обновить
handleCollapse→scrollTop = 0 - Show More/Show Less кнопки: Show More внизу (для старых thoughts) — без изменений. Show Less — без изменений.
- Тестировать layout shift при live streaming
Оценки
Сложность реализации: 5/10
- Основная логика сосредоточена в одном файле (LeadThoughtsGroup.tsx)
- Scroll-sync перевернуть — умеренно сложно (6 точек изменения: queueScrollSync, handleScroll, handleCollapse, syncScrollableBody, useLayoutEffect, shouldAnimate)
- Нет зависимости от внешнего useAutoScrollBottom — LeadThoughtsGroupRow имеет свой собственный scroll management
- Не нужно трогать groupTimelineItems() или ActivityTimeline
Уверенность в оценке: 8/10
- Код хорошо изолирован — весь scroll management внутри одного компонента
- Единственный серьёзный риск — layout shift при real-time prepend (edge case #5)
- CSS overflow-anchor может не работать идеально для insert-вверх в scroll container
Оценка рисков
| Риск | Вероятность | Последствие | Mitigation |
|---|---|---|---|
| Layout shift при live streaming | Средняя | Визуальные скачки | overflow-anchor + manual scrollTop adjustment |
| Broken Show More/Less | Низкая | UX деградация | Простой фикс scrollTop |
| Regression в collapsed mode | Низкая | Thoughts не видны | Не затрагивает collapse logic |
| Browser inconsistency overflow-anchor | Низкая | Скачки в Safari | Fallback: manual scroll compensation |