初始化项目启动器代码

This commit is contained in:
xintaofei
2026-03-27 13:05:27 +08:00
parent 77204e2295
commit 7c89e150f9
25 changed files with 1434 additions and 15 deletions

View File

@@ -2,7 +2,7 @@
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["welcome", "folder-*", "commit-*", "merge-*", "stash-*", "push-*", "settings"],
"windows": ["welcome", "folder-*", "commit-*", "merge-*", "stash-*", "push-*", "settings", "project-boot"],
"permissions": [
"core:default",
"core:window:default",

View File

@@ -9,7 +9,8 @@
"welcome",
"folder-*",
"commit-*",
"settings"
"settings",
"project-boot"
],
"permissions": [
"window-state:default"

View File

@@ -3,6 +3,7 @@ pub mod conversations;
pub mod folder_commands;
pub mod folders;
pub mod mcp;
pub mod project_boot;
pub mod system_settings;
pub mod terminal;
pub mod version_control;

View File

@@ -0,0 +1,92 @@
use std::path::PathBuf;
use crate::app_error::AppCommandError;
#[tauri::command]
pub async fn create_shadcn_project(
project_name: String,
template: String,
preset_code: String,
package_manager: String,
target_dir: String,
) -> Result<String, AppCommandError> {
let project_name = project_name.trim().to_string();
let template = template.trim().to_string();
let preset_code = preset_code.trim().to_string();
let package_manager = package_manager.trim().to_string();
let target_dir = target_dir.trim().to_string();
if project_name.is_empty() {
return Err(AppCommandError::invalid_input("Project name is required"));
}
if template.is_empty() {
return Err(AppCommandError::invalid_input("Template is required"));
}
if target_dir.is_empty() {
return Err(AppCommandError::invalid_input("Target directory is required"));
}
let full_path = PathBuf::from(&target_dir).join(&project_name);
let full_path_str = full_path.to_string_lossy().to_string();
// Check if directory already exists and is non-empty
if full_path.exists() {
let is_empty = full_path
.read_dir()
.map(|mut entries| entries.next().is_none())
.unwrap_or(false);
if !is_empty {
return Err(AppCommandError::already_exists(
"Target directory already exists and is not empty",
));
}
}
// Determine the command based on package manager
let (program, prefix_args): (&str, Vec<&str>) = match package_manager.as_str() {
"pnpm" => ("pnpm", vec!["dlx"]),
"yarn" => ("yarn", vec!["dlx"]),
"bun" => ("bunx", vec![]),
_ => ("npx", vec![]),
};
let mut cmd = crate::process::tokio_command(program);
cmd.args(&prefix_args);
cmd.args([
"shadcn@latest",
"init",
"-n",
&project_name,
"-t",
&template,
"-p",
&preset_code,
"-y",
]);
cmd.current_dir(&target_dir);
let output = cmd.output().await.map_err(|e| {
if e.kind() == std::io::ErrorKind::NotFound {
AppCommandError::dependency_missing(format!(
"{program} is not installed. Please install Node.js first."
))
} else {
AppCommandError::external_command(
"Failed to execute project creation command",
e.to_string(),
)
}
})?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
let detail = if stderr.is_empty() { stdout } else { stderr };
return Err(AppCommandError::external_command(
"Project creation command failed",
detail,
));
}
Ok(full_path_str)
}

View File

@@ -604,3 +604,30 @@ pub async fn open_push_window(
Ok(())
}
#[tauri::command]
pub async fn open_project_boot_window(app: AppHandle) -> Result<(), AppCommandError> {
if let Some(existing) = app.get_webview_window("project-boot") {
ensure_windows_undecorated(&existing);
let _ = existing.unminimize();
existing.set_focus().map_err(|e| {
AppCommandError::window("Failed to focus project boot window", e.to_string())
})?;
return Ok(());
}
let url = WebviewUrl::App("project-boot".into());
let builder = WebviewWindowBuilder::new(&app, "project-boot", url)
.title("Project Boot")
.inner_size(1400.0, 900.0)
.min_inner_size(1100.0, 700.0)
.center();
let window = apply_platform_window_style(builder)
.build()
.map_err(|e| {
AppCommandError::window("Failed to open project boot window", e.to_string())
})?;
ensure_windows_undecorated(&window);
Ok(())
}

View File

@@ -16,7 +16,8 @@ use std::sync::atomic::{AtomicBool, Ordering};
use acp::manager::ConnectionManager;
use commands::{
acp as acp_commands, conversations, folder_commands, folders, mcp as mcp_commands,
notification, system_settings, terminal as terminal_commands, version_control, windows,
notification, project_boot, system_settings, terminal as terminal_commands, version_control,
windows,
};
use tauri::Manager;
use terminal::manager::TerminalManager;
@@ -268,6 +269,8 @@ pub fn run() {
windows::open_merge_window,
windows::open_stash_window,
windows::open_push_window,
windows::open_project_boot_window,
project_boot::create_shadcn_project,
system_settings::get_system_proxy_settings,
system_settings::update_system_proxy_settings,
system_settings::get_system_language_settings,