diff --git a/lua/opencode/api.lua b/lua/opencode/api.lua index 212c1aa8..c9836a14 100644 --- a/lua/opencode/api.lua +++ b/lua/opencode/api.lua @@ -27,6 +27,37 @@ function M.toggle_input() input_window.toggle() end +---Close all windows except the opencode output and input windows (like 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 @@ -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) diff --git a/lua/opencode/config.lua b/lua/opencode/config.lua index 85c02c57..29aaaaee 100644 --- a/lua/opencode/config.lua +++ b/lua/opencode/config.lua @@ -67,6 +67,7 @@ M.defaults = { ['gr'] = { 'references', desc = 'Browse code references' }, [''] = { 'toggle_input', mode = { 'n' }, desc = 'Toggle input window' }, [''] = { 'cycle_variant', mode = { 'n' }, desc = 'Cycle model variants' }, + ['o'] = { 'only_opencode', mode = { 'n' }, desc = 'Close all non-opencode windows' }, ['oS'] = { 'select_child_session', desc = 'Select child session' }, ['oD'] = { 'debug_message', desc = 'Open raw message debug view' }, ['oO'] = { 'debug_output', desc = 'Open raw output debug view' }, @@ -88,6 +89,7 @@ M.defaults = { [''] = { 'switch_mode', mode = { 'n', 'i' }, desc = 'Switch agent mode' }, [''] = { 'cycle_variant', mode = { 'n', 'i' }, desc = 'Cycle model variants' }, [''] = { 'toggle_input', mode = { 'n', 'i' }, desc = 'Toggle input window' }, + ['o'] = { 'only_opencode', mode = { 'n' }, desc = 'Close all non-opencode windows' }, ['gr'] = { 'references', desc = 'Browse code references' }, ['oS'] = { 'select_child_session', desc = 'Select child session' }, ['oD'] = { 'debug_message', desc = 'Open raw message debug view' }, diff --git a/lua/opencode/ui/autocmds.lua b/lua/opencode/ui/autocmds.lua index 4f6b9a11..85e4b55e 100644 --- a/lua/opencode/ui/autocmds.lua +++ b/lua/opencode/ui/autocmds.lua @@ -7,11 +7,11 @@ 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 @@ -19,11 +19,25 @@ function M.setup_autocmds(windows) 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 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, })