Tauri 编辑器应用框架

This commit is contained in:
YHH
2025-10-14 22:53:26 +08:00
parent b20b2ae4ce
commit bd839cf431
18 changed files with 1702 additions and 5 deletions

View File

@@ -0,0 +1,89 @@
import { useState, useEffect } from 'react';
import { Core } from '@esengine/ecs-framework';
import { EditorPluginManager, UIRegistry, MessageHub, SerializerRegistry } from '@esengine/editor-core';
import { SceneInspectorPlugin } from './plugins/SceneInspectorPlugin';
import { TauriAPI } from './api/tauri';
import './styles/App.css';
function App() {
const [core, setCore] = useState<Core | null>(null);
const [pluginManager, setPluginManager] = useState<EditorPluginManager | null>(null);
const [status, setStatus] = useState('Initializing...');
useEffect(() => {
const initializeEditor = async () => {
try {
const coreInstance = Core.create({ debug: true });
const uiRegistry = new UIRegistry();
const messageHub = new MessageHub();
const serializerRegistry = new SerializerRegistry();
Core.services.registerInstance(UIRegistry, uiRegistry);
Core.services.registerInstance(MessageHub, messageHub);
Core.services.registerInstance(SerializerRegistry, serializerRegistry);
const pluginMgr = new EditorPluginManager();
pluginMgr.initialize(coreInstance, Core.services);
await pluginMgr.installEditor(new SceneInspectorPlugin());
const greeting = await TauriAPI.greet('Developer');
console.log(greeting);
setCore(coreInstance);
setPluginManager(pluginMgr);
setStatus('Editor Ready');
} catch (error) {
console.error('Failed to initialize editor:', error);
setStatus('Initialization Failed');
}
};
initializeEditor();
return () => {
Core.destroy();
};
}, []);
return (
<div className="editor-container">
<div className="editor-header">
<h1>ECS Framework Editor</h1>
<span className="status">{status}</span>
</div>
<div className="editor-content">
<div className="sidebar-left">
<h3>Hierarchy</h3>
<p>Scene hierarchy will appear here</p>
</div>
<div className="main-content">
<div className="viewport">
<h3>Viewport</h3>
<p>Scene viewport will appear here</p>
</div>
<div className="bottom-panel">
<h4>Console</h4>
<p>Console output will appear here</p>
</div>
</div>
<div className="sidebar-right">
<h3>Inspector</h3>
<p>Entity inspector will appear here</p>
</div>
</div>
<div className="editor-footer">
<span>Plugins: {pluginManager?.getAllEditorPlugins().length ?? 0}</span>
<span>Core: {core ? 'Active' : 'Inactive'}</span>
</div>
</div>
);
}
export default App;

View File

@@ -0,0 +1,55 @@
import { invoke } from '@tauri-apps/api/core';
/**
* Tauri IPC 通信层
*/
export class TauriAPI {
/**
* 打招呼(测试命令)
*/
static async greet(name: string): Promise<string> {
return await invoke<string>('greet', { name });
}
/**
* 打开项目
*/
static async openProject(path: string): Promise<string> {
return await invoke<string>('open_project', { path });
}
/**
* 保存项目
*/
static async saveProject(path: string, data: string): Promise<void> {
return await invoke<void>('save_project', { path, data });
}
/**
* 导出二进制数据
*/
static async exportBinary(data: Uint8Array, outputPath: string): Promise<void> {
return await invoke<void>('export_binary', {
data: Array.from(data),
outputPath
});
}
}
/**
* 项目信息
*/
export interface ProjectInfo {
name: string;
path: string;
version: string;
}
/**
* 编辑器配置
*/
export interface EditorConfig {
theme: string;
autoSave: boolean;
recentProjects: string[];
}

View File

@@ -0,0 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './styles/index.css';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);

View File

@@ -0,0 +1,130 @@
import type { Core, ServiceContainer } from '@esengine/ecs-framework';
import { IEditorPlugin, EditorPluginCategory, PanelPosition } from '@esengine/editor-core';
import type { MenuItem, ToolbarItem, PanelDescriptor, ISerializer } from '@esengine/editor-core';
/**
* Scene Inspector 插件
*
* 提供场景层级视图和实体检视功能
*/
export class SceneInspectorPlugin implements IEditorPlugin {
readonly name = '@esengine/scene-inspector';
readonly version = '1.0.0';
readonly displayName = 'Scene Inspector';
readonly category = EditorPluginCategory.Inspector;
readonly description = 'Scene hierarchy and entity inspector';
readonly icon = '🔍';
async install(_core: Core, _services: ServiceContainer): Promise<void> {
console.log('[SceneInspectorPlugin] Installed');
}
async uninstall(): Promise<void> {
console.log('[SceneInspectorPlugin] Uninstalled');
}
registerMenuItems(): MenuItem[] {
return [
{
id: 'view-scene-inspector',
label: 'Scene Inspector',
parentId: 'view',
onClick: () => {
console.log('Toggle Scene Inspector');
},
shortcut: 'Ctrl+Shift+I',
order: 100
},
{
id: 'scene-create-entity',
label: 'Create Entity',
parentId: 'scene',
onClick: () => {
console.log('Create new entity');
},
shortcut: 'Ctrl+N',
order: 10
}
];
}
registerToolbar(): ToolbarItem[] {
return [
{
id: 'toolbar-create-entity',
label: 'New Entity',
groupId: 'entity-tools',
icon: '',
onClick: () => {
console.log('Create entity from toolbar');
},
order: 10
},
{
id: 'toolbar-delete-entity',
label: 'Delete Entity',
groupId: 'entity-tools',
icon: '🗑️',
onClick: () => {
console.log('Delete entity from toolbar');
},
order: 20
}
];
}
registerPanels(): PanelDescriptor[] {
return [
{
id: 'panel-scene-hierarchy',
title: 'Scene Hierarchy',
position: PanelPosition.Left,
defaultSize: 250,
resizable: true,
closable: false,
icon: '📋',
order: 10
},
{
id: 'panel-entity-inspector',
title: 'Entity Inspector',
position: PanelPosition.Right,
defaultSize: 300,
resizable: true,
closable: false,
icon: '🔎',
order: 10
}
];
}
getSerializers(): ISerializer[] {
return [
{
serialize: (data: any) => {
const json = JSON.stringify(data);
const encoder = new TextEncoder();
return encoder.encode(json);
},
deserialize: (data: Uint8Array) => {
const decoder = new TextDecoder();
const json = decoder.decode(data);
return JSON.parse(json);
},
getSupportedType: () => 'scene'
}
];
}
async onEditorReady(): Promise<void> {
console.log('[SceneInspectorPlugin] Editor is ready');
}
async onProjectOpen(projectPath: string): Promise<void> {
console.log(`[SceneInspectorPlugin] Project opened: ${projectPath}`);
}
async onProjectClose(): Promise<void> {
console.log('[SceneInspectorPlugin] Project closed');
}
}

View File

@@ -0,0 +1,99 @@
.editor-container {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
background-color: #1e1e1e;
color: #cccccc;
}
.editor-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 16px;
background-color: #2d2d2d;
border-bottom: 1px solid #3e3e3e;
}
.editor-header h1 {
font-size: 16px;
font-weight: 600;
margin: 0;
}
.editor-header .status {
font-size: 12px;
color: #4ec9b0;
}
.editor-content {
display: flex;
flex: 1;
overflow: hidden;
}
.sidebar-left,
.sidebar-right {
width: 250px;
background-color: #252526;
border-right: 1px solid #3e3e3e;
padding: 12px;
overflow-y: auto;
}
.sidebar-right {
border-right: none;
border-left: 1px solid #3e3e3e;
}
.sidebar-left h3,
.sidebar-right h3 {
font-size: 14px;
margin-bottom: 12px;
color: #ffffff;
}
.main-content {
display: flex;
flex-direction: column;
flex: 1;
overflow: hidden;
}
.viewport {
flex: 1;
background-color: #1e1e1e;
border-bottom: 1px solid #3e3e3e;
padding: 12px;
display: flex;
flex-direction: column;
}
.viewport h3 {
font-size: 14px;
margin-bottom: 12px;
color: #ffffff;
}
.bottom-panel {
height: 200px;
background-color: #252526;
padding: 12px;
overflow-y: auto;
}
.bottom-panel h4 {
font-size: 12px;
margin-bottom: 8px;
color: #ffffff;
}
.editor-footer {
display: flex;
justify-content: space-between;
padding: 4px 16px;
background-color: #007acc;
color: #ffffff;
font-size: 12px;
}

View File

@@ -0,0 +1,19 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow: hidden;
}
#root {
width: 100vw;
height: 100vh;
}