From 1e61998b50b113911bb7d1a4d2a718cdfaaa9c18 Mon Sep 17 00:00:00 2001 From: Enrico Ros Date: Tue, 24 Sep 2024 20:11:37 -0700 Subject: [PATCH] ChartJS: improvements --- src/modules/blocks/code/RenderCode.tsx | 5 ++-- .../code/code-renderers/RenderCodeChartJS.tsx | 24 +++++++++++++-- .../code/code-renderers/useDynamicChartJS.ts | 29 ++++++++++++------- .../enhanced-code/EnhancedRenderCode.tsx | 2 +- src/modules/persona/pmix/pmix.ts | 2 +- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/modules/blocks/code/RenderCode.tsx b/src/modules/blocks/code/RenderCode.tsx index 14c099c09..114121733 100644 --- a/src/modules/blocks/code/RenderCode.tsx +++ b/src/modules/blocks/code/RenderCode.tsx @@ -249,10 +249,9 @@ function RenderCodeImpl(props: RenderCodeBaseProps & { : renderMermaid ? : renderSVG ? : (renderPlantUML && (plantUmlSvgData || plantUmlError)) ? - : renderChartJS ? + : renderChartJS ? : } - {/* [overlay] Buttons (Code blocks (SVG, diagrams, HTML, syntax, ...)) */} @@ -294,7 +293,7 @@ function RenderCodeImpl(props: RenderCodeBaseProps & { {/* Fit-To-Screen */} - {(/*(isChartJSCode && showChartJS) ||*/ (isMermaidCode && showMermaid) || (isPlantUMLCode && showPlantUML && !plantUmlError) || (isSVGCode && showSVG && canScaleSVG)) && ( + {((isMermaidCode && showMermaid) || (isPlantUMLCode && showPlantUML && !plantUmlError) || (isSVGCode && showSVG && canScaleSVG)) && ( setFitScreen(on => !on)}> diff --git a/src/modules/blocks/code/code-renderers/RenderCodeChartJS.tsx b/src/modules/blocks/code/code-renderers/RenderCodeChartJS.tsx index cb61026bf..cae48e8eb 100644 --- a/src/modules/blocks/code/code-renderers/RenderCodeChartJS.tsx +++ b/src/modules/blocks/code/code-renderers/RenderCodeChartJS.tsx @@ -1,11 +1,28 @@ import * as React from 'react'; +import type { SxProps } from '@mui/joy/styles/types'; import { Box, Typography } from '@mui/joy'; -import { ChartConstructorType, useDynamicChartJS } from './useDynamicChartJS'; +import { ChartConstructorType, fixupChartJSObject, useDynamicChartJS } from './useDynamicChartJS'; -export function RenderCodeChartJS(props: { chartJSCode: string, fitScreen: boolean }) { +const chartContainerSx: SxProps = { + // required by Chart.js + position: 'relative', + + // width: '100%', + + // limit height of the canvas or it can too large easily + '& canvas': { + // width: '100% !important', + // height: '100%', + // minHeight: '320px', + maxHeight: '640px', + }, +}; + + +export function RenderCodeChartJS(props: { chartJSCode: string }) { // state const [renderError, setRenderError] = React.useState(null); @@ -20,6 +37,7 @@ export function RenderCodeChartJS(props: { chartJSCode: string, fitScreen: boole const parseResult = React.useMemo(() => { try { const config = JSON.parse(props.chartJSCode) as ChartConstructorType['config']; + fixupChartJSObject(config); return { chartConfig: config, parseError: null }; } catch (error: any) { return { chartConfig: null, parseError: 'Invalid Chart.js input: ' + (error.message || 'Unknown error.') }; @@ -67,7 +85,7 @@ export function RenderCodeChartJS(props: { chartJSCode: string, fitScreen: boole // Render the chart return ( - + ); diff --git a/src/modules/blocks/code/code-renderers/useDynamicChartJS.ts b/src/modules/blocks/code/code-renderers/useDynamicChartJS.ts index ed57cbcc2..38fba8b56 100644 --- a/src/modules/blocks/code/code-renderers/useDynamicChartJS.ts +++ b/src/modules/blocks/code/code-renderers/useDynamicChartJS.ts @@ -10,7 +10,6 @@ import { create } from 'zustand'; import type { Chart as ChartConstructorType } from 'chart.js/auto'; import { devDependencies } from '../../../../../package.json'; - import { themeFontFamilyCss } from '~/common/app.theme'; @@ -22,6 +21,24 @@ const CHARTJS_SCRIPT_ID = 'chartjs-cdn'; export type { ChartConstructorType }; +// Code manipulation functions + +export function fixupChartJSObject(chartConfig: ChartConstructorType['config']): void { + // Remove responsive options - we hadle this ourselves by default + delete chartConfig?.options?.responsive; + delete chartConfig?.options?.maintainAspectRatio; +} + +function _initializeChartJS(Chart: typeof ChartConstructorType): typeof ChartConstructorType { + Chart.defaults.font.family = themeFontFamilyCss; + Chart.defaults.font.size = 13; + Chart.defaults.maintainAspectRatio = true; // defaults to 1 for polar and so, 2 for bars and more + Chart.defaults.responsive = true; // re-draw on resize + // Chart.defaults.layout.autoPadding = true; // default padding + return Chart; +} + + // Singleton promise for loading Chart.js let chartJSPromise: Promise | null = null; @@ -47,7 +64,7 @@ function loadCDNScript(): Promise { script.async = true; script.onload = () => { - if ((window as any).Chart) resolve(initializeChartJS((window as any).Chart)); + if ((window as any).Chart) resolve(_initializeChartJS((window as any).Chart)); else reject(new Error('Chart.js failed to load.')); }; @@ -62,14 +79,6 @@ function loadCDNScript(): Promise { return chartJSPromise; } -function initializeChartJS(Chart: typeof ChartConstructorType): typeof ChartConstructorType { - Chart.defaults.font.family = themeFontFamilyCss; - Chart.defaults.font.size = 13; - Chart.defaults.maintainAspectRatio = true; - Chart.defaults.responsive = true; - return Chart; -} - // Store: we share the state across multiple useChartJS hooks interface ChartApiStore { diff --git a/src/modules/blocks/enhanced-code/EnhancedRenderCode.tsx b/src/modules/blocks/enhanced-code/EnhancedRenderCode.tsx index e6a8617d5..5ab562e07 100644 --- a/src/modules/blocks/enhanced-code/EnhancedRenderCode.tsx +++ b/src/modules/blocks/enhanced-code/EnhancedRenderCode.tsx @@ -122,7 +122,7 @@ export function EnhancedRenderCode(props: { }} /> - {props.title === BLOCK_CODE_VND_AGI_CHARTJS ? 'Chart' + (props.isPartial ? ' ...' : '') + {props.title === BLOCK_CODE_VND_AGI_CHARTJS ? 'Chart ' + (props.isPartial ? '.'.repeat(Math.round(props.code.length / 100) % 4) : '') : props.title || 'Code'} diff --git a/src/modules/persona/pmix/pmix.ts b/src/modules/persona/pmix/pmix.ts index 55a0db983..e98ceeefc 100644 --- a/src/modules/persona/pmix/pmix.ts +++ b/src/modules/persona/pmix/pmix.ts @@ -87,7 +87,7 @@ export function bareBonesPromptMixer(_template: string, assistantLlmId: DLLMId | When presenting data that would be better visualized as a chart, output a ChartJS configuration object in this format: \`\`\`chartjs { - // Valid and complete ChartJS configuration JSON object (DO NOT USE FUNCTIONS) + // Valid and complete ChartJS configuration JSON (DO NOT USE FUNCTIONS) } \`\`\` Choose the most suitable chart type based on the data and context. Include only the JSON configuration, without any explanatory text. Ensure the JSON is valid and complete.