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
18 changes: 2 additions & 16 deletions lua/opencode/commands/handlers/session.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ local M = {
actions = {},
}

local session_subcommands = { 'new', 'select', 'navigate', 'compact', 'share', 'unshare', 'agents_init', 'rename', 'toggle_lock' }
local session_subcommands =
{ 'new', 'select', 'navigate', 'compact', 'share', 'unshare', 'agents_init', 'rename', 'toggle_lock' }

---@param message string
local function invalid_arguments(message)
Expand Down Expand Up @@ -522,15 +523,6 @@ function M.actions.timeline()
end)
end

---@param source? string
function M.actions.message_actions(source)
local message_actions = require('opencode.ui.message_actions')
if source == 'mouse' then
return message_actions.open_from_mouse()
end
return message_actions.open_at_cursor()
end

---@param message_id? string
function M.actions.fork_session(message_id)
return with_active_session('No active session to fork', function(state_obj)
Expand Down Expand Up @@ -677,12 +669,6 @@ M.command_defs = {
desc = 'Open timeline picker to navigate/undo/redo/fork to message',
execute = M.actions.timeline,
},
message_actions = {
desc = 'Open message actions',
execute = function(args)
return M.actions.message_actions(args[1])
end,
},
}

return M
2 changes: 0 additions & 2 deletions lua/opencode/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ M.defaults = {
['gr'] = { 'references', desc = 'Browse code references' },
['gf'] = { 'jump_to_file', desc = 'Jump to file at cursor' },
['<CR>'] = { 'jump_to_target_at_cursor', desc = 'Jump to target at cursor' },
['<LeftMouse>'] = { 'message_actions', { 'mouse' }, desc = 'Open message actions' },
['<leader>o<CR>'] = { 'message_actions', desc = 'Open message actions' },
['<M-i>'] = { 'toggle_input', mode = { 'n' }, desc = 'Toggle input window' },
['<M-r>'] = { 'cycle_variant', mode = { 'n' }, desc = 'Cycle model variants' },
['<leader>oS'] = { 'navigate_session_tree', { 'child', 'picker' }, desc = 'Select child session' },
Expand Down
1 change: 0 additions & 1 deletion lua/opencode/types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@

---@class OpencodeKeymapEntry
---@field [1] string # Function name
---@field [2]? string|string[] # Preset command arguments
---@field mode? string|string[] # Mode(s) for the keymap
---@field desc? string # Keymap description
---@field defer_to_completion? boolean # Whether to defer the keymap when completion menu is open
Expand Down
137 changes: 15 additions & 122 deletions lua/opencode/ui/dialog.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,14 @@
---@field keymaps? DialogKeymaps Custom keymap configuration
---@field namespace_prefix? string Prefix for vim.on_key namespace (default: 'opencode_dialog')
---@field hide_input? boolean Whether to hide the input window when dialog is active (default: true)
---@field render_part_id? string Rendered part ID used to resolve mouse clicks against output lines
---@field mouse_select? boolean Whether Dialog should register <LeftMouse> for option selection (default: false)

---@class DialogKeymaps
---@field up? string[] Keys for navigating up (default: {'k', '<Up>'})
---@field down? string[] Keys for navigating down (default: {'j', '<Down>'})
---@field left? string[] Keys for navigating left between groups
---@field right? string[] Keys for navigating right between groups
---@field select? string Key for selecting current option (default: '<CR>')
---@field dismiss? string|string[] Keys for dismissing dialog (default: {'<Esc>', '<C-c>'})
---@field dismiss? string Key for dismissing dialog (default: '<Esc>')
---@field number_shortcuts? boolean Enable 1-9 number shortcuts (default: true)

---@class Dialog
Expand All @@ -29,38 +27,9 @@
---@field private _selected_index integer Currently selected option index
---@field private _active boolean Whether dialog is currently active
---@field private _group_index integer Currently selected group index
---@field private _option_local_lines integer[] 0-based output-local lines for each option
local Dialog = {}
Dialog.__index = Dialog

---@param keymap string|string[]|nil
---@return string[]
local function keymap_list(keymap)
local keymaps = {}
if type(keymap) == 'string' then
if keymap ~= '' then
table.insert(keymaps, keymap)
end
elseif type(keymap) == 'table' then
for _, key in ipairs(keymap) do
if key and key ~= '' then
table.insert(keymaps, key)
end
end
end
return keymaps
end

---@param keymaps string[]
---@return string
local function keymap_legend(keymaps)
local labels = {}
for _, key in ipairs(keymaps) do
table.insert(labels, '`' .. key .. '`')
end
return table.concat(labels, ' or ')
end

---Create a new dialog instance
---@param config DialogConfig Dialog configuration
---@return Dialog
Expand All @@ -74,7 +43,7 @@ function Dialog.new(config)
left = {},
right = {},
select = '<CR>',
dismiss = { '<Esc>', '<C-c>' },
dismiss = '<Esc>',
number_shortcuts = true,
}

Expand All @@ -85,15 +54,13 @@ function Dialog.new(config)
return true
end,
hide_input = true,
mouse_select = false,
} --[[@as DialogConfig]], config)

self._keymaps = {}
self._key_capture_ns = nil
self._selected_index = 1
self._group_index = 1
self._active = false
self._option_local_lines = {}

return self
end
Expand Down Expand Up @@ -281,9 +248,8 @@ function Dialog:format_legend(output, options)
local line = output:add_line(select_text)
end

local dismiss_keymaps = keymap_list(keymaps.dismiss)
if #dismiss_keymaps > 0 then
local line = output:add_line('Close: ' .. keymap_legend(dismiss_keymaps))
if keymaps.dismiss and keymaps.dismiss ~= '' then
local line = output:add_line('Close: `<Esc>`')
end
else
local message = options.unfocused_message or 'Focus Opencode window to interact'
Expand All @@ -302,7 +268,6 @@ end
--- - progress?: string - Progress indicator (e.g., "(1/3)")
--- - content?: string[] - Array of lines to render before options
--- - render_content?: function(output: Output) - Custom function to render content before options
--- - hide_legend?: boolean - Whether to hide movement/select/dismiss instructions
function Dialog:format_dialog(output, config)
if not self._active then
return
Expand Down Expand Up @@ -336,10 +301,9 @@ function Dialog:format_dialog(output, config)

self:format_options(output, config.options or {})

if not config.hide_legend then
output:add_line('')
self:format_legend(output, { unfocused_message = config.unfocused_message })
end
output:add_line('')

self:format_legend(output, { unfocused_message = config.unfocused_message })

local end_line = output:get_line_count()

Expand All @@ -358,8 +322,6 @@ end
---@param output Output Output object to write to
---@param options table[] Array of option objects with {label: string, description?: string}
function Dialog:format_options(output, options)
self._option_local_lines = {}

for i, option in ipairs(options) do
local label = option.label
if option.description and option.description ~= '' then
Expand All @@ -377,12 +339,11 @@ function Dialog:format_options(output, options)
-- add_line returns a 1-based line index; Output extmarks use 0-based
-- keys, so subtract 1 to get the correct extmark key.
local added_idx = output:add_line(line_text)
local option_local_line = added_idx - 1
self._option_local_lines[i] = option_local_line

if is_selected then
output:add_extmark(option_local_line, { line_hl_group = 'OpencodeDialogOptionHover' } --[[@as OutputExtmark]])
output:add_extmark(option_local_line, {
local extmark_idx = added_idx - 1
output:add_extmark(extmark_idx, { line_hl_group = 'OpencodeDialogOptionHover' } --[[@as OutputExtmark]])
output:add_extmark(extmark_idx, {
start_col = 2,
virt_text = { { '› ', 'OpencodeDialogOptionHover' } },
virt_text_pos = 'overlay',
Expand All @@ -391,58 +352,6 @@ function Dialog:format_options(output, options)
end
end

---@return integer|nil
function Dialog:_mouse_option_index()
local render_part_id = self._config.render_part_id
if not render_part_id or render_part_id == '' then
return nil
end

local mouse = vim.fn.getmousepos()
local winid = mouse and mouse.winid
if not winid or winid == 0 or not vim.api.nvim_win_is_valid(winid) then
return nil
end

local buf = self._config.buffer
if not buf or vim.api.nvim_win_get_buf(winid) ~= buf then
return nil
end

local rendered_part = require('opencode.ui.renderer.ctx').render_state:get_part(render_part_id)
if not rendered_part or rendered_part.line_start == nil then
return nil
end

local mouse_line = mouse.line
if not mouse_line or mouse_line <= 0 then
return nil
end

local clicked_output_line = mouse_line - 1
for option_index, option_local_line in ipairs(self._option_local_lines) do
if clicked_output_line == rendered_part.line_start + option_local_line then
return option_index
end
end
end

---@return boolean selected
function Dialog:select_mouse_option()
if not self._active or not self._config.check_focused() then
return false
end

local option_index = self:_mouse_option_index()
if not option_index then
return false
end

self._selected_index = option_index
self._config.on_select(option_index)
return true
end

---Set up buffer-scoped keymaps
function Dialog:_setup_keymaps()
self:_clear_keymaps()
Expand Down Expand Up @@ -541,34 +450,18 @@ function Dialog:_setup_keymaps()
table.insert(self._keymaps, keymaps.select)
end

if self._config.mouse_select ~= false and self._config.render_part_id and self._config.render_part_id ~= '' then
if keymaps.dismiss and keymaps.dismiss ~= '' then
vim.keymap.set(
'n',
'<LeftMouse>',
keymaps.dismiss,
function()
self:select_mouse_option()
self:dismiss()
end,
vim.tbl_extend('force', keymap_opts, {
desc = 'Dialog: select clicked option',
desc = 'Dialog: dismiss',
})
)
table.insert(self._keymaps, '<LeftMouse>')
end

for _, key in ipairs(keymap_list(keymaps.dismiss)) do
if key and key ~= '' then
vim.keymap.set(
'n',
key,
function()
self:dismiss()
end,
vim.tbl_extend('force', keymap_opts, {
desc = 'Dialog: dismiss',
})
)
table.insert(self._keymaps, key)
end
table.insert(self._keymaps, keymaps.dismiss)
end

if keymaps.number_shortcuts then
Expand Down
6 changes: 1 addition & 5 deletions lua/opencode/ui/formatter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -719,11 +719,7 @@ function M.format_part(part, message, is_last_part, get_child_parts)

local role = message.info.role

if part.type == 'message-actions-display' then
local message_actions = require('opencode.ui.message_actions')
message_actions.format_display(output)
content_added = true
elseif role == 'user' then
if role == 'user' then
if is_compaction_part(part) then
format_compaction_divider(output)
content_added = true
Expand Down
Loading
Loading