Skip to content
Open
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: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ require('opencode').setup({
input_position = 'bottom', -- 'bottom' (default) or 'top'. Position of the input window
window_width = 0.40, -- Width as percentage of editor width
zoom_width = 0.8, -- Zoom width as percentage of editor width
input_height = 0.15, -- Input height as percentage of window height
display_model = true, -- Display model name on top winbar
display_context_size = true, -- Display context size in the footer
display_cost = true, -- Display cost in the footer
Expand All @@ -220,9 +219,13 @@ require('opencode').setup({
},
},
input = {
min_height = 0.10, -- min height of prompt input as percentage of window height
max_height = 0.25, -- max height of prompt input as percentage of window height
text = {
wrap = false, -- Wraps text inside input window
},
-- Auto-hide input window when prompt is submitted or focus switches to output window
auto_hide = false,
},
picker = {
snacks_layout = nil -- `layout` opts to pass to Snacks.picker.pick({ layout = ... })
Expand Down
3 changes: 2 additions & 1 deletion lua/opencode/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ M.defaults = {
input_position = 'bottom',
window_width = 0.40,
zoom_width = 0.8,
input_height = 0.15,
picker_width = 100,
display_model = true,
display_context_size = true,
Expand All @@ -137,6 +136,8 @@ M.defaults = {
always_scroll_to_bottom = false,
},
input = {
min_height = 0.10,
max_height = 0.25,
text = {
wrap = false,
},
Expand Down
33 changes: 29 additions & 4 deletions lua/opencode/health.lua
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,38 @@ local function check_configuration()
health.ok(string.format('Window width: %s', config.ui.window_width))
end

if config.ui.input_height <= 0 or config.ui.input_height > 1 then
local min_height = config.ui.input.min_height
local max_height = config.ui.input.max_height
if type(min_height) ~= 'number' or type(max_height) ~= 'number' then
health.warn(
string.format('Invalid input height: %s', config.ui.input_height),
{ 'Input height should be between 0 and 1 (percentage of screen)' }
'Input min/max height configuration incomplete',
{ 'Set both ui.input.min_height and ui.input.max_height to enable auto-resize' }
)
else
health.ok(string.format('Input height: %s', config.ui.input_height))
if min_height <= 0 or min_height > 1 then
health.warn(
string.format('Invalid input min height: %s', min_height),
{ 'Input min height should be between 0 and 1 (percentage of screen)' }
)
else
health.ok(string.format('Input min height: %s', min_height))
end

if max_height <= 0 or max_height > 1 then
health.warn(
string.format('Invalid input max height: %s', max_height),
{ 'Input max height should be between 0 and 1 (percentage of screen)' }
)
else
health.ok(string.format('Input max height: %s', max_height))
end

if min_height > max_height then
health.warn(
'Input min height exceeds max height',
{ 'Ensure ui.input.min_height is less than or equal to ui.input.max_height' }
)
end
end

health.ok('Configuration loaded successfully')
Expand Down
9 changes: 7 additions & 2 deletions lua/opencode/types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@
---@field input_position 'bottom'|'top' # Position of the input window (default: 'bottom')
---@field window_width number
---@field zoom_width number
---@field input_height number
---@field picker_width number|nil # Default width for all pickers (nil uses current window width)
---@field display_model boolean
---@field display_context_size boolean
Expand All @@ -125,11 +124,17 @@
---@field icons { preset: 'text'|'nerdfonts', overrides: table<string,string> }
---@field loading_animation OpencodeLoadingAnimationConfig
---@field output OpencodeUIOutputConfig
---@field input { text: { wrap: boolean } }
---@field input OpencodeUIInputConfig
---@field completion OpencodeCompletionConfig
---@field highlights? OpencodeHighlightConfig
---@field picker OpencodeUIPickerConfig

---@class OpencodeUIInputConfig
---@field text { wrap: boolean }
---@field min_height number
---@field max_height number
---@field auto_hide boolean

---@class OpencodeHighlightConfig
---@field vertical_borders? { tool?: { fg?: string, bg?: string }, user?: { fg?: string, bg?: string }, assistant?: { fg?: string, bg?: string } }

Expand Down
71 changes: 63 additions & 8 deletions lua/opencode/ui/input_window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,54 @@ local M = {}
M._hidden = false
-- Flag to prevent WinClosed autocmd from closing all windows during toggle
M._toggling = false
M._resize_scheduled = false

local function get_content_height(windows)
local line_count = vim.api.nvim_buf_line_count(windows.input_buf)
if line_count <= 0 then
return 1
end
if config.ui.input.text.wrap then
local ok, result = pcall(vim.api.nvim_win_text_height, windows.input_win, {
start_row = 0,
end_row = math.max(0, line_count - 1),
})
if ok and result and result.all then
return result.all
end
end

return line_count
end

local function get_winbar_height(windows)
local ok, winbar = pcall(vim.api.nvim_get_option_value, 'winbar', { win = windows.input_win })
if ok and type(winbar) == 'string' and winbar ~= '' then
return 1
end

return 0
end

local function calculate_height(windows)
local total_height = vim.api.nvim_get_option_value('lines', {})
local min_height = math.max(1, math.floor(total_height * config.ui.input.min_height))
local max_height = math.max(min_height, math.floor(total_height * config.ui.input.max_height))
local content_height = get_content_height(windows) + get_winbar_height(windows)
return math.min(max_height, math.max(min_height, content_height))
end

local function apply_dimensions(windows, height)
if config.ui.position == 'current' then
pcall(vim.api.nvim_win_set_height, windows.input_win, height)
return
end

local total_width = vim.api.nvim_get_option_value('columns', {})
local width = math.floor(total_width * config.ui.window_width)

vim.api.nvim_win_set_config(windows.input_win, { width = width, height = height })
end

function M.create_buf()
local input_buf = vim.api.nvim_create_buf(false, true)
Expand Down Expand Up @@ -226,18 +274,23 @@ function M.update_dimensions(windows)
return
end

local total_height = vim.api.nvim_get_option_value('lines', {})
local height = math.floor(total_height * config.ui.input_height)
local height = calculate_height(windows)
apply_dimensions(windows, height)
end

if config.ui.position == 'current' then
pcall(vim.api.nvim_win_set_height, windows.input_win, height)
function M.schedule_resize(windows)
windows = windows or state.windows
if not M.mounted(windows) or M._resize_scheduled then
return
end

local total_width = vim.api.nvim_get_option_value('columns', {})
local width = math.floor(total_width * config.ui.window_width)

vim.api.nvim_win_set_config(windows.input_win, { width = width, height = height })
M._resize_scheduled = true
vim.schedule(function()
M._resize_scheduled = false
if M.mounted(windows) then
M.update_dimensions(windows)
end
end)
end

function M.refresh_placeholder(windows, input_lines)
Expand Down Expand Up @@ -289,6 +342,7 @@ end
function M.recover_input(windows)
M.set_content(state.input_content, windows)
require('opencode.ui.mention').highlight_all_mentions(windows.input_buf)
M.update_dimensions(windows)
end

function M.focus_input()
Expand Down Expand Up @@ -413,6 +467,7 @@ function M.setup_autocmds(windows, group)
state.input_content = input_lines
M.refresh_placeholder(windows, input_lines)
require('opencode.ui.context_bar').render()
M.schedule_resize(windows)
end,
})
end
Expand Down