Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ CodexMonitor is a macOS Tauri app that orchestrates Codex agents across local wo
- **Components**: presentational only; props in, UI out; no Tauri IPC calls.
- **Hooks**: own state, side-effects, and event wiring (e.g., app-server events).
- **Utils**: pure helpers live in `src/utils/` (no React hooks here).
- **Services**: all Tauri IPC goes through `src/services/tauri.ts`.
- **Services**: all Tauri IPC goes through `src/services/` (prefer `src/services/tauri.ts`; event subscriptions can live in `src/services/events.ts`).
- **Types**: shared UI data types live in `src/types.ts`.
- **Styles**: one CSS file per UI area in `src/styles/` (no global refactors in components).
- **Backend IPC**: add new commands in `src-tauri/src/lib.rs` and mirror them in the service.
Expand Down
8 changes: 4 additions & 4 deletions src/features/app/hooks/useAppServerEvents.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect } from "react";
import { listen } from "@tauri-apps/api/event";
import type { AppServerEvent, ApprovalRequest } from "../../../types";
import { subscribeAppServerEvents } from "../../../services/events";

type AgentDelta = {
workspaceId: string;
Expand Down Expand Up @@ -58,10 +58,10 @@ export function useAppServerEvents(handlers: AppServerEventHandlers) {
useEffect(() => {
let unlisten: (() => void) | null = null;
let canceled = false;
listen<AppServerEvent>("app-server-event", (event) => {
handlers.onAppServerEvent?.(event.payload);
subscribeAppServerEvents((payload) => {
handlers.onAppServerEvent?.(payload);

const { workspace_id, message } = event.payload;
const { workspace_id, message } = payload;
const method = String(message.method ?? "");

if (method === "codex/connected") {
Expand Down
12 changes: 3 additions & 9 deletions src/features/terminal/hooks/useTerminalSession.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import type { RefObject } from "react";
import { listen } from "@tauri-apps/api/event";
import { Terminal } from "@xterm/xterm";
import { FitAddon } from "@xterm/addon-fit";
import "@xterm/xterm/css/xterm.css";
import type { DebugEntry, TerminalStatus, WorkspaceInfo } from "../../../types";
import { buildErrorDebugEntry } from "../../../utils/debugEntries";
import { subscribeTerminalOutput, type TerminalOutputEvent } from "../../../services/events";
import {
openTerminalSession,
resizeTerminalSession,
Expand All @@ -14,12 +14,6 @@ import {

const MAX_BUFFER_CHARS = 200_000;

type TerminalOutputEvent = {
workspaceId: string;
terminalId: string;
data: string;
};

type UseTerminalSessionOptions = {
activeWorkspace: WorkspaceInfo | null;
activeTerminalId: string | null;
Expand Down Expand Up @@ -122,8 +116,8 @@ export function useTerminalSession({
useEffect(() => {
let unlisten: (() => void) | null = null;
let canceled = false;
listen<TerminalOutputEvent>("terminal-output", (event) => {
const { workspaceId, terminalId, data } = event.payload;
subscribeTerminalOutput((payload: TerminalOutputEvent) => {
const { workspaceId, terminalId, data } = payload;
const key = `${workspaceId}:${terminalId}`;
const next = appendBuffer(outputBuffersRef.current.get(key), data);
outputBuffersRef.current.set(key, next);
Expand Down
26 changes: 26 additions & 0 deletions src/services/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { listen } from "@tauri-apps/api/event";
import type { AppServerEvent } from "../types";

export type Unsubscribe = () => void;

export type TerminalOutputEvent = {
workspaceId: string;
terminalId: string;
data: string;
};

export async function subscribeAppServerEvents(
onEvent: (event: AppServerEvent) => void,
): Promise<Unsubscribe> {
return listen<AppServerEvent>("app-server-event", (event) => {
onEvent(event.payload);
});
}

export async function subscribeTerminalOutput(
onEvent: (event: TerminalOutputEvent) => void,
): Promise<Unsubscribe> {
return listen<TerminalOutputEvent>("terminal-output", (event) => {
onEvent(event.payload);
});
}