Skip to content
Closed
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
38 changes: 38 additions & 0 deletions lua/opencode/api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,37 @@ function M.toggle_input()
input_window.toggle()
end

---Close all windows except the opencode output and input windows (like <C-w>o but opencode-aware).
---If input is hidden it will be shown after closing other windows.
function M.only_opencode()
local windows = state.windows
if not windows or not windows.output_win then
return
end

-- Collect all windows in the current tabpage that are NOT opencode windows
local tabpage = vim.api.nvim_get_current_tabpage()
local all_wins = vim.api.nvim_tabpage_list_wins(tabpage)
for _, win in ipairs(all_wins) do
if win ~= windows.output_win
and win ~= windows.input_win
and win ~= windows.footer_win
and vim.api.nvim_win_is_valid(win)
then
local cfg = vim.api.nvim_win_get_config(win)
-- Skip floating windows that don't belong to opencode
if cfg.relative == '' then
pcall(vim.api.nvim_win_close, win, false)
end
end
end

-- If input was hidden, show it
if input_window.is_hidden() then
input_window._show()
end
end

function M.open_input()
return core.open({ new_session = false, focus = 'input', start_insert = true })
end
Expand Down Expand Up @@ -1109,6 +1140,13 @@ M.commands = {
end,
},

only_opencode = {
desc = 'Close all non-opencode windows (keep output + input)',
fn = function()
M.only_opencode()
end,
},

hide = {
desc = 'Hide opencode windows (preserve buffers for fast restore)',
fn = function(args)
Expand Down
2 changes: 2 additions & 0 deletions lua/opencode/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ M.defaults = {
['gr'] = { 'references', desc = 'Browse code references' },
['<M-i>'] = { 'toggle_input', mode = { 'n' }, desc = 'Toggle input window' },
['<M-r>'] = { 'cycle_variant', mode = { 'n' }, desc = 'Cycle model variants' },
['<C-w>o'] = { 'only_opencode', mode = { 'n' }, desc = 'Close all non-opencode windows' },
['<leader>oS'] = { 'select_child_session', desc = 'Select child session' },
['<leader>oD'] = { 'debug_message', desc = 'Open raw message debug view' },
['<leader>oO'] = { 'debug_output', desc = 'Open raw output debug view' },
Expand All @@ -88,6 +89,7 @@ M.defaults = {
['<M-m>'] = { 'switch_mode', mode = { 'n', 'i' }, desc = 'Switch agent mode' },
['<M-r>'] = { 'cycle_variant', mode = { 'n', 'i' }, desc = 'Cycle model variants' },
['<M-i>'] = { 'toggle_input', mode = { 'n', 'i' }, desc = 'Toggle input window' },
['<C-w>o'] = { 'only_opencode', mode = { 'n' }, desc = 'Close all non-opencode windows' },
['gr'] = { 'references', desc = 'Browse code references' },
['<leader>oS'] = { 'select_child_session', desc = 'Select child session' },
['<leader>oD'] = { 'debug_message', desc = 'Open raw message debug view' },
Expand Down
28 changes: 21 additions & 7 deletions lua/opencode/ui/autocmds.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,37 @@ function M.setup_autocmds(windows)
input_window.setup_autocmds(windows, group)
output_window.setup_autocmds(windows, group)

-- Only keep shared autocmds here (e.g., WinClosed, WinLeave for all windows)
local wins = { windows.input_win, windows.output_win, windows.footer_win }
-- Use a global WinClosed handler that does live lookups to handle
-- the case where input_win is recreated (new ID) after a hide/show cycle.
vim.api.nvim_create_autocmd('WinClosed', {
group = group,
pattern = table.concat(wins, ','),
pattern = '*',
callback = function(opts)
-- Don't close everything if we're just toggling the input window
if input_window._toggling then
return
end

local closed_win = tonumber(opts.match)
if vim.tbl_contains(wins, closed_win) then
vim.schedule(function()
require('opencode.ui.ui').teardown_visible_windows(windows)
end)

-- Live lookup: get the current opencode window IDs at the time of the event
local is_output_win = closed_win == windows.output_win
local is_input_win = windows.input_win ~= nil and closed_win == windows.input_win
local is_footer_win = windows.footer_win ~= nil and closed_win == windows.footer_win

if not is_output_win and not is_input_win and not is_footer_win then
return
end

-- If a non-output opencode window was closed (e.g. input/footer via <C-w>o),
-- and the output window is still valid, don't tear down the whole UI.
if not is_output_win and windows.output_win and vim.api.nvim_win_is_valid(windows.output_win) then
return
end

vim.schedule(function()
require('opencode.ui.ui').teardown_visible_windows(windows)
end)
end,
})

Expand Down
Loading