Extract PlantUML renderer

This commit is contained in:
Enrico Ros
2024-08-02 17:15:39 -07:00
parent dfd77a3832
commit 573143c57d
2 changed files with 68 additions and 57 deletions
+8 -57
View File
@@ -1,5 +1,4 @@
import * as React from 'react';
import { useQuery } from '@tanstack/react-query';
import { useShallow } from 'zustand/react/shallow';
import type { SxProps } from '@mui/joy/styles/types';
@@ -13,7 +12,6 @@ import SchemaIcon from '@mui/icons-material/Schema';
import WrapTextIcon from '@mui/icons-material/WrapText';
import { copyToClipboard } from '~/common/util/clipboardUtils';
import { frontendSideFetch } from '~/common/util/clientFetchers';
import { useUIPreferencesStore } from '~/common/state/store-ui';
import type { CodeBlock } from '../blocks.types';
@@ -23,58 +21,13 @@ import { ButtonStackBlitz, isStackBlitzSupported } from './ButtonStackBlitz';
import { RenderCodeHtmlIFrame } from './RenderCodeHtmlIFrame';
import { heuristicIsBlockTextHTML } from '../html/RenderHtml';
import { patchSvgString, RenderCodeMermaid } from './RenderCodeMermaid';
import { usePlantUmlSvg } from './RenderCodePlantUML';
// style for line-numbers
import './RenderCode.css';
export function getPlantUmlServerUrl(): string {
// set at nextjs build time
return process.env.NEXT_PUBLIC_PLANTUML_SERVER_URL || 'https://www.plantuml.com/plantuml/svg/';
}
async function fetchPlantUmlSvg(plantUmlCode: string): Promise<string | null> {
// Get the PlantUML server from inline env var
let plantUmlServerUrl = getPlantUmlServerUrl();
if (!plantUmlServerUrl.endsWith('/'))
plantUmlServerUrl += '/';
// fetch the PlantUML SVG
let text: string = '';
try {
// Dynamically import the PlantUML encoder - it's a large library that slows down app loading
const { encode: plantUmlEncode } = await import('plantuml-encoder');
// retrieve and manually adapt the SVG, to remove the background
const encodedPlantUML: string = plantUmlEncode(plantUmlCode);
const response = await frontendSideFetch(`${plantUmlServerUrl}${encodedPlantUML}`);
text = await response.text();
} catch (error) {
console.error('Error rendering PlantUML on server:', plantUmlServerUrl, error);
return null;
}
// validate/extract the SVG
const start = text.indexOf('<svg ');
const end = text.indexOf('</svg>');
if (start < 0 || end <= start)
throw new Error('Could not render PlantUML');
// remove the background color
const svg = text
.slice(start, end + 6) // <svg ... </svg>
.replace('background:#FFFFFF;', '');
// check for syntax errors
if (svg.includes('>Syntax Error?</text>'))
throw new Error('llm syntax issue (it happens!). Please regenerate or change the language model.');
return svg;
}
export const OverlayButton = styled(IconButton)(({ theme, variant }) => ({
backgroundColor: variant === 'outlined' ? theme.palette.background.surface : undefined,
'--Icon-fontSize': theme.fontSize.lg,
@@ -84,7 +37,7 @@ export const OverlayButton = styled(IconButton)(({ theme, variant }) => ({
export const overlayButtonsSx: SxProps = {
position: 'absolute', top: 0, right: 0, zIndex: 2, /* top of message and its chips */
display: 'flex', flexDirection: 'row', gap: 1,
opacity: 0, transition: 'opacity 0.2s cubic-bezier(.17,.84,.44,1)',
opacity: 0.1, transition: 'opacity 0.2s cubic-bezier(.17,.84,.44,1)',
// buttongroup: background
'& > div > button': {
// backgroundColor: 'background.surface',
@@ -145,12 +98,7 @@ function RenderCodeImpl(props: RenderCodeImplProps) {
|| (blockCode.startsWith('@startgantt') && blockCode.endsWith('@endgantt'));
let renderPlantUML = isPlantUML && showPlantUML;
const { data: plantUmlHtmlData, error: plantUmlError } = useQuery({
enabled: renderPlantUML,
queryKey: ['plantuml', blockCode],
queryFn: () => fetchPlantUmlSvg(blockCode),
staleTime: 24 * 60 * 60 * 1000, // 1 day
});
const { plantUmlHtmlData, plantUmlError } = usePlantUmlSvg(renderPlantUML, blockCode);
renderPlantUML = renderPlantUML && (!!plantUmlHtmlData || !!plantUmlError);
const isSVG = (blockCode.startsWith('<svg') || blockCode.startsWith('<?xml version="1.0" encoding="UTF-8"?>\n<svg')) && blockCode.endsWith('</svg>');
@@ -215,7 +163,8 @@ function RenderCodeImpl(props: RenderCodeImplProps) {
// lots more style, incl font, background, embossing, radius, etc.
...props.sx,
}}>
}}
>
{/* Markdown Title (File/Type) */}
{showBlockTitle && (
@@ -248,8 +197,10 @@ function RenderCodeImpl(props: RenderCodeImplProps) {
}}
/>}
{/* Buttons */}
{/* Overlay Buttons */}
<Box className='overlay-buttons' sx={{ ...overlayButtonsSx, p: 0.5 }}>
{/* Show HTML */}
{isHTML && (
<Tooltip title={optimizeLightweight ? null : renderHTML ? 'Hide' : 'Show Web Page'}>