mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-10 21:50:14 -07:00
Blocks: rationalize, reduce PH usage and ABR usage.
This commit is contained in:
+7
-6
@@ -3,17 +3,18 @@ import * as React from 'react';
|
||||
import type { SxProps } from '@mui/joy/styles/types';
|
||||
import { Box, Chip, ColorPaletteProp } from '@mui/joy';
|
||||
|
||||
import { ScaledTextBlockRenderer } from '~/modules/blocks/ScaledTextBlockRenderer';
|
||||
|
||||
import type { ContentScaling } from '~/common/app.theme';
|
||||
import type { DMessageId, DMessageRole } from '~/common/stores/chat/chat.message';
|
||||
|
||||
import { ContentPartPlaceholder } from './fragments-content/ContentPartPlaceholder';
|
||||
|
||||
|
||||
// configuration
|
||||
const ACTIVE_COLOR: ColorPaletteProp = 'warning';
|
||||
|
||||
|
||||
const containerSx: SxProps = {
|
||||
marginInlineStart: 1.5,
|
||||
backgroundColor: `${ACTIVE_COLOR}.softBg`,
|
||||
borderRadius: 'lg',
|
||||
// boxShadow: 'xs',
|
||||
@@ -30,7 +31,7 @@ const chipSx: SxProps = {
|
||||
};
|
||||
|
||||
|
||||
export function ContinueFragment(props: {
|
||||
export function BlockOpContinue(props: {
|
||||
contentScaling: ContentScaling,
|
||||
messageId: DMessageId,
|
||||
messageRole: DMessageRole,
|
||||
@@ -46,10 +47,10 @@ export function ContinueFragment(props: {
|
||||
return (
|
||||
<Box sx={containerSx}>
|
||||
|
||||
<ContentPartPlaceholder
|
||||
placeholderText='🧱 Token limit hit.'
|
||||
messageRole={props.messageRole}
|
||||
<ScaledTextBlockRenderer
|
||||
text='🧱 Token limit hit.'
|
||||
contentScaling={props.contentScaling}
|
||||
textRenderVariant='text'
|
||||
// showAsItalic
|
||||
/>
|
||||
|
||||
@@ -47,8 +47,8 @@ import { createTextContentFragment, DMessageFragment, DMessageFragmentId } from
|
||||
import { useUIPreferencesStore } from '~/common/state/store-ui';
|
||||
import { useUXLabsStore } from '~/common/state/store-ux-labs';
|
||||
|
||||
import { BlockOpContinue } from './BlockOpContinue';
|
||||
import { ContentFragments } from './fragments-content/ContentFragments';
|
||||
import { ContinueFragment } from './ContinueFragment';
|
||||
import { DocumentAttachmentFragments } from './fragments-attachment-doc/DocumentAttachmentFragments';
|
||||
import { ImageAttachmentFragments } from './fragments-attachment-image/ImageAttachmentFragments';
|
||||
import { InReferenceToList } from './in-reference-to/InReferenceToList';
|
||||
@@ -226,7 +226,7 @@ export function ChatMessage(props: {
|
||||
// const textDiffs = useSanityTextDiffs(messageText, props.diffPreviousText, showDiff);
|
||||
|
||||
|
||||
const { onMessageAssistantFrom, onMessageFragmentAppend, onMessageFragmentDelete, onMessageFragmentReplace } = props;
|
||||
const { onMessageAssistantFrom, onMessageDelete, onMessageFragmentAppend, onMessageFragmentDelete, onMessageFragmentReplace } = props;
|
||||
|
||||
const handleFragmentNew = React.useCallback(() => {
|
||||
onMessageFragmentAppend?.(messageId, createTextContentFragment(''));
|
||||
@@ -385,9 +385,9 @@ export function ChatMessage(props: {
|
||||
handleCloseOpsMenu();
|
||||
};
|
||||
|
||||
const handleOpsDelete = (_e: React.MouseEvent) => {
|
||||
props.onMessageDelete?.(messageId);
|
||||
};
|
||||
const handleOpsDelete = React.useCallback(() => {
|
||||
onMessageDelete?.(messageId);
|
||||
}, [messageId, onMessageDelete]);
|
||||
|
||||
|
||||
// Context Menu
|
||||
@@ -712,6 +712,7 @@ export function ChatMessage(props: {
|
||||
onFragmentBlank={handleFragmentNew}
|
||||
onFragmentDelete={handleFragmentDelete}
|
||||
onFragmentReplace={handleFragmentReplace}
|
||||
onMessageDelete={props.onMessageDelete ? handleOpsDelete : undefined}
|
||||
|
||||
onContextMenu={(props.onMessageFragmentReplace && ENABLE_CONTEXT_MENU) ? handleBlocksContextMenu : undefined}
|
||||
onDoubleClick={(props.onMessageFragmentReplace /*&& doubleClickToEdit disabled, as we may have shift too */) ? handleBlocksDoubleClick : undefined}
|
||||
@@ -734,7 +735,7 @@ export function ChatMessage(props: {
|
||||
|
||||
{/* Continue... */}
|
||||
{props.isBottom && messageGenerator?.tokenStopReason === 'out-of-tokens' && !!props.onMessageContinue && (
|
||||
<ContinueFragment
|
||||
<BlockOpContinue
|
||||
contentScaling={adjContentScaling}
|
||||
messageId={messageId}
|
||||
messageRole={messageRole}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { createDMessageDataInlineText, createDocAttachmentFragment, DMessageAtta
|
||||
import { useContextWorkspaceId } from '~/common/stores/workspace/WorkspaceIdProvider';
|
||||
import { useScrollToBottom } from '~/common/scroll-to-bottom/useScrollToBottom';
|
||||
|
||||
import { TextFragmentEditor } from '../fragments-content/TextFragmentEditor';
|
||||
import { BlockEdit_TextFragment } from '../fragments-content/BlockEdit_TextFragment';
|
||||
import { buttonIconForFragment, DocSelColor } from './DocAttachmentFragmentButton';
|
||||
import { useLiveFileSync } from './livefile-sync/useLiveFileSync';
|
||||
|
||||
@@ -258,7 +258,7 @@ export function DocAttachmentFragment(props: {
|
||||
{/* Show / Edit the Document Attachment Part */}
|
||||
{isEditing ? (
|
||||
// Document Editor
|
||||
<TextFragmentEditor
|
||||
<BlockEdit_TextFragment
|
||||
textPartText={fragmentDocPart.data.text}
|
||||
fragmentId={fragmentId}
|
||||
contentScaling={props.contentScaling}
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ const textAreaSlotPropsDone = {
|
||||
* Very similar to <InlineTextArea /> but with externally controlled state rather than internal.
|
||||
* Made it for as the editing alternative for <ContentPartText />.
|
||||
*/
|
||||
export function TextFragmentEditor(props: {
|
||||
export function BlockEdit_TextFragment(props: {
|
||||
// current value
|
||||
textPartText: string,
|
||||
fragmentId: DMessageFragmentId,
|
||||
@@ -0,0 +1,76 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import type { SxProps } from '@mui/joy/styles/types';
|
||||
import { Box, Chip } from '@mui/joy';
|
||||
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
|
||||
|
||||
import { ScaledTextBlockRenderer } from '~/modules/blocks/ScaledTextBlockRenderer';
|
||||
|
||||
import type { ContentScaling } from '~/common/app.theme';
|
||||
|
||||
|
||||
const containerSx: SxProps = {
|
||||
marginInlineStart: 1.5,
|
||||
backgroundColor: 'neutral.softBg',
|
||||
borderRadius: 'lg',
|
||||
|
||||
// layout
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 1,
|
||||
};
|
||||
|
||||
const chipSx: SxProps = {
|
||||
px: 2,
|
||||
};
|
||||
|
||||
|
||||
export function BlockOpEmpty(props: {
|
||||
text: string,
|
||||
contentScaling: ContentScaling,
|
||||
onDelete?: () => void,
|
||||
}) {
|
||||
|
||||
// state
|
||||
// const { showPromisedOverlay } = useOverlayComponents();
|
||||
|
||||
// derived state
|
||||
// const { onDelete } = props;
|
||||
|
||||
// const handleConfirmDelete = React.useCallback(async () => {
|
||||
// if (onDelete && await showPromisedOverlay('chat-message-delete-confirmation', { rejectWithValue: false }, ({ onResolve, onUserReject }) =>
|
||||
// <ConfirmationModal
|
||||
// open onClose={onUserReject} onPositive={() => onResolve(true)}
|
||||
// confirmationText='Are you sure you want to delete this message?'
|
||||
// positiveActionText='Delete'
|
||||
// title='Delete Message'
|
||||
// />,
|
||||
// )) onDelete();
|
||||
// }, [onDelete, showPromisedOverlay]);
|
||||
|
||||
return (
|
||||
<Box sx={containerSx}>
|
||||
|
||||
<ScaledTextBlockRenderer
|
||||
text={props.text}
|
||||
contentScaling={props.contentScaling}
|
||||
textRenderVariant='text'
|
||||
showAsItalic
|
||||
/>
|
||||
|
||||
{!!props.onDelete && (
|
||||
<Chip
|
||||
color='neutral'
|
||||
variant='outlined'
|
||||
size={props.contentScaling === 'md' ? 'lg' : 'md'}
|
||||
onClick={props.onDelete}
|
||||
sx={chipSx}
|
||||
startDecorator={<DeleteOutlineIcon />}
|
||||
>
|
||||
Delete
|
||||
</Chip>
|
||||
)}
|
||||
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { ScaledTextBlockRenderer } from '~/modules/blocks/ScaledTextBlockRenderer';
|
||||
|
||||
import type { ContentScaling } from '~/common/app.theme';
|
||||
import type { DMessageRole } from '~/common/stores/chat/chat.message';
|
||||
|
||||
|
||||
export function BlockPartError(props: {
|
||||
errorText: string,
|
||||
messageRole: DMessageRole,
|
||||
contentScaling: ContentScaling,
|
||||
}) {
|
||||
return (
|
||||
<ScaledTextBlockRenderer
|
||||
text={props.errorText}
|
||||
contentScaling={props.contentScaling}
|
||||
textRenderVariant='text'
|
||||
showAsDanger
|
||||
/>
|
||||
);
|
||||
}
|
||||
+1
-1
@@ -11,7 +11,7 @@ import type { DMessageContentFragment, DMessageFragmentId, DMessageImageRefPart
|
||||
import { ContentScaling, themeScalingMap } from '~/common/app.theme';
|
||||
|
||||
|
||||
export function ContentPartImageRef(props: {
|
||||
export function BlockPartImageRef(props: {
|
||||
imageRefPart: DMessageImageRefPart,
|
||||
fragmentId: DMessageFragmentId,
|
||||
contentScaling: ContentScaling,
|
||||
+4
-9
@@ -1,30 +1,25 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { AutoBlocksRenderer } from '~/modules/blocks/AutoBlocksRenderer';
|
||||
import { ScaledTextBlockRenderer } from '~/modules/blocks/ScaledTextBlockRenderer';
|
||||
|
||||
import type { ContentScaling } from '~/common/app.theme';
|
||||
import type { DMessageRole } from '~/common/stores/chat/chat.message';
|
||||
|
||||
|
||||
export function ContentPartPlaceholder(props: {
|
||||
export function BlockPartPlaceholder(props: {
|
||||
placeholderText: string,
|
||||
messageRole: DMessageRole,
|
||||
contentScaling: ContentScaling,
|
||||
showAsDanger?: boolean,
|
||||
showAsItalic?: boolean,
|
||||
// showAsProgress?: boolean,
|
||||
}) {
|
||||
// const placeholder = (
|
||||
return (
|
||||
<AutoBlocksRenderer
|
||||
<ScaledTextBlockRenderer
|
||||
text={props.placeholderText}
|
||||
fromRole={props.messageRole}
|
||||
contentScaling={props.contentScaling}
|
||||
fitScreen={false}
|
||||
isMobile={false /* assumption that the Placeholder Part doesn't react to size, and we assume desktop */}
|
||||
showAsDanger={props.showAsDanger}
|
||||
showAsItalic={props.showAsItalic}
|
||||
textRenderVariant='text'
|
||||
showAsItalic={props.showAsItalic}
|
||||
/>
|
||||
);
|
||||
//
|
||||
+1
-1
@@ -15,7 +15,7 @@ import { explainServiceErrors } from '../explainServiceErrors';
|
||||
* The OG part, comprised of text, which can be markdown, have code blocks, etc.
|
||||
* Uses BlocksRenderer to render the markdown/code/html/text, etc.
|
||||
*/
|
||||
export function ContentPartText_AutoBlocks(props: {
|
||||
export function BlockPartText_AutoBlocks(props: {
|
||||
// current value
|
||||
textPartText: string,
|
||||
setEditedText: (fragmentId: DMessageFragmentId, value: string, applyNow: boolean) => void,
|
||||
@@ -1,19 +1,22 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import type { SxProps } from '@mui/joy/styles/types';
|
||||
import { Box, Button, Sheet, Typography } from '@mui/joy';
|
||||
import { Box, Button, Sheet } from '@mui/joy';
|
||||
|
||||
import { BlocksContainer } from '~/modules/blocks/BlocksContainers';
|
||||
import { ScaledTextBlockRenderer } from '~/modules/blocks/ScaledTextBlockRenderer';
|
||||
|
||||
import type { ContentScaling } from '~/common/app.theme';
|
||||
import type { DMessageRole } from '~/common/stores/chat/chat.message';
|
||||
import { DMessageContentFragment, DMessageFragment, DMessageFragmentId, isContentFragment, isTextPart } from '~/common/stores/chat/chat.fragments';
|
||||
|
||||
import type { ChatMessageTextPartEditState } from '../ChatMessage';
|
||||
import { ContentPartImageRef } from './ContentPartImageRef';
|
||||
import { ContentPartPlaceholder } from './ContentPartPlaceholder';
|
||||
import { ContentPartText_AutoBlocks } from './ContentPartText_AutoBlocks';
|
||||
import { TextFragmentEditor } from './TextFragmentEditor';
|
||||
import { BlockEdit_TextFragment } from './BlockEdit_TextFragment';
|
||||
import { BlockOpEmpty } from './BlockOpEmpty';
|
||||
import { BlockPartError } from './BlockPartError';
|
||||
import { BlockPartImageRef } from './BlockPartImageRef';
|
||||
import { BlockPartPlaceholder } from './BlockPartPlaceholder';
|
||||
import { BlockPartText_AutoBlocks } from './BlockPartText_AutoBlocks';
|
||||
|
||||
|
||||
const editLayoutSx: SxProps = {
|
||||
@@ -60,6 +63,7 @@ export function ContentFragments(props: {
|
||||
onFragmentBlank: () => void
|
||||
onFragmentDelete: (fragmentId: DMessageFragmentId) => void,
|
||||
onFragmentReplace: (fragmentId: DMessageFragmentId, newFragment: DMessageContentFragment) => void,
|
||||
onMessageDelete?: () => void,
|
||||
|
||||
onContextMenu?: (event: React.MouseEvent) => void;
|
||||
onDoubleClick?: (event: React.MouseEvent) => void;
|
||||
@@ -90,16 +94,13 @@ export function ContentFragments(props: {
|
||||
|
||||
return <Box aria-label='message body' sx={isEditingText ? editLayoutSx : fromAssistant ? startLayoutSx : endLayoutSx}>
|
||||
|
||||
{/* The overall message is empty - show an indication of it */}
|
||||
{/* Empty Message Block - if empty */}
|
||||
{props.showEmptyNotice && (
|
||||
<Sheet variant='solid' color='neutral' invertedColors sx={{ mx: 1.5 }}>
|
||||
<ContentPartPlaceholder
|
||||
placeholderText={`empty ${fromAssistant ? 'assistant ' : fromUser ? 'user ' : ''}message - please edit or delete`}
|
||||
messageRole={props.messageRole}
|
||||
contentScaling={props.contentScaling}
|
||||
showAsItalic
|
||||
/>
|
||||
</Sheet>
|
||||
<BlockOpEmpty
|
||||
text={`empty ${fromAssistant ? 'model ' : fromUser ? 'user ' : ''}message`}
|
||||
contentScaling={props.contentScaling}
|
||||
onDelete={props.onMessageDelete}
|
||||
/>
|
||||
)}
|
||||
|
||||
{props.fragments.map((fragment) => {
|
||||
@@ -111,7 +112,7 @@ export function ContentFragments(props: {
|
||||
// editing for text parts
|
||||
if (props.textEditsState && (isTextPart(fragment.part) || fragment.part.pt === 'error')) {
|
||||
return (
|
||||
<TextFragmentEditor
|
||||
<BlockEdit_TextFragment
|
||||
key={'edit-' + fragment.fId}
|
||||
textPartText={isTextPart(fragment.part) ? fragment.part.text : fragment.part.error}
|
||||
fragmentId={fragment.fId}
|
||||
@@ -129,20 +130,18 @@ export function ContentFragments(props: {
|
||||
switch (fragment.part.pt) {
|
||||
case 'error':
|
||||
return (
|
||||
<ContentPartPlaceholder
|
||||
<BlockPartError
|
||||
key={fragment.fId}
|
||||
placeholderText={fragment.part.error}
|
||||
errorText={fragment.part.error}
|
||||
messageRole={props.messageRole}
|
||||
contentScaling={props.contentScaling}
|
||||
showAsDanger
|
||||
// showAsItalic
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
case 'image_ref':
|
||||
return (
|
||||
<ContentPartImageRef
|
||||
<BlockPartImageRef
|
||||
key={fragment.fId}
|
||||
imageRefPart={fragment.part}
|
||||
fragmentId={fragment.fId}
|
||||
@@ -154,7 +153,7 @@ export function ContentFragments(props: {
|
||||
|
||||
case 'ph':
|
||||
return (
|
||||
<ContentPartPlaceholder
|
||||
<BlockPartPlaceholder
|
||||
key={fragment.fId}
|
||||
placeholderText={fragment.part.pText}
|
||||
messageRole={props.messageRole}
|
||||
@@ -166,7 +165,7 @@ export function ContentFragments(props: {
|
||||
// This is the most frequent part by far, and can be broken down into sub-blocks
|
||||
case 'text':
|
||||
return (
|
||||
<ContentPartText_AutoBlocks
|
||||
<BlockPartText_AutoBlocks
|
||||
key={fragment.fId}
|
||||
// ref={blocksRendererRef}
|
||||
textPartText={fragment.part.text}
|
||||
@@ -309,9 +308,13 @@ export function ContentFragments(props: {
|
||||
case '_pt_sentinel':
|
||||
default:
|
||||
return (
|
||||
<Typography key={fragment.fId} level='body-sm' color='danger'>
|
||||
Unknown Content fragment: {fragment.part.pt}
|
||||
</Typography>
|
||||
<ScaledTextBlockRenderer
|
||||
key={fragment.fId}
|
||||
text={`Unknown Content fragment: ${fragment.part.pt}`}
|
||||
contentScaling={props.contentScaling}
|
||||
textRenderVariant='text'
|
||||
showAsDanger
|
||||
/>
|
||||
);
|
||||
}
|
||||
}).filter(Boolean)}
|
||||
|
||||
@@ -20,6 +20,7 @@ export type GlobalOverlayId = // string - disabled so we keep an orderliness
|
||||
| 'chat-attachments-clear'
|
||||
| 'chat-delete-confirmation'
|
||||
| 'chat-reset-confirmation'
|
||||
| 'chat-message-delete-confirmation'
|
||||
| 'livefile-overwrite'
|
||||
| 'shortcuts-confirm-close'
|
||||
| 'blocks-off-enhance-code'
|
||||
|
||||
@@ -10,7 +10,7 @@ import { EnhancedRenderCode } from './enhanced-code/EnhancedRenderCode';
|
||||
import { RenderDangerousHtml } from './danger-html/RenderDangerousHtml';
|
||||
import { RenderImageURL } from './image/RenderImageURL';
|
||||
import { RenderMarkdown, RenderMarkdownMemo } from './markdown/RenderMarkdown';
|
||||
import { RenderPlainChatText } from './plaintext/RenderPlainChatText';
|
||||
import { RenderPlainText } from './plaintext/RenderPlainText';
|
||||
import { RenderTextDiff } from './textdiff/RenderTextDiff';
|
||||
import { ToggleExpansionButton } from './ToggleExpansionButton';
|
||||
import { useAutoBlocksMemoSemiStable, useTextCollapser } from './blocks.hooks';
|
||||
@@ -122,12 +122,14 @@ export function AutoBlocksRenderer(props: {
|
||||
case 'md-bk':
|
||||
const RenderMarkdownMemoOrNot = optimizeMemoBeforeLastBlock ? RenderMarkdownMemo : RenderMarkdown;
|
||||
return (props.textRenderVariant === 'text' || fromSystem || isUserCommand) ? (
|
||||
<RenderPlainChatText
|
||||
// Keep in sync with ScaledPlainTextRenderer
|
||||
<RenderPlainText
|
||||
key={'txt-bk-' + index}
|
||||
content={bkInput.content}
|
||||
sx={scaledTypographySx}
|
||||
/>
|
||||
) : (
|
||||
// Keep in sync with ScaledMarkdownRenderer
|
||||
<RenderMarkdownMemoOrNot
|
||||
key={'md-bk-' + index}
|
||||
content={bkInput.content}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import type { ContentScaling } from '~/common/app.theme';
|
||||
|
||||
import { BlocksContainer } from './BlocksContainers';
|
||||
import { RenderMarkdown } from './markdown/RenderMarkdown';
|
||||
import { RenderPlainText } from './plaintext/RenderPlainText';
|
||||
import { useScaledTypographySx } from './blocks.styles';
|
||||
|
||||
|
||||
/**
|
||||
* Smaller and lighter-weight version of AutoBlocksRenderer for rendering just some text
|
||||
*/
|
||||
export function ScaledTextBlockRenderer(props: {
|
||||
text: string,
|
||||
contentScaling: ContentScaling,
|
||||
textRenderVariant: 'text' | 'markdown',
|
||||
showAsDanger?: boolean,
|
||||
showAsItalic?: boolean,
|
||||
}) {
|
||||
|
||||
// state
|
||||
const scaledTypographySx = useScaledTypographySx(props.contentScaling, !!props.showAsDanger, !!props.showAsItalic);
|
||||
|
||||
return (
|
||||
<BlocksContainer>
|
||||
{props.textRenderVariant === 'markdown' ? <RenderMarkdown content={props.text} sx={scaledTypographySx} />
|
||||
: props.textRenderVariant === 'text' ? <RenderPlainText content={props.text} sx={scaledTypographySx} />
|
||||
: ('unknown textRenderVariant: ' + props.textRenderVariant)}
|
||||
</BlocksContainer>
|
||||
);
|
||||
}
|
||||
+1
-1
@@ -10,7 +10,7 @@ import { extractChatCommand } from '../../../apps/chat/commands/commands.registr
|
||||
* Renders a text block with chat commands.
|
||||
* NOTE: should remove the commands parsing dependency.
|
||||
*/
|
||||
export const RenderPlainChatText = (props: { content: string; sx?: SxProps; }) => {
|
||||
export const RenderPlainText = (props: { content: string; sx?: SxProps; }) => {
|
||||
|
||||
const elements = extractChatCommand(props.content);
|
||||
|
||||
Reference in New Issue
Block a user