Dashboard: Session message counts, workspace path, Agent persona
This commit is contained in:
1 parent
286a91204f
commit
b1c1e462cc
9 files changed
+113
-15
No files matched your search
@@ -26,7 +26,7 @@ public static class SessionEndpoint
|
||||
|
||||
private static async Task HandleList(HttpContext ctx, DatabaseService db)
|
||||
{
|
||||
var sessions = await db.GetSessions();
|
||||
var sessions = await db.GetSessionsWithStats();
|
||||
await ctx.Response.WriteAsJsonAsync(sessions);
|
||||
}
|
||||
|
||||
|
||||
@@ -214,6 +214,47 @@ public class DatabaseService
|
||||
"SELECT * FROM sessions WHERE is_deleted = 0 ORDER BY updated_at DESC");
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<object>> GetSessionsWithStats()
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
var sessions = await conn.QueryAsync<SessionRecord>(
|
||||
"SELECT * FROM sessions WHERE is_deleted = 0 ORDER BY updated_at DESC");
|
||||
|
||||
var results = new List<object>();
|
||||
foreach (var s in sessions)
|
||||
{
|
||||
var activeCount = await conn.ExecuteScalarAsync<int>(@"
|
||||
SELECT COUNT(*) FROM messages m
|
||||
JOIN chains c ON m.chain_id = c.chain_id
|
||||
WHERE c.session_id = @sessionId AND c.archive_id IS NULL",
|
||||
new { sessionId = s.SessionId });
|
||||
|
||||
var totalCount = await conn.ExecuteScalarAsync<int>(@"
|
||||
SELECT COUNT(*) FROM messages m
|
||||
JOIN chains c ON m.chain_id = c.chain_id
|
||||
WHERE c.session_id = @sessionId",
|
||||
new { sessionId = s.SessionId });
|
||||
|
||||
results.Add(new
|
||||
{
|
||||
s.SessionId,
|
||||
s.Name,
|
||||
s.Description,
|
||||
s.Image,
|
||||
s.AgentId,
|
||||
s.Workspace,
|
||||
s.Context,
|
||||
s.CustomCommands,
|
||||
s.IsDeleted,
|
||||
s.CreatedAt,
|
||||
s.UpdatedAt,
|
||||
MessageCount = activeCount,
|
||||
TotalMessageCount = totalCount
|
||||
});
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public async Task<SessionRecord> GetSession(string sessionId)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
|
||||
@@ -19,6 +19,10 @@ body, input, select, button, textarea {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
input {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
input, textarea, select {
|
||||
padding: var(--s2);
|
||||
}
|
||||
@@ -55,6 +59,8 @@ button:disabled {
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: 8em;
|
||||
min-height: 8em;
|
||||
height: 12em;
|
||||
min-height: 12em;
|
||||
max-height: 12em;
|
||||
resize: none;
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
.dashboard section {
|
||||
margin-bottom: calc(var(--s4) * 2);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
@@ -80,7 +81,39 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card.placeholder {
|
||||
#newAgentCard,
|
||||
#newSessionCard {
|
||||
margin-top: var(--s4);
|
||||
}
|
||||
|
||||
#newAgentCard .card,
|
||||
#newSessionCard .card {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.card-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--s1);
|
||||
margin-top: var(--s1);
|
||||
}
|
||||
|
||||
.card-meta:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.card-meta-item {
|
||||
opacity: var(--semi-transparent);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.card-workspace {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.card-persona {
|
||||
font-style: italic;
|
||||
}
|
||||
@@ -140,7 +140,6 @@
|
||||
|
||||
.fe-textarea {
|
||||
flex: 1;
|
||||
resize: none;
|
||||
border: none;
|
||||
background: var(--f2-bg);
|
||||
color: var(--text);
|
||||
|
||||
+13
-6
@@ -5,6 +5,10 @@
|
||||
gap: var(--s3);
|
||||
}
|
||||
|
||||
.form label {
|
||||
margin-top: var(--s4);
|
||||
}
|
||||
|
||||
.avatar-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -60,23 +64,26 @@
|
||||
|
||||
.custom-command-item {
|
||||
display: flex;
|
||||
gap: var(--s2);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.custom-command-fields {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--s2);
|
||||
}
|
||||
|
||||
.custom-command-fields .cmd-name {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.custom-command-fields .cmd-command {
|
||||
flex: 5;
|
||||
}
|
||||
|
||||
.custom-command-item .remove-cmd-btn {
|
||||
flex-shrink: 0;
|
||||
margin-left: var(--s2);
|
||||
color: #ff6b6b;
|
||||
border-color: #ff6b6b;
|
||||
padding: var(--s2) var(--s3);
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.custom-command-item .remove-cmd-btn:hover {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.placeholder {
|
||||
.settings-page .placeholder {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -6,6 +6,6 @@
|
||||
padding: calc(var(--s4) * 2);
|
||||
}
|
||||
|
||||
.placeholder-icon {
|
||||
.settings-page .placeholder-icon {
|
||||
font-size: 4em;
|
||||
}
|
||||
@@ -38,12 +38,14 @@
|
||||
<h2>Sessions</h2>
|
||||
</div>
|
||||
<div class="card-grid" id="sessionCards"></div>
|
||||
<div class="card-grid" id="newSessionCard"></div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="section-header">
|
||||
<h2>Agents</h2>
|
||||
</div>
|
||||
<div class="card-grid" id="agentCards"></div>
|
||||
<div class="card-grid" id="newAgentCard"></div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,11 @@ registerPage('dashboard', {
|
||||
const agent = s.agentId ? agentMap[s.agentId] : null;
|
||||
const card = document.createElement('div');
|
||||
card.className = 'card clickable';
|
||||
const msgLabel = s.messageCount !== undefined
|
||||
? `${s.messageCount}${s.totalMessageCount !== s.messageCount ? ` (${s.totalMessageCount})` : ''} Nachrichten`
|
||||
: '';
|
||||
const workspaceLabel = s.workspace ? s.workspace : '';
|
||||
|
||||
card.innerHTML = `
|
||||
<div class="card-image">${s.image ? `<img src="${s.image}" alt="">` : ''}</div>
|
||||
<div class="card-body">
|
||||
@@ -23,6 +28,10 @@ registerPage('dashboard', {
|
||||
${agent ? `<span class="badge">${esc(agent.name)}</span>` : ''}
|
||||
</div>
|
||||
${s.description ? `<div class="card-desc">${esc(s.description)}</div>` : ''}
|
||||
<div class="card-meta">
|
||||
${msgLabel ? `<span class="card-meta-item">💬 ${esc(msgLabel)}</span>` : ''}
|
||||
${workspaceLabel ? `<span class="card-meta-item card-workspace" title="${esc(workspaceLabel)}">📁 ${esc(workspaceLabel)}</span>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
card.addEventListener('click', () => navigateTo('session', { id: s.sessionId }));
|
||||
@@ -34,7 +43,7 @@ registerPage('dashboard', {
|
||||
newSession.className = 'card placeholder clickable';
|
||||
newSession.innerHTML = '<div class="card-body"><div class="card-title">+ Neue Session</div></div>';
|
||||
newSession.addEventListener('click', () => navigateTo('session'));
|
||||
grid.appendChild(newSession);
|
||||
document.getElementById('newSessionCard').appendChild(newSession);
|
||||
|
||||
// Agents
|
||||
const agentGrid = document.getElementById('agentCards');
|
||||
@@ -51,6 +60,7 @@ registerPage('dashboard', {
|
||||
<span class="badge">${esc(a.model)}</span>
|
||||
</div>
|
||||
${a.description ? `<div class="card-desc">${esc(a.description)}</div>` : ''}
|
||||
${a.persona ? `<div class="card-meta"><span class="card-meta-item card-persona">🎭 ${esc(a.persona)}</span></div>` : ''}
|
||||
</div>
|
||||
`;
|
||||
card.addEventListener('click', () => navigateTo('agent', { id: a.agentId }));
|
||||
@@ -61,7 +71,7 @@ registerPage('dashboard', {
|
||||
placeholder.className = 'card placeholder clickable';
|
||||
placeholder.innerHTML = '<div class="card-body"><div class="card-title">+ Neuer Agent</div></div>';
|
||||
placeholder.addEventListener('click', () => navigateTo('agent'));
|
||||
agentGrid.appendChild(placeholder);
|
||||
document.getElementById('newAgentCard').appendChild(placeholder);
|
||||
},
|
||||
destroy() {}
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user