diff --git a/src/web/public/terminal-ui.js b/src/web/public/terminal-ui.js index 3ec27a55..d95960b2 100644 --- a/src/web/public/terminal-ui.js +++ b/src/web/public/terminal-ui.js @@ -4605,6 +4605,15 @@ Object.assign(CodemanApp.prototype, { /** Insert editable text at the active prompt without pressing Enter. */ insertTerminalText(text) { if (!this.activeSessionId || !text) return; + // A tile or the split's Pane B holds the keyboard: the text belongs to that + // pane's session. Those panes have no local-echo overlay, and the main + // overlay is parked behind the grid, so send it straight to the pane. + const pane = this._focusedPane?.(); + if (pane && !pane.isPrimary) { + this._sendInputAsync(pane.sessionId, text); + pane.terminal?.focus(); + return; + } // Under predict the text goes out via sendInput (bypasses onData), so the // hook never sees it: clear outstanding predictions here instead. if (this._localEchoPolicy === 'predict') this._predictiveEcho?.clearPredictions(); @@ -4628,6 +4637,16 @@ Object.assign(CodemanApp.prototype, { if (!this.activeSessionId) return; if (typeof CjkInput !== 'undefined') CjkInput.clear(); + // A tile or Pane B holds the keyboard: its TUI owns the editable buffer + // (no local-echo overlay there), so kill the line in that pane's session, + // never in the parked main pane's. + const pane = this._focusedPane?.(); + if (pane && !pane.isPrimary) { + this._sendInputAsync(pane.sessionId, '\x15'); + this.showToast?.('Input cleared', 'success'); + pane.terminal?.focus(); + return; + } if (this._inputFlushTimeout) { clearTimeout(this._inputFlushTimeout); this._inputFlushTimeout = null; diff --git a/test/path-picker-ui.test.ts b/test/path-picker-ui.test.ts index 8dcb3c4a..6fa30b3a 100644 --- a/test/path-picker-ui.test.ts +++ b/test/path-picker-ui.test.ts @@ -225,6 +225,60 @@ describe('mobile filesystem picker actions', () => { expect(terminalHarness.cjkClear).toHaveBeenCalled(); }); + it('sends a picked path to the tile or Pane B that holds the keyboard, never the parked main overlay', () => { + const appendText = vi.fn(); + const sendInput = vi.fn(); + const _sendInputAsync = vi.fn(); + const mainFocus = vi.fn(); + const tileFocus = vi.fn(); + const app = { + activeSessionId: 'session-1', + _localEchoEnabled: true, + _localEchoOverlay: { appendText }, + terminal: { focus: mainFocus }, + sendInput, + _sendInputAsync, + _focusedPane: () => ({ terminal: { focus: tileFocus }, sessionId: 'tile-2', isPrimary: false, tile: {} }), + }; + + terminalHarness.mixin.insertTerminalText.call(app, '/mnt/d/AI/project'); + + expect(_sendInputAsync).toHaveBeenCalledWith('tile-2', '/mnt/d/AI/project'); + expect(appendText).not.toHaveBeenCalled(); + expect(sendInput).not.toHaveBeenCalled(); + expect(tileFocus).toHaveBeenCalledOnce(); + expect(mainFocus).not.toHaveBeenCalled(); + }); + + it('clears the prompt of the tile or Pane B that holds the keyboard with Ctrl+U, leaving the main overlay alone', () => { + const clear = vi.fn(); + const sendInput = vi.fn(() => Promise.resolve()); + const _sendInputAsync = vi.fn(); + const showToast = vi.fn(); + const tileFocus = vi.fn(); + const app = { + activeSessionId: 'session-1', + _inputFlushTimeout: null, + _pendingInput: 'main pane text', + _localEchoEnabled: true, + _localEchoOverlay: { getFlushed: () => ({ count: 0, text: '' }), clear, suppressBufferDetection: vi.fn() }, + sendInput, + _sendInputAsync, + showToast, + terminal: { focus: vi.fn() }, + _focusedPane: () => ({ terminal: { focus: tileFocus }, sessionId: 'tile-2', isPrimary: false, tile: {} }), + }; + + terminalHarness.mixin.clearTerminalInput.call(app); + + expect(_sendInputAsync).toHaveBeenCalledWith('tile-2', '\x15'); + expect(sendInput).not.toHaveBeenCalled(); + expect(clear).not.toHaveBeenCalled(); + expect(app._pendingInput).toBe('main pane text'); + expect(showToast).toHaveBeenCalledWith('Input cleared', 'success'); + expect(tileFocus).toHaveBeenCalledOnce(); + }); + it('uses Ctrl+U to clear the TUI-owned prompt when local echo is disabled', () => { const sendInput = vi.fn(() => Promise.resolve()); const app = {