Skip to content

Commit ec0e723

Browse files
committed
enter shell mode only when the input is empty
1 parent ae61a4f commit ec0e723

File tree

2 files changed

+76
-13
lines changed

2 files changed

+76
-13
lines changed

cli/src/state/atoms/__tests__/shell.test.ts

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
navigateShellHistoryDownAtom,
1111
addToShellHistoryAtom,
1212
} from "../shell.js"
13-
import { textBufferStringAtom } from "../textBuffer.js"
13+
import { textBufferStringAtom, setTextAtom } from "../textBuffer.js"
1414

1515
// Mock child_process to avoid actual command execution
1616
vi.mock("child_process", () => ({
@@ -56,12 +56,13 @@ describe("shell mode - comprehensive tests", () => {
5656
})
5757

5858
describe("shell mode activation", () => {
59-
it("should toggle shell mode on and off", () => {
59+
it("should toggle shell mode on and off when input is empty", () => {
6060
// Initial state
6161
expect(store.get(shellModeActiveAtom)).toBe(false)
6262
expect(store.get(inputModeAtom)).toBe("normal")
63+
expect(store.get(textBufferStringAtom)).toBe("")
6364

64-
// Toggle on
65+
// Toggle on (input is empty)
6566
store.set(toggleShellModeAtom)
6667
expect(store.get(shellModeActiveAtom)).toBe(true)
6768
expect(store.get(inputModeAtom)).toBe("shell")
@@ -72,6 +73,36 @@ describe("shell mode - comprehensive tests", () => {
7273
expect(store.get(inputModeAtom)).toBe("normal")
7374
})
7475

76+
it("should NOT enter shell mode when input is not empty", () => {
77+
// Set some text in the buffer
78+
store.set(setTextAtom, "some text")
79+
expect(store.get(textBufferStringAtom)).toBe("some text")
80+
81+
// Try to toggle on
82+
store.set(toggleShellModeAtom)
83+
84+
// Should NOT activate shell mode
85+
expect(store.get(shellModeActiveAtom)).toBe(false)
86+
expect(store.get(inputModeAtom)).toBe("normal")
87+
88+
// Text should still be there
89+
expect(store.get(textBufferStringAtom)).toBe("some text")
90+
})
91+
92+
it("should exit shell mode even when text is present", () => {
93+
// Enter shell mode (with empty input)
94+
store.set(toggleShellModeAtom)
95+
expect(store.get(shellModeActiveAtom)).toBe(true)
96+
97+
// Add some text
98+
store.set(setTextAtom, "some command")
99+
100+
// Toggle off should work even with text
101+
store.set(toggleShellModeAtom)
102+
expect(store.get(shellModeActiveAtom)).toBe(false)
103+
expect(store.get(inputModeAtom)).toBe("normal")
104+
})
105+
75106
it("should reset history index when toggling on", () => {
76107
// Set a non-default history index
77108
store.set(shellHistoryIndexAtom, 5)
@@ -95,8 +126,8 @@ describe("shell mode - comprehensive tests", () => {
95126
expect(store.get(shellHistoryIndexAtom)).toBe(-1)
96127
})
97128

98-
it("should handle multiple rapid toggles", () => {
99-
// Toggle multiple times
129+
it("should handle multiple rapid toggles when input is empty", () => {
130+
// Toggle multiple times (with empty input)
100131
store.set(toggleShellModeAtom)
101132
expect(store.get(shellModeActiveAtom)).toBe(true)
102133

@@ -351,7 +382,7 @@ describe("shell mode - comprehensive tests", () => {
351382
})
352383

353384
describe("Shift+1 key detection", () => {
354-
it("should detect Shift+1 and toggle shell mode", async () => {
385+
it("should detect Shift+1 and toggle shell mode when input is empty", async () => {
355386
const shift1Key: Key = {
356387
name: "shift-1",
357388
sequence: "!",
@@ -361,6 +392,9 @@ describe("shell mode - comprehensive tests", () => {
361392
paste: false,
362393
}
363394

395+
// Ensure input is empty
396+
expect(store.get(textBufferStringAtom)).toBe("")
397+
364398
// Press Shift+1
365399
await store.set(keyboardHandlerAtom, shift1Key)
366400

@@ -379,7 +413,7 @@ describe("shell mode - comprehensive tests", () => {
379413
paste: false,
380414
}
381415

382-
// Activate
416+
// Activate (with empty input)
383417
await store.set(keyboardHandlerAtom, shift1Key)
384418
expect(store.get(shellModeActiveAtom)).toBe(true)
385419

@@ -388,6 +422,28 @@ describe("shell mode - comprehensive tests", () => {
388422
expect(store.get(shellModeActiveAtom)).toBe(false)
389423
expect(store.get(inputModeAtom)).toBe("normal")
390424
})
425+
426+
it("should NOT activate shell mode via Shift+1 when input has text", async () => {
427+
const shift1Key: Key = {
428+
name: "shift-1",
429+
sequence: "!",
430+
ctrl: false,
431+
meta: false,
432+
shift: true,
433+
paste: false,
434+
}
435+
436+
// Add text to input
437+
store.set(setTextAtom, "some command")
438+
expect(store.get(textBufferStringAtom)).toBe("some command")
439+
440+
// Try to activate with Shift+1
441+
await store.set(keyboardHandlerAtom, shift1Key)
442+
443+
// Should NOT activate shell mode
444+
expect(store.get(shellModeActiveAtom)).toBe(false)
445+
expect(store.get(inputModeAtom)).toBe("normal")
446+
})
391447
})
392448

393449
describe("edge cases", () => {

cli/src/state/atoms/shell.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { atom } from "jotai"
66
import { addMessageAtom, inputModeAtom, type InputMode } from "./ui.js"
77
import { exec } from "child_process"
88
import { chatMessagesAtom } from "./extension.js"
9-
import { clearTextAtom, setTextAtom } from "./textBuffer.js"
9+
import { clearTextAtom, setTextAtom, textBufferIsEmptyAtom } from "./textBuffer.js"
1010

1111
// ============================================================================
1212
// Shell Mode Atoms
@@ -29,19 +29,26 @@ export const shellHistoryIndexAtom = atom<number>(-1)
2929

3030
/**
3131
* Action atom to toggle shell mode
32+
* Only enters shell mode if input is empty, but always allows exiting
3233
*/
3334
export const toggleShellModeAtom = atom(null, (get, set) => {
3435
const isCurrentlyActive = get(shellModeActiveAtom)
35-
set(shellModeActiveAtom, !isCurrentlyActive)
36+
const isEmpty = get(textBufferIsEmptyAtom)
3637

3738
if (!isCurrentlyActive) {
38-
// Entering shell mode
39+
// Entering shell mode - only allow if input is empty
40+
if (!isEmpty) {
41+
// Don't enter shell mode if there's already text in the input
42+
return
43+
}
44+
set(shellModeActiveAtom, true)
3945
set(inputModeAtom, "shell" as InputMode)
4046
set(shellHistoryIndexAtom, -1)
4147
// Clear text buffer when entering shell mode
4248
set(clearTextAtom)
4349
} else {
44-
// Exiting shell mode
50+
// Exiting shell mode - always allow
51+
set(shellModeActiveAtom, false)
4552
set(inputModeAtom, "normal" as InputMode)
4653
set(shellHistoryIndexAtom, -1)
4754
// Clear text buffer when exiting shell mode
@@ -184,10 +191,10 @@ export const executeShellCommandAtom = atom(null, async (get, set, command: stri
184191

185192
const currentMessages = get(chatMessagesAtom)
186193
set(chatMessagesAtom, [...currentMessages, chatMessage])
187-
} catch (error: any) {
194+
} catch (error: unknown) {
188195
// Handle errors and display them in the message system
189196

190-
const errorOutput = `❌ Error: ${error.message}`
197+
const errorOutput = `❌ Error: ${error instanceof Error ? error.message : error}`
191198

192199
// Display as error message for visibility
193200
const errorMessage = {

0 commit comments

Comments
 (0)