mirror of https://github.com/buster-so/buster.git
scroll to bottom button
This commit is contained in:
parent
6818c46578
commit
b29fb18a3c
|
@ -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 (
|
||||
<div
|
||||
data-testid="reasoning-scroll-to-bottom"
|
||||
data-testid="scroll-to-bottom-button"
|
||||
className={cn(
|
||||
'absolute right-4 bottom-4 z-10 duration-300',
|
||||
isAutoScrollEnabled
|
||||
? 'pointer-events-none scale-90 opacity-0'
|
||||
: 'pointer-events-auto scale-100 cursor-pointer opacity-100'
|
||||
: 'pointer-events-auto scale-100 cursor-pointer opacity-100',
|
||||
className
|
||||
)}>
|
||||
<AppTooltip title="Stick to bottom" sideOffset={12} delayDuration={500}>
|
||||
<button
|
||||
|
@ -30,4 +32,4 @@ export const ReasoningScrollToBottom: React.FC<{
|
|||
);
|
||||
});
|
||||
|
||||
ReasoningScrollToBottom.displayName = 'ReasoningScrollToBottom';
|
||||
ScrollToBottomButton.displayName = 'ScrollToBottomButton';
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import type { Value, AnyPluginConfig } from 'platejs';
|
||||
import { Plate, type TPlateEditor } from 'platejs/react';
|
||||
import React, { useImperativeHandle, useRef } from 'react';
|
||||
import React, { useEffect, useImperativeHandle, useRef } from 'react';
|
||||
import { useDebounceFn, useMemoizedFn } from '@/hooks';
|
||||
import { useAutoScroll } from '@/hooks/useAutoScroll';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
@ -13,6 +13,7 @@ import { useReportEditor } from './useReportEditor';
|
|||
import type { ReportElementsWithIds, ReportElementWithId } from '@buster/server-shared/reports';
|
||||
import { platejsToMarkdown } from './plugins/markdown-kit/platejs-conversions';
|
||||
import { ShimmerText } from '@/components/ui/typography/ShimmerText';
|
||||
import { ScrollToBottomButton } from '../buttons/ScrollToBottomButton';
|
||||
|
||||
interface ReportEditorProps {
|
||||
// We accept the generic Value type but recommend using ReportTypes.Value for type safety
|
||||
|
@ -69,7 +70,8 @@ export const ReportEditor = React.memo(
|
|||
const isReady = useRef(false);
|
||||
const editorContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { isAutoScrollEnabled } = useAutoScroll(editorContainerRef, {
|
||||
const { isAutoScrollEnabled, enableAutoScroll, disableAutoScroll, scrollToBottom } =
|
||||
useAutoScroll(editorContainerRef, {
|
||||
enabled: isStreaming,
|
||||
bottomThreshold: 50,
|
||||
observeSubTree: true
|
||||
|
@ -126,6 +128,14 @@ export const ReportEditor = React.memo(
|
|||
wait: 1500
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isStreaming) {
|
||||
enableAutoScroll();
|
||||
} else {
|
||||
disableAutoScroll();
|
||||
}
|
||||
}, [isStreaming]);
|
||||
|
||||
if (!editor) return null;
|
||||
|
||||
return (
|
||||
|
@ -146,6 +156,13 @@ export const ReportEditor = React.memo(
|
|||
/>
|
||||
</ThemeWrapper>
|
||||
{postEditorChildren}
|
||||
{isStreaming && (
|
||||
<ScrollToBottomButton
|
||||
isAutoScrollEnabled={isAutoScrollEnabled}
|
||||
scrollToBottom={scrollToBottom}
|
||||
className="fixed right-8 bottom-8 z-10"
|
||||
/>
|
||||
)}
|
||||
</EditorContainer>
|
||||
</Plate>
|
||||
);
|
||||
|
|
|
@ -15,7 +15,7 @@ export function StreamingText(props: PlateTextProps) {
|
|||
<PlateText
|
||||
className={cn(
|
||||
'streaming-node',
|
||||
isStreaming && ['animate-highlight-fade'],
|
||||
isStreaming && 'animate-highlight-fade',
|
||||
// Only show the animated dot on the last streaming text node
|
||||
isLastStreamingText && [
|
||||
'after:ml-1.5 after:inline-block after:h-3 after:w-3 after:animate-pulse after:rounded-full after:bg-purple-500 after:align-middle after:content-[""]'
|
||||
|
|
|
@ -11,7 +11,7 @@ import { ScrollArea } from '@/components/ui/scroll-area';
|
|||
import { useAutoScroll } from '@/hooks/useAutoScroll';
|
||||
import { ReasoningMessageSelector } from './ReasoningMessages';
|
||||
import { BlackBoxMessage } from './ReasoningMessages/ReasoningBlackBoxMessage';
|
||||
import { ReasoningScrollToBottom } from './ReasoningScrollToBottom';
|
||||
import { ScrollToBottomButton } from '@/components/ui/buttons/ScrollToBottomButton';
|
||||
import type { BusterChatMessage, IBusterChat } from '@/api/asset_interfaces/chat';
|
||||
|
||||
interface ReasoningControllerProps {
|
||||
|
@ -79,7 +79,7 @@ export const ReasoningController: React.FC<ReasoningControllerProps> = ({ chatId
|
|||
</div>
|
||||
</ScrollArea>
|
||||
|
||||
<ReasoningScrollToBottom
|
||||
<ScrollToBottomButton
|
||||
isAutoScrollEnabled={isAutoScrollEnabled}
|
||||
scrollToBottom={scrollToBottom}
|
||||
/>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue