| ... | @@ -725,6 +725,19 @@ function ComposerPromptEditorTiptapInner(props: ComposerPromptEditorProps) { | ... | @@ -725,6 +725,19 @@ function ComposerPromptEditorTiptapInner(props: ComposerPromptEditorProps) { |
| 725 | ); | 725 | ); |
| 726 | }, []); | 726 | }, []); |
| 727 | | 727 | |
| | 728 | const editorAttributes = useMemo( |
| | 729 | () => ({ |
| | 730 | class: cn( |
| | 731 | "composer-tiptap block max-h-50 min-h-17.5 w-full overflow-y-auto whitespace-pre-wrap wrap-break-word bg-transparent leading-relaxed text-foreground focus:outline-none", |
| | 732 | className, |
| | 733 | ), |
| | 734 | "data-testid": "composer-editor", |
| | 735 | "data-composer-rich-text": richText ? "true" : "false", |
| | 736 | "aria-placeholder": placeholder, |
| | 737 | }), |
| | 738 | [className, placeholder, richText], |
| | 739 | ); |
| | 740 | |
| 728 | const editor = useEditor( | 741 | const editor = useEditor( |
| 729 | { | 742 | { |
| 730 | extensions: [ | 743 | extensions: [ |
| ... | @@ -787,15 +800,7 @@ function ComposerPromptEditorTiptapInner(props: ComposerPromptEditorProps) { | ... | @@ -787,15 +800,7 @@ function ComposerPromptEditorTiptapInner(props: ComposerPromptEditorProps) { |
| 787 | ), | 800 | ), |
| 788 | editable: !disabled, | 801 | editable: !disabled, |
| 789 | editorProps: { | 802 | editorProps: { |
| 790 | attributes: { | 803 | attributes: editorAttributes, |
| 791 | class: cn( | | |
| 792 | "composer-tiptap block max-h-50 min-h-17.5 w-full overflow-y-auto whitespace-pre-wrap wrap-break-word bg-transparent leading-relaxed text-foreground focus:outline-none", | | |
| 793 | className, | | |
| 794 | ), | | |
| 795 | "data-testid": "composer-editor", | | |
| 796 | "data-composer-rich-text": richText ? "true" : "false", | | |
| 797 | "aria-placeholder": placeholder, | | |
| 798 | }, | | |
| 799 | handleKeyDown: (view, event) => { | 804 | handleKeyDown: (view, event) => { |
| 800 | if ( | 805 | if ( |
| 801 | isMacPlatform(navigator.platform) && | 806 | isMacPlatform(navigator.platform) && |
| ... | @@ -995,6 +1000,17 @@ function ComposerPromptEditorTiptapInner(props: ComposerPromptEditorProps) { | ... | @@ -995,6 +1000,17 @@ function ComposerPromptEditorTiptapInner(props: ComposerPromptEditorProps) { |
| 995 | editorHolder.current = editor; | 1000 | editorHolder.current = editor; |
| 996 | }, [editor]); | 1001 | }, [editor]); |
| 997 | | 1002 | |
| | 1003 | // Tiptap forwards option changes to the view from a passive effect, so a |
| | 1004 | // class change here would reach the ProseMirror element one tick after |
| | 1005 | // React commits. The chat composer measures its resting and expanded |
| | 1006 | // geometry in layout effects that run first, and it clamps the prompt |
| | 1007 | // through `className`, so the attributes are pushed to the view here for |
| | 1008 | // those measurements to see the layout they are about to reserve for. |
| | 1009 | useLayoutEffect(() => { |
| | 1010 | if (!editor?.isInitialized) return; |
| | 1011 | editor.view.setProps({ attributes: editorAttributes }); |
| | 1012 | }, [editor, editorAttributes]); |
| | 1013 | |
| 998 | const readSnapshot = useCallback(() => { | 1014 | const readSnapshot = useCallback(() => { |
| 999 | const snapshot = snapshotRef.current; | 1015 | const snapshot = snapshotRef.current; |
| 1000 | if (!editor) return snapshot; | 1016 | if (!editor) return snapshot; |