Custom workspace dropdown + Launch-Unreal button

Replace the ugly native <select> on the connect screen with a themed
custom dropdown (Select component). Add a Launch-Unreal button that appears
when the chosen working folder contains a .uproject: backend find_uproject
resolves the scope to disk and searches it (and one level down); the button
opens the project via the opener plugin. Bundled the UE icon (currentColor,
so white-on-dark / black-on-light per theme). New strings translated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Bonchellon
2026-07-07 15:28:17 +03:00
parent 4249d5ba66
commit d9f99bc58b
6 changed files with 157 additions and 10 deletions

View File

@ -610,6 +610,54 @@ async fn open_in_explorer(state: State<'_, AppState>, target: String) -> Result<
Ok(())
}
/// Look for an Unreal `.uproject` inside the working-folder scope (resolved to a
/// local dir via `p4 where`). Checks the folder itself and one level of subdirs.
/// Returns the local path of the first match, or "" if the scope isn't a UE project.
#[tauri::command]
async fn find_uproject(state: State<'_, AppState>, scope: String) -> Result<String, String> {
if scope.is_empty() {
return Ok(String::new());
}
let conn = current(&state)?;
let spec = format!("{}/...", scope.trim_end_matches(['/', '.']));
let dir = match run_json(&conn, &["where", &spec])?
.into_iter()
.next()
.and_then(|v| v.get("path").and_then(|p| p.as_str()).map(String::from))
{
Some(p) => p.trim_end_matches(['.', '/', '\\']).to_string(),
None => return Ok(String::new()),
};
let is_uproject = |p: &std::path::Path| {
p.extension().map_or(false, |x| x.eq_ignore_ascii_case("uproject"))
};
// the scope folder itself
if let Ok(entries) = std::fs::read_dir(&dir) {
let mut subdirs = Vec::new();
for e in entries.flatten() {
let p = e.path();
if is_uproject(&p) {
return Ok(p.to_string_lossy().to_string());
}
if p.is_dir() {
subdirs.push(p);
}
}
// one level down
for sd in subdirs {
if let Ok(inner) = std::fs::read_dir(&sd) {
for e in inner.flatten() {
let p = e.path();
if is_uproject(&p) {
return Ok(p.to_string_lossy().to_string());
}
}
}
}
}
Ok(String::new())
}
/// Files and metadata of a submitted changelist (History detail).
#[tauri::command]
async fn p4_describe(state: State<'_, AppState>, change: String) -> Result<Value, String> {
@ -702,6 +750,7 @@ pub fn run() {
p4_scan,
p4_undo_change,
open_in_explorer,
find_uproject,
p4_describe,
read_file,
read_depot,