Skip to content

Commit e1a87e0

Browse files
committed
Unlink leftover clipboard files on dispose and armed quit
Idle Ctrl+C already unlinks Corbits-created ephemeralPath files. Quit via dispose or a second Ctrl+C skipped that path, so clipboard temp files stayed on disk. Operator-owned path mentions stay untouched.
1 parent 85e7a62 commit e1a87e0

2 files changed

Lines changed: 56 additions & 0 deletions

File tree

src/tui/prompt-slash-exit.test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,57 @@ describe("Ctrl+C exit", () => {
386386
}
387387
});
388388
});
389+
390+
test("dispose unlinks ephemeralPath and leaves the operator path", async () => {
391+
await withShell(async ({ shell }) => {
392+
const dir = mkdtempSync(join(tmpdir(), "ctrlc-attach-dispose-"));
393+
const ephemeral = join(dir, "ours.png");
394+
const operator = join(dir, "theirs.png");
395+
writeFileSync(ephemeral, "ephemeral-bytes");
396+
writeFileSync(operator, "operator-bytes");
397+
try {
398+
addPendingAttachment(shell, pendingImage("ours", { ephemeralPath: ephemeral }));
399+
addPendingAttachment(shell, pendingImage("theirs", { path: operator }));
400+
401+
shell.dispose();
402+
403+
expect(existsSync(ephemeral)).toBe(false);
404+
expect(existsSync(operator)).toBe(true);
405+
} finally {
406+
rmSync(dir, { recursive: true, force: true });
407+
}
408+
});
409+
});
410+
411+
test("busy double-Ctrl+C quit unlinks ephemeralPath and leaves the operator path", async () => {
412+
await withShell(async ({ shell }) => {
413+
const dir = mkdtempSync(join(tmpdir(), "ctrlc-attach-quit-"));
414+
const ephemeral = join(dir, "ours.png");
415+
const operator = join(dir, "theirs.png");
416+
writeFileSync(ephemeral, "ephemeral-bytes");
417+
writeFileSync(operator, "operator-bytes");
418+
try {
419+
setShellRunState(shell, "busy");
420+
addPendingAttachment(shell, pendingImage("ours", { ephemeralPath: ephemeral }));
421+
addPendingAttachment(shell, pendingImage("theirs", { path: operator }));
422+
setShellExitHandler(shell, () => {
423+
shell.dispose();
424+
});
425+
426+
handleCtrlC(shell, 0);
427+
expect(shell.pendingAttachments).toHaveLength(2);
428+
expect(existsSync(ephemeral)).toBe(true);
429+
expect(existsSync(operator)).toBe(true);
430+
431+
handleCtrlC(shell, 1);
432+
433+
expect(existsSync(ephemeral)).toBe(false);
434+
expect(existsSync(operator)).toBe(true);
435+
} finally {
436+
rmSync(dir, { recursive: true, force: true });
437+
}
438+
});
439+
});
389440
});
390441

391442
function pendingImage(id: string, extra?: Partial<PendingImageAttachment>): PendingImageAttachment {

src/tui/shell.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5422,6 +5422,9 @@ export function handleCtrlC(shell: AppShell, now = Date.now(), options?: FlashOp
54225422
ctrlCArmedAt.delete(shell);
54235423
const onExit = shellExitHandlers.get(shell);
54245424
if (onExit !== undefined) {
5425+
// Host teardown usually disposes; unlink here too so a stub/delayed
5426+
// onExit cannot leave Corbits-created clipboard files behind.
5427+
clearPendingAttachments(shell);
54255428
onExit();
54265429
return;
54275430
}
@@ -6325,6 +6328,8 @@ export function createAppShell(renderer: ShellRenderer, options?: AppShellOption
63256328
abortOverlayHostReservations(shell);
63266329
disposed = true;
63276330
shell.disposed = true;
6331+
// Quit paths that skip idle Ctrl+C still drop Corbits-created files.
6332+
clearPendingAttachments(shell);
63286333
if (wireKeys) {
63296334
renderer.keyInput.off("keypress", onKey);
63306335
renderer.keyInput.off("paste", onPaste);

0 commit comments

Comments
 (0)