import * as React from 'react';
import { useShallow } from 'zustand/react/shallow';
import type { SxProps } from '@mui/joy/styles/types';
import { Box, ButtonGroup, Sheet, Tooltip, Typography } from '@mui/joy';
import ChangeHistoryTwoToneIcon from '@mui/icons-material/ChangeHistoryTwoTone';
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import FitScreenIcon from '@mui/icons-material/FitScreen';
import HtmlIcon from '@mui/icons-material/Html';
import NumbersRoundedIcon from '@mui/icons-material/NumbersRounded';
import SchemaIcon from '@mui/icons-material/Schema';
import WrapTextIcon from '@mui/icons-material/WrapText';
import { copyToClipboard } from '~/common/util/clipboardUtils';
import { useUIPreferencesStore } from '~/common/state/store-ui';
import type { CodeBlock } from '../blocks.types';
import { ButtonCodePen, isCodePenSupported } from './ButtonCodePen';
import { ButtonJsFiddle, isJSFiddleSupported } from './ButtonJSFiddle';
import { ButtonStackBlitz, isStackBlitzSupported } from './ButtonStackBlitz';
import { OverlayButton, overlayButtonsActiveSx, overlayButtonsClassName, overlayButtonsSx } from '../OverlayButton';
import { RenderCodeHtmlIFrame } from './RenderCodeHtmlIFrame';
import { RenderCodeMermaid } from './RenderCodeMermaid';
import { RenderCodeSVG } from './RenderCodeSVG';
import { RenderCodeSyntax } from './RenderCodeSyntax';
import { heuristicIsBlockPlantUML, RenderCodePlantUML, usePlantUmlSvg } from './RenderCodePlantUML';
import { heuristicIsBlockPureHTML } from '../html/RenderHtmlResponse';
// style for line-numbers
import './RenderCode.css';
// configuration
const ALWAYS_SHOW_OVERLAY = false;
// RenderCode
export const renderCodeMemoOrNot = (memo: boolean) => memo ? RenderCodeMemo : RenderCode;
const RenderCodeMemo = React.memo(RenderCode);
interface RenderCodeBaseProps {
codeBlock: CodeBlock,
fitScreen?: boolean,
initialShowHTML?: boolean,
noCopyButton?: boolean,
optimizeLightweight?: boolean,
sx?: SxProps,
}
function RenderCode(props: RenderCodeBaseProps) {
return (
}>
<_DynamicPrism {...props} />
);
}
// Lazy loader of the heavy prism functions
const _DynamicPrism = React.lazy(async () => {
// Dynamically import the code highlight functions
const { highlightCode, inferCodeLanguage } = await import('./codePrism');
return {
default: (props: RenderCodeBaseProps) => ,
};
});
// Actual implemetation of the code rendering
const renderCodecontainerSx: SxProps = {
// position the overlay buttons - this has to be one level up from the code, otherwise the buttons will h-scroll with the code
position: 'relative',
// fade in children buttons
[`&:hover > .${overlayButtonsClassName}`]: overlayButtonsActiveSx,
};
function RenderCodeImpl(props: RenderCodeBaseProps & {
highlightCode: (inferredCodeLanguage: string | null, blockCode: string, addLineNumbers: boolean) => string,
inferCodeLanguage: (blockTitle: string, code: string) => string | null,
}) {
// state
const [isHovering, setIsHovering] = React.useState(false);
const [fitScreen, setFitScreen] = React.useState(!!props.fitScreen);
const [showHTML, setShowHTML] = React.useState(props.initialShowHTML === true);
const [showMermaid, setShowMermaid] = React.useState(true);
const [showPlantUML, setShowPlantUML] = React.useState(true);
const [showSVG, setShowSVG] = React.useState(true);
const { showLineNumbers, showSoftWrap, setShowLineNumbers, setShowSoftWrap } = useUIPreferencesStore(useShallow(state => ({
showLineNumbers: state.renderCodeLineNumbers,
showSoftWrap: state.renderCodeSoftWrap,
setShowLineNumbers: state.setRenderCodeLineNumbers,
setShowSoftWrap: state.setRenderCodeSoftWrap,
})));
// derived props
const {
codeBlock: { blockTitle, blockCode, complete: blockComplete },
highlightCode, inferCodeLanguage,
optimizeLightweight,
} = props;
// handlers
const handleMouseOverEnter = React.useCallback(() => setIsHovering(true), []);
const handleMouseOverLeave = React.useCallback(() => setIsHovering(false), []);
const handleCopyToClipboard = React.useCallback((e: React.MouseEvent) => {
e.stopPropagation();
copyToClipboard(blockCode, 'Code');
}, [blockCode]);
// heuristics for specialized rendering
const isHTMLCode = heuristicIsBlockPureHTML(blockCode);
const renderHTML = isHTMLCode && showHTML;
const isMermaidCode = blockTitle === 'mermaid' && blockComplete;
const renderMermaid = isMermaidCode && showMermaid;
const isPlantUMLCode = heuristicIsBlockPlantUML(blockCode);
let renderPlantUML = isPlantUMLCode && showPlantUML;
const { data: plantUmlSvgData, error: plantUmlError } = usePlantUmlSvg(renderPlantUML, blockCode);
renderPlantUML = renderPlantUML && (!!plantUmlSvgData || !!plantUmlError);
const isSVGCode = (blockCode.startsWith('