fix: dynamically determine the chat window height (#1588)

The AI chat window was not allowing the user to resize it due to an
interacting scss class being removed. This replaces the max-height from
the removed class with a dynamic calculation to resize properly.
This commit is contained in:
Sylvie Crowe 2024-12-20 03:18:44 -08:00 committed by GitHub
parent fca868e597
commit 9f9190b93c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -4,6 +4,7 @@
import { Button } from "@/app/element/button";
import { Markdown } from "@/app/element/markdown";
import { TypingIndicator } from "@/app/element/typingindicator";
import { useDimensionsWithExistingRef } from "@/app/hook/useDimensions";
import { RpcResponseHelper, WshClient } from "@/app/store/wshclient";
import { RpcApi } from "@/app/store/wshclientapi";
import { makeFeBlockRouteId } from "@/app/store/wshrouter";
@ -506,84 +507,88 @@ interface ChatWindowProps {
messages: ChatMessageType[];
msgWidths: Object;
model: WaveAiModel;
height: number;
}
const ChatWindow = memo(
forwardRef<OverlayScrollbarsComponentRef, ChatWindowProps>(({ chatWindowRef, messages, msgWidths, model }, ref) => {
const [isUserScrolling, setIsUserScrolling] = useState(false);
forwardRef<OverlayScrollbarsComponentRef, ChatWindowProps>(
({ chatWindowRef, messages, msgWidths, model, height }, ref) => {
const [isUserScrolling, setIsUserScrolling] = useState(false);
const osRef = useRef<OverlayScrollbarsComponentRef>(null);
const prevMessagesLenRef = useRef(messages.length);
const osRef = useRef<OverlayScrollbarsComponentRef>(null);
const prevMessagesLenRef = useRef(messages.length);
useImperativeHandle(ref, () => osRef.current as OverlayScrollbarsComponentRef);
useImperativeHandle(ref, () => osRef.current as OverlayScrollbarsComponentRef);
useEffect(() => {
if (osRef.current && osRef.current.osInstance()) {
const { viewport } = osRef.current.osInstance().elements();
const curMessagesLen = messages.length;
if (prevMessagesLenRef.current !== curMessagesLen || !isUserScrolling) {
setIsUserScrolling(false);
viewport.scrollTo({
behavior: "auto",
top: chatWindowRef.current?.scrollHeight || 0,
});
}
prevMessagesLenRef.current = curMessagesLen;
}
}, [messages, isUserScrolling]);
useEffect(() => {
if (osRef.current && osRef.current.osInstance()) {
const { viewport } = osRef.current.osInstance().elements();
const handleUserScroll = () => {
setIsUserScrolling(true);
};
viewport.addEventListener("wheel", handleUserScroll, { passive: true });
viewport.addEventListener("touchmove", handleUserScroll, { passive: true });
return () => {
viewport.removeEventListener("wheel", handleUserScroll);
viewport.removeEventListener("touchmove", handleUserScroll);
if (osRef.current && osRef.current.osInstance()) {
osRef.current.osInstance().destroy();
useEffect(() => {
if (osRef.current && osRef.current.osInstance()) {
const { viewport } = osRef.current.osInstance().elements();
const curMessagesLen = messages.length;
if (prevMessagesLenRef.current !== curMessagesLen || !isUserScrolling) {
setIsUserScrolling(false);
viewport.scrollTo({
behavior: "auto",
top: chatWindowRef.current?.scrollHeight || 0,
});
}
};
}
}, []);
const handleScrollbarInitialized = (instance: OverlayScrollbars) => {
const { viewport } = instance.elements();
viewport.removeAttribute("tabindex");
viewport.scrollTo({
behavior: "auto",
top: chatWindowRef.current?.scrollHeight || 0,
});
};
prevMessagesLenRef.current = curMessagesLen;
}
}, [messages, isUserScrolling]);
const handleScrollbarUpdated = (instance: OverlayScrollbars) => {
const { viewport } = instance.elements();
viewport.removeAttribute("tabindex");
};
useEffect(() => {
if (osRef.current && osRef.current.osInstance()) {
const { viewport } = osRef.current.osInstance().elements();
return (
<OverlayScrollbarsComponent
ref={osRef}
className="scrollable"
options={{ scrollbars: { autoHide: "leave" } }}
events={{ initialized: handleScrollbarInitialized, updated: handleScrollbarUpdated }}
>
<div ref={chatWindowRef} className="chat-window" style={msgWidths}>
<div className="filler"></div>
{messages.map((chitem, idx) => (
<ChatItem key={idx} chatItem={chitem} model={model} />
))}
</div>
</OverlayScrollbarsComponent>
);
})
const handleUserScroll = () => {
setIsUserScrolling(true);
};
viewport.addEventListener("wheel", handleUserScroll, { passive: true });
viewport.addEventListener("touchmove", handleUserScroll, { passive: true });
return () => {
viewport.removeEventListener("wheel", handleUserScroll);
viewport.removeEventListener("touchmove", handleUserScroll);
if (osRef.current && osRef.current.osInstance()) {
osRef.current.osInstance().destroy();
}
};
}
}, []);
const handleScrollbarInitialized = (instance: OverlayScrollbars) => {
const { viewport } = instance.elements();
viewport.removeAttribute("tabindex");
viewport.scrollTo({
behavior: "auto",
top: chatWindowRef.current?.scrollHeight || 0,
});
};
const handleScrollbarUpdated = (instance: OverlayScrollbars) => {
const { viewport } = instance.elements();
viewport.removeAttribute("tabindex");
};
return (
<OverlayScrollbarsComponent
ref={osRef}
className="scrollable"
options={{ scrollbars: { autoHide: "leave" } }}
events={{ initialized: handleScrollbarInitialized, updated: handleScrollbarUpdated }}
style={{ maxHeight: height }}
>
<div ref={chatWindowRef} className="chat-window" style={msgWidths}>
<div className="filler"></div>
{messages.map((chitem, idx) => (
<ChatItem key={idx} chatItem={chitem} model={model} />
))}
</div>
</OverlayScrollbarsComponent>
);
}
)
);
interface ChatInputProps {
@ -657,6 +662,8 @@ const WaveAi = ({ model }: { model: WaveAiModel; blockId: string }) => {
const chatWindowRef = useRef<HTMLDivElement>(null);
const osRef = useRef<OverlayScrollbarsComponentRef>(null);
const inputRef = useRef<HTMLTextAreaElement>(null);
const waveAiDims = useDimensionsWithExistingRef(waveaiRef);
const chatInputDims = useDimensionsWithExistingRef(inputRef);
const [value, setValue] = useState("");
const [selectedBlockIdx, setSelectedBlockIdx] = useState<number | null>(null);
@ -829,6 +836,8 @@ const WaveAi = ({ model }: { model: WaveAiModel; blockId: string }) => {
messages={messages}
msgWidths={msgWidths}
model={model}
height={waveAiDims?.height - chatInputDims?.height - 28 ?? 400}
// the 28 is a magic number it the moment but it makes the spacing look good
/>
</div>
<div className="waveai-controls">