Skip to content

Commit 017158e

Browse files
committed
feat: improve sessions panel with date grouping and visible icons
- Group sessions by date (Today, Yesterday, This Week, This Month, Older) - Add date group headers with separator lines - Replace Nerd Font icons with Unicode symbols (●, ○, ▸) - Add ChatGPTSessionHeader and ChatGPTSessionCursor highlight groups - Skip header lines when selecting/renaming/deleting sessions - Maintain existing navigation and keymap behavior
1 parent afdb0d1 commit 017158e

File tree

3 files changed

+118
-33
lines changed

3 files changed

+118
-33
lines changed

lua/chatgpt.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ M.setup = function(options)
4747
vim.api.nvim_set_hl(0, "ChatGPTSenderUser", { fg = "#89b4fa", bold = true, default = true })
4848
vim.api.nvim_set_hl(0, "ChatGPTSenderAssistant", { fg = "#a6e3a1", bold = true, default = true })
4949

50+
vim.api.nvim_set_hl(0, "ChatGPTSessionHeader", { fg = "#6c7086", bold = true, default = true })
51+
vim.api.nvim_set_hl(0, "ChatGPTSessionCursor", { fg = "#f9e2af", bold = true, default = true })
52+
5053
vim.cmd("highlight default link ChatGPTSelectedMessage ColorColumn")
5154

5255
config.setup(options)

lua/chatgpt/config.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ function M.defaults()
4444
border_right_sign = "",
4545
max_line_length = 120,
4646
sessions_window = {
47-
active_sign = " ",
48-
inactive_sign = " ",
49-
current_line_sign = "",
47+
active_sign = " ",
48+
inactive_sign = " ",
49+
current_line_sign = "",
5050
border = {
5151
style = "rounded",
5252
text = {

lua/chatgpt/flows/chat/sessions.lua

Lines changed: 112 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,62 @@ local InputWidget = require("chatgpt.common.input_widget")
99

1010
local namespace_id = vim.api.nvim_create_namespace("ChatGPTNS")
1111

12+
-- Get date group for a timestamp
13+
local function get_date_group(ts)
14+
local now = os.time()
15+
local today_start = os.time({
16+
year = os.date("%Y"),
17+
month = os.date("%m"),
18+
day = os.date("%d"),
19+
hour = 0,
20+
min = 0,
21+
sec = 0,
22+
})
23+
local yesterday_start = today_start - 86400
24+
local week_start = today_start - (86400 * 7)
25+
local month_start = today_start - (86400 * 30)
26+
27+
if ts >= today_start then
28+
return "Today"
29+
elseif ts >= yesterday_start then
30+
return "Yesterday"
31+
elseif ts >= week_start then
32+
return "This Week"
33+
elseif ts >= month_start then
34+
return "This Month"
35+
else
36+
return "Older"
37+
end
38+
end
39+
1240
M.set_current_line = function()
1341
M.current_line, _ = unpack(vim.api.nvim_win_get_cursor(M.panel.winid))
1442
M.render_list()
1543
end
1644

45+
-- Get the session index for the current cursor line
46+
M.get_current_session_index = function()
47+
return M.line_to_session and M.line_to_session[M.current_line] or nil
48+
end
49+
1750
M.set_session = function()
18-
M.active_line = M.current_line
19-
local selected = M.sessions[M.current_line]
51+
local session_idx = M.get_current_session_index()
52+
if not session_idx then
53+
return -- On a header line
54+
end
55+
M.active_line = session_idx
56+
local selected = M.sessions[session_idx]
2057
local session = Session.new({ filename = selected.filename })
2158
M.render_list()
2259
M.set_session_cb(session)
2360
end
2461

2562
M.rename_session = function()
26-
M.active_line = M.current_line
27-
local selected = M.sessions[M.current_line]
63+
local session_idx = M.get_current_session_index()
64+
if not session_idx then
65+
return -- On a header line
66+
end
67+
local selected = M.sessions[session_idx]
2868
local session = Session.new({ filename = selected.filename })
2969
local input_widget = InputWidget("New Name:", function(value)
3070
if value ~= nil and value ~= "" then
@@ -37,12 +77,16 @@ M.rename_session = function()
3777
end
3878

3979
M.delete_session = function()
40-
local selected = M.sessions[M.current_line]
41-
if M.active_line ~= M.current_line then
80+
local session_idx = M.get_current_session_index()
81+
if not session_idx then
82+
return -- On a header line
83+
end
84+
if M.active_line ~= session_idx then
85+
local selected = M.sessions[session_idx]
4286
local session = Session.new({ filename = selected.filename })
4387
session:delete()
4488
M.sessions = Session.list_sessions()
45-
if M.active_line > M.current_line then
89+
if M.active_line > session_idx then
4690
M.active_line = M.active_line - 1
4791
end
4892
M.render_list()
@@ -54,34 +98,72 @@ end
5498
M.render_list = function()
5599
vim.api.nvim_buf_clear_namespace(M.panel.bufnr, namespace_id, 0, -1)
56100

57-
local details = {}
58-
for i, session in pairs(M.sessions) do
59-
local icon = i == M.active_line and Config.options.chat.sessions_window.active_sign
60-
or Config.options.chat.sessions_window.inactive_sign
61-
local cls = i == M.active_line and Config.options.highlights.active_session or "Comment"
62-
local name = Utils.trimText(session.name, 30)
63-
local vt = {
64-
{ (M.current_line == i and Config.options.chat.sessions_window.current_line_sign or " ") .. icon .. name, cls },
65-
}
66-
table.insert(details, vt)
101+
-- Group sessions by date
102+
local groups = {}
103+
local group_order = { "Today", "Yesterday", "This Week", "This Month", "Older" }
104+
for _, g in ipairs(group_order) do
105+
groups[g] = {}
106+
end
107+
108+
for i, session in ipairs(M.sessions) do
109+
local group = get_date_group(session.ts)
110+
table.insert(groups[group], { index = i, session = session })
67111
end
68112

69-
local line = 1
113+
-- Build display list with headers
114+
local display_lines = {}
115+
local line_to_session = {} -- Maps display line to session index
116+
117+
for _, group_name in ipairs(group_order) do
118+
local group_sessions = groups[group_name]
119+
if #group_sessions > 0 then
120+
-- Add header
121+
table.insert(display_lines, { type = "header", text = group_name })
122+
-- Add sessions
123+
for _, entry in ipairs(group_sessions) do
124+
table.insert(display_lines, { type = "session", index = entry.index, session = entry.session })
125+
line_to_session[#display_lines] = entry.index
126+
end
127+
end
128+
end
129+
130+
M.line_to_session = line_to_session
131+
132+
-- Create buffer lines
70133
local empty_lines = {}
71-
for _ = 1, #details do
134+
for _ = 1, #display_lines do
72135
table.insert(empty_lines, "")
73136
end
74-
75-
vim.api.nvim_buf_set_lines(M.panel.bufnr, line - 1, line - 1 + #empty_lines, false, empty_lines)
76-
for _, d in ipairs(details) do
77-
M.vts[line - 1] = vim.api.nvim_buf_set_extmark(
78-
M.panel.bufnr,
79-
namespace_id,
80-
line - 1,
81-
0,
82-
{ virt_text = d, virt_text_pos = "overlay" }
83-
)
84-
line = line + 1
137+
vim.api.nvim_buf_set_lines(M.panel.bufnr, 0, -1, false, empty_lines)
138+
139+
-- Render each line
140+
for line_num, item in ipairs(display_lines) do
141+
local line_idx = line_num - 1
142+
143+
if item.type == "header" then
144+
-- Render header with separator line
145+
local header_text = "── " .. item.text .. " " .. string.rep("", 20)
146+
vim.api.nvim_buf_set_extmark(M.panel.bufnr, namespace_id, line_idx, 0, {
147+
virt_text = { { header_text, "ChatGPTSessionHeader" } },
148+
virt_text_pos = "overlay",
149+
})
150+
else
151+
-- Render session
152+
local i = item.index
153+
local session = item.session
154+
local is_active = (i == M.active_line)
155+
local is_current = (M.line_to_session[line_num] == M.line_to_session[M.current_line])
156+
157+
local cursor = is_current and Config.options.chat.sessions_window.current_line_sign or " "
158+
local icon = is_active and Config.options.chat.sessions_window.active_sign or Config.options.chat.sessions_window.inactive_sign
159+
local cls = is_active and Config.options.highlights.active_session or "Comment"
160+
local name = Utils.trimText(session.name, 28)
161+
162+
vim.api.nvim_buf_set_extmark(M.panel.bufnr, namespace_id, line_idx, 0, {
163+
virt_text = { { cursor, "ChatGPTSessionCursor" }, { icon .. name, cls } },
164+
virt_text_pos = "overlay",
165+
})
166+
end
85167
end
86168
end
87169

0 commit comments

Comments
 (0)