a55555983b
- FAVORITES section at the top collects pinned workspaces (removed from their group listing); a star toggle on each row pins/unpins via setWorkspaceMeta. - Drag-to-reorder within a section using raw pointer events (HTML5 DnD is unreliable in the macOS WKWebView), with a drop-line indicator; on drop the section's `order` is reassigned sequentially and persisted. Cross-section drops are ignored (group membership unchanged). - Trash icon on row hover opens a ConfirmDelete modal that shows the live terminal count and warns before terminating them, then calls close_workspace and re-points the active workspace. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
189 lines
6.3 KiB
TypeScript
189 lines
6.3 KiB
TypeScript
import { invoke, Channel } from "@tauri-apps/api/core";
|
|
import { listen } from "@tauri-apps/api/event";
|
|
import type { Group, WorkspaceView } from "./layoutTypes";
|
|
|
|
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 interface EventRecord {
|
|
id: number;
|
|
surface_id: string;
|
|
workspace_id: string;
|
|
workspace_name: string;
|
|
agent_label: string | null;
|
|
kind: "done" | "wait" | "error" | "exit";
|
|
ts: number;
|
|
read: boolean;
|
|
}
|
|
|
|
export type MarkReadTarget =
|
|
| { target: "all" }
|
|
| { target: "ids"; value: number[] }
|
|
| { target: "surface"; value: string };
|
|
|
|
export async function getEventLog(limit?: number): Promise<{ events: EventRecord[]; unread: number }> {
|
|
return await invoke<{ events: EventRecord[]; unread: number }>("event_log", { limit: limit ?? null });
|
|
}
|
|
|
|
export async function markEventsRead(target: MarkReadTarget): Promise<void> {
|
|
await invoke("mark_read", { target });
|
|
}
|
|
|
|
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 } }
|
|
| { evt: "state"; data: { surface_id: string; state: import("./layoutTypes").SurfaceState } }
|
|
| { evt: "layout_changed"; data: { workspace_id: string } }
|
|
| { evt: "workspace_changed"; data: unknown }
|
|
| { evt: "groups_changed"; data: unknown }
|
|
| { evt: "event"; data: { record: EventRecord } }
|
|
| { evt: "events_read"; data: { ids: number[] } };
|
|
|
|
export function onDaemonEvent(handler: (evt: DaemonEvt) => void): Promise<() => void> {
|
|
return listen<DaemonEvt>("spacesh:evt", (e) => handler(e.payload));
|
|
}
|
|
|
|
export async function focusSurface(surfaceId: string): Promise<void> {
|
|
await invoke("focus", { surfaceId });
|
|
}
|
|
|
|
export function onDaemonRawEvent(name: string, handler: () => void): Promise<() => void> {
|
|
return listen(name, () => handler());
|
|
}
|
|
|
|
// ---- M2 additions ----
|
|
|
|
export interface StatusResult {
|
|
groups: Group[];
|
|
workspaces: WorkspaceView[];
|
|
}
|
|
|
|
export async function getStatusFull(): Promise<StatusResult> {
|
|
return await invoke<StatusResult>("status");
|
|
}
|
|
|
|
export async function splitSurface(surfaceId: string, dir: "right" | "down", command?: string, args: string[] = []): Promise<string> {
|
|
const data = await invoke<{ surface_id: string }>("split_surface", { surfaceId, dir, command: command ?? null, args });
|
|
return data.surface_id;
|
|
}
|
|
|
|
export async function setRatios(workspaceId: string, nodePath: number[], ratios: number[]): Promise<void> {
|
|
await invoke("set_ratios", { workspaceId, nodePath, ratios });
|
|
}
|
|
|
|
export async function moveSurface(surfaceId: string, targetSurfaceId: string, edge: "left" | "right" | "top" | "bottom"): Promise<void> {
|
|
await invoke("move_surface", { surfaceId, targetSurfaceId, edge });
|
|
}
|
|
|
|
export async function applyPreset(workspaceId: string, presetId: string, slots: { command?: string; args?: string[] }[]): Promise<string[]> {
|
|
const data = await invoke<{ surface_ids: string[] }>("apply_preset", {
|
|
workspaceId, presetId,
|
|
slots: slots.map((s) => ({ command: s.command ?? null, args: s.args ?? [] })),
|
|
});
|
|
return data.surface_ids;
|
|
}
|
|
|
|
export async function restartSurface(surfaceId: string): Promise<void> {
|
|
await invoke("restart_surface", { surfaceId });
|
|
}
|
|
|
|
export async function closeWorkspaceCmd(workspaceId: string): Promise<void> {
|
|
await invoke("close_workspace", { workspaceId });
|
|
}
|
|
|
|
export async function setWorkspaceMeta(workspaceId: string, meta: { name?: string; groupId?: string | null; unread?: boolean; order?: number; pinned?: boolean }): Promise<void> {
|
|
await invoke("set_workspace_meta", {
|
|
workspaceId,
|
|
name: meta.name ?? null,
|
|
groupId: meta.groupId === undefined ? null : meta.groupId,
|
|
unread: meta.unread ?? null,
|
|
order: meta.order ?? null,
|
|
pinned: meta.pinned ?? null,
|
|
});
|
|
}
|
|
|
|
export async function createGroup(name: string, color: string): Promise<string> {
|
|
const data = await invoke<{ group_id: string }>("create_group", { name, color });
|
|
return data.group_id;
|
|
}
|
|
|
|
export async function setGroup(groupId: string, meta: { name?: string; color?: string; order?: number }): Promise<void> {
|
|
await invoke("set_group", { groupId, name: meta.name ?? null, color: meta.color ?? null, order: meta.order ?? null });
|
|
}
|
|
|
|
export async function deleteGroup(groupId: string): Promise<void> {
|
|
await invoke("delete_group", { groupId });
|
|
}
|
|
|
|
export async function closeSurfaceCmd(surfaceId: string): Promise<void> {
|
|
await invoke("close_surface", { surfaceId });
|
|
}
|
|
|
|
export interface DaemonHealth { version: string; pid: number; started_at_ms: number }
|
|
|
|
export async function getHealth(): Promise<DaemonHealth> {
|
|
return await invoke<DaemonHealth>("health");
|
|
}
|
|
|
|
export async function setZoom(workspaceId: string, surfaceId: string | null): Promise<void> {
|
|
await invoke("set_zoom", { workspaceId, surfaceId });
|
|
}
|