Remove unused components and assets from the project

Deleted unused components (Attachments, CustomMarkdownRenderer, EnableSearchButton, FlyoutSubMenu) and associated styles (katex.css) to streamline the codebase. This cleanup helps reduce technical debt and improve project maintainability.
This commit is contained in:
geoffsee
2025-05-27 14:39:00 -04:00
committed by Geoff Seemueller
parent d90ab65b04
commit ceeefeff14
33 changed files with 623 additions and 2435 deletions

View File

@@ -61,7 +61,6 @@
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"katex": "^0.16.20", "katex": "^0.16.20",
"lucide-react": "^0.436.0", "lucide-react": "^0.436.0",
"manifold-workflow-engine": "^2.0.2",
"marked": "^15.0.4", "marked": "^15.0.4",
"marked-extended-latex": "^1.1.0", "marked-extended-latex": "^1.1.0",
"marked-footnote": "^1.2.4", "marked-footnote": "^1.2.4",
@@ -75,15 +74,8 @@
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-icons": "^5.4.0", "react-icons": "^5.4.0",
"react-markdown": "^9.0.1",
"react-streaming": "^0.3.44", "react-streaming": "^0.3.44",
"react-textarea-autosize": "^8.5.5", "react-textarea-autosize": "^8.5.5",
"rehype-katex": "^7.0.1",
"rehype-react": "^8.0.0",
"remark-gfm": "^4.0.0",
"remark-math": "^6.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.1",
"shiki": "^1.24.0", "shiki": "^1.24.0",
"terser": "^5.39.0", "terser": "^5.39.0",
"typescript": "^5.7.2", "typescript": "^5.7.2",

View File

@@ -4,9 +4,8 @@ import {
welcome_home_text, welcome_home_text,
welcome_home_tip, welcome_home_tip,
} from "../static-data/welcome_home_text"; } from "../static-data/welcome_home_text";
import CustomMarkdownRenderer, { import {renderMarkdown} from "./markdown/MarkdownComponent";
WelcomeHomeMarkdownRenderer,
} from "./chat/CustomMarkdownRenderer";
function WelcomeHomeMessage({ visible }) { function WelcomeHomeMessage({ visible }) {
const containerVariants = { const containerVariants = {
@@ -60,7 +59,7 @@ function WelcomeHomeMessage({ visible }) {
> >
<Box userSelect={"none"}> <Box userSelect={"none"}>
<motion.div variants={textVariants}> <motion.div variants={textVariants}>
<WelcomeHomeMarkdownRenderer markdown={welcome_home_text} /> {renderMarkdown(welcome_home_text)}
</motion.div> </motion.div>
</Box> </Box>
</motion.div> </motion.div>
@@ -73,7 +72,7 @@ function WelcomeHomeMessage({ visible }) {
color="text.secondary" color="text.secondary"
mt={1} mt={1}
> >
<CustomMarkdownRenderer markdown={welcome_home_tip} /> {renderMarkdown(welcome_home_tip)}
</Box> </Box>
</motion.div> </motion.div>
</VStack> </VStack>

View File

@@ -1,40 +0,0 @@
import React from "react";
import { IconButton, Tag, TagCloseButton, TagLabel } from "@chakra-ui/react";
import { PaperclipIcon } from "lucide-react";
// Add a new component for UploadedItem
export const UploadedItem: React.FC<{
url: string;
onRemove: () => void;
name: string;
}> = ({ url, onRemove, name }) => (
<Tag size="md" borderRadius="full" variant="solid" colorScheme="teal">
<TagLabel>{name || url.split("/").pop()}</TagLabel>
<TagCloseButton onClick={onRemove} />
</Tag>
);
export const AttachmentButton: React.FC<{
onClick: () => void;
disabled: boolean;
}> = ({ onClick, disabled }) => (
<IconButton
aria-label="Attach"
title="Attach"
bg="transparent"
color="text.tertiary"
icon={<PaperclipIcon size={"1.3337rem"} />}
onClick={onClick}
_hover={{
bg: "transparent",
svg: {
stroke: "accent.secondary",
transition: "stroke 0.3s ease-in-out",
},
}}
variant="ghost"
size="sm"
isDisabled={disabled}
_focus={{ boxShadow: "none" }}
/>
);

View File

@@ -1,11 +1,11 @@
import React, { useEffect, useRef, useState } from "react"; import React, { useEffect, useRef, useState } from "react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { Box, Grid, GridItem } from "@chakra-ui/react"; import { Box, Grid, GridItem } from "@chakra-ui/react";
import ChatMessages from "./ChatMessages"; import ChatMessages from "./messages/ChatMessages";
import ChatInput from "./ChatInput"; import ChatInput from "./input/ChatInput";
import chatStore from "../../stores/ClientChatStore"; import chatStore from "../../stores/ClientChatStore";
import menuState from "../../stores/AppMenuStore"; import menuState from "../../stores/AppMenuStore";
import WelcomeHomeMessage from "../WelcomeHomeMessage"; import WelcomeHome from "../WelcomeHome";
const Chat = observer(({ height, width }) => { const Chat = observer(({ height, width }) => {
const scrollRef = useRef(); const scrollRef = useRef();
@@ -26,7 +26,7 @@ const Chat = observer(({ height, width }) => {
gap={0} gap={0}
> >
<GridItem alignSelf="center" hidden={!(chatStore.messages.length < 1)}> <GridItem alignSelf="center" hidden={!(chatStore.messages.length < 1)}>
<WelcomeHomeMessage visible={chatStore.messages.length < 1} /> <WelcomeHome visible={chatStore.messages.length < 1} />
</GridItem> </GridItem>
<GridItem <GridItem

View File

@@ -1,22 +0,0 @@
import React from "react";
import { renderCustomComponents } from "./renderCustomComponents";
import { renderCustomComponents as renderWelcomeHomeMarkdown } from "./RenderWelcomeHomeCustomComponents";
import { Box } from "@chakra-ui/react";
interface CustomMarkdownRendererProps {
markdown: string;
}
const CustomMarkdownRenderer: React.FC<CustomMarkdownRendererProps> = ({
markdown,
}) => {
return <div>{renderCustomComponents(markdown)}</div>;
};
export const WelcomeHomeMarkdownRenderer: React.FC<
CustomMarkdownRendererProps
> = ({ markdown }) => {
return <Box color="text.accent">{renderWelcomeHomeMarkdown(markdown)}</Box>;
};
export default CustomMarkdownRenderer;

View File

@@ -1,49 +0,0 @@
import React from "react";
import { IconButton } from "@chakra-ui/react";
import { Globe2Icon } from "lucide-react";
import clientChatStore from "../../stores/ClientChatStore";
import { observer } from "mobx-react-lite";
export const EnableSearchButton: React.FC<{ disabled: boolean }> = observer(
({ disabled }) => {
const onClick = () => {
if (clientChatStore.tools.includes("web-search")) {
clientChatStore.setTools([]);
} else {
clientChatStore.setTools(["web-search"]);
}
};
const isActive = clientChatStore.tools.includes("web-search");
return (
<IconButton
aria-label={isActive ? "Disable Search" : "Enable Search"}
title={isActive ? "Disable Search" : "Enable Search"}
bg="transparent"
color="text.tertiary"
icon={<Globe2Icon size={"1.3337rem"} />}
onClick={onClick}
isActive={isActive}
_active={{
bg: "transparent",
svg: {
stroke: "brand.100",
transition: "stroke 0.3s ease-in-out",
},
}}
_hover={{
bg: "transparent",
svg: {
stroke: "accent.secondary",
transition: "stroke 0.3s ease-in-out",
},
}}
variant="ghost"
size="sm"
isDisabled={disabled}
_focus={{ boxShadow: "none" }}
/>
);
},
);

View File

@@ -1,135 +0,0 @@
import React, { useRef } from "react";
import { observer } from "mobx-react-lite";
import {
Box,
Divider,
HStack,
Menu,
MenuButton,
MenuItem,
MenuList,
Portal,
Text,
useDisclosure,
useOutsideClick,
} from "@chakra-ui/react";
import { ChevronRight } from "lucide-react";
import { useIsMobile } from "../contexts/MobileContext";
const FlyoutSubMenu: React.FC<{
title: string;
flyoutMenuOptions: { name: string; value: string }[];
onClose: () => void;
handleSelect: (item) => Promise<void>;
isSelected?: (item) => boolean;
parentIsOpen: boolean;
}> = observer(
({
title,
flyoutMenuOptions,
onClose,
handleSelect,
isSelected,
parentIsOpen,
}) => {
const { isOpen, onOpen, onClose: onSubMenuClose } = useDisclosure();
const isMobile = useIsMobile();
const menuRef = new useRef();
useOutsideClick({
ref: menuRef,
enabled: !isMobile,
handler: () => {
onSubMenuClose();
},
});
return (
<Menu
placement="right-start"
isOpen={isOpen && parentIsOpen}
closeOnBlur={true}
onClose={() => {
onSubMenuClose();
}}
closeOnSelect={false}
>
<MenuButton
as={MenuItem}
onClick={onOpen}
ref={menuRef}
bg="background.tertiary"
color="text.primary"
_hover={{ bg: "rgba(0, 0, 0, 0.05)" }}
_focus={{ bg: "rgba(0, 0, 0, 0.1)" }}
>
<HStack width={"100%"} justifyContent={"space-between"}>
<Text>{title}</Text>
<ChevronRight size={"1rem"} />
</HStack>
</MenuButton>
<Portal>
<MenuList
key={title}
maxHeight={56}
overflowY="scroll"
visibility={"visible"}
minWidth="180px"
bg="background.tertiary"
boxShadow="lg"
transform="translateY(-50%)"
zIndex={9999}
position="absolute"
left="100%"
bottom={-10}
sx={{
"::-webkit-scrollbar": {
width: "8px",
},
"::-webkit-scrollbar-thumb": {
background: "background.primary",
borderRadius: "4px",
},
"::-webkit-scrollbar-track": {
background: "background.tertiary",
},
}}
>
{flyoutMenuOptions.map((item, index) => (
<Box key={"itemflybox" + index}>
<MenuItem
key={"itemfly" + index}
onClick={() => {
handleSelect(item);
onSubMenuClose();
onClose();
}}
bg={
isSelected(item)
? "background.secondary"
: "background.tertiary"
}
_hover={{ bg: "rgba(0, 0, 0, 0.05)" }}
_focus={{ bg: "rgba(0, 0, 0, 0.1)" }}
>
{item.name}
</MenuItem>
{index < flyoutMenuOptions.length - 1 && (
<Divider
key={item.name + "-divider"}
color="text.tertiary"
w={"100%"}
/>
)}
</Box>
))}
</MenuList>
</Portal>
</Menu>
);
},
);
export default FlyoutSubMenu;

View File

@@ -1,156 +0,0 @@
import React, { useCallback } from "react";
import {
Box,
Button,
Divider,
Flex,
IconButton,
Menu,
MenuButton,
MenuItem,
MenuList,
Text,
useDisclosure,
} from "@chakra-ui/react";
import { observer } from "mobx-react-lite";
import { ChevronDown, Copy, RefreshCcw, Settings } from "lucide-react";
import ClientChatStore from "../../stores/ClientChatStore";
import clientChatStore from "../../stores/ClientChatStore";
import FlyoutSubMenu from "./FlyoutSubMenu";
import { useIsMobile } from "../contexts/MobileContext";
import { getModelFamily, SUPPORTED_MODELS } from "./SupportedModels";
import { formatConversationMarkdown } from "./exportConversationAsMarkdown";
// Common styles for MenuButton and IconButton
export const MsM_commonButtonStyles = {
bg: "transparent",
color: "text.primary",
borderRadius: "full",
padding: 2,
border: "none",
_hover: { bg: "rgba(255, 255, 255, 0.2)" },
_active: { bg: "rgba(255, 255, 255, 0.3)" },
_focus: { boxShadow: "none" },
};
const InputMenu: React.FC<{ isDisabled?: boolean }> = observer(
({ isDisabled }) => {
const isMobile = useIsMobile();
const { isOpen, onOpen, onClose } = useDisclosure();
const textModels = SUPPORTED_MODELS;
const handleCopyConversation = useCallback(() => {
navigator.clipboard
.writeText(formatConversationMarkdown(ClientChatStore.messages))
.then(() => {
window.alert(
"Conversation copied to clipboard. \n\nPaste it somewhere safe!",
);
onClose();
})
.catch((err) => {
console.error("Could not copy text to clipboard: ", err);
window.alert("Failed to copy conversation. Please try again.");
});
}, [onClose]);
async function selectModelFn({ name, value }) {
if (getModelFamily(value)) {
ClientChatStore.setModel(value);
}
}
function isSelectedModelFn({ name, value }) {
return ClientChatStore.model === value;
}
return (
<Menu
isOpen={isOpen}
onClose={onClose}
onOpen={onOpen}
closeOnSelect={false}
closeOnBlur={true}
>
{isMobile ? (
<MenuButton
as={IconButton}
bg="text.accent"
icon={<Settings size={20} />}
isDisabled={isDisabled}
aria-label="Settings"
_hover={{ bg: "rgba(255, 255, 255, 0.2)" }}
_focus={{ boxShadow: "none" }}
{...MsM_commonButtonStyles}
/>
) : (
<MenuButton
as={Button}
rightIcon={<ChevronDown size={16} />}
isDisabled={isDisabled}
variant="ghost"
display="flex"
justifyContent="space-between"
alignItems="center"
minW="auto"
{...MsM_commonButtonStyles}
>
<Text noOfLines={1} maxW="100px" fontSize="sm">
{ClientChatStore.model}
</Text>
</MenuButton>
)}
<MenuList
bg="background.tertiary"
border="none"
borderRadius="md"
boxShadow="lg"
minW={"10rem"}
>
<Divider color="text.tertiary" />
<FlyoutSubMenu
title="Text Models"
flyoutMenuOptions={textModels.map((m) => ({ name: m, value: m }))}
onClose={onClose}
parentIsOpen={isOpen}
handleSelect={selectModelFn}
isSelected={isSelectedModelFn}
/>
<Divider color="text.tertiary" />
{/*Export conversation button*/}
<MenuItem
bg="background.tertiary"
color="text.primary"
onClick={handleCopyConversation}
_hover={{ bg: "rgba(0, 0, 0, 0.05)" }}
_focus={{ bg: "rgba(0, 0, 0, 0.1)" }}
>
<Flex align="center">
<Copy size="16px" style={{ marginRight: "8px" }} />
<Box>Export</Box>
</Flex>
</MenuItem>
{/*New conversation button*/}
<MenuItem
bg="background.tertiary"
color="text.primary"
onClick={() => {
onClose();
clientChatStore.reset();
}}
_hover={{ bg: "rgba(0, 0, 0, 0.05)" }}
_focus={{ bg: "rgba(0, 0, 0, 0.1)" }}
>
<Flex align="center">
<RefreshCcw size="16px" style={{ marginRight: "8px" }} />
<Box>New</Box>
</Flex>
</MenuItem>
</MenuList>
</Menu>
);
},
);
export default InputMenu;

View File

@@ -1,574 +0,0 @@
import React from "react";
import {
Box,
Code,
Divider,
Heading,
Link,
List,
ListItem,
OrderedList,
Table,
Tbody,
Td,
Text,
Th,
Thead,
Tr,
useColorModeValue,
} from "@chakra-ui/react";
import { marked } from "marked";
import CodeBlock from "../code/CodeBlock";
import ImageWithFallback from "./ImageWithFallback";
import markedKatex from "marked-katex-extension";
import katex from "katex";
try {
if (localStorage) {
marked.use(
markedKatex({
nonStandard: false,
displayMode: true,
throwOnError: false,
strict: true,
colorIsTextColor: true,
errorColor: "red",
}),
);
}
} catch (_) {}
const MemoizedCodeBlock = React.memo(CodeBlock);
const getHeadingProps = (depth: number) => {
switch (depth) {
case 1:
return { as: "h1", size: "xl", mt: 4, mb: 2 };
case 2:
return { as: "h2", size: "lg", mt: 3, mb: 2 };
case 3:
return { as: "h3", size: "md", mt: 2, mb: 1 };
case 4:
return { as: "h4", size: "sm", mt: 2, mb: 1 };
case 5:
return { as: "h5", size: "sm", mt: 2, mb: 1 };
case 6:
return { as: "h6", size: "xs", mt: 2, mb: 1 };
default:
return { as: `h${depth}`, size: "md", mt: 2, mb: 1 };
}
};
interface TableToken extends marked.Tokens.Table {
align: Array<"center" | "left" | "right" | null>;
header: (string | marked.Tokens.TableCell)[];
rows: (string | marked.Tokens.TableCell)[][];
}
const CustomHeading: React.FC<{ text: string; depth: number }> = ({
text,
depth,
}) => {
const headingProps = getHeadingProps(depth);
return (
<Heading
{...headingProps}
wordBreak="break-word"
maxWidth="100%"
color="text.accent"
>
{text}
</Heading>
);
};
const CustomParagraph: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
return (
<Text
as="p"
fontSize="sm"
lineHeight="short"
wordBreak="break-word"
maxWidth="100%"
>
{children}
</Text>
);
};
const CustomBlockquote: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
return (
<Box
as="blockquote"
borderLeft="4px solid"
borderColor="gray.200"
fontStyle="italic"
color="gray.600"
pl={4}
maxWidth="100%"
wordBreak="break-word"
mb={2}
>
{children}
</Box>
);
};
const CustomCodeBlock: React.FC<{ code: string; language?: string }> = ({
code,
language,
}) => {
return (
<MemoizedCodeBlock
language={language}
code={code}
onRenderComplete={() => Promise.resolve()}
/>
);
};
const CustomHr: React.FC = () => <Divider my={4} />;
const CustomList: React.FC<{
ordered?: boolean;
start?: number;
children: React.ReactNode;
}> = ({ ordered, start, children }) => {
const commonStyles = {
fontSize: "sm",
wordBreak: "break-word" as const,
maxWidth: "100%" as const,
stylePosition: "outside" as const,
mb: 2,
pl: 4,
};
return ordered ? (
<OrderedList start={start} {...commonStyles}>
{children}
</OrderedList>
) : (
<List styleType="disc" {...commonStyles}>
{children}
</List>
);
};
const CustomListItem: React.FC<{
children: React.ReactNode;
}> = ({ children }) => {
return <ListItem mb={1}>{children}</ListItem>;
};
const CustomKatex: React.FC<{ math: string; displayMode: boolean }> = ({
math,
displayMode,
}) => {
const renderedMath = katex.renderToString(math, { displayMode });
return (
<Box
as="span"
display={displayMode ? "block" : "inline"}
// bg={bg}
p={displayMode ? 4 : 1}
my={displayMode ? 4 : 0}
borderRadius="md"
overflow="auto"
maxWidth="100%"
dangerouslySetInnerHTML={{ __html: renderedMath }}
/>
);
};
const CustomTable: React.FC<{
header: React.ReactNode[];
align: Array<"center" | "left" | "right" | null>;
rows: React.ReactNode[][];
}> = ({ header, align, rows }) => {
return (
<Table
variant="simple"
size="sm"
my={4}
borderRadius="md"
overflow="hidden"
>
<Thead bg="background.secondary">
<Tr>
{header.map((cell, i) => (
<Th
key={i}
textAlign={align[i] || "left"}
fontWeight="bold"
p={2}
minW={16}
wordBreak="break-word"
>
{cell}
</Th>
))}
</Tr>
</Thead>
<Tbody>
{rows.map((row, rIndex) => (
<Tr key={rIndex}>
{row.map((cell, cIndex) => (
<Td
key={cIndex}
textAlign={align[cIndex] || "left"}
p={2}
wordBreak="break-word"
>
{cell}
</Td>
))}
</Tr>
))}
</Tbody>
</Table>
);
};
const CustomHtmlBlock: React.FC<{ content: string }> = ({ content }) => {
return <Box dangerouslySetInnerHTML={{ __html: content }} mb={2} />;
};
const CustomText: React.FC<{ text: React.ReactNode }> = ({ text }) => {
return (
<Text
fontSize="sm"
lineHeight="short"
color="text.accent"
wordBreak="break-word"
maxWidth="100%"
as="span"
>
{text}
</Text>
);
};
interface CustomStrongProps {
children: React.ReactNode;
}
const CustomStrong: React.FC<CustomStrongProps> = ({ children }) => {
return <Text as="strong">{children}</Text>;
};
const CustomEm: React.FC<{ children: React.ReactNode }> = ({ children }) => {
return (
<Text
as="em"
fontStyle="italic"
lineHeight="short"
wordBreak="break-word"
display="inline"
>
{children}
</Text>
);
};
const CustomDel: React.FC<{ text: string }> = ({ text }) => {
return (
<Text
as="del"
textDecoration="line-through"
lineHeight="short"
wordBreak="break-word"
display="inline"
>
{text}
</Text>
);
};
const CustomCodeSpan: React.FC<{ code: string }> = ({ code }) => {
const bg = useColorModeValue("gray.100", "gray.800");
return (
<Code
fontSize="sm"
bg={bg}
overflowX="clip"
borderRadius="md"
wordBreak="break-word"
maxWidth="100%"
p={0.5}
>
{code}
</Code>
);
};
const CustomMath: React.FC<{ math: string; displayMode?: boolean }> = ({
math,
displayMode = false,
}) => {
return (
<Box
as="span"
display={displayMode ? "block" : "inline"}
p={displayMode ? 4 : 1}
my={displayMode ? 4 : 0}
borderRadius="md"
overflow="auto"
maxWidth="100%"
className={`math ${displayMode ? "math-display" : "math-inline"}`}
>
{math}
</Box>
);
};
const CustomLink: React.FC<{
href: string;
title?: string;
children: React.ReactNode;
}> = ({ href, title, children, ...props }) => {
return (
<Link
href={href}
title={title}
isExternal
sx={{
"& span": {
color: "text.link",
},
}}
maxWidth="100%"
color="teal.500"
wordBreak="break-word"
{...props}
>
{children}
</Link>
);
};
const CustomImage: React.FC<{ href: string; text: string; title?: string }> = ({
href,
text,
title,
}) => {
return (
<ImageWithFallback
src={href}
alt={text}
title={title}
maxW="100%"
width="auto"
height="auto"
my={2}
/>
);
};
function parseTokens(tokens: marked.Token[]): JSX.Element[] {
const output: JSX.Element[] = [];
let blockquoteContent: JSX.Element[] = [];
tokens.forEach((token, i) => {
switch (token.type) {
case "heading":
output.push(
<CustomHeading key={i} text={token.text} depth={token.depth} />,
);
break;
case "paragraph": {
const parsedContent = token.tokens
? parseTokens(token.tokens)
: token.text;
if (blockquoteContent.length > 0) {
blockquoteContent.push(
<CustomParagraph key={i}>{parsedContent}</CustomParagraph>,
);
} else {
output.push(
<CustomParagraph key={i}>{parsedContent}</CustomParagraph>,
);
}
break;
}
case "br":
output.push(<br key={i} />);
break;
case "escape": {
break;
}
case "blockquote_start":
blockquoteContent = [];
break;
case "blockquote_end":
output.push(
<CustomBlockquote key={i}>
{parseTokens(blockquoteContent)}
</CustomBlockquote>,
);
blockquoteContent = [];
break;
case "blockquote": {
output.push(
<CustomBlockquote key={i}>
{token.tokens ? parseTokens(token.tokens) : null}
</CustomBlockquote>,
);
break;
}
case "math":
output.push(
<CustomMath key={i} math={(token as any).value} displayMode={true} />,
);
break;
case "inlineMath":
output.push(
<CustomMath
key={i}
math={(token as any).value}
displayMode={false}
/>,
);
break;
case "inlineKatex":
case "blockKatex": {
const katexToken = token as any;
output.push(
<CustomKatex
key={i}
math={katexToken.text}
displayMode={katexToken.displayMode}
/>,
);
break;
}
case "code":
output.push(
<CustomCodeBlock key={i} code={token.text} language={token.lang} />,
);
break;
case "hr":
output.push(<CustomHr key={i} />);
break;
case "list": {
const { ordered, start, items } = token;
const listItems = items.map((listItem, idx) => {
const nestedContent = parseTokens(listItem.tokens);
return <CustomListItem key={idx}>{nestedContent}</CustomListItem>;
});
output.push(
<CustomList key={i} ordered={ordered} start={start}>
{listItems}
</CustomList>,
);
break;
}
case "table": {
const tableToken = token as TableToken;
output.push(
<CustomTable
key={i}
header={tableToken.header.map((cell) =>
typeof cell === "string" ? cell : parseTokens(cell.tokens || []),
)}
align={tableToken.align}
rows={tableToken.rows.map((row) =>
row.map((cell) =>
typeof cell === "string"
? cell
: parseTokens(cell.tokens || []),
),
)}
/>,
);
break;
}
case "html":
output.push(<CustomHtmlBlock key={i} content={token.text} />);
break;
case "def":
case "space":
break;
case "strong":
output.push(
<CustomStrong key={i}>
{parseTokens(token.tokens || [])}
</CustomStrong>,
);
break;
case "em":
output.push(
<CustomEm key={i}>
{token.tokens ? parseTokens(token.tokens) : token.text}
</CustomEm>,
);
break;
case "codespan":
output.push(<CustomCodeSpan key={i} code={token.text} />);
break;
case "link":
output.push(
<CustomLink key={i} href={token.href} title={token.title}>
{token.tokens ? parseTokens(token.tokens) : token.text}
</CustomLink>,
);
break;
case "image":
output.push(
<CustomImage
key={i}
href={token.href}
title={token.title}
text={token.text}
/>,
);
break;
case "text": {
const parsedContent = token.tokens
? parseTokens(token.tokens)
: token.text;
if (blockquoteContent.length > 0) {
blockquoteContent.push(
<React.Fragment key={i}>{parsedContent}</React.Fragment>,
);
} else {
output.push(<CustomText key={i} text={parsedContent} />);
}
break;
}
default:
console.warn("Unhandled token type:", token.type, token);
}
});
return output;
}
export function renderCustomComponents(markdown: string): JSX.Element[] {
marked.setOptions({
breaks: true,
gfm: true,
silent: false,
async: true,
});
const tokens = marked.lexer(markdown);
return parseTokens(tokens);
}

View File

@@ -20,8 +20,8 @@ import clientChatStore from "../../../stores/ClientChatStore";
import FlyoutSubMenu from "./FlyoutSubMenu"; import FlyoutSubMenu from "./FlyoutSubMenu";
import { useIsMobile } from "../../contexts/MobileContext"; import { useIsMobile } from "../../contexts/MobileContext";
import { useIsMobile as useIsMobileUserAgent } from "../../../layout/_IsMobileHook"; import { useIsMobile as useIsMobileUserAgent } from "../../../layout/_IsMobileHook";
import { getModelFamily, SUPPORTED_MODELS } from "../SupportedModels"; import { getModelFamily, SUPPORTED_MODELS } from "../lib/SupportedModels";
import { formatConversationMarkdown } from "../exportConversationAsMarkdown"; import { formatConversationMarkdown } from "../lib/exportConversationAsMarkdown";
export const MsM_commonButtonStyles = { export const MsM_commonButtonStyles = {
bg: "transparent", bg: "transparent",

View File

@@ -7,12 +7,12 @@ import {
useBreakpointValue, useBreakpointValue,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import chatStore from "../../stores/ClientChatStore"; import chatStore from "../../../stores/ClientChatStore";
import InputMenu from "./flyoutmenu/InputMenu"; import InputMenu from "../input-menu/InputMenu";
import InputTextarea from "./ChatInputTextArea"; import InputTextarea from "./ChatInputTextArea";
import SendButton from "./ChatInputSendButton"; import SendButton from "./ChatInputSendButton";
import { useMaxWidth } from "../../layout/useMaxWidth"; import { useMaxWidth } from "../../../layout/useMaxWidth";
import userOptionsStore from "../../stores/UserOptionsStore"; import userOptionsStore from "../../../stores/UserOptionsStore";
const ChatInput = observer(() => { const ChatInput = observer(() => {
const inputRef = useRef<HTMLTextAreaElement>(null); const inputRef = useRef<HTMLTextAreaElement>(null);

View File

@@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import { Button } from "@chakra-ui/react"; import { Button } from "@chakra-ui/react";
import clientChatStore from "../../stores/ClientChatStore"; import clientChatStore from "../../../stores/ClientChatStore";
import { CirclePause, Send } from "lucide-react"; import { CirclePause, Send } from "lucide-react";
import { motion } from "framer-motion"; import { motion } from "framer-motion";

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
// Function to generate a Markdown representation of the current conversation // Function to generate a Markdown representation of the current conversation
import { type IMessage } from "../../stores/ClientChatStore"; import { type IMessage } from "../../../stores/ClientChatStore";
import { Instance } from "mobx-state-tree"; import { Instance } from "mobx-state-tree";
export function formatConversationMarkdown( export function formatConversationMarkdown(

View File

@@ -1,9 +1,9 @@
import React from "react"; import React from "react";
import CustomMarkdownRenderer from "./CustomMarkdownRenderer"; import MessageMarkdownRenderer from "./MessageMarkdownRenderer";
const ChatMessageContent = ({ content }) => { const ChatMessageContent = ({ content }) => {
return <CustomMarkdownRenderer markdown={content} />; return <MessageMarkdownRenderer markdown={content} />;
}; };
export default React.memo(ChatMessageContent); export default React.memo(ChatMessageContent);

View File

@@ -2,8 +2,8 @@ import React from "react";
import {Box, Grid, GridItem} from "@chakra-ui/react"; import {Box, Grid, GridItem} from "@chakra-ui/react";
import MessageBubble from "./MessageBubble"; import MessageBubble from "./MessageBubble";
import {observer} from "mobx-react-lite"; import {observer} from "mobx-react-lite";
import chatStore from "../../stores/ClientChatStore"; import chatStore from "../../../stores/ClientChatStore";
import {useIsMobile} from "../contexts/MobileContext"; import {useIsMobile} from "../../contexts/MobileContext";
interface ChatMessagesProps { interface ChatMessagesProps {
scrollRef: React.RefObject<HTMLDivElement>; scrollRef: React.RefObject<HTMLDivElement>;

View File

@@ -3,11 +3,10 @@ import { motion } from "framer-motion";
import { Box, Flex, Text } from "@chakra-ui/react"; import { Box, Flex, Text } from "@chakra-ui/react";
import MessageRenderer from "./ChatMessageContent"; import MessageRenderer from "./ChatMessageContent";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { IntermediateStepsComponent } from "./IntermediateStepsComponent";
import MessageEditor from "./MessageEditorComponent"; import MessageEditor from "./MessageEditorComponent";
import UserMessageTools from "./UserMessageTools"; import UserMessageTools from "./UserMessageTools";
import clientChatStore from "../../stores/ClientChatStore"; import clientChatStore from "../../../stores/ClientChatStore";
import UserOptionsStore from "../../stores/UserOptionsStore"; import UserOptionsStore from "../../../stores/UserOptionsStore";
const MotionBox = motion(Box); const MotionBox = motion(Box);
@@ -128,7 +127,6 @@ const MessageBubble = observer(({ msg, scrollRef }) => {
}, },
}} }}
> >
<IntermediateStepsComponent hidden={msg.role === "user"} />
{isEditing ? ( {isEditing ? (
<MessageEditor message={msg} onCancel={handleCancelEdit} /> <MessageEditor message={msg} onCancel={handleCancelEdit} />
) : isLoading ? ( ) : isLoading ? (

View File

@@ -2,7 +2,7 @@ import React, { KeyboardEvent, useState } from "react";
import { Box, Flex, IconButton, Textarea } from "@chakra-ui/react"; import { Box, Flex, IconButton, Textarea } from "@chakra-ui/react";
import { Check, X } from "lucide-react"; import { Check, X } from "lucide-react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import store, { type IMessage } from "../../stores/ClientChatStore"; import store, { type IMessage } from "../../../stores/ClientChatStore";
interface MessageEditorProps { interface MessageEditorProps {
message: IMessage; message: IMessage;

View File

@@ -19,10 +19,11 @@ import {
useColorModeValue, useColorModeValue,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { marked } from "marked"; import { marked } from "marked";
import CodeBlock from "../code/CodeBlock"; import CodeBlock from "../../code/CodeBlock";
import ImageWithFallback from "./ImageWithFallback"; import ImageWithFallback from "../../markdown/ImageWithFallback";
import markedKatex from "marked-katex-extension"; import markedKatex from "marked-katex-extension";
import katex from "katex"; import katex from "katex";
import domPurify from "../lib/domPurify";
try { try {
if (localStorage) { if (localStorage) {
@@ -563,7 +564,7 @@ function parseTokens(tokens: marked.Token[]): JSX.Element[] {
return output; return output;
} }
export function renderCustomComponents(markdown: string): JSX.Element[] { export function renderMessageMarkdown(markdown: string): JSX.Element[] {
marked.setOptions({ marked.setOptions({
breaks: true, breaks: true,
gfm: true, gfm: true,
@@ -571,6 +572,6 @@ export function renderCustomComponents(markdown: string): JSX.Element[] {
async: true, async: true,
}); });
const tokens = marked.lexer(markdown); const tokens = marked.lexer(domPurify(markdown));
return parseTokens(tokens); return parseTokens(tokens);
} }

View File

@@ -0,0 +1,14 @@
import React from "react";
import {renderMessageMarkdown} from "./MessageMarkdown";
interface CustomMarkdownRendererProps {
markdown: string;
}
const MessageMarkdownRenderer: React.FC<CustomMarkdownRendererProps> = ({
markdown,
}) => {
return <div>{renderMessageMarkdown(markdown)}</div>;
};
export default MessageMarkdownRenderer;

View File

@@ -1,19 +0,0 @@
import { visit } from "unist-util-visit";
export default function remarkImageGeneration() {
return (tree) => {
visit(tree, "code", (node, index, parent) => {
if (node.lang === "generation") {
try {
const data = JSON.parse(node.value);
parent.children[index] = {
type: "generation",
data: data,
};
} catch (error) {
console.error("Invalid JSON in image-generation block:", error);
}
}
});
};
}

View File

@@ -1,7 +1,6 @@
import React from "react"; import React from "react";
import { Box, VStack } from "@chakra-ui/react"; import { Box, VStack } from "@chakra-ui/react";
import Markdown from "react-markdown"; import {renderMarkdown} from "../markdown/MarkdownComponent";
import { webComponents } from "../react-markdown/WebComponents";
function LegalDoc({ text }) { function LegalDoc({ text }) {
return ( return (
@@ -13,7 +12,7 @@ function LegalDoc({ text }) {
whiteSpace="pre-wrap" whiteSpace="pre-wrap"
spacing={4} spacing={4}
> >
<Markdown components={webComponents}>{text}</Markdown> {renderMarkdown(text)}
</Box> </Box>
</VStack> </VStack>
</Box> </Box>

View File

@@ -0,0 +1,576 @@
import React from "react";
import {
Box,
Code,
Divider,
Heading,
Link,
List,
ListItem,
OrderedList,
Table,
Tbody,
Td,
Text,
Th,
Thead,
Tr,
useColorModeValue,
} from "@chakra-ui/react";
import {marked} from "marked";
import markedKatex from "marked-katex-extension";
import katex from "katex";
import CodeBlock from "../code/CodeBlock";
import ImageWithFallback from "./ImageWithFallback";
try {
if (localStorage) {
marked.use(
markedKatex({
nonStandard: false,
displayMode: true,
throwOnError: false,
strict: true,
colorIsTextColor: true,
errorColor: "red",
}),
);
}
} catch (_) {
}
const MemoizedCodeBlock = React.memo(CodeBlock);
const getHeadingProps = (depth: number) => {
switch (depth) {
case 1:
return {as: "h1", size: "xl", mt: 4, mb: 2};
case 2:
return {as: "h2", size: "lg", mt: 3, mb: 2};
case 3:
return {as: "h3", size: "md", mt: 2, mb: 1};
case 4:
return {as: "h4", size: "sm", mt: 2, mb: 1};
case 5:
return {as: "h5", size: "sm", mt: 2, mb: 1};
case 6:
return {as: "h6", size: "xs", mt: 2, mb: 1};
default:
return {as: `h${depth}`, size: "md", mt: 2, mb: 1};
}
};
interface TableToken extends marked.Tokens.Table {
align: Array<"center" | "left" | "right" | null>;
header: (string | marked.Tokens.TableCell)[];
rows: (string | marked.Tokens.TableCell)[][];
}
const CustomHeading: React.FC<{ text: string; depth: number }> = ({
text,
depth,
}) => {
const headingProps = getHeadingProps(depth);
return (
<Heading
{...headingProps}
wordBreak="break-word"
maxWidth="100%"
color="text.accent"
>
{text}
</Heading>
);
};
const CustomParagraph: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
return (
<Text
as="p"
fontSize="sm"
lineHeight="short"
wordBreak="break-word"
maxWidth="100%"
>
{children}
</Text>
);
};
const CustomBlockquote: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
return (
<Box
as="blockquote"
borderLeft="4px solid"
borderColor="gray.200"
fontStyle="italic"
color="gray.600"
pl={4}
maxWidth="100%"
wordBreak="break-word"
mb={2}
>
{children}
</Box>
);
};
const CustomCodeBlock: React.FC<{ code: string; language?: string }> = ({
code,
language,
}) => {
return (
<MemoizedCodeBlock
language={language}
code={code}
onRenderComplete={() => Promise.resolve()}
/>
);
};
const CustomHr: React.FC = () => <Divider my={4}/>;
const CustomList: React.FC<{
ordered?: boolean;
start?: number;
children: React.ReactNode;
}> = ({ordered, start, children}) => {
const commonStyles = {
fontSize: "sm",
wordBreak: "break-word" as const,
maxWidth: "100%" as const,
stylePosition: "outside" as const,
mb: 2,
pl: 4,
};
return ordered ? (
<OrderedList start={start} {...commonStyles}>
{children}
</OrderedList>
) : (
<List styleType="disc" {...commonStyles}>
{children}
</List>
);
};
const CustomListItem: React.FC<{
children: React.ReactNode;
}> = ({children}) => {
return <ListItem mb={1}>{children}</ListItem>;
};
const CustomKatex: React.FC<{ math: string; displayMode: boolean }> = ({
math,
displayMode,
}) => {
const renderedMath = katex.renderToString(math, {displayMode});
return (
<Box
as="span"
display={displayMode ? "block" : "inline"}
// bg={bg}
p={displayMode ? 4 : 1}
my={displayMode ? 4 : 0}
borderRadius="md"
overflow="auto"
maxWidth="100%"
dangerouslySetInnerHTML={{__html: renderedMath}}
/>
);
};
const CustomTable: React.FC<{
header: React.ReactNode[];
align: Array<"center" | "left" | "right" | null>;
rows: React.ReactNode[][];
}> = ({header, align, rows}) => {
return (
<Table
variant="simple"
size="sm"
my={4}
borderRadius="md"
overflow="hidden"
>
<Thead bg="background.secondary">
<Tr>
{header.map((cell, i) => (
<Th
key={i}
textAlign={align[i] || "left"}
fontWeight="bold"
p={2}
minW={16}
wordBreak="break-word"
>
{cell}
</Th>
))}
</Tr>
</Thead>
<Tbody>
{rows.map((row, rIndex) => (
<Tr key={rIndex}>
{row.map((cell, cIndex) => (
<Td
key={cIndex}
textAlign={align[cIndex] || "left"}
p={2}
wordBreak="break-word"
>
{cell}
</Td>
))}
</Tr>
))}
</Tbody>
</Table>
);
};
const CustomHtmlBlock: React.FC<{ content: string }> = ({content}) => {
return <Box dangerouslySetInnerHTML={{__html: content}} mb={2}/>;
};
const CustomText: React.FC<{ text: React.ReactNode }> = ({text}) => {
return (
<Text
fontSize="sm"
lineHeight="short"
color="text.accent"
wordBreak="break-word"
maxWidth="100%"
as="span"
>
{text}
</Text>
);
};
interface CustomStrongProps {
children: React.ReactNode;
}
const CustomStrong: React.FC<CustomStrongProps> = ({children}) => {
return <Text as="strong">{children}</Text>;
};
const CustomEm: React.FC<{ children: React.ReactNode }> = ({children}) => {
return (
<Text
as="em"
fontStyle="italic"
lineHeight="short"
wordBreak="break-word"
display="inline"
>
{children}
</Text>
);
};
const CustomDel: React.FC<{ text: string }> = ({text}) => {
return (
<Text
as="del"
textDecoration="line-through"
lineHeight="short"
wordBreak="break-word"
display="inline"
>
{text}
</Text>
);
};
const CustomCodeSpan: React.FC<{ code: string }> = ({code}) => {
const bg = useColorModeValue("gray.100", "gray.800");
return (
<Code
fontSize="sm"
bg={bg}
overflowX="clip"
borderRadius="md"
wordBreak="break-word"
maxWidth="100%"
p={0.5}
>
{code}
</Code>
);
};
const CustomMath: React.FC<{ math: string; displayMode?: boolean }> = ({
math,
displayMode = false,
}) => {
return (
<Box
as="span"
display={displayMode ? "block" : "inline"}
p={displayMode ? 4 : 1}
my={displayMode ? 4 : 0}
borderRadius="md"
overflow="auto"
maxWidth="100%"
className={`math ${displayMode ? "math-display" : "math-inline"}`}
>
{math}
</Box>
);
};
const CustomLink: React.FC<{
href: string;
title?: string;
children: React.ReactNode;
}> = ({href, title, children, ...props}) => {
return (
<Link
href={href}
title={title}
isExternal
sx={{
"& span": {
color: "text.link",
},
}}
maxWidth="100%"
color="teal.500"
wordBreak="break-word"
{...props}
>
{children}
</Link>
);
};
const CustomImage: React.FC<{ href: string; text: string; title?: string }> = ({
href,
text,
title,
}) => {
return (
<ImageWithFallback
src={href}
alt={text}
title={title}
maxW="100%"
width="auto"
height="auto"
my={2}
/>
);
};
function parseTokens(tokens: marked.Token[]): JSX.Element[] {
const output: JSX.Element[] = [];
let blockquoteContent: JSX.Element[] = [];
tokens.forEach((token, i) => {
switch (token.type) {
case "heading":
output.push(
<CustomHeading key={i} text={token.text} depth={token.depth}/>,
);
break;
case "paragraph": {
const parsedContent = token.tokens
? parseTokens(token.tokens)
: token.text;
if (blockquoteContent.length > 0) {
blockquoteContent.push(
<CustomParagraph key={i}>{parsedContent}</CustomParagraph>,
);
} else {
output.push(
<CustomParagraph key={i}>{parsedContent}</CustomParagraph>,
);
}
break;
}
case "br":
output.push(<br key={i}/>);
break;
case "escape": {
break;
}
case "blockquote_start":
blockquoteContent = [];
break;
case "blockquote_end":
output.push(
<CustomBlockquote key={i}>
{parseTokens(blockquoteContent)}
</CustomBlockquote>,
);
blockquoteContent = [];
break;
case "blockquote": {
output.push(
<CustomBlockquote key={i}>
{token.tokens ? parseTokens(token.tokens) : null}
</CustomBlockquote>,
);
break;
}
case "math":
output.push(
<CustomMath key={i} math={(token as any).value} displayMode={true}/>,
);
break;
case "inlineMath":
output.push(
<CustomMath
key={i}
math={(token as any).value}
displayMode={false}
/>,
);
break;
case "inlineKatex":
case "blockKatex": {
const katexToken = token as any;
output.push(
<CustomKatex
key={i}
math={katexToken.text}
displayMode={katexToken.displayMode}
/>,
);
break;
}
case "code":
output.push(
<CustomCodeBlock key={i} code={token.text} language={token.lang}/>,
);
break;
case "hr":
output.push(<CustomHr key={i}/>);
break;
case "list": {
const {ordered, start, items} = token;
const listItems = items.map((listItem, idx) => {
const nestedContent = parseTokens(listItem.tokens);
return <CustomListItem key={idx}>{nestedContent}</CustomListItem>;
});
output.push(
<CustomList key={i} ordered={ordered} start={start}>
{listItems}
</CustomList>,
);
break;
}
case "table": {
const tableToken = token as TableToken;
output.push(
<CustomTable
key={i}
header={tableToken.header.map((cell) =>
typeof cell === "string" ? cell : parseTokens(cell.tokens || []),
)}
align={tableToken.align}
rows={tableToken.rows.map((row) =>
row.map((cell) =>
typeof cell === "string"
? cell
: parseTokens(cell.tokens || []),
),
)}
/>,
);
break;
}
case "html":
output.push(<CustomHtmlBlock key={i} content={token.text}/>);
break;
case "def":
case "space":
break;
case "strong":
output.push(
<CustomStrong key={i}>
{parseTokens(token.tokens || [])}
</CustomStrong>,
);
break;
case "em":
output.push(
<CustomEm key={i}>
{token.tokens ? parseTokens(token.tokens) : token.text}
</CustomEm>,
);
break;
case "codespan":
output.push(<CustomCodeSpan key={i} code={token.text}/>);
break;
case "link":
output.push(
<CustomLink key={i} href={token.href} title={token.title}>
{token.tokens ? parseTokens(token.tokens) : token.text}
</CustomLink>,
);
break;
case "image":
output.push(
<CustomImage
key={i}
href={token.href}
title={token.title}
text={token.text}
/>,
);
break;
case "text": {
const parsedContent = token.tokens
? parseTokens(token.tokens)
: token.text;
if (blockquoteContent.length > 0) {
blockquoteContent.push(
<React.Fragment key={i}>{parsedContent}</React.Fragment>,
);
} else {
output.push(<CustomText key={i} text={parsedContent}/>);
}
break;
}
default:
console.warn("Unhandled token type:", token.type, token);
}
});
return output;
}
export function renderMarkdown(markdown: string): JSX.Element[] {
marked.setOptions({
breaks: true,
gfm: true,
silent: false,
async: true,
});
const tokens = marked.lexer(markdown);
return parseTokens(tokens);
}

View File

@@ -1,123 +0,0 @@
import React from "react";
import {
Box,
Divider,
Heading,
Link,
List,
ListItem,
OrderedList,
Text,
UnorderedList,
} from "@chakra-ui/react";
import ImageWithFallback from "../chat/ImageWithFallback";
import { MdCheckCircle } from "react-icons/md";
export const webComponents = {
p: ({ children }) => (
<Text fontSize="sm" lineHeight="short">
{children}
</Text>
),
strong: ({ children }) => <strong>{children}</strong>,
h1: ({ children }) => (
<Heading as="h1" size="xl" mt={4} mb={2}>
{children}
</Heading>
),
h2: ({ children }) => (
<Heading as="h2" size="lg" mt={3} mb={2}>
{children}
</Heading>
),
h3: ({ children }) => (
<Heading as="h3" size="md" mt={2} mb={1}>
{children}
</Heading>
),
h4: ({ children }) => (
<Heading as="h4" size="sm" mt={2} mb={1}>
{children}
</Heading>
),
ul: ({ children }) => (
<UnorderedList
// pl={3}
// mb={2}
fontSize="sm"
// stylePosition="inside" // Keep bullets inside the text flow
>
{children}
</UnorderedList>
),
ol: ({ children }) => (
<OrderedList fontSize="sm" spacing={2}>
{children}
</OrderedList>
),
li: ({ children, ...rest }) => {
const filteredChildren = React.Children.toArray(children)
.filter((child) => !(typeof child === "string" && child.trim() === "\n"))
.map((child, index, array) => {
// if (typeof child === 'string' && index === array.length - 1 && /\n/.test(child)) {
// return '\n';
// }
return child;
});
return <ListItem {...rest}>{filteredChildren}</ListItem>;
},
pre: ({ children }) => (
<Box
as="pre"
whiteSpace="pre-wrap"
bg="background.secondary"
borderRadius="md"
fontSize="sm"
>
{children}
</Box>
),
blockquote: ({ children }) => (
<Box
as="blockquote"
borderLeft="4px solid"
borderColor="gray.200"
fontStyle="italic"
color="gray.600"
pl={4}
>
{children}
</Box>
),
hr: () => <Divider my={4} />,
a: ({ href, children }) => (
<Link
color="teal.500"
href={href}
isExternal
maxWidth="100%"
wordBreak="break-word"
>
{children}
</Link>
),
img: ({ alt, src }) => <ImageWithFallback alt={alt} src={src} />,
icon_list: ({ children }) => (
<List spacing={3}>
{React.Children.map(children, (child) => (
<ListItem>
<Box
as={MdCheckCircle}
color="green.500"
mr={2}
display="inline-block"
/>
{child}
</ListItem>
))}
</List>
),
};

View File

@@ -2,7 +2,7 @@ import React, { useEffect } from "react";
import { Stack } from "@chakra-ui/react"; import { Stack } from "@chakra-ui/react";
import Chat from "../../components/chat/Chat"; import Chat from "../../components/chat/Chat";
import clientChatStore from "../../stores/ClientChatStore"; import clientChatStore from "../../stores/ClientChatStore";
import { getModelFamily } from "../../components/chat/SupportedModels"; import { getModelFamily } from "../../components/chat/lib/SupportedModels";
// renders "/" // renders "/"
export default function IndexPage() { export default function IndexPage() {

View File

@@ -2,7 +2,7 @@ import {OpenAI} from "openai";
import Message from "../models/Message"; import Message from "../models/Message";
import {AssistantSdk} from "./assistant-sdk"; import {AssistantSdk} from "./assistant-sdk";
import {IMessage} from "../../../src/stores/ClientChatStore"; import {IMessage} from "../../../src/stores/ClientChatStore";
import {getModelFamily} from "../../../src/components/chat/SupportedModels"; import {getModelFamily} from "../../../src/components/chat/lib/SupportedModels";
export class ChatSdk { export class ChatSdk {
static async preprocess({ static async preprocess({

View File

@@ -3,7 +3,7 @@ import OpenAI from 'openai';
import ChatSdk from '../sdk/chat-sdk'; import ChatSdk from '../sdk/chat-sdk';
import Message from "../models/Message"; import Message from "../models/Message";
import O1Message from "../models/O1Message"; import O1Message from "../models/O1Message";
import {getModelFamily, ModelFamily} from "../../../src/components/chat/SupportedModels"; import {getModelFamily, ModelFamily} from "../../../src/components/chat/lib/SupportedModels";
import {OpenAiChatSdk} from "../sdk/models/openai"; import {OpenAiChatSdk} from "../sdk/models/openai";
import {GroqChatSdk} from "../sdk/models/groq"; import {GroqChatSdk} from "../sdk/models/groq";
import {ClaudeChatSdk} from "../sdk/models/claude"; import {ClaudeChatSdk} from "../sdk/models/claude";