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
62 changes: 60 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<img src="https://raw.githubusercontent.com/sst/opencode/dev/packages/web/src/assets/logo-ornate-dark.svg" alt="Opencode logo" width="30%" />
</div>

### Quick buffer chat (<leader>o/) EXPERIMENTAL:
### Quick buffer chat (<leader>o/) EXPERIMENTAL

This is an experimental feature that allows you to chat with the AI using the current buffer context. In visual mode, it captures the selected text as context, while in normal mode, it uses the current line. The AI will respond with quick edits to the files that are applied by the plugin.

Expand Down Expand Up @@ -219,6 +219,9 @@ require('opencode').setup({
wrap = false, -- Wraps text inside input window
},
},
picker = {
snacks_layout = nil -- `layout` opts to pass to Snacks.picker.pick({ layout = ... })
},
completion = {
file_sources = {
enabled = true,
Expand Down Expand Up @@ -371,6 +374,61 @@ Available icon keys (see implementation at lua/opencode/ui/icons.lua lines 7-29)
- status_on, status_off
- border, bullet

### Picker Layout

You can customize the layout of the picker used for history, session, references, and timeline

#### Snacks Picker Layout

There's 3 main ways on how to change the snacks picker layout

1. Don't specify the new options -> it'll just default to the user's snack picker layout preset from their snacks config
2. Specify the new options for opencode, e.g.

```lua
require("opencode").setup({
ui = {
picker = {
---@module "snacks"
---@type snacks.picker.layout.Config | nil
snacks_layout = {
layout = { border = "none", box = "vertical", ... }
},
},
},
})
```

3. Specify a [builtin layout preset](https://github.com/folke/snacks.nvim/blob/main/docs/picker.md#%EF%B8%8F-layouts) for snacks picker OR a custom layout defined in your snacks config's `opts.picker.layouts`

```lua
-- opencode.lua
require("opencode").setup({
ui = {
picker = {
---@module "snacks"
---@type snacks.picker.layout.Config | nil
snacks_layout = {
preset = "custom_layout" -- or builtin snacks, like "select", "default", etc
},
},
},
})
```

```lua
-- snacks.lua
{
"folke/snacks.nvim",
opts = {
picker = {
layouts = {
custom_layout = {
layout = { border = "none", box = "vertical", ... }
-- ...
}
```

## 🧰 Usage

### Available Actions
Expand Down Expand Up @@ -458,7 +516,7 @@ Run a prompt in a new session using the Plan agent and disabling current file co
:Opencode run "Fix the bug in the current file" model=github-copilot/claude-sonned-4
```

##👮 Permissions
## 👮 Permissions

Opencode can issue permission requests for potentially destructive operations (file edits, reverting files, running shell commands, or enabling persistent tool access). Permission requests appear inline in the output and must be responded to before the agent performs the action. Visit [Opencode Permissions Documentation](https://opencode.ai/docs/permissions/) for more details.

Expand Down
3 changes: 3 additions & 0 deletions lua/opencode/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ M.defaults = {
wrap = false,
},
},
picker = {
snacks_layout = nil,
},
completion = {
file_sources = {
enabled = true,
Expand Down
15 changes: 5 additions & 10 deletions lua/opencode/types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@
---@field input { text: { wrap: boolean } }
---@field completion OpencodeCompletionConfig
---@field highlights? OpencodeHighlightConfig
---@field picker OpencodeUIPickerConfig

---@class OpencodeHighlightConfig
---@field vertical_borders? { tool?: { fg?: string, bg?: string }, user?: { fg?: string, bg?: string }, assistant?: { fg?: string, bg?: string } }
Expand All @@ -149,6 +150,10 @@
---@field rendering OpencodeUIOutputRenderingConfig
---@field always_scroll_to_bottom boolean

---@class OpencodeUIPickerConfig
---@field snacks_layout? snacks.picker.layout.Config
--- TODO: add more picker-specific presets

---@class OpencodeContextConfig
---@field enabled boolean
---@field cursor_data { enabled: boolean, context_lines?: number }
Expand Down Expand Up @@ -506,13 +511,3 @@
---@field messages number Number of messages reverted
---@field tool_calls number Number of tool calls reverted
---@field files table<string, {additions: number, deletions: number}> Summary of file changes reverted

---@class CodeReference
---@field file_path string Relative or absolute file path
---@field line number|nil Line number (1-indexed)
---@field column number|nil Column number (optional)
---@field message_id string ID of the message containing this reference
---@field match_start number Start position of match in original text
---@field match_end number End position of match in original text
---@field file string Absolute file path (for Snacks picker preview)
---@field pos number[]|nil Position as {line, col} for Snacks picker preview
5 changes: 4 additions & 1 deletion lua/opencode/ui/base_picker.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ local Promise = require('opencode.promise')
---@field width? number Optional width for the picker (defaults to config or current window width)
---@field multi_selection? table<string, boolean> Actions that support multi-selection
---@field preview? "file"|"none"|false Preview mode: "file" for file preview, "none" or false to disable
---@field layout_opts? OpencodeUIPickerConfig

---@class TelescopeEntry
---@field value any
Expand Down Expand Up @@ -395,10 +396,12 @@ local function snacks_picker_ui(opts)
local title = type(opts.title) == 'function' and opts.title() or opts.title
---@cast title string

local layout_opts = opts.layout_opts and opts.layout_opts.snacks_layout or nil

---@type snacks.picker.Config
local snack_opts = {
title = title,
layout = {
layout = layout_opts or {
config = function(layout)
local width = opts.width and (opts.width + 3) or nil -- extra space for snacks UI
if not has_preview then
Expand Down
1 change: 1 addition & 0 deletions lua/opencode/ui/session_picker.lua
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ function M.pick(sessions, callback)
callback = callback,
title = 'Select A Session',
width = config.ui.picker_width or 100,
layout_opts = config.ui.picker,
})
end

Expand Down