adjust file card

This commit is contained in:
Nate Kelley 2025-02-10 12:24:35 -07:00
parent 9fa93deed4
commit 7a1f5418b3
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
6 changed files with 98 additions and 34 deletions

View File

@ -89,5 +89,5 @@ export type BusterChatMessageReasoning_file = {
text: string; text: string;
line_number: number; line_number: number;
modified?: boolean; //defaults to true modified?: boolean; //defaults to true
}; //will be defined if the file has been completed OR on a page refresh }[]; //will be defined if the file has been completed OR on a page refresh
}; };

View File

@ -68,6 +68,10 @@ const useStyles = createStyles(({ token, css }) => ({
`, `,
fileCard: css` fileCard: css`
margin-bottom: 4px; margin-bottom: 4px;
.vertical-divider {
display: none;
}
`, `,
verticalDivider: css` verticalDivider: css`
transition: opacity 0.2s ease-in-out; transition: opacity 0.2s ease-in-out;

View File

@ -1,9 +1,24 @@
import React from 'react'; import React from 'react';
import { ReasoningMessageProps } from '../ReasoningMessageSelector'; import { ReasoningMessageProps } from '../ReasoningMessageSelector';
import { BusterChatMessageReasoning_file } from '@/api/asset_interfaces';
import { AppCodeBlock } from '@/components/text/AppMarkdown/AppCodeBlock';
export const ReasoningMessage_File: React.FC<ReasoningMessageProps> = React.memo( export const ReasoningMessage_File: React.FC<ReasoningMessageProps> = React.memo(
({ reasoningMessage }) => { ({ reasoningMessage, isCompletedStream, isLastMessageItem }) => {
return <div>asdf</div>; const { file, file_name, file_type, file_chunk } =
reasoningMessage as BusterChatMessageReasoning_file;
const showLoader = !isCompletedStream && isLastMessageItem;
return (
<AppCodeBlock
title={file_name}
language={'yaml'}
showCopyButton={false}
showLoader={showLoader}>
{file?.map((chunk) => chunk.text).join('\n')}
</AppCodeBlock>
);
} }
); );

View File

@ -1,4 +1,4 @@
import React, { useCallback, useLayoutEffect, useState } from 'react'; import React, { useLayoutEffect, useState } from 'react';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { AppMaterialIcons } from '../../../icons'; import { AppMaterialIcons } from '../../../icons';
import { createStyles } from 'antd-style'; import { createStyles } from 'antd-style';
@ -6,7 +6,6 @@ import { Button } from 'antd';
import darkTheme from 'react-syntax-highlighter/dist/cjs/styles/prism/vsc-dark-plus'; import darkTheme from 'react-syntax-highlighter/dist/cjs/styles/prism/vsc-dark-plus';
import { Text } from '@/components/text'; import { Text } from '@/components/text';
import { TextPulseLoader } from '../../..'; import { TextPulseLoader } from '../../..';
import { useAntToken } from '@/styles/useAntToken'; import { useAntToken } from '@/styles/useAntToken';
import lightTheme from './light'; import lightTheme from './light';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
@ -20,7 +19,9 @@ export const AppCodeBlock: React.FC<{
style?: React.CSSProperties; style?: React.CSSProperties;
showLoader?: boolean; showLoader?: boolean;
showCopyButton?: boolean; showCopyButton?: boolean;
}> = React.memo((props) => { title?: string;
buttons?: React.ReactNode;
}> = React.memo(({ title, buttons, ...props }) => {
const isDarkMode = useBusterStylesContext((state) => state.isDarkMode); const isDarkMode = useBusterStylesContext((state) => state.isDarkMode);
const { children, className = '', language, showLoader, showCopyButton = true, ...rest } = props; const { children, className = '', language, showLoader, showCopyButton = true, ...rest } = props;
const [style, setStyle] = useState<{ const [style, setStyle] = useState<{
@ -42,17 +43,19 @@ export const AppCodeBlock: React.FC<{
<CodeBlockWrapper <CodeBlockWrapper
code={code} code={code}
isDarkMode={isDarkMode} isDarkMode={isDarkMode}
language={language || ''} language={title || language}
showCopyButton={showCopyButton}> showCopyButton={showCopyButton}>
<div className="w-full overflow-x-auto"> <div className="w-full overflow-x-auto">
<div className="code-wrapper"> <div className="code-wrapper">
{language ? ( {language ? (
<SyntaxHighlighter <SyntaxHighlighter
{...rest} {...rest}
showLineNumbers
className={`${className} !p-3 transition ${!style ? 'opacity-100' : '!m-0 !border-none !p-0 opacity-100'}`} className={`${className} !p-3 transition ${!style ? 'opacity-100' : '!m-0 !border-none !p-0 opacity-100'}`}
children={code} children={code}
language={language} language={language}
style={style} style={style}
lineNumberStyle={{ color: '#000' }}
/> />
) : ( ) : (
<code {...rest} className={className}> <code {...rest} className={className}>
@ -97,9 +100,10 @@ const CodeBlockWrapper: React.FC<{
children: React.ReactNode; children: React.ReactNode;
isDarkMode: boolean; isDarkMode: boolean;
code: string; code: string;
language: string; language?: string;
showCopyButton: boolean; showCopyButton: boolean;
}> = React.memo(({ children, code, showCopyButton, language }) => { buttons?: React.ReactNode;
}> = React.memo(({ children, code, showCopyButton, language, buttons }) => {
const { cx, styles } = useStyles(); const { cx, styles } = useStyles();
const { openSuccessMessage } = useBusterNotifications(); const { openSuccessMessage } = useBusterNotifications();
const token = useAntToken(); const token = useAntToken();
@ -113,21 +117,24 @@ const CodeBlockWrapper: React.FC<{
<div className={cx(styles.container, 'max-h-fit')}> <div className={cx(styles.container, 'max-h-fit')}>
<div className={cx(styles.containerHeader, 'flex items-center justify-between')}> <div className={cx(styles.containerHeader, 'flex items-center justify-between')}>
<Text className="pl-2">{language}</Text> <Text className="pl-2">{language}</Text>
{showCopyButton && ( <div className="flex items-center space-x-1">
<Button {showCopyButton && (
style={{ <Button
color: token.colorTextSecondary style={{
}} color: token.colorTextSecondary
type="text" }}
onClick={(e) => { type="text"
e.preventDefault(); onClick={(e) => {
e.stopPropagation(); e.preventDefault();
copyCode(); e.stopPropagation();
}} copyCode();
icon={<AppMaterialIcons icon="content_copy" />}> }}
Copy icon={<AppMaterialIcons icon="content_copy" />}>
</Button> Copy
)} </Button>
)}
{buttons}
</div>
</div> </div>
{children} {children}

View File

@ -1,5 +1,8 @@
import { busterAppStyleConfig } from '@/styles/busterAntDStyleConfig'; import { busterAppStyleConfig } from '@/styles/busterAntDStyleConfig';
import React from 'react'; import React from 'react';
const token = busterAppStyleConfig.token!;
export default { export default {
'code[class*="language-"]': { 'code[class*="language-"]': {
color: '#393A34', color: '#393A34',
@ -77,7 +80,7 @@ export default {
border: '1px solid #dddddd' border: '1px solid #dddddd'
}, },
comment: { comment: {
color: '#008000', color: token.colorTextTertiary,
fontStyle: 'italic' fontStyle: 'italic'
}, },
prolog: { prolog: {
@ -126,25 +129,25 @@ export default {
color: '#36acaa' color: '#36acaa'
}, },
atrule: { atrule: {
color: busterAppStyleConfig.token?.colorPrimary color: token.colorPrimary
}, },
keyword: { keyword: {
color: busterAppStyleConfig.token?.colorPrimary color: token.colorPrimary
}, },
'attr-value': { 'attr-value': {
color: busterAppStyleConfig.token?.colorPrimary color: token.colorPrimary
}, },
'.language-autohotkey .token.selector': { '.language-autohotkey .token.selector': {
color: busterAppStyleConfig.token?.colorPrimary color: token.colorPrimary
}, },
'.language-json .token.boolean': { '.language-json .token.boolean': {
color: busterAppStyleConfig.token?.colorPrimary color: token.colorPrimary
}, },
'.language-json .token.number': { '.language-json .token.number': {
color: busterAppStyleConfig.token?.colorPrimary color: token.colorPrimary
}, },
'code[class*="language-css"]': { 'code[class*="language-css"]': {
color: busterAppStyleConfig.token?.colorPrimary color: token.colorPrimary
}, },
function: { function: {
color: '#393A34' color: '#393A34'

View File

@ -5,7 +5,8 @@ import {
type BusterChatMessageRequest, type BusterChatMessageRequest,
BusterChatMessage_fileMetadata, BusterChatMessage_fileMetadata,
BusterChatMessageReasoning_thought, BusterChatMessageReasoning_thought,
BusterChatMessageReasoning_thoughtPill BusterChatMessageReasoning_thoughtPill,
BusterChatMessageReasoning_file
} from '@/api/asset_interfaces'; } from '@/api/asset_interfaces';
import { faker } from '@faker-js/faker'; import { faker } from '@faker-js/faker';
@ -86,6 +87,37 @@ export const createMockResponseMessageFile = (): BusterChatMessage_file => {
}; };
}; };
export const createMockReasoningMessageFile = (): BusterChatMessageReasoning_file => {
return {
id: 'swag',
type: 'file',
file_type: 'metric',
status: 'completed',
file_name: faker.company.name(),
version_number: 1,
version_id: faker.string.uuid(),
file: [
{
text: 'name: my-service\nversion: 1.0.0\ndescription: A sample service',
line_number: 1,
modified: false
},
{
text: 'dependencies:\n - name: redis\n version: 6.2.0\n - name: postgres\n version: 13.4',
line_number: 2
},
{
text: 'ports:\n - 8080\n - 9000\n - 6379',
line_number: 3
},
{
text: 'environment:\n NODE_ENV: production\n LOG_LEVEL: info\n DB_HOST: localhost',
line_number: 4
}
]
};
};
export const MOCK_CHAT: BusterChat = { export const MOCK_CHAT: BusterChat = {
id: '0', id: '0',
title: 'Mock Chat', title: 'Mock Chat',
@ -95,7 +127,10 @@ export const MOCK_CHAT: BusterChat = {
id: '123', id: '123',
created_at: '2025-01-01', created_at: '2025-01-01',
request_message: createMockUserMessage(), request_message: createMockUserMessage(),
reasoning: [...Array.from({ length: 1 }, () => createMockResponseMessageThought())], reasoning: [
...Array.from({ length: 1 }, () => createMockResponseMessageThought()),
createMockReasoningMessageFile()
],
response_messages: [ response_messages: [
createMockResponseMessageText(), createMockResponseMessageText(),
createMockResponseMessageFile(), createMockResponseMessageFile(),