Skip to content

Commit f324da5

Browse files
committed
fix(workspace): probe WSL lazily, run wsl.exe off IPC thread (#241)
1 parent 1aa9886 commit f324da5

6 files changed

Lines changed: 59 additions & 40 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "terax",
33
"private": true,
4-
"version": "0.6.4",
4+
"version": "0.6.5",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "terax"
3-
version = "0.6.4"
3+
version = "0.6.5"
44
description = "Terax — an open-source AI-native terminal emulator"
55
authors = ["crynta"]
66
edition = "2021"

src-tauri/src/modules/workspace.rs

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -97,48 +97,66 @@ fn run_wsl(args: &[&str]) -> Result<String, String> {
9797
Ok(decode_command_output(&out.stdout))
9898
}
9999

100+
#[cfg(windows)]
101+
fn list_distros_blocking() -> Result<Vec<WslDistro>, String> {
102+
let out = run_wsl(&["--list", "--verbose"])?;
103+
let mut distros = Vec::new();
104+
for raw in out.lines().skip(1) {
105+
let line = raw.trim();
106+
if line.is_empty() {
107+
continue;
108+
}
109+
let default = line.starts_with('*');
110+
let line = line.trim_start_matches('*').trim();
111+
let parts: Vec<&str> = line.split_whitespace().collect();
112+
if parts.len() < 3 {
113+
continue;
114+
}
115+
let state_idx = parts.len() - 2;
116+
let name = parts[..state_idx].join(" ");
117+
let state = parts[state_idx];
118+
distros.push(WslDistro {
119+
name,
120+
default,
121+
running: state.eq_ignore_ascii_case("Running"),
122+
});
123+
}
124+
Ok(distros)
125+
}
126+
100127
#[tauri::command]
101-
pub fn wsl_list_distros() -> Result<Vec<WslDistro>, String> {
128+
pub async fn wsl_list_distros() -> Result<Vec<WslDistro>, String> {
102129
#[cfg(not(windows))]
103130
{
104131
Ok(Vec::new())
105132
}
106133
#[cfg(windows)]
107134
{
108-
let out = run_wsl(&["--list", "--verbose"])?;
109-
let mut distros = Vec::new();
110-
for raw in out.lines().skip(1) {
111-
let line = raw.trim();
112-
if line.is_empty() {
113-
continue;
114-
}
115-
let default = line.starts_with('*');
116-
let line = line.trim_start_matches('*').trim();
117-
let parts: Vec<&str> = line.split_whitespace().collect();
118-
if parts.len() < 3 {
119-
continue;
120-
}
121-
let state_idx = parts.len() - 2;
122-
let name = parts[..state_idx].join(" ");
123-
let state = parts[state_idx];
124-
distros.push(WslDistro {
125-
name,
126-
default,
127-
running: state.eq_ignore_ascii_case("Running"),
128-
});
129-
}
130-
Ok(distros)
135+
tauri::async_runtime::spawn_blocking(list_distros_blocking)
136+
.await
137+
.map_err(|e| e.to_string())?
131138
}
132139
}
133140

134141
#[tauri::command]
135-
pub fn wsl_default_distro() -> Result<Option<String>, String> {
136-
let distros = wsl_list_distros()?;
137-
Ok(distros
138-
.iter()
139-
.find(|d| d.default)
140-
.map(|d| d.name.clone())
141-
.or_else(|| distros.first().map(|d| d.name.clone())))
142+
pub async fn wsl_default_distro() -> Result<Option<String>, String> {
143+
#[cfg(not(windows))]
144+
{
145+
Ok(None)
146+
}
147+
#[cfg(windows)]
148+
{
149+
tauri::async_runtime::spawn_blocking(|| {
150+
let distros = list_distros_blocking()?;
151+
Ok(distros
152+
.iter()
153+
.find(|d| d.default)
154+
.map(|d| d.name.clone())
155+
.or_else(|| distros.first().map(|d| d.name.clone())))
156+
})
157+
.await
158+
.map_err(|e| e.to_string())?
159+
}
142160
}
143161

144162
#[tauri::command]

src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://schema.tauri.app/config/2",
33
"productName": "Terax",
4-
"version": "0.6.4",
4+
"version": "0.6.5",
55
"identifier": "app.crynta.terax",
66
"build": {
77
"beforeDevCommand": "pnpm dev",

src/modules/statusbar/WorkspaceEnvSelector.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
} from "@/modules/workspace";
1414
import { Refresh01Icon, ServerStack03Icon } from "@hugeicons/core-free-icons";
1515
import { HugeiconsIcon } from "@hugeicons/react";
16-
import { useEffect } from "react";
1716

1817
type Props = {
1918
onSelect: (env: WorkspaceEnv) => void;
@@ -28,14 +27,16 @@ export function WorkspaceEnvSelector({ onSelect }: Props) {
2827
const error = useWorkspaceEnvStore((s) => s.error);
2928
const refreshDistros = useWorkspaceEnvStore((s) => s.refreshDistros);
3029

31-
useEffect(() => {
32-
void refreshDistros();
33-
}, [refreshDistros]);
30+
const handleOpenChange = (open: boolean) => {
31+
if (open && distros.length === 0 && !loading) {
32+
void refreshDistros();
33+
}
34+
};
3435

3536
const label = env.kind === "wsl" ? `WSL: ${env.distro}` : "Windows";
3637

3738
return (
38-
<DropdownMenu>
39+
<DropdownMenu onOpenChange={handleOpenChange}>
3940
<DropdownMenuTrigger asChild>
4041
<button
4142
type="button"

0 commit comments

Comments
 (0)