mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-07-08 08:27:39 +08:00
Move all renderer source from src/renderer/src/* up one level to
src/renderer/*, removing the redundant nested src directory.
- Update path aliases (@renderer, @types, @logger, @data) and TanStack
Router paths in electron.vite.config.ts; update tsconfig.{json,web,node}
path mappings and include globs.
- Fix Vite root-relative script paths in the 8 renderer HTML entries.
- Update cross-process relative imports in main/preload (language,
apiServer models, preload index) to drop the /src segment.
- Switch renderer test imports of the logger mock to the @test-mocks alias.
- Update hardcoded renderer paths in scripts and their fixtures, lint
configs (eslint/oxlint/biome), CODEOWNERS, docs, and the data-classify tool.
- Convert deep (../../+) relative imports within the renderer to the
@renderer alias (69 files, 108 imports); keep single-level relatives.
- Fix doc links broken by the move and correct one pre-existing broken
link in naming-conventions.md.
218 lines
5.4 KiB
TypeScript
218 lines
5.4 KiB
TypeScript
/// <reference lib="webworker" />
|
||
|
||
interface WorkerResponse {
|
||
type: 'initialized' | 'init-error' | 'system-error'
|
||
id?: string
|
||
output?: PyodideOutput
|
||
error?: string
|
||
}
|
||
|
||
// 定义输出结构类型
|
||
interface PyodideOutput {
|
||
result: any
|
||
text: string | null
|
||
error: string | null
|
||
image?: string
|
||
}
|
||
|
||
const PYODIDE_INDEX_URL = 'https://cdn.jsdelivr.net/pyodide/v0.28.0/full/'
|
||
const PYODIDE_MODULE_URL = PYODIDE_INDEX_URL + 'pyodide.mjs'
|
||
|
||
const MATPLOTLIB_SHIM_CODE = `
|
||
def __cherry_studio_matplotlib_setup():
|
||
import os
|
||
# Set backend before importing pyplot
|
||
os.environ["MPLBACKEND"] = "AGG"
|
||
import io
|
||
import base64
|
||
import matplotlib.pyplot as plt
|
||
|
||
# Save original show function
|
||
_original_show = plt.show
|
||
|
||
# Define and replace with new show function
|
||
def _new_show(*args, **kwargs):
|
||
global pyodide_matplotlib_image
|
||
fig = plt.gcf()
|
||
|
||
if not fig.canvas.get_renderer()._renderer:
|
||
return
|
||
|
||
buf = io.BytesIO()
|
||
fig.savefig(buf, format='png')
|
||
buf.seek(0)
|
||
|
||
img_str = base64.b64encode(buf.read()).decode('utf-8')
|
||
|
||
# Pass data via global variable
|
||
pyodide_matplotlib_image = f"data:image/png;base64,{img_str}"
|
||
|
||
plt.clf()
|
||
plt.close(fig)
|
||
|
||
# Replace global show function
|
||
plt.show = _new_show
|
||
|
||
__cherry_studio_matplotlib_setup()
|
||
del __cherry_studio_matplotlib_setup
|
||
`
|
||
|
||
// 声明全局变量用于输出
|
||
let output: PyodideOutput = {
|
||
result: null,
|
||
text: null,
|
||
error: null
|
||
}
|
||
|
||
const pyodidePromise = (async () => {
|
||
// 重置输出变量
|
||
output = {
|
||
result: null,
|
||
text: null,
|
||
error: null
|
||
}
|
||
|
||
try {
|
||
// 动态加载 Pyodide 脚本
|
||
const pyodideModule = await import(/* @vite-ignore */ PYODIDE_MODULE_URL)
|
||
|
||
// 加载 Pyodide 并捕获标准输出/错误
|
||
return await pyodideModule.loadPyodide({
|
||
indexURL: PYODIDE_INDEX_URL,
|
||
stdout: (text: string) => {
|
||
if (output.text) {
|
||
output.text += `${text}\n`
|
||
} else {
|
||
output.text = `${text}\n`
|
||
}
|
||
},
|
||
stderr: (text: string) => {
|
||
if (output.error) {
|
||
output.error += `${text}\n`
|
||
} else {
|
||
output.error = `${text}\n`
|
||
}
|
||
}
|
||
})
|
||
} catch (error: unknown) {
|
||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||
|
||
// 通知主线程初始化错误
|
||
self.postMessage({
|
||
type: 'init-error',
|
||
error: errorMessage
|
||
} as WorkerResponse)
|
||
|
||
throw error
|
||
}
|
||
})()
|
||
|
||
// 处理结果,确保所有类型都能安全序列化
|
||
function processResult(result: any): any {
|
||
try {
|
||
if (result && typeof result.toJs === 'function') {
|
||
return processResult(result.toJs())
|
||
}
|
||
|
||
if (Array.isArray(result)) {
|
||
return result.map((item) => processResult(item))
|
||
}
|
||
|
||
if (typeof result === 'object' && result !== null) {
|
||
return Object.fromEntries(Object.entries(result).map(([key, value]) => [key, processResult(value)]))
|
||
}
|
||
|
||
return result
|
||
} catch (error: unknown) {
|
||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||
return { __error__: 'Result processing failed', details: errorMessage }
|
||
}
|
||
}
|
||
|
||
// 通知主线程已加载
|
||
pyodidePromise
|
||
.then(() => {
|
||
self.postMessage({ type: 'initialized' } as WorkerResponse)
|
||
})
|
||
.catch((error: unknown) => {
|
||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||
self.postMessage({
|
||
type: 'init-error',
|
||
error: errorMessage
|
||
} as WorkerResponse)
|
||
})
|
||
|
||
// 处理消息
|
||
self.onmessage = async (event) => {
|
||
const { id, python } = event.data
|
||
|
||
// 重置输出变量
|
||
output = {
|
||
result: null,
|
||
text: null,
|
||
error: null
|
||
}
|
||
|
||
let globals
|
||
|
||
try {
|
||
const pyodide = await pyodidePromise
|
||
// 创建一个新的全局作用域
|
||
globals = pyodide.globals.get('dict')()
|
||
|
||
// 载入需要的包
|
||
try {
|
||
await pyodide.loadPackagesFromImports(python)
|
||
} catch (error: unknown) {
|
||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||
throw new Error(`Failed to load required packages: ${errorMessage}`)
|
||
}
|
||
|
||
// 执行代码
|
||
try {
|
||
// 注入 Matplotlib 垫片代码
|
||
if (python.includes('matplotlib')) {
|
||
await pyodide.runPythonAsync(MATPLOTLIB_SHIM_CODE, { globals })
|
||
}
|
||
|
||
output.result = await pyodide.runPythonAsync(python, { globals })
|
||
|
||
// 处理结果,确保安全序列化
|
||
output.result = processResult(output.result)
|
||
|
||
// 检查是否有 Matplotlib 图像输出
|
||
const image = globals.get('pyodide_matplotlib_image')
|
||
if (image) {
|
||
output.image = image
|
||
}
|
||
} catch (error: unknown) {
|
||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||
// 不设置 output.result,但设置错误信息
|
||
if (output.error) {
|
||
output.error += `\nExecution error:\n${errorMessage}`
|
||
} else {
|
||
output.error = `Execution error:\n${errorMessage}`
|
||
}
|
||
}
|
||
} catch (error: unknown) {
|
||
// 处理所有其他错误
|
||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||
|
||
if (output.error) {
|
||
output.error += `\nSystem error:\n${errorMessage}`
|
||
} else {
|
||
output.error = `System error:\n${errorMessage}`
|
||
}
|
||
|
||
// 发送错误信息
|
||
self.postMessage({
|
||
type: 'system-error',
|
||
id,
|
||
error: errorMessage
|
||
} as WorkerResponse)
|
||
} finally {
|
||
globals?.destroy()
|
||
self.postMessage({ id, output } as WorkerResponse)
|
||
}
|
||
}
|