@vheins/local-memory-mcp 0.3.23 → 0.3.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dashboard/public/app.js +323 -82
- package/dist/dashboard/public/index.html +255 -83
- package/dist/dashboard/server.js +16 -1
- package/dist/dashboard/server.js.map +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +45 -13
- package/dist/router.js.map +1 -1
- package/dist/storage/sqlite.d.ts +4 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +121 -12
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/tasks.e2e.test.js +82 -3
- package/dist/tasks.e2e.test.js.map +1 -1
- package/dist/tools/memory.acknowledge.d.ts.map +1 -1
- package/dist/tools/memory.acknowledge.js +0 -2
- package/dist/tools/memory.acknowledge.js.map +1 -1
- package/dist/tools/memory.delete.d.ts.map +1 -1
- package/dist/tools/memory.delete.js +0 -2
- package/dist/tools/memory.delete.js.map +1 -1
- package/dist/tools/memory.search.d.ts.map +1 -1
- package/dist/tools/memory.search.js +0 -1
- package/dist/tools/memory.search.js.map +1 -1
- package/dist/tools/memory.store.d.ts.map +1 -1
- package/dist/tools/memory.store.js +0 -2
- package/dist/tools/memory.store.js.map +1 -1
- package/dist/tools/schemas.d.ts +30 -3
- package/dist/tools/schemas.d.ts.map +1 -1
- package/dist/tools/schemas.js +16 -4
- package/dist/tools/schemas.js.map +1 -1
- package/dist/tools/task.bulk-manage.d.ts.map +1 -1
- package/dist/tools/task.bulk-manage.js +0 -2
- package/dist/tools/task.bulk-manage.js.map +1 -1
- package/dist/tools/task.manage.d.ts.map +1 -1
- package/dist/tools/task.manage.js +19 -4
- package/dist/tools/task.manage.js.map +1 -1
- package/dist/types.d.ts +22 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -94,94 +94,95 @@ function getActionColor(action) {
|
|
|
94
94
|
return colors[action] || colors.search;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
function
|
|
98
|
-
const
|
|
99
|
-
search:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
tail: 'left-2 -bottom-1.5 border-r-blue-500 dark:border-r-blue-600',
|
|
105
|
-
},
|
|
106
|
-
read: {
|
|
107
|
-
bubble: 'bg-emerald-500 dark:bg-emerald-600 text-white',
|
|
108
|
-
label: 'text-emerald-100',
|
|
109
|
-
meta: 'text-emerald-200',
|
|
110
|
-
align: 'items-start',
|
|
111
|
-
tail: 'left-2 -bottom-1.5 border-r-emerald-500 dark:border-r-emerald-600',
|
|
112
|
-
},
|
|
113
|
-
write: {
|
|
114
|
-
bubble: 'bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-100',
|
|
115
|
-
label: 'text-gray-500 dark:text-gray-400',
|
|
116
|
-
meta: 'text-gray-400 dark:text-gray-500',
|
|
117
|
-
align: 'items-start',
|
|
118
|
-
tail: 'left-2 -bottom-1.5 border-r-gray-100 dark:border-r-gray-700',
|
|
119
|
-
},
|
|
120
|
-
update: {
|
|
121
|
-
bubble: 'bg-amber-100 dark:bg-amber-900 text-amber-900 dark:text-amber-100',
|
|
122
|
-
label: 'text-amber-600 dark:text-amber-400',
|
|
123
|
-
meta: 'text-amber-500 dark:text-amber-500',
|
|
124
|
-
align: 'items-start',
|
|
125
|
-
tail: 'left-2 -bottom-1.5 border-r-amber-100 dark:border-r-amber-900',
|
|
126
|
-
},
|
|
127
|
-
delete: {
|
|
128
|
-
bubble: 'bg-red-100 dark:bg-red-900 text-red-900 dark:text-red-100',
|
|
129
|
-
label: 'text-red-500 dark:text-red-400',
|
|
130
|
-
meta: 'text-red-400',
|
|
131
|
-
align: 'items-start',
|
|
132
|
-
tail: 'left-2 -bottom-1.5 border-r-red-100 dark:border-r-red-900',
|
|
133
|
-
},
|
|
97
|
+
function getActionBubbleColor(action) {
|
|
98
|
+
const colors = {
|
|
99
|
+
search: 'from-blue-500 to-blue-600',
|
|
100
|
+
read: 'from-emerald-500 to-emerald-600',
|
|
101
|
+
write: 'from-indigo-500 to-indigo-600',
|
|
102
|
+
update: 'from-amber-500 to-amber-600',
|
|
103
|
+
delete: 'from-rose-500 to-rose-600'
|
|
134
104
|
};
|
|
135
|
-
return
|
|
105
|
+
return colors[action] || 'from-slate-500 to-slate-600';
|
|
136
106
|
}
|
|
137
107
|
|
|
138
108
|
function renderActionBubble(action) {
|
|
139
|
-
|
|
140
|
-
const isRight = false; // Force left alignment
|
|
141
|
-
|
|
142
|
-
// Main content line
|
|
109
|
+
// Agent Request Bubble
|
|
143
110
|
let mainText = '';
|
|
144
111
|
let subText = '';
|
|
145
112
|
|
|
146
113
|
if (action.action === 'search') {
|
|
147
114
|
mainText = `🔍 "${action.query || ''}"`;
|
|
148
115
|
subText = action.result_count != null ? `${action.result_count} result${action.result_count !== 1 ? 's' : ''} found` : '';
|
|
116
|
+
} else if (action.task_id) {
|
|
117
|
+
mainText = action.task_title || action.task_code || action.task_id.substring(0, 8);
|
|
118
|
+
const verb = { write: '💾 Created Task', update: '🔄 Updated Task', delete: '🗑️ Deleted Task' }[action.action] || action.action;
|
|
119
|
+
subText = action.task_code ? `${verb} [${action.task_code}]` : verb;
|
|
149
120
|
} else {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
121
|
+
mainText = action.memory_title || (action.memory_id ? action.memory_id.substring(0, 8) + '…' : '—');
|
|
122
|
+
const typeLabel = action.memory_type ? `[${action.memory_type}]` : '';
|
|
123
|
+
const verbs = {
|
|
124
|
+
write: '💾 Stored',
|
|
125
|
+
update: '🔄 Updated',
|
|
126
|
+
delete: '🗑️ Deleted',
|
|
127
|
+
read: '📖 Read',
|
|
128
|
+
agent_handoff: '🤝 Handoff',
|
|
129
|
+
agent_registered: '📝 Registration'
|
|
130
|
+
};
|
|
131
|
+
const verb = verbs[action.action] || verbs[action.memory_type] || action.action;
|
|
132
|
+
subText = [verb, typeLabel].filter(Boolean).join(' ');
|
|
162
133
|
}
|
|
163
134
|
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
<div class="flex flex-col ${s.align} mb-4">
|
|
170
|
-
<div class="flex items-center gap-1.5 mb-1 px-1">
|
|
171
|
-
<div class="w-5 h-5 rounded-full ${s.bubble} flex items-center justify-center flex-shrink-0">
|
|
135
|
+
const colorGradient = getActionBubbleColor(action.action);
|
|
136
|
+
const agentBubble = `
|
|
137
|
+
<div class="flex flex-col agent-align">
|
|
138
|
+
<div class="chat-bubble chat-bubble-agent bg-gradient-to-br ${colorGradient}">
|
|
139
|
+
<div class="flex items-center gap-1.5 mb-1 opacity-80">
|
|
172
140
|
<svg class="w-3 h-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">${getActionIcon(action.action)}</svg>
|
|
141
|
+
<span class="text-[10px] font-bold uppercase tracking-wider">${action.action}</span>
|
|
173
142
|
</div>
|
|
174
|
-
<
|
|
143
|
+
<p class="text-sm font-medium leading-snug break-words">${mainText}</p>
|
|
144
|
+
${subText ? `<p class="text-[10px] mt-1 opacity-70 font-semibold uppercase tracking-tight">${subText}</p>` : ''}
|
|
175
145
|
</div>
|
|
176
|
-
<
|
|
177
|
-
<div class="${s.bubble} rounded-2xl rounded-bl-sm px-3 py-2 shadow-sm">
|
|
178
|
-
<p class="text-sm font-medium leading-snug break-words">${mainText}</p>
|
|
179
|
-
${subText ? `<p class="text-xs mt-0.5 ${s.meta}">${subText}</p>` : ''}
|
|
180
|
-
</div>
|
|
181
|
-
</div>
|
|
182
|
-
<span class="text-[10px] text-gray-400 dark:text-gray-500 mt-1 px-1">${formatActionDate(action.created_at)}</span>
|
|
146
|
+
<span class="chat-timestamp">${formatActionDate(action.created_at)}</span>
|
|
183
147
|
</div>
|
|
184
148
|
`;
|
|
149
|
+
|
|
150
|
+
// MCP Response Bubble (if exists)
|
|
151
|
+
let mcpBubble = '';
|
|
152
|
+
if (action.response) {
|
|
153
|
+
let responseContent = '';
|
|
154
|
+
try {
|
|
155
|
+
const resp = typeof action.response === 'string' ? JSON.parse(action.response) : action.response;
|
|
156
|
+
if (resp.content && Array.isArray(resp.content)) {
|
|
157
|
+
responseContent = resp.content
|
|
158
|
+
.filter(c => c.type === 'text')
|
|
159
|
+
.map(c => c.text)
|
|
160
|
+
.join('\n');
|
|
161
|
+
} else if (resp.message) {
|
|
162
|
+
responseContent = resp.message;
|
|
163
|
+
} else {
|
|
164
|
+
responseContent = JSON.stringify(resp, null, 2);
|
|
165
|
+
}
|
|
166
|
+
} catch (e) {
|
|
167
|
+
responseContent = action.response;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
mcpBubble = `
|
|
171
|
+
<div class="flex flex-col mcp-align">
|
|
172
|
+
<div class="chat-bubble chat-bubble-mcp">
|
|
173
|
+
<div class="flex items-center gap-1.5 mb-1 opacity-60">
|
|
174
|
+
<div class="w-2 h-2 rounded-full bg-sky-500"></div>
|
|
175
|
+
<span class="text-[10px] font-bold uppercase tracking-wider">MCP REPLY</span>
|
|
176
|
+
</div>
|
|
177
|
+
<div class="markdown-body text-xs prose-sm prose-slate dark:prose-invert">
|
|
178
|
+
${renderMarkdown(responseContent)}
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return agentBubble + mcpBubble;
|
|
185
186
|
}
|
|
186
187
|
|
|
187
188
|
function renderRecentActions() {
|
|
@@ -510,6 +511,7 @@ function updateCountdown() {
|
|
|
510
511
|
btn.addEventListener('click', () => {
|
|
511
512
|
const isDark = document.documentElement.classList.toggle('dark');
|
|
512
513
|
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
|
514
|
+
scheduleTabIndicatorPosition(currentTab);
|
|
513
515
|
});
|
|
514
516
|
}
|
|
515
517
|
});
|
|
@@ -610,6 +612,10 @@ async function loadStats() {
|
|
|
610
612
|
document.getElementById('decisionCount').textContent = data.byType?.decision || 0;
|
|
611
613
|
document.getElementById('mistakeCount').textContent = data.byType?.mistake || 0;
|
|
612
614
|
document.getElementById('patternCount').textContent = data.byType?.pattern || 0;
|
|
615
|
+
const handoffCountEl = document.getElementById('handoffCount');
|
|
616
|
+
const registeredCountEl = document.getElementById('registeredCount');
|
|
617
|
+
if (handoffCountEl) handoffCountEl.textContent = data.byType?.agent_handoff || 0;
|
|
618
|
+
if (registeredCountEl) registeredCountEl.textContent = data.byType?.agent_registered || 0;
|
|
613
619
|
|
|
614
620
|
// Fill Task stats
|
|
615
621
|
if (data.taskStats) {
|
|
@@ -622,6 +628,15 @@ async function loadStats() {
|
|
|
622
628
|
document.getElementById('inProgressStatCount').textContent = data.taskStats.inProgress || 0;
|
|
623
629
|
document.getElementById('completedStatCount').textContent = data.taskStats.completed || 0;
|
|
624
630
|
document.getElementById('blockedStatCount').textContent = data.taskStats.blocked || 0;
|
|
631
|
+
|
|
632
|
+
// Also update column headers
|
|
633
|
+
const todoCountEl = document.getElementById('todoCount');
|
|
634
|
+
const inProgressCountEl = document.getElementById('inProgressCount');
|
|
635
|
+
const completedCountEl = document.getElementById('completedCount');
|
|
636
|
+
|
|
637
|
+
if (todoCountEl) todoCountEl.textContent = data.taskStats.todo || 0;
|
|
638
|
+
if (inProgressCountEl) inProgressCountEl.textContent = data.taskStats.inProgress || 0;
|
|
639
|
+
if (completedCountEl) completedCountEl.textContent = data.taskStats.completed || 0;
|
|
625
640
|
|
|
626
641
|
updateTaskStatusChart(data.taskStats);
|
|
627
642
|
}
|
|
@@ -646,16 +661,18 @@ function updateTypeChart(byType) {
|
|
|
646
661
|
byType?.decision || 0,
|
|
647
662
|
byType?.mistake || 0,
|
|
648
663
|
byType?.code_fact || 0,
|
|
649
|
-
byType?.pattern || 0
|
|
664
|
+
byType?.pattern || 0,
|
|
665
|
+
byType?.agent_handoff || 0,
|
|
666
|
+
byType?.agent_registered || 0
|
|
650
667
|
];
|
|
651
668
|
|
|
652
669
|
charts.typeChart = new Chart(ctx, {
|
|
653
670
|
type: 'doughnut',
|
|
654
671
|
data: {
|
|
655
|
-
labels: ['Decision', 'Mistake', 'Code Fact', 'Pattern'],
|
|
672
|
+
labels: ['Decision', 'Mistake', 'Code Fact', 'Pattern', 'Handoff', 'Registration'],
|
|
656
673
|
datasets: [{
|
|
657
674
|
data: counts,
|
|
658
|
-
backgroundColor: ['#fb7185', '#c084fc', '#38bdf8', '#34d399'],
|
|
675
|
+
backgroundColor: ['#fb7185', '#c084fc', '#38bdf8', '#34d399', '#fb923c', '#a3e635'],
|
|
659
676
|
borderWidth: 2,
|
|
660
677
|
borderColor: isDark ? '#1e293b' : '#ffffff',
|
|
661
678
|
hoverOffset: 12
|
|
@@ -1361,6 +1378,7 @@ function renderDetailPanel(data) {
|
|
|
1361
1378
|
<div class="text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400 mb-3">Source Info</div>
|
|
1362
1379
|
<div class="space-y-2 text-sm">
|
|
1363
1380
|
<div><strong>Agent:</strong> ${escapeHtml(data.agent || 'unknown')}</div>
|
|
1381
|
+
<div><strong>Role:</strong> ${escapeHtml(data.role || 'unknown')}</div>
|
|
1364
1382
|
<div><strong>Model:</strong> ${escapeHtml(data.model || 'unknown')}</div>
|
|
1365
1383
|
<div><strong>Repo:</strong> ${escapeHtml(data.scope?.repo || 'N/A')}</div>
|
|
1366
1384
|
</div>
|
|
@@ -1375,6 +1393,27 @@ function renderDetailPanel(data) {
|
|
|
1375
1393
|
</div>
|
|
1376
1394
|
</div>
|
|
1377
1395
|
|
|
1396
|
+
${data.type === 'agent_registered' ? `
|
|
1397
|
+
<div class="rounded-xl border border-lime-200 dark:border-lime-900 bg-lime-50 dark:bg-lime-900/20 p-4">
|
|
1398
|
+
<div class="text-xs uppercase tracking-wide text-lime-600 dark:text-lime-400 mb-2">Agent Status</div>
|
|
1399
|
+
<div class="flex items-center gap-2">
|
|
1400
|
+
<span class="inline-flex items-center px-2 py-1 rounded text-xs font-bold uppercase ${data.status === 'active' ? 'bg-lime-500 text-white' : 'bg-gray-400 text-white'}">
|
|
1401
|
+
${escapeHtml(data.status)}
|
|
1402
|
+
</span>
|
|
1403
|
+
</div>
|
|
1404
|
+
</div>
|
|
1405
|
+
` : ''}
|
|
1406
|
+
|
|
1407
|
+
${data.type === 'agent_handoff' ? `
|
|
1408
|
+
<div class="rounded-xl border border-orange-200 dark:border-orange-900 bg-orange-50 dark:bg-orange-900/20 p-4">
|
|
1409
|
+
<div class="text-xs uppercase tracking-wide text-orange-600 dark:text-orange-400 mb-2">Handoff Details</div>
|
|
1410
|
+
<div class="space-y-2 text-sm">
|
|
1411
|
+
<div><strong>Completed at:</strong> ${data.completed_at ? new Date(data.completed_at).toLocaleString() : 'Pending'}</div>
|
|
1412
|
+
<div><strong>Task Status:</strong> <span class="capitalize px-1.5 py-0.5 rounded bg-orange-100 text-orange-700 dark:bg-orange-900/40 dark:text-orange-300">${data.status}</span></div>
|
|
1413
|
+
</div>
|
|
1414
|
+
</div>
|
|
1415
|
+
` : ''}
|
|
1416
|
+
|
|
1378
1417
|
${data.supersedes ? `
|
|
1379
1418
|
<div class="rounded-xl border border-blue-200 dark:border-blue-900 bg-blue-50 dark:bg-blue-900/20 p-4">
|
|
1380
1419
|
<div class="text-xs uppercase tracking-wide text-blue-500 dark:text-blue-400 mb-2">Supersedes</div>
|
|
@@ -1655,6 +1694,7 @@ async function loadData() {
|
|
|
1655
1694
|
checkStatus(),
|
|
1656
1695
|
loadRecentActions(),
|
|
1657
1696
|
]);
|
|
1697
|
+
scheduleTabIndicatorPosition(currentTab);
|
|
1658
1698
|
}
|
|
1659
1699
|
|
|
1660
1700
|
let currentTasks = [];
|
|
@@ -1787,7 +1827,7 @@ function renderTaskCards(containerId, tasks, clear = false) {
|
|
|
1787
1827
|
${t.depends_on ? `
|
|
1788
1828
|
<div class="mt-2 pt-2 border-t border-gray-50 dark:border-gray-600 flex items-center gap-1.5">
|
|
1789
1829
|
<svg class="w-3 h-3 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6"></path></svg>
|
|
1790
|
-
<span class="text-[10px] font-medium text-amber-600 dark:text-amber-400">Depends on: ${t.depends_on.substring(0, 8)}</span>
|
|
1830
|
+
<span class="text-[10px] font-medium text-amber-600 dark:text-amber-400">Depends on: ${t.depends_on_code || t.depends_on.substring(0, 8)}</span>
|
|
1791
1831
|
</div>
|
|
1792
1832
|
` : ''}
|
|
1793
1833
|
<div class="mt-3 flex items-center justify-between">
|
|
@@ -1855,6 +1895,13 @@ async function showTaskDetail(id) {
|
|
|
1855
1895
|
<span class="px-2 py-1 rounded-lg bg-indigo-500/10 text-indigo-600 dark:text-indigo-400 text-xs font-bold border border-indigo-500/20">P${task.priority}</span>
|
|
1856
1896
|
<span class="px-2 py-1 rounded-lg bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-400 text-xs font-bold border border-slate-200 dark:border-slate-700 uppercase">${task.phase}</span>
|
|
1857
1897
|
</div>
|
|
1898
|
+
${task.depends_on ? `
|
|
1899
|
+
<div class="mt-3 pt-3 border-t border-slate-100 dark:border-slate-700/50 flex items-center gap-2">
|
|
1900
|
+
<svg class="w-3.5 h-3.5 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6"></path></svg>
|
|
1901
|
+
<div class="text-[10px] font-bold text-slate-400 uppercase tracking-wider">Depends on:</div>
|
|
1902
|
+
<span class="text-xs font-mono font-bold text-amber-600 dark:text-amber-400 bg-amber-500/10 px-1.5 py-0.5 rounded border border-amber-500/20">${task.depends_on_code || task.depends_on.substring(0, 8)}</span>
|
|
1903
|
+
</div>
|
|
1904
|
+
` : ''}
|
|
1858
1905
|
</div>
|
|
1859
1906
|
<div class="p-4 bg-white dark:bg-slate-800/40 rounded-xl border border-slate-100 dark:border-slate-800">
|
|
1860
1907
|
<div class="text-[10px] font-bold text-slate-400 uppercase mb-2">Owner</div>
|
|
@@ -1877,6 +1924,8 @@ async function showTaskDetail(id) {
|
|
|
1877
1924
|
</div>
|
|
1878
1925
|
</div>
|
|
1879
1926
|
|
|
1927
|
+
${renderTaskComments(task.comments)}
|
|
1928
|
+
|
|
1880
1929
|
${task.doc_path ? `
|
|
1881
1930
|
<div class="p-4 bg-sky-500/5 dark:bg-sky-500/10 rounded-xl border border-sky-500/20">
|
|
1882
1931
|
<div class="text-[10px] font-bold text-sky-600/60 dark:text-sky-400/60 uppercase mb-2">Documentation</div>
|
|
@@ -1912,14 +1961,128 @@ function getPriorityColor(p) {
|
|
|
1912
1961
|
return 'text-gray-500 dark:text-gray-400';
|
|
1913
1962
|
}
|
|
1914
1963
|
|
|
1964
|
+
function formatTaskStatusLabel(status) {
|
|
1965
|
+
return String(status || '')
|
|
1966
|
+
.replace(/_/g, ' ')
|
|
1967
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
function renderTaskComments(comments) {
|
|
1971
|
+
if (!comments || comments.length === 0) {
|
|
1972
|
+
return `
|
|
1973
|
+
<div class="p-5 bg-white dark:bg-slate-800/40 rounded-2xl border border-slate-100 dark:border-slate-800">
|
|
1974
|
+
<div class="text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-3">History</div>
|
|
1975
|
+
<div class="text-sm italic text-slate-400">No historical comments yet</div>
|
|
1976
|
+
</div>
|
|
1977
|
+
`;
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
return `
|
|
1981
|
+
<div class="p-5 bg-white dark:bg-slate-800/40 rounded-2xl border border-slate-100 dark:border-slate-800">
|
|
1982
|
+
<div class="flex items-center justify-between gap-3 mb-4">
|
|
1983
|
+
<div class="text-[10px] font-bold text-slate-400 uppercase tracking-widest">History</div>
|
|
1984
|
+
<div class="text-[10px] text-slate-400">${comments.length} comment${comments.length === 1 ? '' : 's'}</div>
|
|
1985
|
+
</div>
|
|
1986
|
+
<div class="space-y-4">
|
|
1987
|
+
${comments.map((item) => `
|
|
1988
|
+
<div class="relative pl-5">
|
|
1989
|
+
<div class="absolute left-0 top-1.5 h-full w-px bg-slate-200 dark:bg-slate-700"></div>
|
|
1990
|
+
<div class="absolute left-[-4px] top-1.5 w-2.5 h-2.5 rounded-full bg-sky-500 shadow-[0_0_0_4px_rgba(14,165,233,0.12)]"></div>
|
|
1991
|
+
<div class="rounded-xl border border-slate-100 dark:border-slate-700 bg-slate-50/80 dark:bg-slate-900/40 p-4">
|
|
1992
|
+
<div class="flex flex-wrap items-center gap-2 mb-2">
|
|
1993
|
+
<span class="text-xs font-bold text-slate-800 dark:text-slate-100">${escapeHtml(item.agent || 'unknown')}</span>
|
|
1994
|
+
<span class="text-[10px] text-slate-400">•</span>
|
|
1995
|
+
<span class="text-[11px] text-slate-500 dark:text-slate-400">${escapeHtml(item.model || 'unknown')}</span>
|
|
1996
|
+
<span class="ml-auto text-[10px] text-slate-400">${new Date(item.created_at).toLocaleString()}</span>
|
|
1997
|
+
</div>
|
|
1998
|
+
${(item.previous_status || item.next_status) ? `
|
|
1999
|
+
<div class="flex flex-wrap items-center gap-2 mb-3 text-[10px] font-bold uppercase tracking-wide">
|
|
2000
|
+
<span class="px-2 py-1 rounded-lg bg-slate-200 dark:bg-slate-800 text-slate-600 dark:text-slate-300">${escapeHtml(formatTaskStatusLabel(item.previous_status || 'note'))}</span>
|
|
2001
|
+
<span class="text-slate-400">→</span>
|
|
2002
|
+
<span class="px-2 py-1 rounded-lg bg-sky-500/10 text-sky-600 dark:text-sky-400 border border-sky-500/20">${escapeHtml(formatTaskStatusLabel(item.next_status || 'note'))}</span>
|
|
2003
|
+
</div>
|
|
2004
|
+
` : ''}
|
|
2005
|
+
<div class="text-sm text-slate-600 dark:text-slate-300 leading-relaxed markdown-body">
|
|
2006
|
+
${renderMarkdown(item.comment)}
|
|
2007
|
+
</div>
|
|
2008
|
+
</div>
|
|
2009
|
+
</div>
|
|
2010
|
+
`).join('')}
|
|
2011
|
+
</div>
|
|
2012
|
+
</div>
|
|
2013
|
+
`;
|
|
2014
|
+
}
|
|
2015
|
+
|
|
1915
2016
|
let currentTab = localStorage.getItem('activeTab') || 'dashboard';
|
|
1916
2017
|
|
|
2018
|
+
function syncTabIndicatorTheme(indicator) {
|
|
2019
|
+
const isDark = document.documentElement.classList.contains('dark');
|
|
2020
|
+
indicator.style.background = isDark
|
|
2021
|
+
? 'linear-gradient(135deg, rgba(14,165,233,0.95) 0%, rgba(79,70,229,0.92) 100%)'
|
|
2022
|
+
: 'linear-gradient(135deg, #0ea5e9 0%, #2563eb 100%)';
|
|
2023
|
+
indicator.style.border = isDark
|
|
2024
|
+
? '1px solid rgba(59, 130, 246, 0.32)'
|
|
2025
|
+
: '1px solid rgba(37, 99, 235, 0.18)';
|
|
2026
|
+
indicator.style.boxShadow = isDark
|
|
2027
|
+
? '0 12px 28px rgba(14,165,233,0.2), inset 0 1px 0 rgba(255,255,255,0.12)'
|
|
2028
|
+
: '0 10px 24px rgba(37,99,235,0.24), inset 0 1px 0 rgba(255,255,255,0.28)';
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
function syncActiveTabButtonTheme(button, isActive) {
|
|
2032
|
+
if (!button) return;
|
|
2033
|
+
|
|
2034
|
+
const isDark = document.documentElement.classList.contains('dark');
|
|
2035
|
+
|
|
2036
|
+
if (!isActive) {
|
|
2037
|
+
button.style.color = '';
|
|
2038
|
+
button.style.background = '';
|
|
2039
|
+
button.style.border = '';
|
|
2040
|
+
button.style.boxShadow = '';
|
|
2041
|
+
return;
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
button.style.color = isDark ? '#ffffff' : '#0f172a';
|
|
2045
|
+
button.style.background = isDark
|
|
2046
|
+
? 'linear-gradient(135deg, rgba(14,165,233,0.95) 0%, rgba(79,70,229,0.92) 100%)'
|
|
2047
|
+
: 'rgba(255,255,255,0.96)';
|
|
2048
|
+
button.style.border = isDark
|
|
2049
|
+
? '1px solid rgba(59, 130, 246, 0.32)'
|
|
2050
|
+
: '1px solid rgba(148, 163, 184, 0.22)';
|
|
2051
|
+
button.style.boxShadow = isDark
|
|
2052
|
+
? '0 12px 28px rgba(14,165,233,0.2), inset 0 1px 0 rgba(255,255,255,0.12)'
|
|
2053
|
+
: '0 10px 24px rgba(148,163,184,0.16), inset 0 -3px 0 rgba(37,99,235,0.9)';
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
function positionTabIndicator(tab) {
|
|
2057
|
+
const indicator = document.getElementById('tabIndicator');
|
|
2058
|
+
const rail = document.getElementById('tabRail');
|
|
2059
|
+
const targetButton = document.getElementById(`${tab}TabBtn`);
|
|
2060
|
+
|
|
2061
|
+
if (!indicator || !rail || !targetButton) return;
|
|
2062
|
+
syncTabIndicatorTheme(indicator);
|
|
2063
|
+
|
|
2064
|
+
const railRect = rail.getBoundingClientRect();
|
|
2065
|
+
const buttonRect = targetButton.getBoundingClientRect();
|
|
2066
|
+
const left = buttonRect.left - railRect.left;
|
|
2067
|
+
|
|
2068
|
+
indicator.style.left = `${left}px`;
|
|
2069
|
+
indicator.style.width = `${buttonRect.width}px`;
|
|
2070
|
+
indicator.style.transform = 'none';
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
function scheduleTabIndicatorPosition(tab = currentTab) {
|
|
2074
|
+
requestAnimationFrame(() => {
|
|
2075
|
+
requestAnimationFrame(() => {
|
|
2076
|
+
positionTabIndicator(tab);
|
|
2077
|
+
});
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
|
|
1917
2081
|
function switchTab(tab) {
|
|
1918
2082
|
const dashTab = document.getElementById('dashboardTabBtn');
|
|
1919
2083
|
const memTab = document.getElementById('memoriesTabBtn');
|
|
1920
2084
|
const taskTab = document.getElementById('tasksTabBtn');
|
|
1921
2085
|
const refTab = document.getElementById('referenceTabBtn');
|
|
1922
|
-
const indicator = document.getElementById('tabIndicator');
|
|
1923
2086
|
|
|
1924
2087
|
const dashContent = document.getElementById('dashboardContent');
|
|
1925
2088
|
const memContent = document.getElementById('memoriesContent');
|
|
@@ -1933,23 +2096,21 @@ function switchTab(tab) {
|
|
|
1933
2096
|
const contents = [dashContent, memContent, taskContent, refContent]; const targetId = tab + 'TabBtn';
|
|
1934
2097
|
const targetContentId = tab + 'Content';
|
|
1935
2098
|
|
|
1936
|
-
|
|
1937
|
-
if (indicator) {
|
|
1938
|
-
if (tab === 'dashboard') indicator.style.transform = 'translateX(0)';
|
|
1939
|
-
if (tab === 'memories') indicator.style.transform = 'translateX(100%)';
|
|
1940
|
-
if (tab === 'tasks') indicator.style.transform = 'translateX(200%)';
|
|
1941
|
-
if (tab === 'reference') indicator.style.transform = 'translateX(300%)';
|
|
1942
|
-
}
|
|
2099
|
+
scheduleTabIndicatorPosition(tab);
|
|
1943
2100
|
|
|
1944
2101
|
// Update button states
|
|
1945
2102
|
tabs.forEach(t => {
|
|
1946
2103
|
if (t) {
|
|
1947
2104
|
if (t.id === targetId) {
|
|
2105
|
+
t.classList.add('tab-active');
|
|
1948
2106
|
t.classList.add('text-white');
|
|
1949
2107
|
t.classList.remove('text-gray-500', 'dark:text-gray-400');
|
|
2108
|
+
syncActiveTabButtonTheme(t, true);
|
|
1950
2109
|
} else {
|
|
2110
|
+
t.classList.remove('tab-active');
|
|
1951
2111
|
t.classList.remove('text-white');
|
|
1952
2112
|
t.classList.add('text-gray-500', 'dark:text-gray-400');
|
|
2113
|
+
syncActiveTabButtonTheme(t, false);
|
|
1953
2114
|
}
|
|
1954
2115
|
}
|
|
1955
2116
|
});
|
|
@@ -1973,6 +2134,19 @@ function switchTab(tab) {
|
|
|
1973
2134
|
syncStickyOffsets();
|
|
1974
2135
|
}
|
|
1975
2136
|
|
|
2137
|
+
window.addEventListener('resize', () => {
|
|
2138
|
+
scheduleTabIndicatorPosition(currentTab);
|
|
2139
|
+
});
|
|
2140
|
+
|
|
2141
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
2142
|
+
const rail = document.getElementById('tabRail');
|
|
2143
|
+
if (rail) {
|
|
2144
|
+
new ResizeObserver(() => {
|
|
2145
|
+
scheduleTabIndicatorPosition(currentTab);
|
|
2146
|
+
}).observe(rail);
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
|
|
1976
2150
|
window.loadTasks = loadTasks;
|
|
1977
2151
|
window.switchTab = switchTab;
|
|
1978
2152
|
window.charts = charts;
|
|
@@ -2219,3 +2393,70 @@ switchTab(currentTab);
|
|
|
2219
2393
|
syncStickyOffsets();
|
|
2220
2394
|
startCountdown();
|
|
2221
2395
|
setInterval(checkStatus, 30000);
|
|
2396
|
+
|
|
2397
|
+
// Memories Filter & Popover logic
|
|
2398
|
+
function toggleFilterPopover() {
|
|
2399
|
+
const popover = document.getElementById('filterPopover');
|
|
2400
|
+
popover.classList.toggle('hidden');
|
|
2401
|
+
updateActiveFilterCount();
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
function toggleExportPopover() {
|
|
2405
|
+
const popover = document.getElementById('exportPopover');
|
|
2406
|
+
popover.classList.toggle('hidden');
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
function updateActiveFilterCount() {
|
|
2410
|
+
const type = document.getElementById('typeFilter').value;
|
|
2411
|
+
const minImp = document.getElementById('minImportanceFilter').value;
|
|
2412
|
+
const maxImp = document.getElementById('maxImportanceFilter').value;
|
|
2413
|
+
let count = 0;
|
|
2414
|
+
if (type) count++;
|
|
2415
|
+
if (minImp) count++;
|
|
2416
|
+
if (maxImp) count++;
|
|
2417
|
+
|
|
2418
|
+
const badge = document.getElementById('activeFilterCount');
|
|
2419
|
+
if (badge) {
|
|
2420
|
+
if (count > 0) {
|
|
2421
|
+
badge.innerText = count;
|
|
2422
|
+
badge.classList.remove('hidden');
|
|
2423
|
+
} else {
|
|
2424
|
+
badge.classList.add('hidden');
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
function resetFilters() {
|
|
2430
|
+
document.getElementById('typeFilter').value = '';
|
|
2431
|
+
document.getElementById('minImportanceFilter').value = '';
|
|
2432
|
+
document.getElementById('maxImportanceFilter').value = '';
|
|
2433
|
+
updateActiveFilterCount();
|
|
2434
|
+
currentPage = 1;
|
|
2435
|
+
loadMemories();
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
document.addEventListener('click', (e) => {
|
|
2439
|
+
const filterPopover = document.getElementById('filterPopover');
|
|
2440
|
+
const filterBtn = document.getElementById('filterPopoverBtn');
|
|
2441
|
+
const exportPopover = document.getElementById('exportPopover');
|
|
2442
|
+
const exportBtn = document.getElementById('exportPopoverBtn');
|
|
2443
|
+
|
|
2444
|
+
if (filterPopover && !filterPopover.contains(e.target) && !filterBtn.contains(e.target)) {
|
|
2445
|
+
filterPopover.classList.add('hidden');
|
|
2446
|
+
}
|
|
2447
|
+
if (exportPopover && !exportPopover.contains(e.target) && !exportBtn.contains(e.target)) {
|
|
2448
|
+
exportPopover.classList.add('hidden');
|
|
2449
|
+
}
|
|
2450
|
+
});
|
|
2451
|
+
|
|
2452
|
+
window.toggleFilterPopover = toggleFilterPopover;
|
|
2453
|
+
window.toggleExportPopover = toggleExportPopover;
|
|
2454
|
+
window.resetFilters = resetFilters;
|
|
2455
|
+
|
|
2456
|
+
// Auto-hide popovers on Escape key
|
|
2457
|
+
document.addEventListener('keydown', (e) => {
|
|
2458
|
+
if (e.key === 'Escape') {
|
|
2459
|
+
document.getElementById('filterPopover')?.classList.add('hidden');
|
|
2460
|
+
document.getElementById('exportPopover')?.classList.add('hidden');
|
|
2461
|
+
}
|
|
2462
|
+
});
|