feat(webchat): add session header sorting controls
This commit is contained in:
@@ -14,6 +14,7 @@ let _searchMode = false;
|
||||
let _slashPopupIndex = -1;
|
||||
let _elements = {};
|
||||
let _pendingAttachments = [];
|
||||
let _sessionSort = 'recent';
|
||||
|
||||
// ── Slash Command Definitions ───────────────────────────────
|
||||
|
||||
@@ -80,6 +81,62 @@ function createTimestampEl(role, timestamp) {
|
||||
return ts;
|
||||
}
|
||||
|
||||
function formatSessionLastActive(timestamp) {
|
||||
if (typeof timestamp !== 'number' || !Number.isFinite(timestamp)) {
|
||||
return 'no activity';
|
||||
}
|
||||
|
||||
const date = new Date(timestamp);
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return 'no activity';
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const sameDay = now.getFullYear() === date.getFullYear()
|
||||
&& now.getMonth() === date.getMonth()
|
||||
&& now.getDate() === date.getDate();
|
||||
|
||||
if (sameDay) {
|
||||
return `today ${new Intl.DateTimeFormat(undefined, { hour: 'numeric', minute: '2-digit' }).format(date)}`;
|
||||
}
|
||||
|
||||
return new Intl.DateTimeFormat(undefined, {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
function sortSessions(sessions, mode) {
|
||||
const copy = [...sessions];
|
||||
if (mode === 'messages') {
|
||||
copy.sort((a, b) => {
|
||||
const byMessages = (b.messageCount ?? 0) - (a.messageCount ?? 0);
|
||||
if (byMessages !== 0) {return byMessages;}
|
||||
return String(a.id ?? '').localeCompare(String(b.id ?? ''));
|
||||
});
|
||||
return copy;
|
||||
}
|
||||
|
||||
if (mode === 'name') {
|
||||
copy.sort((a, b) => String(a.id ?? '').localeCompare(String(b.id ?? '')));
|
||||
return copy;
|
||||
}
|
||||
|
||||
// Default: most recent activity first, then message count, then name.
|
||||
copy.sort((a, b) => {
|
||||
const aTs = typeof a.lastMessageAt === 'number' ? a.lastMessageAt : 0;
|
||||
const bTs = typeof b.lastMessageAt === 'number' ? b.lastMessageAt : 0;
|
||||
const byTime = bTs - aTs;
|
||||
if (byTime !== 0) {return byTime;}
|
||||
const byMessages = (b.messageCount ?? 0) - (a.messageCount ?? 0);
|
||||
if (byMessages !== 0) {return byMessages;}
|
||||
return String(a.id ?? '').localeCompare(String(b.id ?? ''));
|
||||
});
|
||||
return copy;
|
||||
}
|
||||
|
||||
function createMessageEl(role, content, timestamp = Date.now()) {
|
||||
const wrapper = document.createElement('div');
|
||||
|
||||
@@ -549,7 +606,7 @@ async function loadSessions(client) {
|
||||
|
||||
try {
|
||||
const result = await client.call('sessions.list');
|
||||
const sessions = result.sessions ?? [];
|
||||
const sessions = sortSessions(result.sessions ?? [], _sessionSort);
|
||||
|
||||
// Preserve current selection
|
||||
const current = _currentSession;
|
||||
@@ -564,7 +621,9 @@ async function loadSessions(client) {
|
||||
for (const s of sessions) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = s.id;
|
||||
opt.textContent = `${s.id} (${s.messageCount} msgs)`;
|
||||
const msgCount = s.messageCount ?? 0;
|
||||
const msgLabel = msgCount === 1 ? '1 msg' : `${msgCount} msgs`;
|
||||
opt.textContent = `${s.id} · ${msgLabel} · ${formatSessionLastActive(s.lastMessageAt)}`;
|
||||
if (s.id === current) {opt.selected = true;}
|
||||
select.appendChild(opt);
|
||||
}
|
||||
@@ -713,6 +772,11 @@ export const ChatPage = {
|
||||
<div class="flex flex-col h-[calc(100vh-6rem)] md:h-[calc(100vh-3rem)] max-w-3xl">
|
||||
<div class="flex items-center gap-3 pb-3 border-b border-zinc-800 mb-3 flex-wrap">
|
||||
<select id="chat-session-select" class="bg-zinc-900 text-zinc-50 border border-zinc-800 rounded-lg px-3 py-1.5 text-sm outline-none focus:border-blue-500"></select>
|
||||
<select id="chat-session-sort" class="bg-zinc-900 text-zinc-50 border border-zinc-800 rounded-lg px-3 py-1.5 text-sm outline-none focus:border-blue-500" title="Sort sessions">
|
||||
<option value="recent">Sort: Recent</option>
|
||||
<option value="messages">Sort: Most messages</option>
|
||||
<option value="name">Sort: Name</option>
|
||||
</select>
|
||||
<button id="chat-new-session" class="px-3 py-1.5 text-sm font-medium rounded-md border border-zinc-700 bg-zinc-800 text-zinc-200 hover:bg-zinc-700 transition-colors">+ New</button>
|
||||
<button id="chat-load-history" class="px-3 py-1.5 text-sm font-medium rounded-md border border-zinc-700 bg-zinc-800 text-zinc-200 hover:bg-zinc-700 transition-colors">History</button>
|
||||
</div>
|
||||
@@ -741,6 +805,7 @@ export const ChatPage = {
|
||||
|
||||
_elements = {
|
||||
sessionSelect: el.querySelector('#chat-session-select'),
|
||||
sessionSort: el.querySelector('#chat-session-sort'),
|
||||
messages: el.querySelector('#chat-messages'),
|
||||
input: el.querySelector('#chat-input'),
|
||||
sendBtn: el.querySelector('#chat-send'),
|
||||
@@ -759,6 +824,11 @@ export const ChatPage = {
|
||||
_currentSession = _elements.sessionSelect.value || null;
|
||||
});
|
||||
|
||||
_elements.sessionSort.addEventListener('change', () => {
|
||||
_sessionSort = _elements.sessionSort.value || 'recent';
|
||||
loadSessions(client);
|
||||
});
|
||||
|
||||
// Event: new session
|
||||
el.querySelector('#chat-new-session').addEventListener('click', async () => {
|
||||
try {
|
||||
@@ -887,6 +957,7 @@ export const ChatPage = {
|
||||
_sending = false;
|
||||
_searchMode = false;
|
||||
_slashPopupIndex = -1;
|
||||
_sessionSort = 'recent';
|
||||
_elements = {};
|
||||
_pendingAttachments = [];
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user