Compare commits

..

5 Commits

Author SHA1 Message Date
Nate Kelley 4cfacbc1e3
read only partial fix 2025-08-21 21:37:28 -06:00
Nate Kelley 29fe1a1b34
Merge branch 'nate/report-hot-fix' of https://github.com/buster-so/buster into nate/report-hot-fix 2025-08-21 21:22:52 -06:00
Nate Kelley 5123fb56c1
update ready only status 2025-08-21 21:22:08 -06:00
Devin AI 536e21e425 Fix Plate editor re-rendering issue by removing readOnly prop
- Remove readOnly prop from Plate provider to prevent re-renders
- Apply pointer-events-none and user-select-none CSS classes to EditorContainer when readonly
- Remove readOnly prop from usePlateEditor hook
- Editor becomes inert when readonly without causing re-renders

Co-Authored-By: nate@buster.so <nate@buster.so>
2025-08-22 02:24:11 +00:00
Nate Kelley d1d7c959e5
Add padding 2025-08-21 16:18:27 -06:00
5 changed files with 42 additions and 37 deletions

View File

@ -12,7 +12,7 @@ const editorVariants = cva(
cn( cn(
'group/editor', 'group/editor',
'relative w-full cursor-text overflow-x-visible break-words whitespace-pre-wrap select-text', 'relative w-full cursor-text overflow-x-visible break-words whitespace-pre-wrap select-text',
'rounded-md ring-offset-background focus-visible:outline-none', 'ring-offset-background focus-visible:outline-none',
'placeholder:text-muted-foreground/80 **:data-slate-placeholder:!top-1/2 **:data-slate-placeholder:-translate-y-1/2 **:data-slate-placeholder:text-muted-foreground/80 **:data-slate-placeholder:opacity-100!', 'placeholder:text-muted-foreground/80 **:data-slate-placeholder:!top-1/2 **:data-slate-placeholder:-translate-y-1/2 **:data-slate-placeholder:text-muted-foreground/80 **:data-slate-placeholder:opacity-100!',
'[&_strong]:font-bold' '[&_strong]:font-bold'
), ),
@ -21,17 +21,16 @@ const editorVariants = cva(
variant: 'default' variant: 'default'
}, },
variants: { variants: {
disabled: { readOnly: {
true: 'cursor-not-allowed opacity-50' true: ''
}, },
focused: { focused: {
true: 'ring-2 ring-ring ring-offset-2' true: 'ring-2 ring-ring ring-offset-2'
}, },
variant: { variant: {
ai: 'w-full px-0 text-base md:text-sm',
comment: cn('rounded-none border-none bg-transparent 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))]', default: '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', fullWidth: 'px-16 pt-4 pb-72 text-base sm:px-24',
none: '' none: ''
} }
} }
@ -41,20 +40,20 @@ const editorVariants = cva(
export type EditorProps = PlateContentProps & VariantProps<typeof editorVariants>; export type EditorProps = PlateContentProps & VariantProps<typeof editorVariants>;
export const Editor = React.forwardRef<HTMLDivElement, EditorProps>( export const Editor = React.forwardRef<HTMLDivElement, EditorProps>(
({ className, disabled, focused, variant, ...props }, ref) => { ({ className, readOnly, focused, variant, ...props }, ref) => {
return ( return (
<PlateContent <PlateContent
ref={ref} ref={ref}
className={cn( className={cn(
editorVariants({ editorVariants({
disabled, readOnly,
focused, focused,
variant variant
}), }),
className className
)} )}
disabled={disabled} readOnly={readOnly}
disableDefaultStyles disableDefaultStyles={true}
{...props} {...props}
/> />
); );

View File

@ -5,8 +5,7 @@ import { cva, type VariantProps } from 'class-variance-authority';
interface EditorContainerProps { interface EditorContainerProps {
className?: string; className?: string;
variant?: 'default' | 'comment'; variant?: 'default' | 'comment';
readonly?: boolean; readOnly?: boolean;
disabled?: boolean;
} }
const editorContainerVariants = cva( const editorContainerVariants = cva(
@ -27,13 +26,13 @@ const editorContainerVariants = cva(
'has-data-readonly:w-fit has-data-readonly:cursor-default has-data-readonly:border-transparent has-data-readonly:focus-within:[box-shadow:none]' 'has-data-readonly:w-fit has-data-readonly:cursor-default has-data-readonly:border-transparent has-data-readonly:focus-within:[box-shadow:none]'
) )
}, },
readonly: { readOnly: {
true: 'cursor-text' true: 'cursor-default user-select-none '
} }
}, },
defaultVariants: { defaultVariants: {
variant: 'default', variant: 'default',
readonly: false readOnly: false
} }
} }
); );
@ -41,8 +40,7 @@ const editorContainerVariants = cva(
export function EditorContainer({ export function EditorContainer({
className, className,
variant, variant,
disabled, readOnly,
readonly,
...props ...props
}: React.ComponentProps<'div'> & }: React.ComponentProps<'div'> &
VariantProps<typeof editorContainerVariants> & VariantProps<typeof editorContainerVariants> &
@ -51,7 +49,7 @@ export function EditorContainer({
<PlateContainer <PlateContainer
className={cn( className={cn(
'ignore-click-outside/toolbar', 'ignore-click-outside/toolbar',
editorContainerVariants({ variant, readonly }), editorContainerVariants({ variant, readOnly }),
className className
)} )}
{...props} {...props}

View File

@ -22,7 +22,6 @@ interface ReportEditorProps {
variant?: 'default'; variant?: 'default';
className?: string; className?: string;
containerClassName?: string; containerClassName?: string;
disabled?: boolean;
style?: React.CSSProperties; style?: React.CSSProperties;
onValueChange?: (value: string) => void; //markdown onValueChange?: (value: string) => void; //markdown
useFixedToolbarKit?: boolean; useFixedToolbarKit?: boolean;
@ -57,7 +56,6 @@ export const ReportEditor = React.memo(
mode = 'default', mode = 'default',
useFixedToolbarKit = false, useFixedToolbarKit = false,
readOnly = false, readOnly = false,
disabled = false,
isStreaming = false, isStreaming = false,
children children
}, },
@ -66,15 +64,12 @@ export const ReportEditor = React.memo(
// Initialize the editor instance using the custom useEditor hook // Initialize the editor instance using the custom useEditor hook
const isReady = useRef(false); const isReady = useRef(false);
// readOnly = true;
// isStreaming = true;
const editor = useReportEditor({ const editor = useReportEditor({
isStreaming, isStreaming,
mode, mode,
readOnly,
value, value,
initialElements, initialElements,
disabled,
useFixedToolbarKit useFixedToolbarKit
}); });
@ -123,22 +118,18 @@ export const ReportEditor = React.memo(
if (!editor) return null; if (!editor) return null;
return ( return (
<Plate <Plate editor={editor} onValueChange={onValueChangeDebounced}>
editor={editor}
readOnly={readOnly || isStreaming}
onValueChange={onValueChangeDebounced}>
<EditorContainer <EditorContainer
variant={variant} variant={variant}
readonly={readOnly} readOnly={readOnly}
disabled={disabled} className={cn('editor-container relative overflow-auto', containerClassName)}>
className={cn('editor-container overflow-auto', containerClassName)}>
{children} {children}
<ThemeWrapper id={id}> <ThemeWrapper id={id}>
<Editor <Editor
style={style} style={style}
placeholder={placeholder} placeholder={placeholder}
disabled={disabled}
className={cn('editor', className)} className={cn('editor', className)}
readOnly={readOnly || isStreaming}
autoFocus autoFocus
/> />
</ThemeWrapper> </ThemeWrapper>

View File

@ -13,15 +13,15 @@ import type { ReportElementWithId } from '@buster/server-shared/reports';
export const useReportEditor = ({ export const useReportEditor = ({
value, value,
disabled,
isStreaming, isStreaming,
mode = 'default', mode = 'default',
readOnly,
useFixedToolbarKit = false, useFixedToolbarKit = false,
initialElements initialElements
}: { }: {
value: string | undefined; //markdown value: string | undefined; //markdown
initialElements?: Value | ReportElementWithId[]; initialElements?: Value | ReportElementWithId[];
disabled: boolean; readOnly: boolean | undefined;
useFixedToolbarKit?: boolean; useFixedToolbarKit?: boolean;
isStreaming: boolean; isStreaming: boolean;
mode?: 'export' | 'default'; mode?: 'export' | 'default';
@ -43,7 +43,7 @@ export const useReportEditor = ({
const editor = usePlateEditor({ const editor = usePlateEditor({
plugins, plugins,
value: initialElements, value: initialElements,
readOnly: disabled || isStreaming readOnly: readOnly //this is for the initial value
}); });
useEditorServerUpdates({ editor, value, isStreaming }); useEditorServerUpdates({ editor, value, isStreaming });

View File

@ -10,6 +10,7 @@ import { type IReportEditor } from '@/components/ui/report/ReportEditor';
import { ReportEditorSkeleton } from '@/components/ui/report/ReportEditorSkeleton'; import { ReportEditorSkeleton } from '@/components/ui/report/ReportEditorSkeleton';
import { useChatIndividualContextSelector } from '@/layouts/ChatLayout/ChatContext'; import { useChatIndividualContextSelector } from '@/layouts/ChatLayout/ChatContext';
import { useTrackAndUpdateReportChanges } from '@/api/buster-electric/reports/hooks'; import { useTrackAndUpdateReportChanges } from '@/api/buster-electric/reports/hooks';
import { useHotkeys } from 'react-hotkeys-hook';
export const ReportPageController: React.FC<{ export const ReportPageController: React.FC<{
reportId: string; reportId: string;
@ -20,11 +21,28 @@ export const ReportPageController: React.FC<{
}> = React.memo( }> = React.memo(
({ reportId, readOnly = false, className = '', onReady: onReadyProp, mode = 'default' }) => { ({ reportId, readOnly = false, className = '', onReady: onReadyProp, mode = 'default' }) => {
const { data: report } = useGetReport({ reportId, versionNumber: undefined }); const { data: report } = useGetReport({ reportId, versionNumber: undefined });
const isStreamingMessage = useChatIndividualContextSelector((x) => x.isStreamingMessage); let isStreamingMessage = useChatIndividualContextSelector((x) => x.isStreamingMessage);
const content = report?.content || ''; const content = report?.content || '';
const commonClassName = 'sm:px-[max(64px,calc(50%-350px))]'; const commonClassName = 'sm:px-[max(64px,calc(50%-350px))]';
const [useOverride, setUseOverride] = React.useState(false);
useHotkeys('x', () => {
setUseOverride((v) => {
console.log('x', !v);
return !v;
});
});
if (useOverride) {
isStreamingMessage = true;
readOnly = true;
} else {
isStreamingMessage = false;
readOnly = false;
}
const { mutate: updateReport } = useUpdateReport(); const { mutate: updateReport } = useUpdateReport();
const canUpdate = () => { const canUpdate = () => {
@ -59,7 +77,6 @@ export const ReportPageController: React.FC<{
<DynamicReportEditor <DynamicReportEditor
value={content} value={content}
placeholder="Start typing..." placeholder="Start typing..."
disabled={false}
className={commonClassName} className={commonClassName}
containerClassName="pt-9" containerClassName="pt-9"
variant="default" variant="default"