feat(app): UDS bridge (channel/invoke/emit) + xterm.js terminal, M0 e2e works

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 20:24:57 +07:00
parent 1579686fdd
commit 56893c51d0
7 changed files with 447 additions and 3 deletions
+75
View File
@@ -0,0 +1,75 @@
import { invoke, Channel } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
export interface WorkspaceStatus {
workspace_id: string;
path: string;
surfaces: string[];
}
export async function openWorkspace(path: string): Promise<string> {
const data = await invoke<{ workspace_id: string }>("open", { path });
return data.workspace_id;
}
export async function newSurface(
workspaceId: string,
cols: number,
rows: number,
command?: string,
args: string[] = []
): Promise<string> {
const data = await invoke<{ surface_id: string }>("new_surface", {
workspaceId,
command: command ?? null,
args,
cols,
rows,
});
return data.surface_id;
}
export async function sendInput(surfaceId: string, data: Uint8Array): Promise<void> {
await invoke("input", { surfaceId, data: Array.from(data) });
}
export async function resizeSurface(surfaceId: string, cols: number, rows: number): Promise<void> {
await invoke("resize", { surfaceId, cols, rows });
}
export interface AttachResult {
snapshot: string;
cols: number;
rows: number;
}
export async function attachSurface(
surfaceId: string,
onOutput: (bytes: Uint8Array) => void
): Promise<AttachResult> {
const channel = new Channel<number[]>();
channel.onmessage = (msg) => onOutput(new Uint8Array(msg));
return await invoke<AttachResult>("attach", { surfaceId, onOutput: channel });
}
export async function detachSurface(surfaceId: string): Promise<void> {
await invoke("detach", { surfaceId });
}
export async function getStatus(): Promise<WorkspaceStatus[]> {
const data = await invoke<{ workspaces: WorkspaceStatus[] }>("status");
return data.workspaces;
}
export type DaemonEvt =
| { evt: "exit"; data: { surface_id: string; code: number } }
| { evt: "surface_created"; data: { surface_id: string; workspace_id: string } }
| { evt: "surface_closed"; data: { surface_id: string } };
export function onDaemonEvent(handler: (evt: DaemonEvt) => void): Promise<() => void> {
return listen<DaemonEvt>("spacesh:evt", (e) => handler(e.payload));
}
export function onDaemonRawEvent(name: string, handler: () => void): Promise<() => void> {
return listen(name, () => handler());
}