mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-11 06:00:15 -07:00
Extract PlantUML renderer
This commit is contained in:
@@ -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'}>
|
||||
|
||||
Reference in New Issue
Block a user