mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-11 06:00:15 -07:00
Blocks: rename
This commit is contained in:
@@ -3,7 +3,7 @@ import * as React from 'react';
|
||||
import type { SxProps } from '@mui/joy/styles/types';
|
||||
import { Box } from '@mui/joy';
|
||||
|
||||
import { RenderImageURL } from '~/modules/blocks/RenderImageURL';
|
||||
import { RenderImageURL } from '~/modules/blocks/image/RenderImageURL';
|
||||
import { blocksRendererSx } from '~/modules/blocks/BlocksRenderer';
|
||||
|
||||
import type { DMessageContentFragment, DMessageFragmentId, DMessageImageRefPart } from '~/common/stores/chat/chat.fragments';
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { SxProps } from '@mui/joy/styles/types';
|
||||
import { Box } from '@mui/joy';
|
||||
|
||||
import type { DBlobAssetId, DBlobImageAsset } from '~/modules/dblobs/dblobs.types';
|
||||
import { RenderImageURL } from '~/modules/blocks/RenderImageURL';
|
||||
import { RenderImageURL } from '~/modules/blocks/image/RenderImageURL';
|
||||
import { getImageAssetAsBlobURL } from '~/modules/dblobs/dblobs.images';
|
||||
import { t2iGenerateImageContentFragments } from '~/modules/t2i/t2i.client';
|
||||
import { useDBAsset } from '~/modules/dblobs/dblobs.hooks';
|
||||
|
||||
@@ -9,13 +9,13 @@ import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import type { DMessageRole } from '~/common/stores/chat/chat.message';
|
||||
import { ContentScaling, lineHeightChatTextMd, themeScalingMap } from '~/common/app.theme';
|
||||
|
||||
import { RenderChatText } from './RenderChatText';
|
||||
import type { Block, CodeBlock, HtmlBlock, ImageBlock, TextBlock } from './blocks.types';
|
||||
import { RenderChatText } from './text/RenderChatText';
|
||||
import { RenderCode, RenderCodeMemo } from './code/RenderCode';
|
||||
import { RenderHtml } from './RenderHtml';
|
||||
import { RenderImageURL } from './RenderImageURL';
|
||||
import { RenderMarkdown, RenderMarkdownMemo } from './markdown/RenderMarkdown';
|
||||
import { RenderTextDiff } from './RenderTextDiff';
|
||||
import { areBlocksEqual, Block, parseMessageBlocks } from './blocks';
|
||||
import { RenderTextDiff } from './textdiff/RenderTextDiff';
|
||||
import { heuristicIsBlockTextHTML, RenderHtml } from './html/RenderHtml';
|
||||
import { heuristicLegacyImageBlocks, heuristicMarkdownImageReferenceBlocks, RenderImageURL } from './image/RenderImageURL';
|
||||
|
||||
|
||||
// How long is the user collapsed message
|
||||
@@ -51,6 +51,109 @@ export const blocksRendererSx: SxProps = {
|
||||
} as const;
|
||||
|
||||
|
||||
function areBlocksEqual(a: Block, b: Block): boolean {
|
||||
if (a.type !== b.type)
|
||||
return false;
|
||||
|
||||
switch (a.type) {
|
||||
case 'codeb':
|
||||
return a.blockTitle === (b as CodeBlock).blockTitle && a.blockCode === (b as CodeBlock).blockCode && a.complete === (b as CodeBlock).complete;
|
||||
case 'diffb':
|
||||
return false; // diff blocks are never equal
|
||||
case 'htmlb':
|
||||
return a.html === (b as HtmlBlock).html;
|
||||
case 'imageb':
|
||||
return a.url === (b as ImageBlock).url && a.alt === (b as ImageBlock).alt;
|
||||
case 'textb':
|
||||
return a.content === (b as TextBlock).content;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function parseBlocksFromText(text: string, disableParsing: boolean, forceTextDiffs?: TextDiff[]): Block[] {
|
||||
if (disableParsing)
|
||||
return [{ type: 'textb', content: text }];
|
||||
|
||||
if (forceTextDiffs && forceTextDiffs.length >= 1)
|
||||
return [{ type: 'diffb', textDiffs: forceTextDiffs }];
|
||||
|
||||
// special case: this could be generated by a proxy that returns an HTML page instead of the API response
|
||||
if (heuristicIsBlockTextHTML(text))
|
||||
return [{ type: 'htmlb', html: text }];
|
||||
|
||||
// special case: markdown image references (e.g. )
|
||||
const mdImageBlocks = heuristicMarkdownImageReferenceBlocks(text);
|
||||
if (mdImageBlocks)
|
||||
return mdImageBlocks;
|
||||
|
||||
// special case: legacy prodia images
|
||||
const legacyImageBlocks = heuristicLegacyImageBlocks(text);
|
||||
if (legacyImageBlocks)
|
||||
return legacyImageBlocks;
|
||||
|
||||
const regexPatterns = {
|
||||
// was: \w\x20\\.+-_ for tge filename, but was missing too much
|
||||
// REVERTED THIS: was: (`{3,}\n?|$), but was matching backticks within blocks. so now it must end with a newline or stop
|
||||
codeBlock: /`{3,}([\S\x20]+)?\n([\s\S]*?)(`{3,}\n?|$)/g,
|
||||
htmlCodeBlock: /<!DOCTYPE html>([\s\S]*?)<\/html>/gi,
|
||||
svgBlock: /<svg (xmlns|width|viewBox)=([\s\S]*?)<\/svg>/g,
|
||||
};
|
||||
|
||||
const blocks: Block[] = [];
|
||||
let lastIndex = 0;
|
||||
|
||||
while (true) {
|
||||
|
||||
// find the first match (if any) trying all the regexes
|
||||
let match: RegExpExecArray | null = null;
|
||||
let matchType: keyof typeof regexPatterns | null = null;
|
||||
for (const type in regexPatterns) {
|
||||
const regex = regexPatterns[type as keyof typeof regexPatterns];
|
||||
regex.lastIndex = lastIndex;
|
||||
const currentMatch = regex.exec(text);
|
||||
if (currentMatch && (match === null || currentMatch.index < match.index)) {
|
||||
match = currentMatch;
|
||||
matchType = type as keyof typeof regexPatterns;
|
||||
}
|
||||
}
|
||||
if (match === null)
|
||||
break;
|
||||
|
||||
// anything leftover before the match is text
|
||||
if (match.index > lastIndex)
|
||||
blocks.push({ type: 'textb', content: text.slice(lastIndex, match.index) });
|
||||
|
||||
// add the block
|
||||
switch (matchType) {
|
||||
case 'codeBlock':
|
||||
const blockTitle: string = (match[1] || '').trim();
|
||||
const blockCode: string = match[2].trim();
|
||||
const blockEnd: string = match[3];
|
||||
blocks.push({ type: 'codeb', blockTitle, blockCode, complete: blockEnd.startsWith('```') });
|
||||
break;
|
||||
|
||||
case 'htmlCodeBlock':
|
||||
const html: string = `<!DOCTYPE html>${match[1]}</html>`;
|
||||
blocks.push({ type: 'codeb', blockTitle: 'html', blockCode: html, complete: true });
|
||||
break;
|
||||
|
||||
case 'svgBlock':
|
||||
blocks.push({ type: 'codeb', blockTitle: 'svg', blockCode: match[0], complete: true });
|
||||
break;
|
||||
}
|
||||
|
||||
// advance the pointer
|
||||
lastIndex = match.index + match[0].length;
|
||||
}
|
||||
|
||||
// remainder is text
|
||||
if (lastIndex < text.length)
|
||||
blocks.push({ type: 'textb', content: text.slice(lastIndex) });
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
|
||||
type BlocksRendererProps = {
|
||||
// required
|
||||
text: string;
|
||||
@@ -115,45 +218,39 @@ export const BlocksRenderer = React.forwardRef<HTMLDivElement, BlocksRendererPro
|
||||
|
||||
// Memo the styles, to minimize re-renders
|
||||
|
||||
const scaledCodeSx: SxProps = React.useMemo(() => (
|
||||
{
|
||||
my: props.specialCodePlain ? 0 : themeScalingMap[props.contentScaling]?.blockCodeMarginY ?? 0,
|
||||
backgroundColor: props.specialCodePlain ? 'background.surface' : fromAssistant ? 'neutral.plainHoverBg' : 'primary.plainActiveBg',
|
||||
boxShadow: props.specialCodePlain ? undefined : 'inset 2px 0px 5px -4px var(--joy-palette-background-backdrop)', // was 'xs'
|
||||
borderRadius: 'sm',
|
||||
fontFamily: 'code',
|
||||
fontSize: themeScalingMap[props.contentScaling]?.blockCodeFontSize ?? '0.875rem',
|
||||
fontWeight: 'md', // JetBrains Mono has a lighter weight, so we need that extra bump
|
||||
fontVariantLigatures: 'none',
|
||||
lineHeight: themeScalingMap[props.contentScaling]?.blockLineHeight ?? 1.75,
|
||||
minWidth: 260,
|
||||
minHeight: '2.75rem',
|
||||
}
|
||||
), [fromAssistant, props.contentScaling, props.specialCodePlain]);
|
||||
const scaledCodeSx: SxProps = React.useMemo(() => ({
|
||||
my: props.specialCodePlain ? 0 : themeScalingMap[props.contentScaling]?.blockCodeMarginY ?? 0,
|
||||
backgroundColor: props.specialCodePlain ? 'background.surface' : fromAssistant ? 'neutral.plainHoverBg' : 'primary.plainActiveBg',
|
||||
boxShadow: props.specialCodePlain ? undefined : 'inset 2px 0px 5px -4px var(--joy-palette-background-backdrop)', // was 'xs'
|
||||
borderRadius: 'sm',
|
||||
fontFamily: 'code',
|
||||
fontSize: themeScalingMap[props.contentScaling]?.blockCodeFontSize ?? '0.875rem',
|
||||
fontWeight: 'md', // JetBrains Mono has a lighter weight, so we need that extra bump
|
||||
fontVariantLigatures: 'none',
|
||||
lineHeight: themeScalingMap[props.contentScaling]?.blockLineHeight ?? 1.75,
|
||||
minWidth: 260,
|
||||
minHeight: '2.75rem',
|
||||
}), [fromAssistant, props.contentScaling, props.specialCodePlain]);
|
||||
|
||||
const scaledImageSx: SxProps = React.useMemo(() => (
|
||||
{
|
||||
fontSize: themeScalingMap[props.contentScaling]?.blockFontSize ?? undefined,
|
||||
lineHeight: themeScalingMap[props.contentScaling]?.blockLineHeight ?? 1.75,
|
||||
marginBottom: themeScalingMap[props.contentScaling]?.blockImageGap ?? 1.5,
|
||||
}
|
||||
), [props.contentScaling]);
|
||||
const scaledImageSx: SxProps = React.useMemo(() => ({
|
||||
fontSize: themeScalingMap[props.contentScaling]?.blockFontSize ?? undefined,
|
||||
lineHeight: themeScalingMap[props.contentScaling]?.blockLineHeight ?? 1.75,
|
||||
marginBottom: themeScalingMap[props.contentScaling]?.blockImageGap ?? 1.5,
|
||||
}), [props.contentScaling]);
|
||||
|
||||
const scaledTypographySx: SxProps = React.useMemo(() => (
|
||||
{
|
||||
fontSize: themeScalingMap[props.contentScaling]?.blockFontSize ?? undefined,
|
||||
lineHeight: themeScalingMap[props.contentScaling]?.blockLineHeight ?? 1.75,
|
||||
...(props.showAsDanger ? { color: 'danger.500', fontWeight: 500 } : {}),
|
||||
...(props.showAsItalic ? { fontStyle: 'italic' } : {}),
|
||||
}
|
||||
), [props.contentScaling, props.showAsDanger, props.showAsItalic]);
|
||||
const scaledTypographySx: SxProps = React.useMemo(() => ({
|
||||
fontSize: themeScalingMap[props.contentScaling]?.blockFontSize ?? undefined,
|
||||
lineHeight: themeScalingMap[props.contentScaling]?.blockLineHeight ?? 1.75,
|
||||
...(props.showAsDanger ? { color: 'danger.500', fontWeight: 500 } : {}),
|
||||
...(props.showAsItalic ? { fontStyle: 'italic' } : {}),
|
||||
}), [props.contentScaling, props.showAsDanger, props.showAsItalic]);
|
||||
|
||||
|
||||
// Block splitter, with memoand special recycle of former blocks, to help React minimize render work
|
||||
|
||||
const blocks = React.useMemo(() => {
|
||||
// split the complete input text into blocks
|
||||
const newBlocks = parseMessageBlocks(text, fromSystem, renderTextDiff);
|
||||
const newBlocks = parseBlocksFromText(text, fromSystem, renderTextDiff);
|
||||
|
||||
// recycle the previous blocks if they are the same, for stable references to React
|
||||
const recycledBlocks: Block[] = [];
|
||||
@@ -209,7 +306,7 @@ export const BlocksRenderer = React.forwardRef<HTMLDivElement, BlocksRendererPro
|
||||
onImageRegenerate={undefined /* because there could be many of these URL images in a fragment, and we miss the whole partial-edit logic in a text fragment */}
|
||||
scaledImageSx={scaledImageSx} variant='content-part' />
|
||||
: block.type === 'diffb'
|
||||
? <RenderTextDiff key={'text-diff-' + index} diffBlock={block} sx={scaledTypographySx} />
|
||||
? <RenderTextDiff key={'text-diff-' + index} textDiffBlock={block} sx={scaledTypographySx} />
|
||||
: (props.renderTextAsMarkdown && !fromSystem && !isUserCommand)
|
||||
? <RenderMarkdownMemoOrNot key={'text-md-' + index} textBlock={block} sx={scaledTypographySx} />
|
||||
: <RenderChatText key={'text-' + index} textBlock={block} sx={scaledTypographySx} />;
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
import type { Diff as TextDiff } from '@sanity/diff-match-patch';
|
||||
|
||||
import { heuristicIsBlockTextHTML } from './RenderHtml';
|
||||
import { heuristicLegacyImageBlocks, heuristicMarkdownImageReferenceBlocks } from './RenderImageURL';
|
||||
|
||||
// Block types
|
||||
export type Block = CodeBlock | DiffBlock | HtmlBlock | ImageBlock | TextBlock;
|
||||
export type CodeBlock = { type: 'codeb'; blockTitle: string; blockCode: string; complete: boolean; };
|
||||
export type DiffBlock = { type: 'diffb'; textDiffs: TextDiff[] };
|
||||
export type HtmlBlock = { type: 'htmlb'; html: string; };
|
||||
export type ImageBlock = { type: 'imageb'; url: string; alt?: string }; // Added optional alt property
|
||||
export type TextBlock = { type: 'textb'; content: string; }; // for Text or Markdown
|
||||
|
||||
|
||||
export function areBlocksEqual(a: Block, b: Block): boolean {
|
||||
if (a.type !== b.type)
|
||||
return false;
|
||||
|
||||
switch (a.type) {
|
||||
case 'codeb':
|
||||
return a.blockTitle === (b as CodeBlock).blockTitle && a.blockCode === (b as CodeBlock).blockCode && a.complete === (b as CodeBlock).complete;
|
||||
case 'diffb':
|
||||
return false; // diff blocks are never equal
|
||||
case 'htmlb':
|
||||
return a.html === (b as HtmlBlock).html;
|
||||
case 'imageb':
|
||||
return a.url === (b as ImageBlock).url && a.alt === (b as ImageBlock).alt;
|
||||
case 'textb':
|
||||
return a.content === (b as TextBlock).content;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function parseMessageBlocks(text: string, disableParsing: boolean, forceTextDiffs?: TextDiff[]): Block[] {
|
||||
if (disableParsing)
|
||||
return [{ type: 'textb', content: text }];
|
||||
|
||||
if (forceTextDiffs && forceTextDiffs.length >= 1)
|
||||
return [{ type: 'diffb', textDiffs: forceTextDiffs }];
|
||||
|
||||
// special case: this could be generated by a proxy that returns an HTML page instead of the API response
|
||||
if (heuristicIsBlockTextHTML(text))
|
||||
return [{ type: 'htmlb', html: text }];
|
||||
|
||||
// special case: markdown image references (e.g. )
|
||||
const mdImageBlocks = heuristicMarkdownImageReferenceBlocks(text);
|
||||
if (mdImageBlocks)
|
||||
return mdImageBlocks;
|
||||
|
||||
// special case: legacy prodia images
|
||||
const legacyImageBlocks = heuristicLegacyImageBlocks(text);
|
||||
if (legacyImageBlocks)
|
||||
return legacyImageBlocks;
|
||||
|
||||
const regexPatterns = {
|
||||
// was: \w\x20\\.+-_ for tge filename, but was missing too much
|
||||
// REVERTED THIS: was: (`{3,}\n?|$), but was matching backticks within blocks. so now it must end with a newline or stop
|
||||
codeBlock: /`{3,}([\S\x20]+)?\n([\s\S]*?)(`{3,}\n?|$)/g,
|
||||
htmlCodeBlock: /<!DOCTYPE html>([\s\S]*?)<\/html>/gi,
|
||||
svgBlock: /<svg (xmlns|width|viewBox)=([\s\S]*?)<\/svg>/g,
|
||||
};
|
||||
|
||||
const blocks: Block[] = [];
|
||||
let lastIndex = 0;
|
||||
|
||||
while (true) {
|
||||
|
||||
// find the first match (if any) trying all the regexes
|
||||
let match: RegExpExecArray | null = null;
|
||||
let matchType: keyof typeof regexPatterns | null = null;
|
||||
for (const type in regexPatterns) {
|
||||
const regex = regexPatterns[type as keyof typeof regexPatterns];
|
||||
regex.lastIndex = lastIndex;
|
||||
const currentMatch = regex.exec(text);
|
||||
if (currentMatch && (match === null || currentMatch.index < match.index)) {
|
||||
match = currentMatch;
|
||||
matchType = type as keyof typeof regexPatterns;
|
||||
}
|
||||
}
|
||||
if (match === null)
|
||||
break;
|
||||
|
||||
// anything leftover before the match is text
|
||||
if (match.index > lastIndex)
|
||||
blocks.push({ type: 'textb', content: text.slice(lastIndex, match.index) });
|
||||
|
||||
// add the block
|
||||
switch (matchType) {
|
||||
case 'codeBlock':
|
||||
const blockTitle: string = (match[1] || '').trim();
|
||||
const blockCode: string = match[2].trim();
|
||||
const blockEnd: string = match[3];
|
||||
blocks.push({ type: 'codeb', blockTitle, blockCode, complete: blockEnd.startsWith('```') });
|
||||
break;
|
||||
|
||||
case 'htmlCodeBlock':
|
||||
const html: string = `<!DOCTYPE html>${match[1]}</html>`;
|
||||
blocks.push({ type: 'codeb', blockTitle: 'html', blockCode: html, complete: true });
|
||||
break;
|
||||
|
||||
case 'svgBlock':
|
||||
blocks.push({ type: 'codeb', blockTitle: 'svg', blockCode: match[0], complete: true });
|
||||
break;
|
||||
}
|
||||
|
||||
// advance the pointer
|
||||
lastIndex = match.index + match[0].length;
|
||||
}
|
||||
|
||||
// remainder is text
|
||||
if (lastIndex < text.length)
|
||||
blocks.push({ type: 'textb', content: text.slice(lastIndex) });
|
||||
|
||||
return blocks;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { Diff as TextDiff } from '@sanity/diff-match-patch';
|
||||
|
||||
// Block types
|
||||
export type Block =
|
||||
| CodeBlock
|
||||
| HtmlBlock
|
||||
| ImageBlock
|
||||
| TextBlock
|
||||
| TextDiffBlock;
|
||||
|
||||
export type CodeBlock = { type: 'codeb'; blockTitle: string; blockCode: string; complete: boolean; };
|
||||
export type HtmlBlock = { type: 'htmlb'; html: string; };
|
||||
export type ImageBlock = { type: 'imageb'; url: string; alt?: string }; // Added optional alt property
|
||||
export type TextBlock = { type: 'textb'; content: string; }; // for Text or Markdown
|
||||
export type TextDiffBlock = { type: 'diffb'; textDiffs: TextDiff[] };
|
||||
@@ -16,11 +16,11 @@ import { copyToClipboard } from '~/common/util/clipboardUtils';
|
||||
import { frontendSideFetch } from '~/common/util/clientFetchers';
|
||||
import { useUIPreferencesStore } from '~/common/state/store-ui';
|
||||
|
||||
import type { CodeBlock } from '../blocks';
|
||||
import type { CodeBlock } from '../blocks.types';
|
||||
import { ButtonCodePen, isCodePenSupported } from './ButtonCodePen';
|
||||
import { ButtonJsFiddle, isJSFiddleSupported } from './ButtonJSFiddle';
|
||||
import { ButtonStackBlitz, isStackBlitzSupported } from './ButtonStackBlitz';
|
||||
import { heuristicIsBlockTextHTML, IFrameComponent } from '../RenderHtml';
|
||||
import { heuristicIsBlockTextHTML, IFrameComponent } from '../html/RenderHtml';
|
||||
import { patchSvgString, RenderCodeMermaid } from './RenderCodeMermaid';
|
||||
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import WebIcon from '@mui/icons-material/Web';
|
||||
|
||||
import { copyToClipboard } from '~/common/util/clipboardUtils';
|
||||
|
||||
import type { HtmlBlock } from './blocks';
|
||||
import { OverlayButton, overlayButtonsSx } from './code/RenderCode';
|
||||
import type { HtmlBlock } from '../blocks.types';
|
||||
import { OverlayButton, overlayButtonsSx } from '../code/RenderCode';
|
||||
|
||||
|
||||
// this is used by the blocks parser (for full text detection) and by the Code component (for inline rendering)
|
||||
@@ -13,8 +13,8 @@ import WarningRoundedIcon from '@mui/icons-material/WarningRounded';
|
||||
import { GoodTooltip } from '~/common/components/GoodTooltip';
|
||||
import { Link } from '~/common/components/Link';
|
||||
|
||||
import type { ImageBlock } from './blocks';
|
||||
import { OverlayButton, overlayButtonsSx } from './code/RenderCode';
|
||||
import type { ImageBlock } from '../blocks.types';
|
||||
import { OverlayButton, overlayButtonsSx } from '../code/RenderCode';
|
||||
|
||||
|
||||
const mdImageReferenceRegex = /^!\[([^\]]*)]\(([^)]+)\)$/;
|
||||
@@ -5,7 +5,7 @@ import { Box, styled } from '@mui/joy';
|
||||
|
||||
import { lineHeightChatTextMd } from '~/common/app.theme';
|
||||
|
||||
import type { TextBlock } from '../blocks';
|
||||
import type { TextBlock } from '../blocks.types';
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import type { SxProps } from '@mui/joy/styles/types';
|
||||
import { Chip, Typography } from '@mui/joy';
|
||||
import { SxProps } from '@mui/joy/styles/types';
|
||||
|
||||
import { extractChatCommand } from '../../apps/chat/commands/commands.registry';
|
||||
import { extractChatCommand } from '../../../apps/chat/commands/commands.registry';
|
||||
|
||||
import type { TextBlock } from './blocks';
|
||||
import type { TextBlock } from '../blocks.types';
|
||||
|
||||
|
||||
/**
|
||||
* Renders a text block with chat commands.
|
||||
* NOTE: should remove the commands parsing dependency.
|
||||
*/
|
||||
export const RenderChatText = (props: { textBlock: TextBlock; sx?: SxProps; }) => {
|
||||
|
||||
const elements = extractChatCommand(props.textBlock.content);
|
||||
+3
-3
@@ -4,7 +4,7 @@ import { cleanupEfficiency, Diff as TextDiff, DIFF_DELETE, DIFF_INSERT, makeDiff
|
||||
import type { SxProps } from '@mui/joy/styles/types';
|
||||
import { Box, Typography, useTheme } from '@mui/joy';
|
||||
|
||||
import type { DiffBlock } from './blocks';
|
||||
import type { TextDiffBlock } from '../blocks.types';
|
||||
|
||||
|
||||
export function useSanityTextDiffs(_text: string, _diffText: string | undefined, enabled: boolean) {
|
||||
@@ -37,13 +37,13 @@ export function useSanityTextDiffs(_text: string, _diffText: string | undefined,
|
||||
}
|
||||
|
||||
|
||||
export const RenderTextDiff = (props: { diffBlock: DiffBlock; sx?: SxProps; }) => {
|
||||
export const RenderTextDiff = (props: { textDiffBlock: TextDiffBlock; sx?: SxProps; }) => {
|
||||
|
||||
// external state
|
||||
const theme = useTheme();
|
||||
|
||||
// derived state
|
||||
const textDiffs: TextDiff[] = props.diffBlock.textDiffs;
|
||||
const textDiffs: TextDiff[] = props.textDiffBlock.textDiffs;
|
||||
|
||||
// text added
|
||||
const styleAdd = {
|
||||
Reference in New Issue
Block a user