feat(editor): 完善用户代码热更新和环境检测 (#275)
* fix: 更新 bundle-runtime 脚本使用正确的 platform-web 输出文件 原脚本引用的 runtime.browser.js 不存在,改为使用 index.mjs * feat(editor): 完善用户代码热更新和环境检测 ## 热更新改进 - 添加 hotReloadInstances() 方法,通过更新原型链实现真正的热更新 - 组件实例保留数据,仅更新方法 - ComponentRegistry 支持热更新时替换同名组件类 ## 环境检测 - 启动时检测 esbuild 可用性 - 在启动页面底部显示环境状态指示器 - 添加 check_environment Rust 命令和前端 API ## esbuild 打包 - 将 esbuild 二进制文件打包到应用中 - 用户无需全局安装 esbuild - 支持 Windows/macOS/Linux 平台 ## 文件监视优化 - 添加 300ms 防抖,避免重复编译 - 修复路径分隔符混合问题 ## 资源打包修复 - 修复 Tauri 资源配置,保留 engine 目录结构 - 添加 src-tauri/bin/ 和 src-tauri/engine/ 到 gitignore * fix: 将热更新模式改为可选,修复测试失败 - ComponentRegistry 添加 hotReloadEnabled 标志,默认禁用 - 只有启用热更新模式时才会替换同名组件类 - 编辑器环境自动启用热更新模式 - reset() 方法中重置热更新标志 * test: 添加热更新模式的测试用例
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { getVersion } from '@tauri-apps/api/app';
|
||||
import { Globe, ChevronDown, Download, X, Loader2, Trash2 } from 'lucide-react';
|
||||
import { Globe, ChevronDown, Download, X, Loader2, Trash2, CheckCircle, AlertCircle } from 'lucide-react';
|
||||
import { checkForUpdatesOnStartup, installUpdate, type UpdateCheckResult } from '../utils/updater';
|
||||
import { StartupLogo } from './StartupLogo';
|
||||
import { TauriAPI, type EnvironmentCheckResult } from '../api/tauri';
|
||||
import '../styles/StartupPage.css';
|
||||
|
||||
type Locale = 'en' | 'zh';
|
||||
@@ -33,6 +34,8 @@ export function StartupPage({ onOpenProject, onCreateProject, onOpenRecentProjec
|
||||
const [updateInfo, setUpdateInfo] = useState<UpdateCheckResult | null>(null);
|
||||
const [showUpdateBanner, setShowUpdateBanner] = useState(false);
|
||||
const [isInstalling, setIsInstalling] = useState(false);
|
||||
const [envCheck, setEnvCheck] = useState<EnvironmentCheckResult | null>(null);
|
||||
const [showEnvStatus, setShowEnvStatus] = useState(false);
|
||||
const langMenuRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -59,6 +62,24 @@ export function StartupPage({ onOpenProject, onCreateProject, onOpenRecentProjec
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 启动时检测开发环境
|
||||
useEffect(() => {
|
||||
TauriAPI.checkEnvironment().then((result) => {
|
||||
setEnvCheck(result);
|
||||
// 如果环境就绪,在控制台显示信息
|
||||
if (result.ready) {
|
||||
console.log('[Environment] Ready ✓');
|
||||
console.log(`[Environment] esbuild: ${result.esbuild.version} (${result.esbuild.source})`);
|
||||
} else {
|
||||
// 环境有问题,显示提示
|
||||
setShowEnvStatus(true);
|
||||
console.warn('[Environment] Not ready:', result.esbuild.error);
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error('[Environment] Check failed:', error);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const translations = {
|
||||
en: {
|
||||
title: 'ESEngine Editor',
|
||||
@@ -76,7 +97,11 @@ export function StartupPage({ onOpenProject, onCreateProject, onOpenRecentProjec
|
||||
deleteConfirmTitle: 'Delete Project',
|
||||
deleteConfirmMessage: 'Are you sure you want to permanently delete this project? This action cannot be undone.',
|
||||
cancel: 'Cancel',
|
||||
delete: 'Delete'
|
||||
delete: 'Delete',
|
||||
envReady: 'Environment Ready',
|
||||
envNotReady: 'Environment Issue',
|
||||
esbuildReady: 'esbuild ready',
|
||||
esbuildMissing: 'esbuild not found'
|
||||
},
|
||||
zh: {
|
||||
title: 'ESEngine 编辑器',
|
||||
@@ -94,7 +119,11 @@ export function StartupPage({ onOpenProject, onCreateProject, onOpenRecentProjec
|
||||
deleteConfirmTitle: '删除项目',
|
||||
deleteConfirmMessage: '确定要永久删除此项目吗?此操作无法撤销。',
|
||||
cancel: '取消',
|
||||
delete: '删除'
|
||||
delete: '删除',
|
||||
envReady: '环境就绪',
|
||||
envNotReady: '环境问题',
|
||||
esbuildReady: 'esbuild 就绪',
|
||||
esbuildMissing: '未找到 esbuild'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -220,6 +249,43 @@ export function StartupPage({ onOpenProject, onCreateProject, onOpenRecentProjec
|
||||
|
||||
<div className="startup-footer">
|
||||
<span className="startup-version">{versionText}</span>
|
||||
|
||||
{/* 环境状态指示器 | Environment Status Indicator */}
|
||||
{envCheck && (
|
||||
<div
|
||||
className={`startup-env-status ${envCheck.ready ? 'ready' : 'warning'}`}
|
||||
onClick={() => setShowEnvStatus(!showEnvStatus)}
|
||||
title={envCheck.ready ? t.envReady : t.envNotReady}
|
||||
>
|
||||
{envCheck.ready ? (
|
||||
<CheckCircle size={14} />
|
||||
) : (
|
||||
<AlertCircle size={14} />
|
||||
)}
|
||||
{showEnvStatus && (
|
||||
<div className="startup-env-tooltip">
|
||||
<div className="env-tooltip-title">
|
||||
{envCheck.ready ? t.envReady : t.envNotReady}
|
||||
</div>
|
||||
<div className={`env-tooltip-item ${envCheck.esbuild.available ? 'ok' : 'error'}`}>
|
||||
{envCheck.esbuild.available ? (
|
||||
<>
|
||||
<CheckCircle size={12} />
|
||||
<span>esbuild {envCheck.esbuild.version}</span>
|
||||
<span className="env-source">({envCheck.esbuild.source})</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<AlertCircle size={12} />
|
||||
<span>{t.esbuildMissing}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{onLocaleChange && (
|
||||
<div className="startup-locale-dropdown" ref={langMenuRef}>
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user