Skip to content

Commit aec2d87

Browse files
committed
refactor(picker): merge history_picker into timeline_picker
Problem: After dropping the local history.txt, history_picker and timeline_picker rendered the same data through the same base_picker with nearly identical format functions, only differing by title, time display, and the post-selection action. Solution: Extend timeline_picker.pick(messages, opts) with a { title, callback } shape so the caller picks what happens on selection. Move workflow.select_history to feed the timeline picker and refill the input in the callback, then delete history_picker along with its keymap, type, doc snippet, and topology reference.
1 parent f4c9c04 commit aec2d87

9 files changed

Lines changed: 65 additions & 144 deletions

File tree

README.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,6 @@ require('opencode').setup({
206206
undo = { '<C-u>', mode = { 'i', 'n' } }, -- Undo to selected message in timeline picker
207207
fork = { '<C-f>', mode = { 'i', 'n' } }, -- Fork from selected message in timeline picker
208208
},
209-
history_picker = {
210-
delete_entry = { '<C-d>', mode = { 'i', 'n' } }, -- Delete selected entry in the history picker
211-
clear_all = { '<C-X>', mode = { 'i', 'n' } }, -- Clear all entries in the history picker
212-
},
213209
model_picker = {
214210
toggle_favorite = { '<C-f>', mode = { 'i', 'n' } },
215211
},

lua/opencode/commands/handlers/session.lua

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -536,12 +536,13 @@ function M.actions.timeline()
536536
return
537537
end
538538

539-
local timeline_picker = require('opencode.ui.timeline_picker')
540-
timeline_picker.pick(user_messages, function(selected_msg)
541-
if selected_msg then
542-
require('opencode.ui.navigation').goto_message_by_id(selected_msg.info.id)
543-
end
544-
end)
539+
require('opencode.ui.timeline_picker').pick(user_messages, {
540+
callback = function(selected_msg)
541+
if selected_msg then
542+
require('opencode.ui.navigation').goto_message_by_id(selected_msg.info.id)
543+
end
544+
end,
545+
})
545546
end
546547

547548
---@param message_id? string

lua/opencode/commands/handlers/workflow.lua

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,46 @@ function M.actions.quick_chat(message, range)
106106
quick_chat.quick_chat(prompt, { context_config = ctx }, range)
107107
end
108108

109+
---Refill the input buffer from a user message returned by the timeline picker.
110+
---Open the opencode UI first when the input window is not mounted yet.
111+
---@param message OpencodeMessage
112+
local function refill_input_from_message(message)
113+
local windows = state.windows
114+
if not input_window.mounted(windows) then
115+
session_runtime.open({ focus = 'input' })
116+
windows = state.windows
117+
end
118+
if not input_window.mounted(windows) then
119+
return
120+
end
121+
---@cast windows { input_win: integer, input_buf: integer }
122+
if input_window.refill_prompt_from_message(message) then
123+
input_window.focus_input()
124+
end
125+
end
126+
127+
---@param entries OpencodeHistoryEntry[]
128+
local function pick_history_with(entries)
129+
local messages = vim.tbl_map(function(entry)
130+
return entry.message
131+
end, entries)
132+
require('opencode.ui.timeline_picker').pick(messages, {
133+
title = 'Select History Entry',
134+
callback = function(selected_msg)
135+
if selected_msg then
136+
refill_input_from_message(selected_msg)
137+
end
138+
end,
139+
})
140+
end
141+
109142
function M.actions.select_history()
110-
require('opencode.ui.history_picker').pick()
143+
local entries = history.read()
144+
if #entries == 0 then
145+
vim.notify('No history entries found', vim.log.levels.INFO)
146+
return false
147+
end
148+
return pick_history_with(entries)
111149
end
112150

113151
---Refill the input buffer from a history entry reconstructed from the active

lua/opencode/config.lua

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,6 @@ M.defaults = {
120120
undo = { '<C-u>', mode = { 'i', 'n' }, desc = 'Undo to selected message' },
121121
fork = { '<C-f>', mode = { 'i', 'n' }, desc = 'Fork from selected message' },
122122
},
123-
history_picker = {
124-
delete_entry = { '<C-d>', mode = { 'i', 'n' }, desc = 'Delete selected history entries' },
125-
clear_all = { '<C-X>', mode = { 'i', 'n' }, desc = 'Clear all history entries' },
126-
},
127123
model_picker = {
128124
toggle_favorite = { '<C-f>', mode = { 'i', 'n' }, desc = 'Toggle model favorite' },
129125
},

lua/opencode/history.lua

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
local state = require('opencode.state')
2-
local session_module = require('opencode.session')
32
local input_window = require('opencode.ui.input_window')
4-
local Promise = require('opencode.promise')
53

64
---@class OpencodeHistoryEntry
75
---@field id string Message ID from the server
@@ -21,8 +19,7 @@ local cached_session_id = nil
2119
local prompt_before_history = nil
2220

2321
local function active_session_id()
24-
local session = state.active_session
25-
return session and session.id or nil
22+
return state.active_session and state.active_session.id
2623
end
2724

2825
local function maybe_reset_for_new_session()
@@ -54,7 +51,9 @@ local function entry_has_content(entry)
5451
end
5552

5653
--- Read user-message prompt history for the active session, newest first.
57-
--- Empty list when there is no active session or no user messages yet.
54+
--- Pure read against `state.messages`; if no entry has been populated yet
55+
--- (e.g. right after switching sessions before SSE catches up) the result is
56+
--- an empty list. Populating `state.messages` is the renderer's job.
5857
---@return OpencodeHistoryEntry[]
5958
function M.read()
6059
maybe_reset_for_new_session()
@@ -134,35 +133,6 @@ function M.reset()
134133
prompt_before_history = nil
135134
end
136135

137-
--- Force a fresh fetch from the server for the active session. Used by
138-
--- callers (e.g. the history picker) that want guaranteed-fresh data and
139-
--- don't want to wait for SSE to repopulate state.messages.
140-
---@return Promise<OpencodeHistoryEntry[]>
141-
function M.refresh()
142-
local active = state.active_session
143-
if not active or not active.id then
144-
return Promise.new():resolve({})
145-
end
146-
return session_module
147-
.get_messages(active)
148-
:and_then(function(messages)
149-
local entries = {}
150-
for _, msg in ipairs(messages or {}) do
151-
if msg.info and msg.info.role == 'user' then
152-
local prompt = input_window.build_prompt_from_message(msg)
153-
local entry = { id = msg.info.id, message = msg, prompt = prompt }
154-
if entry_has_content(entry) then
155-
table.insert(entries, entry)
156-
end
157-
end
158-
end
159-
return Promise.new():resolve(entries)
160-
end)
161-
:catch(function()
162-
return Promise.new():resolve({})
163-
end)
164-
end
165-
166136
-- Reset on session changes too. Subscribers react synchronously, so the next
167137
-- read() call sees the new active_session and clears state via
168138
-- maybe_reset_for_new_session.

lua/opencode/types.lua

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@
162162
---@field output_window OpencodeKeymapOutputWindow
163163
---@field session_picker OpencodeSessionPickerKeymap
164164
---@field timeline_picker OpencodeTimelinePickerKeymap
165-
---@field history_picker OpencodeHistoryPickerKeymap
166165
---@field quick_chat OpencodeQuickChatKeymap
167166

168167
---@class OpencodeSessionPickerKeymap
@@ -176,10 +175,6 @@
176175
---@field undo OpencodeKeymapEntry
177176
---@field fork OpencodeKeymapEntry
178177

179-
---@class OpencodeHistoryPickerKeymap
180-
---@field delete_entry OpencodeKeymapEntry
181-
---@field clear_all OpencodeKeymapEntry
182-
183178
---@class OpencodeQuickChatKeymap
184179
---@field cancel OpencodeKeymapEntry
185180

lua/opencode/ui/history_picker.lua

Lines changed: 0 additions & 84 deletions
This file was deleted.

lua/opencode/ui/timeline_picker.lua

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ local config = require('opencode.config')
33
local api = require('opencode.api')
44
local base_picker = require('opencode.ui.base_picker')
55

6+
---@class OpencodeTimelinePickerOpts
7+
---@field title? string Picker title (defaults to 'Timeline')
8+
---@field callback fun(msg: OpencodeMessage|nil) Invoked with the selected message
9+
610
---Format message parts for timeline picker
711
---@param msg OpencodeMessage Message object
12+
---@param width number
813
---@return PickerItem
914
local function format_message_item(msg, width)
1015
local preview = msg.parts and msg.parts[1] and msg.parts[1].text or ''
@@ -14,21 +19,26 @@ local function format_message_item(msg, width)
1419
return base_picker.create_time_picker_item(vim.trim(preview), msg.info.time.created, debug_text, width)
1520
end
1621

17-
function M.pick(messages, callback)
22+
---Open a picker over the given user messages. The shared undo/fork actions
23+
---always operate on the selected message via the server APIs; the caller
24+
---decides what to do on plain selection by passing `opts.callback`.
25+
---@param messages OpencodeMessage[]
26+
---@param opts OpencodeTimelinePickerOpts
27+
function M.pick(messages, opts)
1828
local keymap = config.keymap.timeline_picker
1929
local actions = {
2030
undo = {
2131
key = keymap.undo,
2232
label = 'undo',
23-
fn = function(selected, opts)
33+
fn = function(selected, _opts)
2434
api.undo(selected.info.id)
2535
end,
2636
reload = false,
2737
},
2838
fork = {
2939
key = keymap.fork,
3040
label = 'fork',
31-
fn = function(selected, opts)
41+
fn = function(selected, _opts)
3242
api.fork_session(selected.info.id)
3343
end,
3444
reload = false,
@@ -39,8 +49,8 @@ function M.pick(messages, callback)
3949
items = messages,
4050
format_fn = format_message_item,
4151
actions = actions,
42-
callback = callback,
43-
title = 'Timeline',
52+
callback = opts.callback,
53+
title = opts.title or 'Timeline',
4454
width = config.ui.picker_width,
4555
layout_opts = config.ui.picker,
4656
})

scripts/dependency-topology/topology.jsonc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@
132132
"opencode.ui.mention", // @mention UI
133133
"opencode.ui.file_picker", // file browser
134134
"opencode.ui.picker", // generic picker
135-
"opencode.ui.history_picker", // history browser
136135
"opencode.ui.mcp_picker", // MCP tool browser
137136
"opencode.ui.permission.permission" // permission display
138137
]

0 commit comments

Comments
 (0)