Tauri 编辑器应用框架
This commit is contained in:
993
package-lock.json
generated
993
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
packages/editor-app/index.html
Normal file
12
packages/editor-app/index.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>ECS Framework Editor</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
31
packages/editor-app/package.json
Normal file
31
packages/editor-app/package.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "@esengine/editor-app",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "ECS Framework Editor Application - Cross-platform desktop editor",
|
||||||
|
"type": "module",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"tauri": "tauri",
|
||||||
|
"tauri:dev": "tauri dev",
|
||||||
|
"tauri:build": "tauri build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@esengine/ecs-framework": "file:../core",
|
||||||
|
"@esengine/editor-core": "file:../editor-core",
|
||||||
|
"@tauri-apps/api": "^2.2.0",
|
||||||
|
"@tauri-apps/plugin-shell": "^2.0.0",
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tauri-apps/cli": "^2.2.0",
|
||||||
|
"@types/react": "^18.3.12",
|
||||||
|
"@types/react-dom": "^18.3.1",
|
||||||
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
|
"typescript": "^5.8.3",
|
||||||
|
"vite": "^6.0.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
29
packages/editor-app/src-tauri/Cargo.toml
Normal file
29
packages/editor-app/src-tauri/Cargo.toml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
[package]
|
||||||
|
name = "ecs-editor"
|
||||||
|
version = "1.0.0"
|
||||||
|
description = "ECS Framework Editor - Cross-platform desktop editor"
|
||||||
|
authors = ["yhh"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "ecs_editor_lib"
|
||||||
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tauri-build = { version = "2", features = [] }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tauri = { version = "2", features = [] }
|
||||||
|
tauri-plugin-shell = "2"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
incremental = true
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
lto = true
|
||||||
|
opt-level = "s"
|
||||||
|
panic = "abort"
|
||||||
|
strip = true
|
||||||
3
packages/editor-app/src-tauri/build.rs
Normal file
3
packages/editor-app/src-tauri/build.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
tauri_build::build()
|
||||||
|
}
|
||||||
25
packages/editor-app/src-tauri/src/commands.rs
Normal file
25
packages/editor-app/src-tauri/src/commands.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ProjectInfo {
|
||||||
|
pub name: String,
|
||||||
|
pub path: String,
|
||||||
|
pub version: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct EditorConfig {
|
||||||
|
pub theme: String,
|
||||||
|
pub auto_save: bool,
|
||||||
|
pub recent_projects: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for EditorConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
theme: "dark".to_string(),
|
||||||
|
auto_save: true,
|
||||||
|
recent_projects: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
packages/editor-app/src-tauri/src/lib.rs
Normal file
7
packages/editor-app/src-tauri/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// ECS Editor Library
|
||||||
|
|
||||||
|
pub mod commands;
|
||||||
|
pub mod project;
|
||||||
|
|
||||||
|
pub use commands::*;
|
||||||
|
pub use project::*;
|
||||||
54
packages/editor-app/src-tauri/src/main.rs
Normal file
54
packages/editor-app/src-tauri/src/main.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// Prevents additional console window on Windows in release
|
||||||
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
use tauri::Manager;
|
||||||
|
|
||||||
|
// IPC Commands
|
||||||
|
#[tauri::command]
|
||||||
|
fn greet(name: &str) -> String {
|
||||||
|
format!("Hello, {}! Welcome to ECS Framework Editor.", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn open_project(path: String) -> Result<String, String> {
|
||||||
|
// 项目打开逻辑
|
||||||
|
Ok(format!("Project opened: {}", path))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn save_project(path: String, data: String) -> Result<(), String> {
|
||||||
|
// 项目保存逻辑
|
||||||
|
std::fs::write(&path, data)
|
||||||
|
.map_err(|e| format!("Failed to save project: {}", e))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn export_binary(data: Vec<u8>, output_path: String) -> Result<(), String> {
|
||||||
|
// 二进制导出逻辑
|
||||||
|
std::fs::write(&output_path, data)
|
||||||
|
.map_err(|e| format!("Failed to export binary: {}", e))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
tauri::Builder::default()
|
||||||
|
.plugin(tauri_plugin_shell::init())
|
||||||
|
.setup(|app| {
|
||||||
|
// 应用启动时的初始化逻辑
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
let window = app.get_webview_window("main").unwrap();
|
||||||
|
window.open_devtools();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
greet,
|
||||||
|
open_project,
|
||||||
|
save_project,
|
||||||
|
export_binary
|
||||||
|
])
|
||||||
|
.run(tauri::generate_context!())
|
||||||
|
.expect("error while running tauri application");
|
||||||
|
}
|
||||||
42
packages/editor-app/src-tauri/src/project.rs
Normal file
42
packages/editor-app/src-tauri/src/project.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Project {
|
||||||
|
pub name: String,
|
||||||
|
pub path: PathBuf,
|
||||||
|
pub scenes: Vec<String>,
|
||||||
|
pub assets: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Project {
|
||||||
|
pub fn new(name: String, path: PathBuf) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
scenes: Vec::new(),
|
||||||
|
assets: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(path: &PathBuf) -> Result<Self, String> {
|
||||||
|
let content = std::fs::read_to_string(path)
|
||||||
|
.map_err(|e| format!("Failed to read project file: {}", e))?;
|
||||||
|
|
||||||
|
serde_json::from_str(&content)
|
||||||
|
.map_err(|e| format!("Failed to parse project file: {}", e))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save(&self) -> Result<(), String> {
|
||||||
|
let mut project_file = self.path.clone();
|
||||||
|
project_file.push("project.json");
|
||||||
|
|
||||||
|
let content = serde_json::to_string_pretty(self)
|
||||||
|
.map_err(|e| format!("Failed to serialize project: {}", e))?;
|
||||||
|
|
||||||
|
std::fs::write(&project_file, content)
|
||||||
|
.map_err(|e| format!("Failed to write project file: {}", e))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
59
packages/editor-app/src-tauri/tauri.conf.json
Normal file
59
packages/editor-app/src-tauri/tauri.conf.json
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"productName": "ECS Framework Editor",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"identifier": "com.esengine.editor",
|
||||||
|
"build": {
|
||||||
|
"beforeDevCommand": "npm run dev",
|
||||||
|
"devUrl": "http://localhost:5173",
|
||||||
|
"beforeBuildCommand": "npm run build",
|
||||||
|
"frontendDist": "../dist"
|
||||||
|
},
|
||||||
|
"bundle": {
|
||||||
|
"active": true,
|
||||||
|
"targets": "all",
|
||||||
|
"icon": [
|
||||||
|
"icons/32x32.png",
|
||||||
|
"icons/128x128.png",
|
||||||
|
"icons/128x128@2x.png",
|
||||||
|
"icons/icon.icns",
|
||||||
|
"icons/icon.ico"
|
||||||
|
],
|
||||||
|
"windows": {
|
||||||
|
"certificateThumbprint": null,
|
||||||
|
"digestAlgorithm": "sha256",
|
||||||
|
"timestampUrl": ""
|
||||||
|
},
|
||||||
|
"macOS": {
|
||||||
|
"entitlements": null,
|
||||||
|
"exceptionDomain": "",
|
||||||
|
"frameworks": [],
|
||||||
|
"providerShortName": null,
|
||||||
|
"signingIdentity": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"title": "ECS Framework Editor",
|
||||||
|
"width": 1280,
|
||||||
|
"height": 800,
|
||||||
|
"minWidth": 800,
|
||||||
|
"minHeight": 600,
|
||||||
|
"resizable": true,
|
||||||
|
"fullscreen": false,
|
||||||
|
"decorations": true,
|
||||||
|
"transparent": false,
|
||||||
|
"center": true,
|
||||||
|
"skipTaskbar": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"security": {
|
||||||
|
"csp": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": {
|
||||||
|
"shell": {
|
||||||
|
"open": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
89
packages/editor-app/src/App.tsx
Normal file
89
packages/editor-app/src/App.tsx
Normal 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;
|
||||||
55
packages/editor-app/src/api/tauri.ts
Normal file
55
packages/editor-app/src/api/tauri.ts
Normal 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[];
|
||||||
|
}
|
||||||
10
packages/editor-app/src/main.tsx
Normal file
10
packages/editor-app/src/main.tsx
Normal 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>
|
||||||
|
);
|
||||||
130
packages/editor-app/src/plugins/SceneInspectorPlugin.ts
Normal file
130
packages/editor-app/src/plugins/SceneInspectorPlugin.ts
Normal 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');
|
||||||
|
}
|
||||||
|
}
|
||||||
99
packages/editor-app/src/styles/App.css
Normal file
99
packages/editor-app/src/styles/App.css
Normal 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;
|
||||||
|
}
|
||||||
19
packages/editor-app/src/styles/index.css
Normal file
19
packages/editor-app/src/styles/index.css
Normal 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;
|
||||||
|
}
|
||||||
23
packages/editor-app/tsconfig.json
Normal file
23
packages/editor-app/tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
27
packages/editor-app/vite.config.ts
Normal file
27
packages/editor-app/vite.config.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import react from '@vitejs/plugin-react';
|
||||||
|
|
||||||
|
const host = process.env.TAURI_DEV_HOST;
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
clearScreen: false,
|
||||||
|
server: {
|
||||||
|
host: host || false,
|
||||||
|
port: 5173,
|
||||||
|
strictPort: true,
|
||||||
|
hmr: host
|
||||||
|
? {
|
||||||
|
protocol: 'ws',
|
||||||
|
host,
|
||||||
|
port: 5183,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
envPrefix: ['VITE_', 'TAURI_'],
|
||||||
|
build: {
|
||||||
|
target: 'es2021',
|
||||||
|
minify: !process.env.TAURI_DEBUG ? 'esbuild' : false,
|
||||||
|
sourcemap: !!process.env.TAURI_DEBUG,
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user