mirror of
https://github.com/siteboon/claudecodeui.git
synced 2025-12-10 14:59:46 +00:00
feat: Add pagination support for session messages and enhance loading logic in ChatInterface
This commit is contained in:
@@ -219,8 +219,22 @@ app.get('/api/projects/:projectName/sessions', authenticateToken, async (req, re
|
||||
app.get('/api/projects/:projectName/sessions/:sessionId/messages', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const { projectName, sessionId } = req.params;
|
||||
const messages = await getSessionMessages(projectName, sessionId);
|
||||
res.json({ messages });
|
||||
const { limit, offset } = req.query;
|
||||
|
||||
// Parse limit and offset if provided
|
||||
const parsedLimit = limit ? parseInt(limit, 10) : null;
|
||||
const parsedOffset = offset ? parseInt(offset, 10) : 0;
|
||||
|
||||
const result = await getSessionMessages(projectName, sessionId, parsedLimit, parsedOffset);
|
||||
|
||||
// Handle both old and new response formats
|
||||
if (Array.isArray(result)) {
|
||||
// Backward compatibility: no pagination parameters were provided
|
||||
res.json({ messages: result });
|
||||
} else {
|
||||
// New format with pagination info
|
||||
res.json(result);
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
|
||||
@@ -385,8 +385,8 @@ async function parseJsonlSessions(filePath) {
|
||||
);
|
||||
}
|
||||
|
||||
// Get messages for a specific session
|
||||
async function getSessionMessages(projectName, sessionId) {
|
||||
// Get messages for a specific session with pagination support
|
||||
async function getSessionMessages(projectName, sessionId, limit = null, offset = 0) {
|
||||
const projectDir = path.join(process.env.HOME, '.claude', 'projects', projectName);
|
||||
|
||||
try {
|
||||
@@ -394,7 +394,7 @@ async function getSessionMessages(projectName, sessionId) {
|
||||
const jsonlFiles = files.filter(file => file.endsWith('.jsonl'));
|
||||
|
||||
if (jsonlFiles.length === 0) {
|
||||
return [];
|
||||
return { messages: [], total: 0, hasMore: false };
|
||||
}
|
||||
|
||||
const messages = [];
|
||||
@@ -423,12 +423,34 @@ async function getSessionMessages(projectName, sessionId) {
|
||||
}
|
||||
|
||||
// Sort messages by timestamp
|
||||
return messages.sort((a, b) =>
|
||||
const sortedMessages = messages.sort((a, b) =>
|
||||
new Date(a.timestamp || 0) - new Date(b.timestamp || 0)
|
||||
);
|
||||
|
||||
const total = sortedMessages.length;
|
||||
|
||||
// If no limit is specified, return all messages (backward compatibility)
|
||||
if (limit === null) {
|
||||
return sortedMessages;
|
||||
}
|
||||
|
||||
// Apply pagination - for recent messages, we need to slice from the end
|
||||
// offset 0 should give us the most recent messages
|
||||
const startIndex = Math.max(0, total - offset - limit);
|
||||
const endIndex = total - offset;
|
||||
const paginatedMessages = sortedMessages.slice(startIndex, endIndex);
|
||||
const hasMore = startIndex > 0;
|
||||
|
||||
return {
|
||||
messages: paginatedMessages,
|
||||
total,
|
||||
hasMore,
|
||||
offset,
|
||||
limit
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`Error reading messages for session ${sessionId}:`, error);
|
||||
return [];
|
||||
return limit === null ? [] : { messages: [], total: 0, hasMore: false };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -373,11 +373,18 @@ router.get('/sessions', async (req, res) => {
|
||||
for (const sessionId of sessionDirs) {
|
||||
const sessionPath = path.join(cursorChatsPath, sessionId);
|
||||
const storeDbPath = path.join(sessionPath, 'store.db');
|
||||
let dbStatMtimeMs = null;
|
||||
|
||||
try {
|
||||
// Check if store.db exists
|
||||
await fs.access(storeDbPath);
|
||||
|
||||
// Capture store.db mtime as a reliable fallback timestamp (last activity)
|
||||
try {
|
||||
const stat = await fs.stat(storeDbPath);
|
||||
dbStatMtimeMs = stat.mtimeMs;
|
||||
} catch (_) {}
|
||||
|
||||
// Open SQLite database
|
||||
const db = await open({
|
||||
filename: storeDbPath,
|
||||
@@ -412,7 +419,26 @@ router.get('/sessions', async (req, res) => {
|
||||
|
||||
if (row.key === 'agent') {
|
||||
sessionData.name = data.name || sessionData.name;
|
||||
sessionData.createdAt = data.createdAt;
|
||||
// Normalize createdAt to ISO string in milliseconds
|
||||
let createdAt = data.createdAt;
|
||||
if (typeof createdAt === 'number') {
|
||||
if (createdAt < 1e12) {
|
||||
createdAt = createdAt * 1000; // seconds -> ms
|
||||
}
|
||||
sessionData.createdAt = new Date(createdAt).toISOString();
|
||||
} else if (typeof createdAt === 'string') {
|
||||
const n = Number(createdAt);
|
||||
if (!Number.isNaN(n)) {
|
||||
const ms = n < 1e12 ? n * 1000 : n;
|
||||
sessionData.createdAt = new Date(ms).toISOString();
|
||||
} else {
|
||||
// Assume it's already an ISO/date string
|
||||
const d = new Date(createdAt);
|
||||
sessionData.createdAt = isNaN(d.getTime()) ? null : d.toISOString();
|
||||
}
|
||||
} else {
|
||||
sessionData.createdAt = sessionData.createdAt || null;
|
||||
}
|
||||
sessionData.mode = data.mode;
|
||||
sessionData.agentId = data.agentId;
|
||||
sessionData.latestRootBlobId = data.latestRootBlobId;
|
||||
@@ -497,6 +523,13 @@ router.get('/sessions', async (req, res) => {
|
||||
}
|
||||
|
||||
await db.close();
|
||||
|
||||
// Finalize createdAt: use parsed meta value when valid, else fall back to store.db mtime
|
||||
if (!sessionData.createdAt) {
|
||||
if (dbStatMtimeMs && Number.isFinite(dbStatMtimeMs)) {
|
||||
sessionData.createdAt = new Date(dbStatMtimeMs).toISOString();
|
||||
}
|
||||
}
|
||||
|
||||
sessions.push(sessionData);
|
||||
|
||||
@@ -505,6 +538,18 @@ router.get('/sessions', async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: ensure createdAt is a valid ISO string (use session directory mtime as last resort)
|
||||
for (const s of sessions) {
|
||||
if (!s.createdAt) {
|
||||
try {
|
||||
const sessionDir = path.join(cursorChatsPath, s.id);
|
||||
const st = await fs.stat(sessionDir);
|
||||
s.createdAt = new Date(st.mtimeMs).toISOString();
|
||||
} catch {
|
||||
s.createdAt = new Date().toISOString();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort sessions by creation date (newest first)
|
||||
sessions.sort((a, b) => {
|
||||
if (!a.createdAt) return 1;
|
||||
|
||||
Reference in New Issue
Block a user