diff --git a/lua/opencode.lua b/lua/opencode.lua index 1e6880b..47d9cc7 100644 --- a/lua/opencode.lua +++ b/lua/opencode.lua @@ -3,6 +3,7 @@ local M = {} M.ask = require("opencode.ui.ask").ask M.select = require("opencode.ui.select").select +M.select_session = require("opencode.ui.select_session").select_session M.prompt = require("opencode.api.prompt").prompt M.operator = require("opencode.api.operator").operator diff --git a/lua/opencode/cli/client.lua b/lua/opencode/cli/client.lua index 8553065..33926a9 100644 --- a/lua/opencode/cli/client.lua +++ b/lua/opencode/cli/client.lua @@ -214,6 +214,31 @@ function M.get_commands(port, callback) M.call(port, "/command", "GET", nil, callback) end +---@class opencode.cli.client.SessionTime +---@field created integer +---@field updated integer + +---@class opencode.cli.client.Session +---@field id string +---@field title string +---@field time opencode.cli.client.SessionTime + +---Get sessions from `opencode`. +--- +---@param port number +---@param callback fun(sessions: opencode.cli.client.Session[]) +function M.get_sessions(port, callback) + M.call(port, "/session", "GET", nil, callback) +end + +---Select session in `opencode`. +--- +---@param port number +---@param session_id number +function M.select_session(port, session_id) + M.call(port, "/tui/select-session", "POST", { sessionID = session_id }, nil) +end + ---@class opencode.cli.client.PathResponse ---@field directory string ---@field worktree string diff --git a/lua/opencode/config.lua b/lua/opencode/config.lua index 2b1a3d7..7f860e4 100644 --- a/lua/opencode/config.lua +++ b/lua/opencode/config.lua @@ -89,6 +89,7 @@ local defaults = { prompts = true, commands = { ["session.new"] = "Start a new session", + ["session.select"] = "Select a session", ["session.share"] = "Share the current session", ["session.interrupt"] = "Interrupt the current session", ["session.compact"] = "Compact the current session (reduce context size)", diff --git a/lua/opencode/ui/select.lua b/lua/opencode/ui/select.lua index 9e74829..98186d9 100644 --- a/lua/opencode/ui/select.lua +++ b/lua/opencode/ui/select.lua @@ -221,7 +221,11 @@ function M.select(opts) require("opencode").prompt(prompt.prompt, prompt) end elseif choice.__type == "command" then - require("opencode").command(choice.name) + if choice.name == "session.select" then + require("opencode").select_session() + else + require("opencode").command(choice.name) + end elseif choice.__type == "provider" then if choice.name == "toggle" then require("opencode").toggle() diff --git a/lua/opencode/ui/select_session.lua b/lua/opencode/ui/select_session.lua new file mode 100644 index 0000000..8207420 --- /dev/null +++ b/lua/opencode/ui/select_session.lua @@ -0,0 +1,61 @@ +local M = {} + +function M.select_session() + require("opencode.cli.server") + .get_port() + :next(function(port) + return require("opencode.promise").new(function(resolve) + require("opencode.cli.client").get_sessions(port, function(sessions) + resolve({ sessions = sessions, port = port }) + end) + end) + end) + :next(function(session_data) + local sessions = {} + for _, session in ipairs(session_data.sessions) do + ---@type opencode.cli.client.Session + local item = { + id = session.id, + title = session.title, + time = { + created = math.floor(session.time.created), + updated = math.floor(session.time.updated), + }, + } + table.insert(sessions, item) + end + + table.sort(sessions, function(a, b) + return a.time.updated > b.time.updated + end) + + vim.ui.select(sessions, { + prompt = "Select session (recently updated first):", + format_item = function(item) + local title_length = 60 + local updated = os.date("%b %d, %Y %H:%M:%S", item.time.updated / 1000) + local title = M.ellipsize(item.title, title_length) + return ("%s%s%s"):format(title, string.rep(" ", title_length - #title), updated) + end, + }, function(choice) + if choice then + require("opencode.cli.client").select_session(session_data.port, choice.id) + end + end) + end) + :catch(function(err) + vim.notify(err, vim.log.levels.ERROR) + end) +end + +function M.ellipsize(s, max_len) + if vim.fn.strdisplaywidth(s) <= max_len then + return s + end + local truncated = vim.fn.strcharpart(s, 0, max_len - 3) + truncated = truncated:gsub("%s+%S*$", "") + + return truncated .. "..." +end + +return M