From 420bcf4340fd1ea0db48b24da6c60bffc88fc1c3 Mon Sep 17 00:00:00 2001 From: Akshat Date: Wed, 29 Jul 2026 01:13:10 +0530 Subject: [PATCH 1/2] fix(tui): enter bash mode for large pasted ! commands Large pastes get folded into a marker, so checking getText() for a leading ! missed them. Use getExpandedText() instead. --- .changeset/bash-mode-large-paste.md | 5 ++ .../tui/components/editor/custom-editor.ts | 11 ++- .../components/editor/custom-editor.test.ts | 69 +++++++++++++++++++ 3 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 .changeset/bash-mode-large-paste.md diff --git a/.changeset/bash-mode-large-paste.md b/.changeset/bash-mode-large-paste.md new file mode 100644 index 0000000000..c384ff1972 --- /dev/null +++ b/.changeset/bash-mode-large-paste.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Enter shell mode when pasting a large `!` command into an empty prompt, even if the paste is folded into a marker. diff --git a/apps/kimi-code/src/tui/components/editor/custom-editor.ts b/apps/kimi-code/src/tui/components/editor/custom-editor.ts index b1677e87b2..3c70d9adec 100644 --- a/apps/kimi-code/src/tui/components/editor/custom-editor.ts +++ b/apps/kimi-code/src/tui/components/editor/custom-editor.ts @@ -523,14 +523,13 @@ export class CustomEditor extends Editor { const emptyPromptBeforeInput = this.inputMode === 'prompt' && this.getText().length === 0; super.handleInput(normalized); - // Enter bash mode when `!...` is pasted into an empty prompt. The typed path - // above handles the single `!` keystroke; this catches bracketed / Ctrl-V - // pastes whose content starts with `!`. Strip the leading `!` so the buffer - // holds only the command, exactly like the typed path. - if (emptyPromptBeforeInput && this.inputMode === 'prompt' && this.getText().startsWith('!')) { + // Enter bash mode when `!...` is pasted into an empty prompt. Check + // getExpandedText() so folded large pastes still match on the leading `!`. + const expanded = this.getExpandedText(); + if (emptyPromptBeforeInput && this.inputMode === 'prompt' && expanded.startsWith('!')) { this.inputMode = 'bash'; this.onInputModeChange?.('bash'); - this.setText(this.getText().slice(1)); + this.setText(expanded.slice(1)); } this.reopenAutocompleteAfterInput(); diff --git a/apps/kimi-code/test/tui/components/editor/custom-editor.test.ts b/apps/kimi-code/test/tui/components/editor/custom-editor.test.ts index cf7184b3bd..2d1bd3151e 100644 --- a/apps/kimi-code/test/tui/components/editor/custom-editor.test.ts +++ b/apps/kimi-code/test/tui/components/editor/custom-editor.test.ts @@ -725,6 +725,75 @@ describe('CustomEditor bash mode via paste', () => { expect(editor.inputMode).toBe('bash'); expect(editor.getText()).toBe(''); }); + + it('enters bash mode when a folded large ! paste (>1000 chars) lands in an empty prompt', () => { + const editor = makeEditor(); + const modes: Array<'prompt' | 'bash'> = []; + editor.onInputModeChange = (mode) => modes.push(mode); + + // 1 + 1000 = 1001 chars → pi-tui folds into a [paste #N …] marker + const body = `printf ok #${'x'.repeat(1000 - 'printf ok #'.length)}`; + const pasted = `!${body}`; + expect(pasted.length).toBe(1001); + + editor.handleInput(`${PASTE_START}${pasted}${PASTE_END}`); + + expect(editor.inputMode).toBe('bash'); + expect(modes).toEqual(['bash']); + expect(editor.getText()).toBe(body); + expect(editor.getText()).not.toContain('[paste #'); + expect(editor.getText().startsWith('!')).toBe(false); + }); + + it('enters bash mode for a ! paste at the 1000-char fold boundary', () => { + const editor = makeEditor(); + const body = `printf ok #${'x'.repeat(999 - 'printf ok #'.length)}`; + const pasted = `!${body}`; + expect(pasted.length).toBe(1000); + + editor.handleInput(`${PASTE_START}${pasted}${PASTE_END}`); + + expect(editor.inputMode).toBe('bash'); + expect(editor.getText()).toBe(body); + }); + + it('enters bash mode when a folded multiline ! paste (>10 lines) lands in an empty prompt', () => { + const editor = makeEditor(); + // 11 lines beginning with ! → folded by line count + const lines = ['!echo one', ...Array.from({ length: 10 }, (_, i) => `echo line${i}`)]; + expect(lines.length).toBe(11); + const pasted = lines.join('\n'); + + editor.handleInput(`${PASTE_START}${pasted}${PASTE_END}`); + + expect(editor.inputMode).toBe('bash'); + expect(editor.getText()).toBe(['echo one', ...lines.slice(1)].join('\n')); + expect(editor.getText()).not.toContain('[paste #'); + }); + + it('enters bash mode for a ! paste at the 10-line fold boundary', () => { + const editor = makeEditor(); + const lines = ['!echo one', ...Array.from({ length: 9 }, (_, i) => `echo line${i}`)]; + expect(lines.length).toBe(10); + const pasted = lines.join('\n'); + + editor.handleInput(`${PASTE_START}${pasted}${PASTE_END}`); + + expect(editor.inputMode).toBe('bash'); + expect(editor.getText()).toBe(['echo one', ...lines.slice(1)].join('\n')); + }); + + it('does not enter bash mode for a large paste without a leading !', () => { + const editor = makeEditor(); + const pasted = `printf ok #${'x'.repeat(1001 - 'printf ok #'.length)}`; + expect(pasted.length).toBe(1001); + expect(pasted.startsWith('!')).toBe(false); + + editor.handleInput(`${PASTE_START}${pasted}${PASTE_END}`); + + expect(editor.inputMode).toBe('prompt'); + expect(editor.getText()).toMatch(/\[paste #1 \d+ chars\]/); + }); }); describe('CustomEditor bash mode file completion', () => { From ed63e758b090b19bebda374b1cc354e703d822c4 Mon Sep 17 00:00:00 2001 From: Akshat Date: Wed, 29 Jul 2026 01:21:11 +0530 Subject: [PATCH 2/2] fix(tui): skip paste expansion when prompt was not empty Addressing review feedback: getExpandedText() was running on every keystroke, re-materializing folded paste content each time. Move it inside the empty-prompt guard. --- .../src/tui/components/editor/custom-editor.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/kimi-code/src/tui/components/editor/custom-editor.ts b/apps/kimi-code/src/tui/components/editor/custom-editor.ts index 3c70d9adec..ba2cec9c15 100644 --- a/apps/kimi-code/src/tui/components/editor/custom-editor.ts +++ b/apps/kimi-code/src/tui/components/editor/custom-editor.ts @@ -525,11 +525,13 @@ export class CustomEditor extends Editor { // Enter bash mode when `!...` is pasted into an empty prompt. Check // getExpandedText() so folded large pastes still match on the leading `!`. - const expanded = this.getExpandedText(); - if (emptyPromptBeforeInput && this.inputMode === 'prompt' && expanded.startsWith('!')) { - this.inputMode = 'bash'; - this.onInputModeChange?.('bash'); - this.setText(expanded.slice(1)); + if (emptyPromptBeforeInput && this.inputMode === 'prompt') { + const expanded = this.getExpandedText(); + if (expanded.startsWith('!')) { + this.inputMode = 'bash'; + this.onInputModeChange?.('bash'); + this.setText(expanded.slice(1)); + } } this.reopenAutocompleteAfterInput();