feat: add remove chat button and logic

This commit is contained in:
tusharmagar 2026-02-16 15:28:20 +05:30
parent c5c36ed0e4
commit 503693775a
6 changed files with 46 additions and 2 deletions

View file

@ -348,6 +348,10 @@ export function setupIpcHandlers() {
'runs:list': async (_event, args) => {
return runsCore.listRuns(args.cursor);
},
'runs:delete': async (_event, args) => {
await runsCore.deleteRun(args.runId);
return { success: true };
},
'models:list': async () => {
return await listOnboardingModels();
},

View file

@ -2277,6 +2277,17 @@ function App() {
onSelectRun: (runIdToLoad) => {
void navigateToView({ type: 'chat', runId: runIdToLoad })
},
onDeleteRun: async (runIdToDelete) => {
try {
await window.ipc.invoke('runs:delete', { runId: runIdToDelete })
if (runId === runIdToDelete) {
void navigateToView({ type: 'chat', runId: null })
}
await loadRuns()
} catch (err) {
console.error('Failed to delete run:', err)
}
},
onSelectBackgroundTask: (taskName) => {
void navigateToView({ type: 'task', name: taskName })
},

View file

@ -130,6 +130,7 @@ const SERVICE_LABELS: Record<string, string> = {
type TasksActions = {
onNewChat: () => void
onSelectRun: (runId: string) => void
onDeleteRun: (runId: string) => void
onSelectBackgroundTask?: (taskName: string) => void
}
@ -1056,7 +1057,7 @@ function TasksSection({
</div>
<SidebarMenu>
{runs.map((run) => (
<SidebarMenuItem key={run.id}>
<SidebarMenuItem key={run.id} className="group/chat-item">
<SidebarMenuButton
isActive={currentRunId === run.id}
onClick={() => actions?.onSelectRun(run.id)}
@ -1067,10 +1068,21 @@ function TasksSection({
) : null}
<span className="min-w-0 flex-1 truncate text-sm">{run.title || '(Untitled chat)'}</span>
{run.createdAt ? (
<span className="shrink-0 text-[10px] text-muted-foreground">
<span className="shrink-0 text-[10px] text-muted-foreground group-hover/chat-item:hidden">
{formatRunTime(run.createdAt)}
</span>
) : null}
<button
type="button"
className="shrink-0 hidden group-hover/chat-item:flex items-center justify-center text-muted-foreground hover:text-destructive transition-colors"
onClick={(e) => {
e.stopPropagation()
actions?.onDeleteRun(run.id)
}}
aria-label="Delete chat"
>
<Trash2 className="size-3.5" />
</button>
</div>
</SidebarMenuButton>
</SidebarMenuItem>

View file

@ -12,6 +12,7 @@ export interface IRunsRepo {
fetch(id: string): Promise<z.infer<typeof Run>>;
list(cursor?: string): Promise<z.infer<typeof ListRunsResponse>>;
appendEvents(runId: string, events: z.infer<typeof RunEvent>[]): Promise<void>;
delete(id: string): Promise<void>;
}
/**
@ -236,4 +237,9 @@ export class FSRunsRepo implements IRunsRepo {
...(nextCursor ? { nextCursor } : {}),
};
}
async delete(id: string): Promise<void> {
const filePath = path.join(WorkDir, 'runs', `${id}.jsonl`);
await fsp.unlink(filePath);
}
}

View file

@ -65,6 +65,11 @@ export async function stop(runId: string, force: boolean = false): Promise<void>
// This avoids duplicate events and ensures proper sequencing.
}
export async function deleteRun(runId: string): Promise<void> {
const repo = container.resolve<IRunsRepo>('runsRepo');
await repo.delete(runId);
}
export async function fetchRun(runId: string): Promise<z.infer<typeof Run>> {
const repo = container.resolve<IRunsRepo>('runsRepo');
return repo.fetch(runId);

View file

@ -173,6 +173,12 @@ const ipcSchemas = {
}),
res: ListRunsResponse,
},
'runs:delete': {
req: z.object({
runId: z.string(),
}),
res: z.object({ success: z.boolean() }),
},
'runs:events': {
req: z.null(),
res: z.null(),