diff --git a/src-tauri/src/pty/mod.rs b/src-tauri/src/pty/mod.rs
index 7c5509c..b7467a3 100644
--- a/src-tauri/src/pty/mod.rs
+++ b/src-tauri/src/pty/mod.rs
@@ -70,6 +70,10 @@ impl PtyService {
break;
}
}
+ // WSL (Windows Subsystem for Linux)
+ if std::path::Path::new(r"C:\Windows\System32\wsl.exe").exists() {
+ shells.push(ShellInfo { name: "WSL".to_string(), path: r"C:\Windows\System32\wsl.exe".to_string() });
+ }
}
shells
diff --git a/src/components/session/SessionContainer.vue b/src/components/session/SessionContainer.vue
index 2e5c825..751d501 100644
--- a/src/components/session/SessionContainer.vue
+++ b/src/components/session/SessionContainer.vue
@@ -14,6 +14,19 @@
/>
+
+
+
+
+
sessionStore.sessions.filter((s) => s.protocol === "ssh"),
);
+const localSessions = computed(() =>
+ sessionStore.sessions.filter((s) => s.protocol === "local"),
+);
+
const rdpSessions = computed(() =>
sessionStore.sessions.filter((s) => s.protocol === "rdp"),
);
diff --git a/src/components/session/TabBadge.vue b/src/components/session/TabBadge.vue
index f1a6158..e491030 100644
--- a/src/components/session/TabBadge.vue
+++ b/src/components/session/TabBadge.vue
@@ -31,7 +31,7 @@ import { computed } from "vue";
const props = defineProps<{
/** Connection protocol — drives the protocol-dot colour. */
- protocol: "ssh" | "rdp";
+ protocol: "ssh" | "rdp" | "local";
/** Username from the active session (if known). */
username?: string;
/** Raw tags from the connection record. */
@@ -40,9 +40,10 @@ const props = defineProps<{
status?: "connected" | "disconnected";
}>();
-/** Green=connected SSH, blue=connected RDP, red=disconnected. */
+/** Green=connected SSH, blue=connected RDP, purple=local, red=disconnected. */
const protocolDotClass = computed(() => {
if (props.status === "disconnected") return "bg-[#f85149]";
+ if (props.protocol === "local") return "bg-[#bc8cff]";
return props.protocol === "ssh" ? "bg-[#3fb950]" : "bg-[#1f6feb]";
});
diff --git a/src/components/session/TabBar.vue b/src/components/session/TabBar.vue
index 937f6e9..a6490e5 100644
--- a/src/components/session/TabBar.vue
+++ b/src/components/session/TabBar.vue
@@ -42,18 +42,39 @@
-
-
+
+
+
+
+
+
+ No shells found
+
+
+
diff --git a/src/stores/session.store.ts b/src/stores/session.store.ts
index b2c8dba..7b471c0 100644
--- a/src/stores/session.store.ts
+++ b/src/stores/session.store.ts
@@ -9,7 +9,7 @@ export interface Session {
id: string;
connectionId: number;
name: string;
- protocol: "ssh" | "rdp";
+ protocol: "ssh" | "rdp" | "local";
active: boolean;
username?: string;
status: "connected" | "disconnected";
@@ -70,7 +70,9 @@ export const useSessionStore = defineStore("session", () => {
// Disconnect the backend session using the protocol-appropriate command
try {
- if (session.protocol === "rdp") {
+ if (session.protocol === "local") {
+ await invoke("disconnect_pty", { sessionId: session.id });
+ } else if (session.protocol === "rdp") {
await invoke("disconnect_rdp", { sessionId: session.id });
} else {
await invoke("disconnect_session", { sessionId: session.id });
@@ -315,6 +317,34 @@ export const useSessionStore = defineStore("session", () => {
}
}
+ /** Spawn a local shell as a full-size tab. */
+ async function spawnLocalTab(shellName: string, shellPath: string): Promise {
+ try {
+ const sessionId = await invoke("spawn_local_shell", {
+ shellPath,
+ cols: 120,
+ rows: 40,
+ });
+
+ sessions.value.push({
+ id: sessionId,
+ connectionId: 0,
+ name: shellName,
+ protocol: "local",
+ active: true,
+ status: "connected",
+ });
+
+ // Listen for PTY close
+ listen(`pty:close:${sessionId}`, () => markDisconnected(sessionId));
+
+ activeSessionId.value = sessionId;
+ } catch (err: unknown) {
+ const msg = err instanceof Error ? err.message : String(err);
+ alert(`Failed to spawn local shell: ${msg}`);
+ }
+ }
+
/** Apply a theme to all active terminal instances. */
function setTheme(theme: ThemeDefinition): void {
activeTheme.value = theme;
@@ -344,6 +374,7 @@ export const useSessionStore = defineStore("session", () => {
activateSession,
closeSession,
connect,
+ spawnLocalTab,
moveSession,
setTheme,
setTerminalDimensions,