优化web/server模式下的目录选择,现在支持目录树选择,而不是硬文本写入

This commit is contained in:
xintaofei
2026-03-30 14:59:23 +08:00
parent 9b9169f61d
commit 8d393b3b4f
23 changed files with 1077 additions and 344 deletions

View File

@@ -2934,6 +2934,98 @@ where
})?
}
// ─── Directory browser helpers (for web/server mode) ───
#[cfg_attr(feature = "tauri-runtime", tauri::command)]
pub async fn get_home_directory() -> Result<String, AppCommandError> {
dirs::home_dir()
.map(|p| p.to_string_lossy().to_string())
.ok_or_else(|| AppCommandError::io_error("Could not determine home directory"))
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DirectoryEntry {
pub name: String,
pub path: String,
pub has_children: bool,
}
#[cfg_attr(feature = "tauri-runtime", tauri::command)]
pub async fn list_directory_entries(
path: String,
) -> Result<Vec<DirectoryEntry>, AppCommandError> {
let root = PathBuf::from(&path);
if !root.is_dir() {
return Err(AppCommandError::io_error("Path is not a directory")
.with_detail(path));
}
let mut entries: Vec<DirectoryEntry> = Vec::new();
let read_dir = std::fs::read_dir(&root).map_err(|e| {
AppCommandError::io_error("Failed to read directory").with_detail(e.to_string())
})?;
for entry in read_dir {
let entry = match entry {
Ok(e) => e,
Err(_) => continue,
};
let file_type = match entry.file_type() {
Ok(ft) => ft,
Err(_) => continue,
};
// Follow symlinks: check if the resolved path is a directory
let is_dir = if file_type.is_symlink() {
entry.path().is_dir()
} else {
file_type.is_dir()
};
if !is_dir {
continue;
}
let name = entry.file_name().to_string_lossy().to_string();
// Skip hidden directories (starting with '.')
if name.starts_with('.') {
continue;
}
let abs_path = entry.path().to_string_lossy().to_string();
// Peek into subdirectory to check if it has child directories
let has_children = match std::fs::read_dir(entry.path()) {
Ok(sub) => sub
.filter_map(|e| e.ok())
.any(|e| {
let ft = e.file_type().ok();
let is_sub_dir = ft.map_or(false, |ft| {
if ft.is_symlink() {
e.path().is_dir()
} else {
ft.is_dir()
}
});
if !is_sub_dir {
return false;
}
let sub_name = e.file_name().to_string_lossy().to_string();
!sub_name.starts_with('.')
}),
Err(_) => false,
};
entries.push(DirectoryEntry {
name,
path: abs_path,
has_children,
});
}
// Sort by name, case-insensitive
entries.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
Ok(entries)
}
#[cfg_attr(feature = "tauri-runtime", tauri::command)]
pub async fn get_file_tree(
path: String,

View File

@@ -270,6 +270,8 @@ mod tauri_app {
folders::save_folder_opened_conversations,
folders::start_file_tree_watch,
folders::stop_file_tree_watch,
folders::get_home_directory,
folders::list_directory_entries,
folders::get_file_tree,
folders::read_file_base64,
folders::read_file_preview,

View File

@@ -110,6 +110,24 @@ pub async fn get_git_branch(
Ok(Json(result))
}
pub async fn get_home_directory() -> Result<Json<String>, AppCommandError> {
let result = folder_commands::get_home_directory().await?;
Ok(Json(result))
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListDirectoryEntriesParams {
pub path: String,
}
pub async fn list_directory_entries(
Json(params): Json<ListDirectoryEntriesParams>,
) -> Result<Json<Vec<folder_commands::DirectoryEntry>>, AppCommandError> {
let result = folder_commands::list_directory_entries(params.path).await?;
Ok(Json(result))
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetFileTreeParams {

View File

@@ -50,6 +50,8 @@ pub fn build_router(state: Arc<AppState>, token: String, static_dir: std::path::
.route("/create_folder_directory", post(handlers::folders::create_folder_directory))
.route("/save_folder_opened_conversations", post(handlers::folders::save_folder_opened_conversations))
.route("/get_git_branch", post(handlers::folders::get_git_branch))
.route("/get_home_directory", post(handlers::folders::get_home_directory))
.route("/list_directory_entries", post(handlers::folders::list_directory_entries))
.route("/get_file_tree", post(handlers::folders::get_file_tree))
.route("/start_file_tree_watch", post(handlers::folders::start_file_tree_watch))
.route("/stop_file_tree_watch", post(handlers::folders::stop_file_tree_watch))