feat(CodeBlock): support matplotlib in code execution (#8069)

* feat(CodeBlock): support matplotlib in code execution

* refactor: update output style and docs

* refactor: use ImageViewer

* refactor: manage service config, increase timeout and retry count

* refactor: improve worker message logging

* chore: upgrade pyodide to 0.28.0

* docs: fix typos
This commit is contained in:
one
2025-07-21 21:19:06 +08:00
committed by GitHub
parent 63c3937050
commit 929f7445ed
7 changed files with 309 additions and 127 deletions
@@ -12,6 +12,7 @@ import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import ImageViewer from '../ImageViewer'
import CodePreview from './CodePreview'
import { SPECIAL_VIEW_COMPONENTS, SPECIAL_VIEWS } from './constants'
import HtmlArtifactsCard from './HtmlArtifactsCard'
@@ -48,7 +49,7 @@ export const CodeBlockView: React.FC<Props> = memo(({ children, language, onSave
const [viewMode, setViewMode] = useState<ViewMode>('special')
const [isRunning, setIsRunning] = useState(false)
const [output, setOutput] = useState('')
const [executionResult, setExecutionResult] = useState<{ text: string; image?: string } | null>(null)
const [tools, setTools] = useState<CodeTool[]>([])
const { registerTool, removeTool } = useCodeTool(setTools)
@@ -87,16 +88,18 @@ export const CodeBlockView: React.FC<Props> = memo(({ children, language, onSave
const handleRunScript = useCallback(() => {
setIsRunning(true)
setOutput('')
setExecutionResult(null)
pyodideService
.runScript(children, {}, codeExecution.timeoutMinutes * 60000)
.then((formattedOutput) => {
setOutput(formattedOutput)
.then((result) => {
setExecutionResult(result)
})
.catch((error) => {
logger.error('Unexpected error:', error)
setOutput(`Unexpected error: ${error.message || 'Unknown error'}`)
setExecutionResult({
text: `Unexpected error: ${error.message || 'Unknown error'}`
})
})
.finally(() => {
setIsRunning(false)
@@ -241,7 +244,14 @@ export const CodeBlockView: React.FC<Props> = memo(({ children, language, onSave
{renderHeader}
<CodeToolbar tools={tools} />
{renderContent}
{isExecutable && output && <StatusBar>{output}</StatusBar>}
{isExecutable && executionResult && (
<StatusBar>
{executionResult.text}
{executionResult.image && (
<ImageViewer src={executionResult.image} alt="Matplotlib plot" style={{ cursor: 'pointer' }} />
)}
</StatusBar>
)}
</CodeBlockWrapper>
)
})