claude-memory-layer 1.0.19 → 1.0.21

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/src/ui/app.js CHANGED
@@ -14,6 +14,10 @@ const state = {
14
14
  retrievalTraces: null,
15
15
  adherenceSummary: null,
16
16
  adherenceWindow: '24h',
17
+ userPromptSearchQuery: '',
18
+ userPromptItems: [],
19
+ userPromptPage: 1,
20
+ userPromptPageSize: 30,
17
21
  currentLevel: 'L0',
18
22
  currentSort: 'recent',
19
23
  currentView: 'overview',
@@ -149,6 +153,39 @@ function setupEventListeners() {
149
153
  searchInput.addEventListener('input', debounce((e) => handleSearch(e.target.value), 300));
150
154
  }
151
155
 
156
+ // User prompt search
157
+ const userPromptSearch = document.getElementById('user-prompt-search');
158
+ if (userPromptSearch) {
159
+ userPromptSearch.addEventListener('input', debounce(async (e) => {
160
+ state.userPromptSearchQuery = e.target.value || '';
161
+ state.userPromptPage = 1;
162
+ await loadUserPromptsView();
163
+ }, 250));
164
+ }
165
+ const userPromptRefresh = document.getElementById('user-prompt-refresh');
166
+ if (userPromptRefresh) {
167
+ userPromptRefresh.addEventListener('click', async () => {
168
+ await loadUserPromptsView();
169
+ });
170
+ }
171
+ const userPromptPrev = document.getElementById('user-prompt-prev');
172
+ if (userPromptPrev) {
173
+ userPromptPrev.addEventListener('click', async () => {
174
+ if (state.userPromptPage <= 1) return;
175
+ state.userPromptPage -= 1;
176
+ await renderUserPromptList();
177
+ });
178
+ }
179
+ const userPromptNext = document.getElementById('user-prompt-next');
180
+ if (userPromptNext) {
181
+ userPromptNext.addEventListener('click', async () => {
182
+ const totalPages = Math.max(1, Math.ceil((state.userPromptItems?.length || 0) / state.userPromptPageSize));
183
+ if (state.userPromptPage >= totalPages) return;
184
+ state.userPromptPage += 1;
185
+ await renderUserPromptList();
186
+ });
187
+ }
188
+
152
189
  // Project selector
153
190
  const projectSelect = document.getElementById('project-select');
154
191
  if (projectSelect) {
@@ -1197,6 +1234,7 @@ function switchView(viewName) {
1197
1234
  switch (viewName) {
1198
1235
  case 'knowledge-graph': loadKnowledgeGraphView(); break;
1199
1236
  case 'memory-banks': loadMemoryBanksView(); break;
1237
+ case 'user-prompts': loadUserPromptsView(); break;
1200
1238
  case 'configuration': loadConfigurationView(); break;
1201
1239
  }
1202
1240
  }
@@ -1430,6 +1468,94 @@ async function loadMemoryBankLevel(level) {
1430
1468
  }
1431
1469
  }
1432
1470
 
1471
+ // --- User Prompts View ---
1472
+
1473
+ async function renderUserPromptList() {
1474
+ const listEl = document.getElementById('user-prompt-list');
1475
+ const pageEl = document.getElementById('user-prompt-page');
1476
+ const prevBtn = document.getElementById('user-prompt-prev');
1477
+ const nextBtn = document.getElementById('user-prompt-next');
1478
+ const metaEl = document.getElementById('user-prompt-meta');
1479
+ if (!listEl) return;
1480
+
1481
+ const items = state.userPromptItems || [];
1482
+ const pageSize = state.userPromptPageSize;
1483
+ const totalPages = Math.max(1, Math.ceil(items.length / pageSize));
1484
+ if (state.userPromptPage > totalPages) state.userPromptPage = totalPages;
1485
+
1486
+ const start = (state.userPromptPage - 1) * pageSize;
1487
+ const paged = items.slice(start, start + pageSize);
1488
+
1489
+ if (pageEl) pageEl.textContent = `${state.userPromptPage} / ${totalPages}`;
1490
+ if (prevBtn) prevBtn.disabled = state.userPromptPage <= 1;
1491
+ if (nextBtn) nextBtn.disabled = state.userPromptPage >= totalPages;
1492
+
1493
+ if (metaEl) {
1494
+ const sessionCount = new Set(items.map(i => i.sessionId)).size;
1495
+ metaEl.textContent = `${items.length} prompts · ${sessionCount} sessions${state.userPromptSearchQuery ? ` · query: "${state.userPromptSearchQuery}"` : ''}`;
1496
+ }
1497
+
1498
+ if (paged.length === 0) {
1499
+ listEl.innerHTML = '<div style="padding:20px; text-align:center; color:var(--text-muted);">No user prompts found.</div>';
1500
+ return;
1501
+ }
1502
+
1503
+ // Group current page by session
1504
+ const groups = new Map();
1505
+ for (const e of paged) {
1506
+ const key = e.sessionId || 'unknown';
1507
+ const arr = groups.get(key) || [];
1508
+ arr.push(e);
1509
+ groups.set(key, arr);
1510
+ }
1511
+
1512
+ const html = Array.from(groups.entries()).map(([sessionId, sessionItems]) => {
1513
+ const heading = `
1514
+ <div style="margin:10px 0 6px; font-size:12px; color:var(--text-muted); font-weight:600;">
1515
+ <i class="ri-chat-1-line"></i> Session ${escapeHtml((sessionId || '').slice(0, 16))}... · ${sessionItems.length} prompts
1516
+ </div>
1517
+ `;
1518
+
1519
+ const cards = sessionItems.map((e) => `
1520
+ <div class="event-item" style="cursor:pointer;" onclick="openDetailModal('${e.id}')">
1521
+ <div class="event-header">
1522
+ <span class="event-type-badge type-user-prompt">user_prompt</span>
1523
+ <span class="event-time">${new Date(e.timestamp).toLocaleString()}</span>
1524
+ </div>
1525
+ <div class="event-content" style="-webkit-line-clamp:4;">${escapeHtml(e.preview || '')}</div>
1526
+ </div>
1527
+ `).join('');
1528
+
1529
+ return heading + cards;
1530
+ }).join('');
1531
+
1532
+ listEl.innerHTML = html;
1533
+ }
1534
+
1535
+ async function loadUserPromptsView() {
1536
+ const listEl = document.getElementById('user-prompt-list');
1537
+ if (!listEl) return;
1538
+
1539
+ listEl.innerHTML = '<div style="padding:20px; text-align:center; color:var(--text-muted);">Loading user prompts...</div>';
1540
+
1541
+ try {
1542
+ const params = {
1543
+ type: 'user_prompt',
1544
+ sort: 'recent',
1545
+ limit: 500,
1546
+ q: state.userPromptSearchQuery || undefined
1547
+ };
1548
+ const res = await fetch(apiUrl(`${API_BASE}/events`, params));
1549
+ const data = await res.json();
1550
+ const items = data.events || [];
1551
+ state.userPromptItems = items;
1552
+
1553
+ await renderUserPromptList();
1554
+ } catch (error) {
1555
+ listEl.innerHTML = `<div style="padding:20px; text-align:center; color:var(--error);">Failed to load user prompts: ${escapeHtml(error.message)}</div>`;
1556
+ }
1557
+ }
1558
+
1433
1559
  // --- Configuration View ---
1434
1560
 
1435
1561
  async function loadConfigurationView() {
package/src/ui/index.html CHANGED
@@ -51,6 +51,10 @@
51
51
  <i class="ri-brain-line"></i>
52
52
  <span>Memory Banks</span>
53
53
  </li>
54
+ <li class="nav-item" data-nav="user-prompts">
55
+ <i class="ri-message-2-line"></i>
56
+ <span>User Prompts</span>
57
+ </li>
54
58
  <li class="nav-item" data-nav="configuration">
55
59
  <i class="ri-settings-4-line"></i>
56
60
  <span>Configuration</span>
@@ -363,6 +367,41 @@
363
367
  </div>
364
368
  </div>
365
369
 
370
+ <!-- ========== VIEW: User Prompts ========== -->
371
+ <div id="view-user-prompts" class="page-view">
372
+ <header class="top-header">
373
+ <div class="page-title">
374
+ <h1>User Prompts</h1>
375
+ <p>Search and browse recent user prompts</p>
376
+ </div>
377
+ </header>
378
+ <div class="card" style="margin-bottom:16px;">
379
+ <div style="display:flex; gap:10px; align-items:center;">
380
+ <div class="search-wrapper" style="width:420px; max-width:100%;">
381
+ <i class="ri-search-line"></i>
382
+ <input type="text" id="user-prompt-search" class="search-input" placeholder="Search user prompts...">
383
+ </div>
384
+ <button id="user-prompt-refresh" class="btn btn-secondary"><i class="ri-refresh-line"></i><span>Refresh</span></button>
385
+ </div>
386
+ </div>
387
+ <div class="card">
388
+ <div class="card-header" style="align-items:flex-end;">
389
+ <div>
390
+ <div class="card-title"><i class="ri-history-line"></i><span>Latest User Prompt History</span></div>
391
+ <div id="user-prompt-meta" style="font-size:12px; color:var(--text-muted); margin-top:6px;"></div>
392
+ </div>
393
+ <div style="display:flex; gap:8px; align-items:center;">
394
+ <button id="user-prompt-prev" class="sort-btn">Prev</button>
395
+ <span id="user-prompt-page" style="font-size:12px; color:var(--text-muted);">1 / 1</span>
396
+ <button id="user-prompt-next" class="sort-btn">Next</button>
397
+ </div>
398
+ </div>
399
+ <div id="user-prompt-list" class="event-list">
400
+ <div style="padding:20px; text-align:center; color:var(--text-muted);">Loading...</div>
401
+ </div>
402
+ </div>
403
+ </div>
404
+
366
405
  <!-- ========== VIEW: Configuration ========== -->
367
406
  <div id="view-configuration" class="page-view">
368
407
  <header class="top-header">
package/src/ui/style.css CHANGED
@@ -502,8 +502,11 @@ body {
502
502
  }
503
503
 
504
504
  .type-user { background: rgba(59, 130, 246, 0.2); color: #60A5FA; }
505
+ .type-user-prompt { background: rgba(59, 130, 246, 0.2); color: #60A5FA; }
505
506
  .type-agent { background: rgba(16, 185, 129, 0.2); color: #34D399; }
507
+ .type-agent-response { background: rgba(16, 185, 129, 0.2); color: #34D399; }
506
508
  .type-tool { background: rgba(245, 158, 11, 0.2); color: #FBBF24; }
509
+ .type-tool-observation { background: rgba(245, 158, 11, 0.2); color: #FBBF24; }
507
510
  .type-system { background: rgba(139, 92, 246, 0.2); color: #A78BFA; }
508
511
 
509
512
  .event-time {
@@ -597,6 +600,10 @@ body {
597
600
  color: var(--accent-primary);
598
601
  font-weight: 600;
599
602
  }
603
+ .sort-btn:disabled {
604
+ opacity: 0.35;
605
+ cursor: not-allowed;
606
+ }
600
607
 
601
608
  /* Access Badge */
602
609
  .access-badge {