Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import type { HttpRequestTarget } from "@/urls"
import { CanvasMode } from "@/types/patternDoc"
import { streamPatternChat } from "./streamPatternChat"

/** Panel expanded/collapsed by the chat header minimize control. */
/** Scrollable message thread region between header and composer. */
export const CHAT_MESSAGE_PANEL_ID = "chat-message-panel"

interface ChatAreaProps {
Expand Down Expand Up @@ -51,6 +51,8 @@ interface ChatAreaProps {
canvasMode?: CanvasMode
selectedReferencePattern?: string | null
patternChatSessionId?: string | null
/** When false, the chat shell sizes to its composer content only. */
fillHeight?: boolean
}

const ChatArea: React.FC<ChatAreaProps> = ({
Expand Down Expand Up @@ -79,40 +81,25 @@ const ChatArea: React.FC<ChatAreaProps> = ({
canvasMode,
selectedReferencePattern,
patternChatSessionId,
fillHeight = true,
}) => {
const [content, setContent] = useState<string>("")
const [loading, setLoading] = useState<boolean>(false)
const [isMinimized, setIsMinimized] = useState<boolean>(false)
const messagePanelRef = useRef<HTMLDivElement>(null)
const threadContentRef = useRef<HTMLDivElement>(null)
const { sendPatternMessage } = usePatternChatAPI()

useScrollPanelOnContentResize(
messagePanelRef,
threadContentRef,
Boolean(currentUserMessage) && !isMinimized,
Boolean(currentUserMessage),
)

const handleMinimize = () => {
setIsMinimized(true)
}

const handleRestore = () => {
setIsMinimized(false)
}

const handleSuggestedPromptSelect = (query: string) => {
if (isMinimized) {
setIsMinimized(false)
}
setContent(query)
}

const processMessage = async (): Promise<void> => {
if (isMinimized) {
setIsMinimized(false)
}

if (
canvasMode === CanvasMode.PATTERN_DOC &&
selectedReferencePattern &&
Expand Down Expand Up @@ -173,24 +160,22 @@ const ChatArea: React.FC<ChatAreaProps> = ({
position: "relative",
display: "flex",
width: "100%",
height: "100%",
maxHeight: "100%",
height: fillHeight ? "100%" : "auto",
maxHeight: fillHeight ? "100%" : "none",
minHeight: 0,
flexDirection: "column",
boxSizing: "border-box",
overflow: "hidden",
borderTop: "1px solid",
borderColor: "divider",
backgroundColor: (theme) => theme.palette.action.selected,
...(!fillHeight
? { borderTop: "1px solid", borderColor: "divider" }
: {}),
}}
>
{currentUserMessage ? (
<Box sx={{ flexShrink: 0, width: "100%" }}>
<ChatHeader
onMinimize={isMinimized ? handleRestore : handleMinimize}
onClearConversation={onClearConversation}
isMinimized={isMinimized}
messagePanelId={CHAT_MESSAGE_PANEL_ID}
horizontalPadding={chatHorizontalPadding}
/>
</Box>
Expand All @@ -201,10 +186,9 @@ const ChatArea: React.FC<ChatAreaProps> = ({
id={CHAT_MESSAGE_PANEL_ID}
role="region"
aria-label="Chat messages"
aria-hidden={isMinimized ? true : undefined}
sx={{
flex: "1 1 auto",
minHeight: 0,
flex: fillHeight ? "1 1 auto" : "0 0 auto",
minHeight: fillHeight ? 0 : undefined,
width: "100%",
overflowY: "auto",
overflowX: "hidden",
Expand All @@ -225,13 +209,12 @@ const ChatArea: React.FC<ChatAreaProps> = ({
width: "100%",
px: chatHorizontalPadding,
py: currentUserMessage ? 1 : 0,
display: isMinimized ? "none" : "flex",
display: "flex",
}}
>
{currentUserMessage ? (
<ChatAreaMessageThread
currentUserMessage={currentUserMessage}
isMinimized={isMinimized}
showProgressTracker={showProgressTracker}
showAuctionStreaming={showAuctionStreaming}
showRecruiterStreaming={showRecruiterStreaming}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import GrafanaSessionLink from "./GrafanaSessionLink"

export interface ChatAreaMessageThreadProps {
currentUserMessage: string
isMinimized: boolean
showProgressTracker: boolean
showAuctionStreaming: boolean
showRecruiterStreaming: boolean
Expand All @@ -45,7 +44,6 @@ export interface ChatAreaMessageThreadProps {

const ChatAreaMessageThread: React.FC<ChatAreaMessageThreadProps> = ({
currentUserMessage,
isMinimized,
showProgressTracker,
showAuctionStreaming,
showRecruiterStreaming,
Expand All @@ -65,8 +63,7 @@ const ChatAreaMessageThread: React.FC<ChatAreaMessageThreadProps> = ({
const hasAgentFinalResponse =
showFinalResponse &&
!hasApiError &&
(isAgentLoading || Boolean(agentResponse?.response?.trim())) &&
!isMinimized
(isAgentLoading || Boolean(agentResponse?.response?.trim()))

return (
<Stack
Expand All @@ -91,53 +88,47 @@ const ChatAreaMessageThread: React.FC<ChatAreaMessageThreadProps> = ({
</StatusMessage>
) : null}

{!isMinimized && currentUserMessage.trim() ? (
{currentUserMessage.trim() ? (
<UserMessage content={currentUserMessage} />
) : null}

{showProgressTracker && (
<Box sx={{ width: "100%", display: isMinimized ? "none" : "block" }}>
<GroupCommunicationFeed
isVisible={!isMinimized && showProgressTracker}
onComplete={onStreamComplete}
onSenderHighlight={onSenderHighlight}
graphConfig={graphConfig}
prompt={currentUserMessage}
executionKey={executionKey}
apiError={hasApiError}
observabilitySessionId={observabilitySessionId}
/>
</Box>
)}
{showProgressTracker ? (
<GroupCommunicationFeed
isVisible={showProgressTracker}
onComplete={onStreamComplete}
onSenderHighlight={onSenderHighlight}
graphConfig={graphConfig}
prompt={currentUserMessage}
executionKey={executionKey}
apiError={hasApiError}
observabilitySessionId={observabilitySessionId}
/>
) : null}

{showAuctionStreaming && (
<Box sx={{ width: "100%", display: isMinimized ? "none" : "block" }}>
<AuctionStreamingFeed
isVisible={!isMinimized && showAuctionStreaming}
prompt={currentUserMessage}
apiError={hasApiError}
auctionStreamingState={auctionState}
onSenderHighlight={onSenderHighlight}
graphConfig={graphConfig}
observabilitySessionId={observabilitySessionId}
/>
</Box>
)}
{showAuctionStreaming ? (
<AuctionStreamingFeed
isVisible={showAuctionStreaming}
prompt={currentUserMessage}
apiError={hasApiError}
auctionStreamingState={auctionState}
onSenderHighlight={onSenderHighlight}
graphConfig={graphConfig}
observabilitySessionId={observabilitySessionId}
/>
) : null}

{showRecruiterStreaming && (
<Box sx={{ width: "100%", display: isMinimized ? "none" : "block" }}>
<RecruiterStreamingFeed
isVisible={!isMinimized && showRecruiterStreaming}
prompt={currentUserMessage}
apiError={hasApiError}
recruiterStreamingState={recruiterState}
onStreamComplete={onStreamComplete}
onSenderHighlight={onSenderHighlight}
graphConfig={graphConfig}
observabilitySessionId={observabilitySessionId}
/>
</Box>
)}
{showRecruiterStreaming ? (
<RecruiterStreamingFeed
isVisible={showRecruiterStreaming}
prompt={currentUserMessage}
apiError={hasApiError}
recruiterStreamingState={recruiterState}
onStreamComplete={onStreamComplete}
onSenderHighlight={onSenderHighlight}
graphConfig={graphConfig}
observabilitySessionId={observabilitySessionId}
/>
) : null}

{hasAgentFinalResponse ? (
<Message icon={<ChatAgentAvatar />}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@

import React from "react"
import DeleteOutline from "@mui/icons-material/DeleteOutline"
import UnfoldLess from "@mui/icons-material/UnfoldLess"
import UnfoldMore from "@mui/icons-material/UnfoldMore"
import type { Theme } from "@mui/material/styles"
import { Box, IconButton, Tooltip } from "@open-ui-kit/core"

Expand All @@ -29,19 +27,12 @@ function chatHeaderIconButtonSx(theme: Theme) {
}

interface ChatHeaderProps {
onMinimize?: () => void
onClearConversation?: () => void
isMinimized?: boolean
/** ID of the expandable message panel controlled by the minimize button. */
messagePanelId?: string
horizontalPadding?: { xs: number; sm: number; md: number; lg: number }
}

const ChatHeader: React.FC<ChatHeaderProps> = ({
onMinimize,
onClearConversation,
isMinimized = false,
messagePanelId,
horizontalPadding = { xs: 2, sm: 4, md: 8, lg: 15 },
}) => {
return (
Expand All @@ -50,43 +41,22 @@ const ChatHeader: React.FC<ChatHeaderProps> = ({
display: "flex",
width: "100%",
alignItems: "center",
justifyContent: "space-between",
justifyContent: "flex-end",
px: horizontalPadding,
py: 1,
}}
>
<Box sx={{ display: "flex", alignItems: "center" }}>
{onMinimize ? (
<Tooltip title={isMinimized ? "Maximize" : "Minimize"}>
<IconButton
onClick={onMinimize}
aria-label={isMinimized ? "Maximize" : "Minimize"}
aria-expanded={!isMinimized}
aria-controls={messagePanelId}
sx={chatHeaderIconButtonSx}
>
{isMinimized ? (
<UnfoldMore aria-hidden />
) : (
<UnfoldLess aria-hidden />
)}
</IconButton>
</Tooltip>
) : null}
</Box>
<Box sx={{ display: "flex", alignItems: "center" }}>
{onClearConversation ? (
<Tooltip title="Clear conversation">
<IconButton
onClick={onClearConversation}
aria-label="Clear conversation"
sx={chatHeaderIconButtonSx}
>
<DeleteOutline />
</IconButton>
</Tooltip>
) : null}
</Box>
{onClearConversation ? (
<Tooltip title="Clear conversation">
<IconButton
onClick={onClearConversation}
aria-label="Clear conversation"
sx={chatHeaderIconButtonSx}
>
<DeleteOutline />
</IconButton>
</Tooltip>
) : null}
</Box>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Copyright AGNTCY Contributors (https://github.com/agntcy)
* SPDX-License-Identifier: Apache-2.0
**/

import React from "react"

import ResizablePanelSeparator from "@/components/layout/ResizablePanelSeparator"

type ChatPanelSeparatorProps = {
disabled?: boolean
}

const ChatPanelSeparator: React.FC<ChatPanelSeparatorProps> = ({
disabled = false,
}) => (
<ResizablePanelSeparator
id="chat-panel-separator"
aria-label="Resize agent chat"
orientation="vertical"
disabled={disabled}
/>
)

export default ChatPanelSeparator
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Copyright AGNTCY Contributors (https://github.com/agntcy)
* SPDX-License-Identifier: Apache-2.0
*
* Resizable graph/chat panel ids and size constraints (react-resizable-panels).
*/

import {
RESIZABLE_PANEL_MAX_SIZE,
RESIZABLE_PANEL_MIN_SIZE,
} from "@/components/layout/resizablePanelLayout"

export const MAIN_VERTICAL_GROUP_ID = "main-vertical"

export const GRAPH_PANEL_ID = "graph"
export const CHAT_PANEL_ID = "chat"

/** Room for header, composer, and a short scrollable message area. */
export const CHAT_MIN_SIZE = "12rem"

export const CHAT_MAX_SIZE = RESIZABLE_PANEL_MAX_SIZE

/** Keeps at least a quarter of the main column for the graph when chat is tall. */
export const GRAPH_MIN_SIZE = RESIZABLE_PANEL_MIN_SIZE

export const CHAT_PANEL_AUTO_SIZE_MAX_ATTEMPTS = 10
Loading
Loading