From dd9b33927c0de1dd3867ae963fb8bf96b119c524 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:11:38 +0000 Subject: [PATCH 1/2] Add streaming loading message to report editor - Add shimmer loading message at bottom of editor when streaming - Use existing ShimmerText component for consistency - Position with absolute positioning and backdrop blur - Show 'Generating content...' message during streaming Co-Authored-By: nate@buster.so --- apps/web/src/components/ui/report/ReportEditor.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/web/src/components/ui/report/ReportEditor.tsx b/apps/web/src/components/ui/report/ReportEditor.tsx index abeedd8ce..02c4c0729 100644 --- a/apps/web/src/components/ui/report/ReportEditor.tsx +++ b/apps/web/src/components/ui/report/ReportEditor.tsx @@ -11,6 +11,7 @@ import { ThemeWrapper } from './ThemeWrapper/ThemeWrapper'; 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'; interface ReportEditorProps { // We accept the generic Value type but recommend using ReportTypes.Value for type safety @@ -141,6 +142,11 @@ export const ReportEditor = React.memo( className={cn('editor', className)} autoFocus /> + {isStreaming && ( +
+ +
+ )} From 8cd289ea59fbd1291f950155f2e4a43be4ad604f Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Thu, 21 Aug 2025 22:05:44 -0600 Subject: [PATCH 2/2] Add a loading indactor that is stylized --- apps/web/src/components/ui/report/Editor.tsx | 3 +-- .../src/components/ui/report/ReportEditor.tsx | 15 ++++------- .../ui/report/ThemeWrapper/ThemeWrapper.tsx | 19 +++++-------- .../GeneratingContent.tsx | 27 +++++++++++++++++++ .../ReportPageController.tsx | 11 +++++++- 5 files changed, 49 insertions(+), 26 deletions(-) create mode 100644 apps/web/src/controllers/ReportPageControllers/GeneratingContent.tsx diff --git a/apps/web/src/components/ui/report/Editor.tsx b/apps/web/src/components/ui/report/Editor.tsx index 948f39b7a..314a63348 100644 --- a/apps/web/src/components/ui/report/Editor.tsx +++ b/apps/web/src/components/ui/report/Editor.tsx @@ -28,7 +28,6 @@ const editorVariants = cva( true: 'ring-2 ring-ring ring-offset-2' }, variant: { - ai: 'w-full px-0 text-base md:text-sm', comment: cn('rounded-none border-none bg-transparent text-sm'), default: 'size-full px-16 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]', fullWidth: 'size-full px-16 pt-4 pb-72 text-base sm:px-24', @@ -41,7 +40,7 @@ const editorVariants = cva( export type EditorProps = PlateContentProps & VariantProps; export const Editor = React.forwardRef( - ({ className, disabled, focused, variant, ...props }, ref) => { + ({ className, disabled, focused, variant, children, ...props }, ref) => { return ( ; @@ -60,16 +61,14 @@ export const ReportEditor = React.memo( readOnly = false, disabled = false, isStreaming = false, - children + children, + postEditorChildren }, ref ) => { // Initialize the editor instance using the custom useEditor hook const isReady = useRef(false); - // readOnly = true; - // isStreaming = true; - const editor = useReportEditor({ isStreaming, mode, @@ -132,7 +131,7 @@ export const ReportEditor = React.memo( variant={variant} readonly={readOnly} disabled={disabled} - className={cn('editor-container overflow-auto', containerClassName)}> + className={cn('editor-container relative overflow-auto', containerClassName)}> {children} - {isStreaming && ( -
- -
- )}
+ {postEditorChildren} ); diff --git a/apps/web/src/components/ui/report/ThemeWrapper/ThemeWrapper.tsx b/apps/web/src/components/ui/report/ThemeWrapper/ThemeWrapper.tsx index 449590600..58c8f180f 100644 --- a/apps/web/src/components/ui/report/ThemeWrapper/ThemeWrapper.tsx +++ b/apps/web/src/components/ui/report/ThemeWrapper/ThemeWrapper.tsx @@ -46,18 +46,11 @@ const EDITOR_THEME = { ...CSS_VARIABLES_THEME, ...THEME_RESET_STYLE }; export function ThemeWrapper({ children, className, defaultTheme, id }: ThemeWrapperProps) { return ( - <> -
- {children} -
- - {/* */} - +
+ {children} +
); } diff --git a/apps/web/src/controllers/ReportPageControllers/GeneratingContent.tsx b/apps/web/src/controllers/ReportPageControllers/GeneratingContent.tsx new file mode 100644 index 000000000..b533f73dd --- /dev/null +++ b/apps/web/src/controllers/ReportPageControllers/GeneratingContent.tsx @@ -0,0 +1,27 @@ +import { queryKeys } from '@/api/query_keys'; +import { ShimmerText } from '@/components/ui/typography/ShimmerText'; +import { cn } from '@/lib/classMerge'; +import { useQuery } from '@tanstack/react-query'; + +export const GeneratingContent = ({ + messageId, + className +}: { + messageId: string; + className?: string; +}) => { + const { data: text } = useQuery({ + ...queryKeys.chatsBlackBoxMessages(messageId), + notifyOnChangeProps: ['data'], + select: (data) => data + }); + + return ( +
+
+ +
+
+ ); +}; diff --git a/apps/web/src/controllers/ReportPageControllers/ReportPageController.tsx b/apps/web/src/controllers/ReportPageControllers/ReportPageController.tsx index 7120b5098..ca5fdc2a3 100644 --- a/apps/web/src/controllers/ReportPageControllers/ReportPageController.tsx +++ b/apps/web/src/controllers/ReportPageControllers/ReportPageController.tsx @@ -10,6 +10,8 @@ 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'; export const ReportPageController: React.FC<{ reportId: string; @@ -21,8 +23,10 @@ export const ReportPageController: React.FC<{ ({ reportId, readOnly = false, className = '', onReady: onReadyProp, mode = 'default' }) => { const { data: report } = useGetReport({ reportId, versionNumber: undefined }); const isStreamingMessage = useChatIndividualContextSelector((x) => x.isStreamingMessage); + const messageId = useChatIndividualContextSelector((x) => x.currentMessageId); const content = report?.content || ''; + const showGeneratingContent = messageId && isStreamingMessage; const commonClassName = 'sm:px-[max(64px,calc(50%-350px))]'; const { mutate: updateReport } = useUpdateReport(); @@ -68,7 +72,12 @@ export const ReportPageController: React.FC<{ readOnly={readOnly || !report} mode={mode} onReady={onReadyProp} - isStreaming={isStreamingMessage}> + isStreaming={isStreamingMessage} + postEditorChildren={ + showGeneratingContent ? ( + + ) : null + }>