新增AGC首页自定义工作目录

新增用户目录创建命令与首页接线

保持普通用户权限并避免ACL和UAC耦合

补充里程碑与实施计划
This commit is contained in:
2026-09-15 14:38:04 +08:00
parent 8ae7ed0128
commit fa2a7caa95
5 changed files with 109 additions and 2 deletions
@@ -811,6 +811,41 @@ pub(crate) async fn pick_local_project_directory(
Ok(Some(path.to_string_lossy().into_owned()))
}
/// Create a user-requested child directory using the current user's normal
/// filesystem rights. This deliberately does not inspect or rewrite ACLs and
/// never attempts elevation.
#[tauri::command]
pub(crate) fn create_local_project_directory(
parent_path: String,
directory_name: String,
) -> Result<String, String> {
let parent = Path::new(parent_path.trim());
if parent.as_os_str().is_empty() || !parent.is_absolute() {
return Err("父目录必须是绝对路径".to_string());
}
if project_path_has_control_chars(parent) {
return Err("父目录不能包含控制字符".to_string());
}
if !parent.is_dir() {
return Err("父目录不存在或不是文件夹".to_string());
}
let name = directory_name.trim();
if name.is_empty() || name == "." || name == ".." || name.chars().any(|c| c == '/' || c == '\\')
{
return Err("目录名称无效".to_string());
}
if name.chars().any(|c| c.is_control()) {
return Err("目录名称不能包含控制字符".to_string());
}
let target = parent.join(name);
fs::create_dir(&target).map_err(|error| match error.kind() {
std::io::ErrorKind::AlreadyExists => "目录已存在".to_string(),
std::io::ErrorKind::PermissionDenied => "没有权限在此位置创建目录".to_string(),
_ => format!("创建目录失败:{error}"),
})?;
Ok(target.to_string_lossy().into_owned())
}
#[tauri::command]
pub(crate) async fn pick_local_file(app: tauri::AppHandle) -> Result<Option<String>, String> {
let (sender, receiver) = tokio::sync::oneshot::channel();
@@ -2636,6 +2636,7 @@ fn main() {
is_local_project_directory_non_empty,
inspect_local_project_directory,
pick_local_project_directory,
create_local_project_directory,
rename_local_game_project,
suggest_automatic_project_name,
polish_local_project_prompt,
@@ -895,14 +895,26 @@ export function useHomeProjectCreation({
setProjectAction('creating');
setStatus('正在选择新项目文件夹');
try {
const selectedPath = await invoke<string | null>(
const parentPath = await invoke<string | null>(
'pick_local_project_directory',
projectPath.trim() ? { initialPath: projectPath.trim() } : undefined,
);
if (!selectedPath) {
if (!parentPath) {
setStatus('已取消');
return;
}
const directoryName = window.prompt('请输入新目录名称');
if (!directoryName?.trim()) {
setStatus('已取消');
return;
}
const selectedPath = await invoke<string>(
'create_local_project_directory',
{
parentPath,
directoryName: directoryName.trim(),
},
);
setProjectPath(selectedPath);
projectActionRef.current = null;
setProjectAction(null);