Lots of UI fixes
This commit is contained in:
parent
889a3f9e3b
commit
5f184b4a1d
@ -1,5 +1,3 @@
|
||||
# About Backstory
|
||||
|
||||
The backstory about Backstory...
|
||||
|
||||
## Backstory is two things
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
||||
import React, { ReactElement, JSXElementConstructor, useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import Card from '@mui/material/Card';
|
||||
import { styled } from '@mui/material/styles';
|
||||
@ -15,6 +15,7 @@ import Box from '@mui/material/Box';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { SxProps } from '@mui/material';
|
||||
|
||||
|
||||
import { ResumeBuilder } from './ResumeBuilder';
|
||||
@ -24,6 +25,7 @@ import { VectorVisualizer } from './VectorVisualizer';
|
||||
import { Controls } from './Controls';
|
||||
import { Conversation, ConversationHandle } from './Conversation';
|
||||
import { Scrollable } from './AutoScroll';
|
||||
import { BackstoryTab } from './BackstoryTab';
|
||||
|
||||
import './App.css';
|
||||
import './Conversation.css';
|
||||
@ -43,34 +45,20 @@ const getConnectionBase = (loc: any): string => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface TabPanelProps {
|
||||
children?: React.ReactNode;
|
||||
index: number;
|
||||
tab: number;
|
||||
}
|
||||
|
||||
function CustomTabPanel(props: TabPanelProps) {
|
||||
const { children, tab, index, ...other } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="TabPanel"
|
||||
role="tabpanel"
|
||||
style={{ "display": tab === index ? "flex": "none" }}
|
||||
id={`tabpanel-${index}`}
|
||||
aria-labelledby={`tab-${index}`}
|
||||
{...other}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
interface TabProps {
|
||||
label?: string,
|
||||
path: string,
|
||||
tabProps?: {
|
||||
label?: string,
|
||||
sx?: SxProps,
|
||||
icon?: string | ReactElement<unknown, string | JSXElementConstructor<any>> | undefined,
|
||||
iconPosition?: "bottom" | "top" | "start" | "end" | undefined
|
||||
}
|
||||
};
|
||||
|
||||
const App = () => {
|
||||
const [sessionId, setSessionId] = useState<string | undefined>(undefined);
|
||||
const [connectionBase,] = useState<string>(getConnectionBase(window.location))
|
||||
const [selectedPath, setSelectedPath] = useState<string>("");
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const [isMenuClosing, setIsMenuClosing] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<number>(0);
|
||||
@ -132,15 +120,154 @@ const App = () => {
|
||||
};
|
||||
|
||||
|
||||
// Extract the sessionId from the URL if present, otherwise
|
||||
// request a sessionId from the server.
|
||||
const validPaths = useMemo(() => ['chat', 'notes', 'tasks'], []); // allowed paths
|
||||
const tabs: TabProps[] = useMemo(() => {
|
||||
const backstoryPreamble: MessageList = [
|
||||
{
|
||||
role: 'content',
|
||||
title: 'Welcome to Backstory',
|
||||
content: `
|
||||
Backstory is a RAG enabled expert system with access to real-time data running self-hosted
|
||||
(no cloud) versions of industry leading Large and Small Language Models (LLM/SLMs).
|
||||
It was written by James Ketrenos in order to provide answers to
|
||||
questions potential employers may have about his work history.
|
||||
|
||||
What would you like to know about James?
|
||||
`
|
||||
}
|
||||
];
|
||||
|
||||
const backstoryQuestions = [
|
||||
<Box sx={{ display: "flex", flexDirection: isMobile ? "column" : "row" }}>
|
||||
<ChatQuery text="What is James Ketrenos' work history?" submitQuery={handleSubmitChatQuery} />
|
||||
<ChatQuery text="What programming languages has James used?" submitQuery={handleSubmitChatQuery} />
|
||||
<ChatQuery text="What are James' professional strengths?" submitQuery={handleSubmitChatQuery} />
|
||||
<ChatQuery text="What are today's headlines on CNBC.com?" submitQuery={handleSubmitChatQuery} />
|
||||
</Box>,
|
||||
<Box sx={{ p: 1 }}>
|
||||
<MuiMarkdown>
|
||||
As with all LLM interactions, the results may not be 100% accurate. If you have questions about my career,
|
||||
I'd love to hear from you. You can send me an email at **james_backstory@ketrenos.com**.
|
||||
</MuiMarkdown>
|
||||
</Box>
|
||||
];
|
||||
|
||||
const tabSx = { flexGrow: 1, fontSize: '1rem' };
|
||||
|
||||
return [{
|
||||
label: "",
|
||||
path: "",
|
||||
tabProps: {
|
||||
label: "Backstory",
|
||||
sx: tabSx,
|
||||
icon:
|
||||
<Avatar sx={{
|
||||
width: 24,
|
||||
height: 24
|
||||
}}
|
||||
variant="rounded"
|
||||
alt="Backstory logo"
|
||||
src="/logo192.png" />,
|
||||
iconPosition: "start"
|
||||
},
|
||||
children: (
|
||||
<Scrollable
|
||||
sx={{
|
||||
maxWidth: "1024px",
|
||||
height: "calc(100vh - 72px)",
|
||||
}}
|
||||
>
|
||||
<Conversation
|
||||
ref={chatRef}
|
||||
{...{
|
||||
type: "chat",
|
||||
prompt: "What would you like to know about James?",
|
||||
sessionId,
|
||||
connectionBase,
|
||||
setSnack,
|
||||
preamble: backstoryPreamble,
|
||||
defaultPrompts: backstoryQuestions
|
||||
}}
|
||||
/>
|
||||
</Scrollable>
|
||||
)
|
||||
}, {
|
||||
label: "Resume Builder",
|
||||
path: "resume-builder",
|
||||
children: (
|
||||
<ResumeBuilder sx={{
|
||||
margin: "0 auto",
|
||||
height: "calc(100vh - 72px)",
|
||||
overflow: "auto",
|
||||
backgroundColor: "#F5F5F5",
|
||||
display: "flex",
|
||||
flexGrow: 1
|
||||
}} {...{ setSnack, connectionBase, sessionId }}
|
||||
/>
|
||||
)
|
||||
}, {
|
||||
label: "Context Visualizer",
|
||||
path: "context-visualizer",
|
||||
children:
|
||||
<Scrollable
|
||||
sx={{
|
||||
maxWidth: "1024px",
|
||||
height: "calc(100vh - 72px)",
|
||||
}}
|
||||
>
|
||||
<VectorVisualizer sx={{ p: 1 }} {...{ connectionBase, sessionId, setSnack }} />
|
||||
</Scrollable>
|
||||
}, {
|
||||
label: "About",
|
||||
path: "about",
|
||||
children: (
|
||||
<Scrollable
|
||||
sx={{
|
||||
maxWidth: "1024px",
|
||||
height: "calc(100vh - 72px)",
|
||||
flexDirection: "column",
|
||||
margin: "0 auto",
|
||||
p: 1,
|
||||
}}
|
||||
>
|
||||
<Message
|
||||
{...{
|
||||
sx: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
p: 1,
|
||||
m: 0,
|
||||
flexGrow: 0,
|
||||
},
|
||||
message: { role: 'content', title: "About Backstory", content: about },
|
||||
submitQuery: handleSubmitChatQuery,
|
||||
connectionBase,
|
||||
sessionId,
|
||||
setSnack
|
||||
}} />
|
||||
<Box sx={{ display: "flex", flexGrow: 1, p: 0, m: 0 }} />
|
||||
</Scrollable>
|
||||
)
|
||||
}, {
|
||||
path: "settings",
|
||||
tabProps: {
|
||||
sx: { flexShrink: 1, flexGrow: 0, fontSize: '1rem' },
|
||||
icon: <SettingsIcon />
|
||||
},
|
||||
children: (
|
||||
<Box className="ChatBox">
|
||||
{sessionId !== undefined &&
|
||||
<Controls {...{ sessionId, setSnack, connectionBase }} />
|
||||
}
|
||||
</Box >
|
||||
)
|
||||
}];
|
||||
}, [about, connectionBase, sessionId, setSnack, isMobile]);
|
||||
|
||||
useEffect(() => {
|
||||
const url = new URL(window.location.href);
|
||||
const pathParts = url.pathname.split('/').filter(Boolean); // [path, sessionId]
|
||||
|
||||
const fetchSession = async (pathOverride?: string) => {
|
||||
const fetchSession = async () => {
|
||||
try {
|
||||
const response = await fetch(connectionBase + `/api/context`, {
|
||||
method: 'POST',
|
||||
@ -155,30 +282,29 @@ const App = () => {
|
||||
const data = await response.json();
|
||||
setSessionId(data.id);
|
||||
|
||||
const newPath = pathOverride || 'chat'; // default fallback
|
||||
window.history.replaceState({}, '', `/${newPath}/${data.id}`);
|
||||
const newPath = `/${data.id}`;
|
||||
window.history.replaceState({}, '', newPath);
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
setSnack("Server is temporarily down", "error");
|
||||
}
|
||||
};
|
||||
|
||||
if (pathParts.length < 2) {
|
||||
if (pathParts.length < 1) {
|
||||
console.log("No session id or path -- creating new session");
|
||||
fetchSession();
|
||||
} else {
|
||||
const currentPath = pathParts[0];
|
||||
const session = pathParts[1];
|
||||
|
||||
if (!validPaths.includes(currentPath)) {
|
||||
const currentPath = pathParts.length < 2 ? '' : pathParts[0];
|
||||
const session = pathParts.length < 2 ? pathParts[0] : pathParts[1];
|
||||
let tabIndex = tabs.findIndex((tab) => tab.path === currentPath);
|
||||
if (-1 === tabIndex) {
|
||||
console.log(`Invalid path "${currentPath}" -- redirecting to default`);
|
||||
fetchSession(); // or you could window.location.replace if you want
|
||||
} else {
|
||||
console.log(`Path: ${currentPath}, Session id: ${session}`);
|
||||
tabIndex = 0;
|
||||
}
|
||||
setSessionId(session);
|
||||
setSelectedPath(currentPath);
|
||||
setActiveTab(tabIndex);
|
||||
}
|
||||
}
|
||||
}, [setSessionId, setSelectedPath, connectionBase, setSnack, validPaths]);
|
||||
}, [setSessionId, connectionBase, setSnack, tabs]);
|
||||
|
||||
const handleMenuClose = () => {
|
||||
setIsMenuClosing(true);
|
||||
@ -196,159 +322,38 @@ const App = () => {
|
||||
};
|
||||
|
||||
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
|
||||
if (newValue > tabs.length) {
|
||||
return;
|
||||
}
|
||||
setActiveTab(newValue);
|
||||
const tabPath = tabs[newValue].path;
|
||||
if (tabPath) {
|
||||
window.history.pushState({}, '', `/${tabPath}/${sessionId}`);
|
||||
} else {
|
||||
window.history.pushState({}, '', `/${sessionId}`);
|
||||
}
|
||||
handleMenuClose();
|
||||
};
|
||||
|
||||
const handleTabSelect = (newPath: string) => {
|
||||
if (!sessionId) return; // safety
|
||||
setSelectedPath(newPath);
|
||||
window.history.pushState({}, '', `/${newPath}/${sessionId}`);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handlePopState = () => {
|
||||
const url = new URL(window.location.href);
|
||||
const pathParts = url.pathname.split('/').filter(Boolean);
|
||||
const currentPath = pathParts.length < 2 ? '' : pathParts[0];
|
||||
const session = pathParts.length < 2 ? pathParts[0] : pathParts[1];
|
||||
|
||||
if (pathParts.length >= 2) {
|
||||
const path = pathParts[0];
|
||||
const session = pathParts[1];
|
||||
|
||||
if (validPaths.includes(path)) {
|
||||
setSelectedPath(path);
|
||||
let tabIndex = tabs.findIndex((tab) => tab.path === currentPath);
|
||||
if (-1 === tabIndex) {
|
||||
console.log(`Invalid path "${currentPath}" -- redirecting to default`);
|
||||
tabIndex = 0;
|
||||
}
|
||||
setSessionId(session);
|
||||
}
|
||||
}
|
||||
setActiveTab(tabIndex);
|
||||
};
|
||||
|
||||
window.addEventListener('popstate', handlePopState);
|
||||
return () => window.removeEventListener('popstate', handlePopState);
|
||||
}, [setSelectedPath, setSessionId, validPaths]);
|
||||
|
||||
const menuDrawer = (
|
||||
<Card className="MenuCard">
|
||||
<Tabs sx={{ display: "flex", flexGrow: 1 }}
|
||||
orientation="vertical"
|
||||
value={activeTab}
|
||||
indicatorColor="secondary"
|
||||
textColor="inherit"
|
||||
variant="scrollable"
|
||||
allowScrollButtonsMobile
|
||||
onChange={handleTabChange}
|
||||
aria-label="Backstory navigation">
|
||||
<Tab sx={{ fontSize: '1rem' }} label="Backstory"
|
||||
value={0}
|
||||
icon={
|
||||
<Avatar sx={{
|
||||
width: 24,
|
||||
height: 24
|
||||
}}
|
||||
variant="rounded"
|
||||
alt="Backstory logo"
|
||||
src="/logo192.png" />
|
||||
}
|
||||
iconPosition="start" />
|
||||
<Tab
|
||||
value={1}
|
||||
sx={{ fontSize: '1rem' }} wrapped
|
||||
label="Resume Builder"
|
||||
/>
|
||||
<Tab
|
||||
value={2}
|
||||
sx={{ fontSize: '1rem' }} wrapped
|
||||
label="Context Visualizer"
|
||||
/>
|
||||
<Tab
|
||||
value={3}
|
||||
sx={{ fontSize: '1rem' }} label="About" />
|
||||
<Tab
|
||||
value={4}
|
||||
sx={{ fontSize: '1rem' }} icon={<SettingsIcon />} />
|
||||
</Tabs>
|
||||
</Card>
|
||||
);
|
||||
|
||||
const tabs = useMemo(() => {
|
||||
const chatPreamble: MessageList = [
|
||||
{
|
||||
role: 'content',
|
||||
title: 'Welcome to Backstory',
|
||||
content: `
|
||||
Backstory is a RAG enabled expert system with access to real-time data running self-hosted
|
||||
(no cloud) versions of industry leading Large and Small Language Models (LLM/SLMs).
|
||||
It was written by James Ketrenos in order to provide answers to
|
||||
questions potential employers may have about his work history.
|
||||
|
||||
What would you like to know about James?
|
||||
`
|
||||
}
|
||||
];
|
||||
|
||||
const chatQuestions = [
|
||||
<Box sx={{ display: "flex", flexDirection: isMobile ? "column" : "row" }}>
|
||||
<ChatQuery text="What is James Ketrenos' work history?" submitQuery={handleSubmitChatQuery} />
|
||||
<ChatQuery text="What programming languages has James used?" submitQuery={handleSubmitChatQuery} />
|
||||
<ChatQuery text="What are James' professional strengths?" submitQuery={handleSubmitChatQuery} />
|
||||
<ChatQuery text="What are today's headlines on CNBC.com?" submitQuery={handleSubmitChatQuery} />
|
||||
</Box>,
|
||||
<Box sx={{ p: 1 }}>
|
||||
<MuiMarkdown>
|
||||
As with all LLM interactions, the results may not be 100% accurate. If you have questions about my career,
|
||||
I'd love to hear from you. You can send me an email at **james_backstory@ketrenos.com**.
|
||||
</MuiMarkdown>
|
||||
</Box>
|
||||
];
|
||||
|
||||
return [
|
||||
<Scrollable
|
||||
sx={{
|
||||
maxWidth: "1024px",
|
||||
height: "calc(100vh - 72px)",
|
||||
}}
|
||||
>
|
||||
<Conversation
|
||||
ref={chatRef}
|
||||
{...{
|
||||
type: "chat",
|
||||
prompt: "What would you like to know about James?",
|
||||
sessionId,
|
||||
connectionBase,
|
||||
setSnack,
|
||||
preamble: chatPreamble,
|
||||
defaultPrompts: chatQuestions
|
||||
}}
|
||||
/>
|
||||
</Scrollable>,
|
||||
<ResumeBuilder sx={{
|
||||
margin: "0 auto",
|
||||
height: "calc(100vh - 72px)",
|
||||
overflow: "auto",
|
||||
backgroundColor: "#F5F5F5",
|
||||
display: "flex",
|
||||
flexGrow: 1
|
||||
}} {...{ setSnack, connectionBase, sessionId }} />,
|
||||
<Scrollable
|
||||
sx={{
|
||||
maxWidth: "1024px",
|
||||
height: "calc(100vh - 72px)",
|
||||
}}
|
||||
>
|
||||
<VectorVisualizer {...{ connectionBase, sessionId, setSnack }} />
|
||||
</Scrollable>,
|
||||
<Box className="ChatBox">
|
||||
<Box className="Conversation">
|
||||
<Message {...{ message: { role: 'content', content: about }, submitQuery: handleSubmitChatQuery, connectionBase, sessionId, setSnack }} />
|
||||
</Box>
|
||||
</Box>,
|
||||
<Box className="ChatBox">
|
||||
{sessionId !== undefined &&
|
||||
<Controls {...{ sessionId, setSnack, connectionBase }} />
|
||||
}
|
||||
</Box>
|
||||
];
|
||||
}, [about, connectionBase, sessionId, setSnack, isMobile]);
|
||||
|
||||
}, [setSessionId, tabs]);
|
||||
|
||||
/* toolbar height is 64px + 8px margin-top */
|
||||
const Offset = styled('div')(() => ({ minHeight: '72px', height: '72px' }));
|
||||
@ -406,34 +411,7 @@ const App = () => {
|
||||
allowScrollButtonsMobile
|
||||
onChange={handleTabChange}
|
||||
aria-label="Backstory navigation">
|
||||
<Tab sx={{ fontSize: '1rem' }} label="Backstory"
|
||||
value={0}
|
||||
icon={
|
||||
<Avatar sx={{
|
||||
width: 24,
|
||||
height: 24
|
||||
}}
|
||||
variant="rounded"
|
||||
alt="Backstory logo"
|
||||
src="/logo192.png" />
|
||||
}
|
||||
iconPosition="start" />
|
||||
<Tab
|
||||
value={1}
|
||||
sx={{ fontSize: '1rem' }} wrapped
|
||||
label="Resume Builder"
|
||||
/>
|
||||
<Tab
|
||||
value={2}
|
||||
sx={{ fontSize: '1rem' }} wrapped
|
||||
label="Context Visualizer"
|
||||
/>
|
||||
<Tab
|
||||
value={3}
|
||||
sx={{ fontSize: '1rem' }} label="About" />
|
||||
<Tab
|
||||
value={4}
|
||||
sx={{ flexShrink: 1, flexGrow: 0, fontSize: '1rem' }} icon={<SettingsIcon />} />
|
||||
{tabs.map((tab, index) => <Tab key={index} value={index} label={tab.label} {...tab.tabProps} />)}
|
||||
</Tabs>
|
||||
}
|
||||
</Box>
|
||||
@ -467,12 +445,24 @@ const App = () => {
|
||||
}}
|
||||
>
|
||||
<Toolbar />
|
||||
{menuDrawer}
|
||||
<Card className="MenuCard">
|
||||
<Tabs sx={{ display: "flex", flexGrow: 1 }}
|
||||
orientation="vertical"
|
||||
value={activeTab}
|
||||
indicatorColor="secondary"
|
||||
textColor="inherit"
|
||||
variant="scrollable"
|
||||
allowScrollButtonsMobile
|
||||
onChange={handleTabChange}
|
||||
aria-label="Backstory navigation">
|
||||
{tabs.map((tab, index) => <Tab key={index} value={index} label={tab.label} {...tab.tabProps} />)}
|
||||
</Tabs>
|
||||
</Card>
|
||||
</Drawer>
|
||||
</Box>
|
||||
{
|
||||
tabs.map((tab: any, i: number) =>
|
||||
<CustomTabPanel key={i} tab={activeTab} index={i}>{tab}</CustomTabPanel>
|
||||
<BackstoryTab key={i} active={i === activeTab}>{tab.children}</BackstoryTab>
|
||||
)
|
||||
}
|
||||
</Box>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState, RefObject } from 'react';
|
||||
import { useEffect, useRef, RefObject } from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import { SxProps, Theme } from '@mui/material';
|
||||
|
||||
@ -53,15 +53,15 @@ const useAutoScrollToBottom = (
|
||||
behavior: smooth ? 'smooth' : 'auto'
|
||||
});
|
||||
} else {
|
||||
// console.log('Not scrolling', {
|
||||
// isNearBottom,
|
||||
// isUserScrollingUp,
|
||||
// scrollHeight: container.scrollHeight,
|
||||
// scrollTop: container.scrollTop,
|
||||
// clientHeight: container.clientHeight,
|
||||
// threshold,
|
||||
// delta: container.scrollHeight - container.scrollTop - container.clientHeight
|
||||
// });
|
||||
console.log('Not scrolling', {
|
||||
isNearBottom,
|
||||
isUserScrollingUp,
|
||||
scrollHeight: container.scrollHeight,
|
||||
scrollTop: container.scrollTop,
|
||||
clientHeight: container.clientHeight,
|
||||
threshold,
|
||||
delta: container.scrollHeight - container.scrollTop - container.clientHeight
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -19,7 +19,8 @@ interface ChatBubbleProps {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
function ChatBubble({ role, isFullWidth, children, sx, className, title }: ChatBubbleProps) {
|
||||
function ChatBubble(props: ChatBubbleProps) {
|
||||
const { role, isFullWidth, children, sx, className, title } = props;
|
||||
const theme = useTheme();
|
||||
|
||||
const defaultRadius = '16px';
|
||||
@ -122,7 +123,7 @@ function ChatBubble({ role, isFullWidth, children, sx, className, title }: ChatB
|
||||
<Accordion
|
||||
defaultExpanded
|
||||
className={className}
|
||||
sx={{ ...styles[role] }}
|
||||
sx={{ ...styles[role], ...sx }}
|
||||
>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
@ -134,9 +135,7 @@ function ChatBubble({ role, isFullWidth, children, sx, className, title }: ChatB
|
||||
{children}
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -444,7 +444,8 @@ const Conversation = forwardRef<ConversationHandle, ConversationProps>(({
|
||||
<Box className={className || "Conversation"}
|
||||
ref={scrollRef}
|
||||
sx={{
|
||||
display: "flex", flexDirection: "column", flexGrow: 1, p: 1, mt: 0,
|
||||
p: 1,
|
||||
mt: 0,
|
||||
...sx
|
||||
}}>
|
||||
{
|
||||
|
@ -18,6 +18,7 @@ import Collapse from '@mui/material/Collapse';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import { ExpandMore } from './ExpandMore';
|
||||
import { SxProps, Theme } from '@mui/material';
|
||||
|
||||
import { ChatBubble } from './ChatBubble';
|
||||
import { StyledMarkdown } from './StyledMarkdown';
|
||||
@ -61,12 +62,14 @@ interface MessageMetaProps {
|
||||
type MessageList = MessageData[];
|
||||
|
||||
interface MessageProps {
|
||||
sx?: SxProps<Theme>,
|
||||
message?: MessageData,
|
||||
isFullWidth?: boolean,
|
||||
submitQuery?: (text: string) => void,
|
||||
sessionId?: string,
|
||||
connectionBase: string,
|
||||
setSnack: SetSnackType,
|
||||
className?: string,
|
||||
};
|
||||
|
||||
interface ChatQueryInterface {
|
||||
@ -216,7 +219,8 @@ const ChatQuery = ({ text, submitQuery }: ChatQueryInterface) => {
|
||||
);
|
||||
}
|
||||
|
||||
const Message = ({ message, submitQuery, isFullWidth, sessionId, setSnack, connectionBase }: MessageProps) => {
|
||||
const Message = (props: MessageProps) => {
|
||||
const { message, submitQuery, isFullWidth, sessionId, setSnack, connectionBase, sx, className } = props;
|
||||
const [expanded, setExpanded] = useState<boolean>(false);
|
||||
const textFieldRef = useRef(null);
|
||||
|
||||
@ -237,7 +241,7 @@ const Message = ({ message, submitQuery, isFullWidth, sessionId, setSnack, conne
|
||||
|
||||
return (
|
||||
<ChatBubble
|
||||
className="Message"
|
||||
className={className || "Message"}
|
||||
isFullWidth={isFullWidth}
|
||||
role={message.role}
|
||||
title={message.title}
|
||||
@ -246,9 +250,10 @@ const Message = ({ message, submitQuery, isFullWidth, sessionId, setSnack, conne
|
||||
flexDirection: "column",
|
||||
pb: message.metadata ? 0 : "8px",
|
||||
m: 0,
|
||||
mb: 1,
|
||||
mt: 1,
|
||||
marginBottom: "0px !important", // Remove whitespace from expanded Accordion
|
||||
// overflowX: "auto"
|
||||
...sx,
|
||||
}}>
|
||||
<CardContent ref={textFieldRef} sx={{ position: "relative", display: "flex", flexDirection: "column", overflowX: "auto", m: 0, p: 0 }}>
|
||||
<CopyBubble content={message?.content} />
|
||||
|
@ -9,6 +9,7 @@ import Button from '@mui/material/Button';
|
||||
import SendIcon from '@mui/icons-material/Send';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import Switch from '@mui/material/Switch';
|
||||
import { SxProps, Theme } from '@mui/material';
|
||||
|
||||
import { SetSnackType } from './Snack';
|
||||
|
||||
@ -47,6 +48,7 @@ interface VectorVisualizerProps {
|
||||
setSnack: SetSnackType;
|
||||
inline?: boolean;
|
||||
rag?: any;
|
||||
sx?: SxProps<Theme>;
|
||||
}
|
||||
|
||||
interface ChromaResult {
|
||||
@ -98,7 +100,8 @@ const symbolMap: Record<string, string> = {
|
||||
'query': 'circle',
|
||||
};
|
||||
|
||||
const VectorVisualizer: React.FC<VectorVisualizerProps> = ({ setSnack, rag, inline, connectionBase, sessionId }) => {
|
||||
const VectorVisualizer: React.FC<VectorVisualizerProps> = (props: VectorVisualizerProps) => {
|
||||
const { setSnack, rag, inline, connectionBase, sessionId, sx } = props;
|
||||
const [plotData, setPlotData] = useState<PlotData | null>(null);
|
||||
const [newQuery, setNewQuery] = useState<string>('');
|
||||
const [newQueryEmbedding, setNewQueryEmbedding] = useState<ChromaResult | undefined>(undefined);
|
||||
@ -305,7 +308,8 @@ const VectorVisualizer: React.FC<VectorVisualizerProps> = ({ setSnack, rag, inli
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1
|
||||
flexGrow: 1,
|
||||
...sx
|
||||
}}>
|
||||
{
|
||||
!inline &&
|
||||
|
Loading…
x
Reference in New Issue
Block a user