mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 23:49:41 +02:00
fix(tui): confirm a kill with y, and make the dialog say what it would kill
Killing demanded the session's NAME typed out in full. That is the right
ceremony for dropping a production database and the wrong one for closing a
pane you are looking at; the beta tester's verdict was "thats stupid, just make
me type Y to confirm". `x` then `y` is already two deliberate keystrokes on a
row the user selected, and the conversation lives in its transcript, which a
kill does not touch.
Everything that is not `y` CANCELS rather than being ignored, so a stray key
closes the dialog instead of leaving a destructive prompt armed and waiting for
whatever gets typed next. Enter cancels too: it is the key most likely to be
hit by reflex, and this is the one dialog that destroys something.
⚠️ Found while verifying the new dialog: it did not name the session. The label
was computed as `row.session.name ?? id.slice(0, 8)`, and `??` falls back only
on null or undefined, so every session the server left with an EMPTY name — all
of them, until the TUI started naming its own — sailed through and the box read
"Kill ?". A destructive prompt that cannot say what it will destroy is worse
than no prompt, and it is now a single keystroke. The caller passes the same
label the LIST shows, so the dialog names the row in front of the user.
The typed-name machinery goes with it: TuiConfirmState.typed, setConfirmInput(),
confirmAccepts() and the 'typing'/'reject' steps are all removed rather than
left as unreachable branches.
This commit is contained in:
@@ -556,23 +556,32 @@ describe('codeman tui (under a pty)', () => {
|
||||
it('opens and closes the help overlay', async () => {
|
||||
term.write('?');
|
||||
await waitFor(() => frameLines(output).some((line) => line.includes('Keys')), 'the help overlay');
|
||||
expect(frameLines(output).join('\n')).toContain('kill (typed confirmation)');
|
||||
expect(frameLines(output).join('\n')).toContain('kill (y to confirm)');
|
||||
|
||||
term.write('\u001b');
|
||||
await waitFor(() => !frameLines(output).some((line) => line.includes('Keys')), 'escape to close the overlay');
|
||||
});
|
||||
|
||||
it('asks for the session name before killing anything', async () => {
|
||||
it('asks for a y before killing anything, and names what it would kill', async () => {
|
||||
term.write('x');
|
||||
await waitFor(() => frameLines(output).some((line) => line.includes('Kill session')), 'the kill confirmation');
|
||||
const overlay = frameLines(output).join('\n');
|
||||
expect(overlay).toContain('Type the name to confirm');
|
||||
expect(overlay).toContain('press y to kill');
|
||||
expect(overlay).toContain('w2-beta');
|
||||
|
||||
term.write('\u001b');
|
||||
await waitFor(() => !frameLines(output).some((line) => line.includes('Kill session')), 'escape to cancel the kill');
|
||||
});
|
||||
|
||||
it('cancels the kill on any key that is not y, and kills nothing', async () => {
|
||||
term.write('x');
|
||||
await waitFor(() => frameLines(output).some((line) => line.includes('Kill session')), 'the kill confirmation');
|
||||
term.write('n');
|
||||
await waitFor(() => !frameLines(output).some((line) => line.includes('Kill session')), 'the dialog to close');
|
||||
// The session is still listed: `n` cancelled rather than killed.
|
||||
await waitFor(() => frameLines(output).some((line) => line.includes('w2-beta')), 'w2-beta to still be listed');
|
||||
});
|
||||
|
||||
it('sends a one-line prompt with p', async () => {
|
||||
term.write('p');
|
||||
await waitFor(() => frameLines(output)[ROWS - 1].startsWith(' >'), 'the composer to open');
|
||||
|
||||
Reference in New Issue
Block a user