From aaa2b429097027843ec2b1d96256d3cce15191f2 Mon Sep 17 00:00:00 2001 From: harryli0088 Date: Mon, 22 Sep 2025 21:28:24 -0400 Subject: [PATCH 1/4] basic draft of condensed chat --- src/components/Chat/Chat.module.scss | 39 +++++++- src/components/Chat/Chat.tsx | 135 +++++++++++++++++++++++---- src/components/Settings/Settings.tsx | 14 +-- src/redux/chatHistorySlice.ts | 18 ++-- 4 files changed, 170 insertions(+), 36 deletions(-) diff --git a/src/components/Chat/Chat.module.scss b/src/components/Chat/Chat.module.scss index 8495004..0a1ca45 100644 --- a/src/components/Chat/Chat.module.scss +++ b/src/components/Chat/Chat.module.scss @@ -7,10 +7,12 @@ height: calc(100%); #chat-scroll-container { - height: calc(100% - 35px - 2.5rem); + height: calc(100% - 35px); overflow-y: auto; margin-bottom: 3px; padding: 0.5rem; + padding-top: 1rem; + padding-bottom: 1.5rem; pre { white-space: pre-wrap; @@ -59,6 +61,35 @@ text-align: right; } } + + .condensed-chat { + background-color: #333; + padding: 0.5rem; + border-radius: 5px; + border: 1px solid #555; + + margin-bottom: 1.5rem; + + color: white; + position: relative; + + > p:first-child { + margin-top: 0; + } + + a { + font-size: 0.8rem; + cursor: pointer; + text-decoration: underline; + } + + .show-all-content-container { + padding: 0.5rem; + padding-bottom: 0; + border-radius: 5px; + background-color: #222; + } + } } .chat-status-badge { @@ -69,8 +100,10 @@ left: 0; right: 0; padding: 0.5rem; - background: #22222280; - background: linear-gradient(0deg, rgba(34,34,34,0.75) 0%, rgba(34,34,34,0) 100%); + + :global(.mantine-Badge-root) { + box-shadow: 0 0 3px 3px rgba(0,0,0,0.3); + } } diff --git a/src/components/Chat/Chat.tsx b/src/components/Chat/Chat.tsx index 270f65d..ba2def6 100644 --- a/src/components/Chat/Chat.tsx +++ b/src/components/Chat/Chat.tsx @@ -6,7 +6,7 @@ import { StreamLanguage } from '@codemirror/language'; import { sparql } from '@codemirror/legacy-modes/mode/sparql'; import { Badge, Button, Modal, TextInput } from "@mantine/core"; import { useEffect, useRef, useState } from "react"; -import { IconCaretRight, IconZoomCode } from '@tabler/icons-react'; +import { IconCaretDown, IconCaretRight, IconCaretUp, IconZoomCode } from '@tabler/icons-react'; import { ErrorMessage } from 'components/ErrorMessage'; import { LLMWarning } from 'components/LLMWarning'; @@ -22,16 +22,17 @@ import { useAppDispatch, useAppSelector } from 'redux/store'; import { tryParsingOutQuery } from 'utils/tryParsingOutQuery'; import styles from "./Chat.module.scss" +import { LinkQChatMessageType } from 'redux/chatHistorySlice'; export function Chat() { const fullChatHistory = useAppSelector(state => state.chatHistory.fullChatHistory) const simpleChatHistory = useAppSelector(state => state.chatHistory.simpleChatHistory) - const showFullChatHistory = useAppSelector(state => state.chatHistory.showFullChatHistory) + const chatHistoryDisplay = useAppSelector(state => state.chatHistory.chatHistoryDisplay) - //based on showFullChatHistory, decide which chat history to display to the user - const chatHistory = showFullChatHistory ? fullChatHistory : simpleChatHistory + //based on chatHistoryDisplay, decide which chat history to display to the user + const chatHistory = chatHistoryDisplay==="full" ? fullChatHistory : simpleChatHistory const chatScrollBottomRef = useRef(null) useEffect(() => { @@ -55,20 +56,16 @@ export function Chat() {
- {chatHistory.map((c, i) => { - return ( -
-
- {showFullChatHistory &&

{c.name}, chat #{c.chatId}

} - { - c.role === "assistant" - ? - :
{c.content}
- } -
-
- ) - })} +
+ {chatHistoryDisplay==="condensed" ? ( + condenseChat(fullChatHistory).map((c,i) => ( + + )) + ) : ( + chatHistory.map((c, i) => ( + + )) + )}
@@ -99,6 +96,68 @@ export function Chat() { ) } +function RenderCondensedMessage({ + condensedChat, + setInputText, +}:{ + condensedChat:CondensedChatType, + setInputText: React.Dispatch>, +}) { + const [showDetails, setShowDetails] = useState(false) + + const firstChatMessage = condensedChat[0] + return ( +
+ {firstChatMessage.stage && ( + <> +

{firstChatMessage.stage.mainStage}

+

{firstChatMessage.stage.subStage}

+ {firstChatMessage.stage.description &&

{firstChatMessage.stage.description}

} + + )} + + + {showDetails && ( +
+
+
+ {condensedChat.map((c, i) => ( + + ))} +
+
+ )} +
+ ) +} + +function RenderChatMessage({ + chat, + setInputText, +}:{ + chat:LinkQChatMessageType, + setInputText: React.Dispatch>, +}) { + const chatHistoryDisplay = useAppSelector(state => state.chatHistory.chatHistoryDisplay) + + return ( +
+
+ {chatHistoryDisplay==="full"||chatHistoryDisplay==="condensed" &&

{chat.name}, chat #{chat.chatId}

} + { + chat.role === "assistant" + ? + :
{chat.content}
+ } +
+
+ ) +} + function RenderLLMResponse({ setInputText, text, @@ -220,6 +279,42 @@ function LinkQDetailedBadgeStatus() { } return ( -
{displayMessage}
+
+ {displayMessage} +
) -} \ No newline at end of file +} + +type CondensedChatType = LinkQChatMessageType[] + +function condenseChat(fullChatHistory: LinkQChatMessageType[]):CondensedChatType[] { + const condensedChat:CondensedChatType[] = []; + + for(let i=0; i state.settings.baseURL) const model = useAppSelector(state => state.settings.model) const showStateDiagramStatus = useAppSelector(state => state.settings.showStateDiagramStatus) - const showFullChatHistory = useAppSelector(state => state.chatHistory.showFullChatHistory) + const chatHistoryDisplay = useAppSelector(state => state.chatHistory.chatHistoryDisplay) const [showSettingsModal, setShowSettingsModal] = useState(false) const closeSettingsModal = () => setShowSettingsModal(false) @@ -48,10 +48,12 @@ export function Settings() { return ( <> - dispatch(toggleShowFullChatHistory())} - label="Show full chat history" + ({ + value,label + }))} value={chatHistoryDisplay} - onChange={(value) => value && dispatch(setChatHistoryDisplay(value as ChatHistoryDisplayType))} + onChange={(value) => value && dispatch(setChatHistoryDisplay( + value as ChatHistoryDisplayType + ))} />
Date: Tue, 23 Sep 2025 12:50:42 -0400 Subject: [PATCH 3/4] removed unused imports --- src/components/Chat/Chat.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Chat/Chat.tsx b/src/components/Chat/Chat.tsx index ba2def6..2d3e0cd 100644 --- a/src/components/Chat/Chat.tsx +++ b/src/components/Chat/Chat.tsx @@ -6,7 +6,7 @@ import { StreamLanguage } from '@codemirror/language'; import { sparql } from '@codemirror/legacy-modes/mode/sparql'; import { Badge, Button, Modal, TextInput } from "@mantine/core"; import { useEffect, useRef, useState } from "react"; -import { IconCaretDown, IconCaretRight, IconCaretUp, IconZoomCode } from '@tabler/icons-react'; +import { IconCaretRight, IconZoomCode } from '@tabler/icons-react'; import { ErrorMessage } from 'components/ErrorMessage'; import { LLMWarning } from 'components/LLMWarning'; From 54dc4a2bddbd16863c935b8fc72f5f6612cf4a58 Mon Sep 17 00:00:00 2001 From: harryli0088 Date: Tue, 23 Sep 2025 13:21:03 -0400 Subject: [PATCH 4/4] added comments to condensing function --- src/components/Chat/Chat.tsx | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/components/Chat/Chat.tsx b/src/components/Chat/Chat.tsx index 2d3e0cd..5e2506d 100644 --- a/src/components/Chat/Chat.tsx +++ b/src/components/Chat/Chat.tsx @@ -285,32 +285,50 @@ function LinkQDetailedBadgeStatus() { ) } +//the condensed chat type is just one or two grouped chat messages type CondensedChatType = LinkQChatMessageType[] +/** + * Converts the full chat history into the condensed/grouped view for better traceability. + * If two neighboring chat messages have the same stage details, they are condensed/grouped together + * @param fullChatHistory + * @returns array of condensed chat types + */ function condenseChat(fullChatHistory: LinkQChatMessageType[]):CondensedChatType[] { const condensedChat:CondensedChatType[] = []; + //loop through the full chat history for(let i=0; i