Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lua/opencode/ui/input_window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,11 @@ end

---Show the input window by recreating it
function M._show()
-- Child sessions must never show the input window
if state.active_session and state.active_session.parentID then
return
end

local windows = state.windows
if not windows or not windows.input_buf or not windows.output_win then
return
Expand Down
7 changes: 7 additions & 0 deletions lua/opencode/ui/ui.lua
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,10 @@ end

---@param opts? { restore_position?: boolean, start_insert?: boolean }
function M.focus_input(opts)
if state.active_session and state.active_session.parentID then
return
end

opts = opts or {}
local windows = state.windows
if not windows then
Expand Down Expand Up @@ -557,6 +561,9 @@ function M.toggle_pane()
if state.windows and current_win == state.windows.input_win then
output_window.focus_output(true)
else
if state.active_session and state.active_session.parentID then
return
end
input_window.focus_input()
end
end
Expand Down
26 changes: 26 additions & 0 deletions tests/unit/input_window_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -548,4 +548,30 @@ describe('input_window', function()
assert.is_false(input_window._hidden)
end)
end)

describe('child session input visibility', function()
after_each(function()
state.session.clear_active()
end)

it('_show() is a no-op when active session is a child session', function()
state.session.set_active({ id = 'child1', parentID = 'parent1' })
input_window._hidden = true

input_window._show()

assert.is_true(input_window._hidden)
end)

it('_show() proceeds when active session is a root session', function()
state.session.set_active({ id = 'root1' })
input_window._hidden = true

-- _show will early-return due to missing windows, but it should pass the guard
input_window._show()

-- _hidden remains true because windows are nil, but the parentID guard was passed
assert.is_true(input_window._hidden)
end)
end)
end)
33 changes: 33 additions & 0 deletions tests/unit/services_session_runtime_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,39 @@ describe('opencode.services.session_runtime', function()
end)
end)

describe('child session UI guards', function()
local input_window = require('opencode.ui.input_window')

after_each(function()
state.session.clear_active()
end)

it('toggle_pane does not show input when in a child session', function()
state.session.set_active({ id = 'child1', parentID = 'parent1' })
stub(input_window, 'focus_input')

-- Simulate being in the output window (not input)
state.ui.set_windows({ input_win = -1, output_win = vim.api.nvim_get_current_win(), input_buf = 1, output_buf = 2 })

ui.toggle_pane()

assert.stub(input_window.focus_input).was_not_called()
input_window.focus_input:revert()
end)

it('focus_input is a no-op when in a child session', function()
state.session.set_active({ id = 'child1', parentID = 'parent1' })
stub(input_window, 'is_hidden').returns(true)
stub(input_window, '_show')

ui.focus_input()

assert.stub(input_window._show).was_not_called()
input_window.is_hidden:revert()
input_window._show:revert()
end)
end)

describe('send_message', function()
it('delegates message-sending coverage to services_messaging_spec', function()
-- This spec focuses on session_runtime responsibilities.
Expand Down
Loading