From c91e5bb6bcfb96ffb24d806f6d7133a33808ad8c Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 02:30:50 +0000 Subject: [PATCH 1/3] feat: integrate useAutoScroll hook with ReportEditor for streaming content - Add useAutoScroll hook to ReportEditor component - Configure auto-scroll to be enabled only when isStreaming is true - Add ref forwarding to EditorContainer for scroll container access - Hook observes DOM mutations from StreamContentPlugin updates - Maintains user scroll interaction handling (disable when scrolling up) Co-Authored-By: nate@buster.so --- .../components/ui/report/EditorContainer.tsx | 31 +++++++++++-------- .../src/components/ui/report/ReportEditor.tsx | 9 ++++++ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/apps/web/src/components/ui/report/EditorContainer.tsx b/apps/web/src/components/ui/report/EditorContainer.tsx index c0a5fbcb6..b5c44a3f3 100644 --- a/apps/web/src/components/ui/report/EditorContainer.tsx +++ b/apps/web/src/components/ui/report/EditorContainer.tsx @@ -1,6 +1,7 @@ import { cn } from '@/lib/utils'; import { PlateContainer } from 'platejs/react'; import { cva, type VariantProps } from 'class-variance-authority'; +import React from 'react'; interface EditorContainerProps { className?: string; @@ -38,23 +39,27 @@ const editorContainerVariants = cva( } ); -export function EditorContainer({ - className, - variant, - disabled, - readonly, - ...props -}: React.ComponentProps<'div'> & - VariantProps & - EditorContainerProps) { +export const EditorContainer = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & + VariantProps & + EditorContainerProps +>(({ className, variant, disabled, readonly, children, ...htmlProps }, ref) => { return ( - + {...htmlProps} + > + + {children} + + ); -} +}); + +EditorContainer.displayName = 'EditorContainer'; diff --git a/apps/web/src/components/ui/report/ReportEditor.tsx b/apps/web/src/components/ui/report/ReportEditor.tsx index abeedd8ce..dd7428f03 100644 --- a/apps/web/src/components/ui/report/ReportEditor.tsx +++ b/apps/web/src/components/ui/report/ReportEditor.tsx @@ -4,6 +4,7 @@ import type { Value, AnyPluginConfig } from 'platejs'; import { Plate, type TPlateEditor } from 'platejs/react'; import React, { useImperativeHandle, useRef } from 'react'; import { useDebounceFn, useMemoizedFn } from '@/hooks'; +import { useAutoScroll } from '@/hooks/useAutoScroll'; import { cn } from '@/lib/utils'; import { Editor } from './Editor'; import { EditorContainer } from './EditorContainer'; @@ -65,6 +66,13 @@ export const ReportEditor = React.memo( ) => { // Initialize the editor instance using the custom useEditor hook const isReady = useRef(false); + const editorContainerRef = useRef(null); + + const { isAutoScrollEnabled } = useAutoScroll(editorContainerRef, { + enabled: isStreaming, + bottomThreshold: 50, + observeSubTree: true + }); // readOnly = true; // isStreaming = true; @@ -128,6 +136,7 @@ export const ReportEditor = React.memo( readOnly={readOnly || isStreaming} onValueChange={onValueChangeDebounced}> Date: Thu, 21 Aug 2025 22:11:07 -0600 Subject: [PATCH 2/3] rename prop --- .../src/components/ui/report/ReportEditor.tsx | 6 +++--- .../ReportPageController.tsx | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/apps/web/src/components/ui/report/ReportEditor.tsx b/apps/web/src/components/ui/report/ReportEditor.tsx index 539560758..1c4589417 100644 --- a/apps/web/src/components/ui/report/ReportEditor.tsx +++ b/apps/web/src/components/ui/report/ReportEditor.tsx @@ -30,7 +30,7 @@ interface ReportEditorProps { onReady?: (editor: IReportEditor) => void; id?: string; mode?: 'export' | 'default'; - children?: React.ReactNode; + preEditorChildren?: React.ReactNode; postEditorChildren?: React.ReactNode; } @@ -60,7 +60,7 @@ export const ReportEditor = React.memo( useFixedToolbarKit = false, readOnly = false, isStreaming = false, - children, + preEditorChildren, postEditorChildren }, ref @@ -135,7 +135,7 @@ export const ReportEditor = React.memo( variant={variant} readOnly={readOnly} className={cn('editor-container relative overflow-auto', containerClassName)}> - {children} + {preEditorChildren} + } postEditorChildren={ showGeneratingContent ? ( ) : null - }> - - + }> ) : ( )} From b29fb18a3cbc88309b88be61e5b1139a02eacdca Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Thu, 21 Aug 2025 22:17:34 -0600 Subject: [PATCH 3/3] scroll to bottom button --- .../ui/buttons/ScrollToBottomButton.tsx} | 12 ++++---- .../src/components/ui/report/ReportEditor.tsx | 29 +++++++++++++++---- .../ui/report/elements/StreamingText.tsx | 2 +- .../ReasoningController.tsx | 4 +-- .../ReportPageController.tsx | 2 -- apps/web/src/styles/tailwindAnimations.css | 11 ++----- 6 files changed, 36 insertions(+), 24 deletions(-) rename apps/web/src/{controllers/ReasoningController/ReasoningScrollToBottom.tsx => components/ui/buttons/ScrollToBottomButton.tsx} (77%) diff --git a/apps/web/src/controllers/ReasoningController/ReasoningScrollToBottom.tsx b/apps/web/src/components/ui/buttons/ScrollToBottomButton.tsx similarity index 77% rename from apps/web/src/controllers/ReasoningController/ReasoningScrollToBottom.tsx rename to apps/web/src/components/ui/buttons/ScrollToBottomButton.tsx index 923579c8a..b094d6f24 100644 --- a/apps/web/src/controllers/ReasoningController/ReasoningScrollToBottom.tsx +++ b/apps/web/src/components/ui/buttons/ScrollToBottomButton.tsx @@ -3,18 +3,20 @@ import { ChevronDown } from '@/components/ui/icons'; import { AppTooltip } from '@/components/ui/tooltip'; import { cn } from '@/lib/classMerge'; -export const ReasoningScrollToBottom: React.FC<{ +export const ScrollToBottomButton: React.FC<{ isAutoScrollEnabled: boolean; scrollToBottom: () => void; -}> = React.memo(({ isAutoScrollEnabled, scrollToBottom }) => { + className?: string; +}> = React.memo(({ isAutoScrollEnabled, scrollToBottom, className }) => { return (
- diff --git a/apps/web/src/controllers/ReportPageControllers/ReportPageController.tsx b/apps/web/src/controllers/ReportPageControllers/ReportPageController.tsx index ca675b764..e5badb834 100644 --- a/apps/web/src/controllers/ReportPageControllers/ReportPageController.tsx +++ b/apps/web/src/controllers/ReportPageControllers/ReportPageController.tsx @@ -10,9 +10,7 @@ import { type IReportEditor } from '@/components/ui/report/ReportEditor'; import { ReportEditorSkeleton } from '@/components/ui/report/ReportEditorSkeleton'; import { useChatIndividualContextSelector } from '@/layouts/ChatLayout/ChatContext'; import { useTrackAndUpdateReportChanges } from '@/api/buster-electric/reports/hooks'; -import { ShimmerText } from '@/components/ui/typography/ShimmerText'; import { GeneratingContent } from './GeneratingContent'; -import { useHotkeys } from 'react-hotkeys-hook'; export const ReportPageController: React.FC<{ reportId: string; diff --git a/apps/web/src/styles/tailwindAnimations.css b/apps/web/src/styles/tailwindAnimations.css index ff9a9458b..0a86ace6c 100644 --- a/apps/web/src/styles/tailwindAnimations.css +++ b/apps/web/src/styles/tailwindAnimations.css @@ -60,21 +60,16 @@ } } -/* - highlightFade animation: - - Animates a highlight background and a bottom border only (no outline). - - The border is only on the bottom, not using outline. -*/ @keyframes highlightFade { 0% { /* Use highlight background, fallback to brand, then yellow */ background-color: var(--color-highlight-background, var(--color-purple-100, yellow)); - /* Only bottom border is visible at start */ - border-bottom: 1px solid var(--color-highlight-border, var(--color-purple-200, yellow)); + /* Use box-shadow instead of border - doesn't take up space */ + box-shadow: 0 1.5px 0 0 var(--color-highlight-border, var(--color-purple-300, yellow)); } 100% { background-color: var(--color-highlight-to-background, transparent); - border-bottom: 0px solid var(--color-highlight-to-border, transparent); + box-shadow: 0 0 0 0 var(--color-highlight-to-border, transparent); } }