superlocalmemory 3.0.16 → 3.0.18

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.
@@ -1,15 +1,29 @@
1
- // SuperLocalMemory V3 — Recall Lab
1
+ // SuperLocalMemory V3 — Recall Lab with Dual-Display + Pagination
2
2
  // Part of Qualixar | https://superlocalmemory.com
3
3
 
4
+ var recallLabState = {
5
+ allResults: [],
6
+ page: 0,
7
+ perPage: 10,
8
+ query: '',
9
+ synthesis: '',
10
+ };
11
+
4
12
  document.getElementById('recall-lab-search')?.addEventListener('click', function() {
5
13
  var query = document.getElementById('recall-lab-query').value.trim();
6
14
  if (!query) return;
7
15
 
16
+ recallLabState.query = query;
17
+ recallLabState.page = 0;
18
+ var perPageEl = document.getElementById('recall-lab-per-page');
19
+ recallLabState.perPage = perPageEl ? parseInt(perPageEl.value) : 10;
20
+ var fetchLimit = Math.max(recallLabState.perPage * 5, 50);
21
+
8
22
  var resultsDiv = document.getElementById('recall-lab-results');
9
23
  var metaDiv = document.getElementById('recall-lab-meta');
10
24
  resultsDiv.textContent = '';
11
25
  var spinner = document.createElement('div');
12
- spinner.className = 'text-center';
26
+ spinner.className = 'text-center py-4';
13
27
  var spinnerInner = document.createElement('div');
14
28
  spinnerInner.className = 'spinner-border text-primary';
15
29
  spinner.appendChild(spinnerInner);
@@ -18,7 +32,7 @@ document.getElementById('recall-lab-search')?.addEventListener('click', function
18
32
  fetch('/api/v3/recall/trace', {
19
33
  method: 'POST',
20
34
  headers: {'Content-Type': 'application/json'},
21
- body: JSON.stringify({query: query, limit: 10})
35
+ body: JSON.stringify({query: query, limit: fetchLimit, synthesize: true})
22
36
  }).then(function(r) { return r.json(); }).then(function(data) {
23
37
  if (data.error) {
24
38
  resultsDiv.textContent = '';
@@ -32,11 +46,14 @@ document.getElementById('recall-lab-search')?.addEventListener('click', function
32
46
  metaDiv.textContent = '';
33
47
  appendMetaField(metaDiv, 'Query type: ', data.query_type || 'unknown');
34
48
  metaDiv.appendChild(document.createTextNode(' | '));
35
- appendMetaField(metaDiv, 'Results: ', String(data.result_count));
49
+ appendMetaField(metaDiv, 'Results: ', String((data.results || []).length));
36
50
  metaDiv.appendChild(document.createTextNode(' | '));
37
51
  appendMetaField(metaDiv, 'Time: ', (data.retrieval_time_ms || 0).toFixed(0) + 'ms');
38
52
 
39
- if (!data.results || data.results.length === 0) {
53
+ recallLabState.allResults = data.results || [];
54
+ recallLabState.synthesis = data.synthesis || '';
55
+
56
+ if (recallLabState.allResults.length === 0) {
40
57
  resultsDiv.textContent = '';
41
58
  var infoDiv = document.createElement('div');
42
59
  infoDiv.className = 'alert alert-info';
@@ -45,69 +62,204 @@ document.getElementById('recall-lab-search')?.addEventListener('click', function
45
62
  return;
46
63
  }
47
64
 
65
+ renderRecallPage();
66
+ }).catch(function(e) {
48
67
  resultsDiv.textContent = '';
49
- var listGroup = document.createElement('div');
50
- listGroup.className = 'list-group';
51
-
52
- data.results.forEach(function(r, i) {
53
- var channels = r.channel_scores || {};
54
- var maxChannel = Math.max(channels.semantic || 0, channels.bm25 || 0, channels.entity_graph || 0, channels.temporal || 0) || 1;
55
-
56
- var item = document.createElement('div');
57
- item.className = 'list-group-item list-group-item-action';
58
- item.style.cursor = 'pointer';
59
- item.title = 'Click to view full memory';
60
- (function(result) {
61
- item.addEventListener('click', function() {
62
- if (typeof openMemoryDetail === 'function') {
63
- openMemoryDetail({
64
- id: result.fact_id,
65
- content: result.content,
66
- score: result.score,
67
- importance: Math.round((result.confidence || 0.5) * 10),
68
- category: 'recall',
69
- tags: Object.keys(result.channel_scores || {}).join(', '),
70
- created_at: null,
71
- trust_score: result.trust_score,
72
- channel_scores: result.channel_scores
73
- });
74
- }
75
- });
76
- })(r);
68
+ var errDiv = document.createElement('div');
69
+ errDiv.className = 'alert alert-danger';
70
+ errDiv.textContent = 'Error: ' + e.message;
71
+ resultsDiv.appendChild(errDiv);
72
+ });
73
+ });
77
74
 
78
- var header = document.createElement('h6');
79
- header.className = 'mb-1';
80
- header.textContent = (i + 1) + '. ' + (r.content || '').substring(0, 200);
81
- item.appendChild(header);
75
+ function renderRecallPage() {
76
+ var resultsDiv = document.getElementById('recall-lab-results');
77
+ resultsDiv.textContent = '';
82
78
 
83
- var meta = document.createElement('small');
84
- meta.className = 'text-muted';
85
- meta.textContent = 'Score: ' + r.score + ' | Trust: ' + r.trust_score + ' | Confidence: ' + r.confidence;
86
- item.appendChild(meta);
79
+ var results = recallLabState.allResults;
80
+ var start = recallLabState.page * recallLabState.perPage;
81
+ var end = Math.min(start + recallLabState.perPage, results.length);
82
+ var pageResults = results.slice(start, end);
83
+ var totalPages = Math.ceil(results.length / recallLabState.perPage);
87
84
 
85
+ // Synthesis banner (Mode B/C only)
86
+ if (recallLabState.synthesis && recallLabState.page === 0) {
87
+ var synBanner = document.createElement('div');
88
+ synBanner.className = 'alert alert-light border-start border-4 border-primary mb-3';
89
+ var synTitle = document.createElement('strong');
90
+ synTitle.textContent = 'AI Summary';
91
+ synBanner.appendChild(synTitle);
92
+ synBanner.appendChild(document.createElement('br'));
93
+ var synText = document.createElement('span');
94
+ synText.textContent = recallLabState.synthesis;
95
+ synBanner.appendChild(synText);
96
+ resultsDiv.appendChild(synBanner);
97
+ }
98
+
99
+ var listGroup = document.createElement('div');
100
+ listGroup.className = 'list-group';
101
+
102
+ pageResults.forEach(function(r, i) {
103
+ var globalIndex = start + i;
104
+ var channels = r.channel_scores || {};
105
+ var maxChannel = Math.max(channels.semantic || 0, channels.bm25 || 0, channels.entity_graph || 0, channels.temporal || 0) || 1;
106
+ var hasSource = r.source_content && r.source_content.length > 0;
107
+ var displayText = hasSource ? r.source_content : r.content;
108
+
109
+ var item = document.createElement('div');
110
+ item.className = 'list-group-item';
111
+
112
+ // Score badge row
113
+ var scoreRow = document.createElement('div');
114
+ scoreRow.className = 'd-flex justify-content-between align-items-center mb-1';
115
+ var numLabel = document.createElement('strong');
116
+ numLabel.textContent = '#' + (globalIndex + 1);
117
+ scoreRow.appendChild(numLabel);
118
+ var scoreBadges = document.createElement('div');
119
+ scoreBadges.innerHTML = '<span class="badge bg-primary me-1">Score: ' + r.score + '</span>' +
120
+ '<span class="badge bg-secondary me-1">Trust: ' + r.trust_score + '</span>' +
121
+ '<span class="badge bg-outline-info" style="border:1px solid #0dcaf0;color:#0dcaf0;">Conf: ' + r.confidence + '</span>';
122
+ scoreRow.appendChild(scoreBadges);
123
+ item.appendChild(scoreRow);
124
+
125
+ // Original memory text (primary display)
126
+ var contentDiv = document.createElement('div');
127
+ contentDiv.className = 'mb-2';
128
+ contentDiv.style.cssText = 'white-space:pre-wrap; line-height:1.5;';
129
+ var truncated = displayText.length > 500 ? displayText.substring(0, 500) + '...' : displayText;
130
+ contentDiv.textContent = truncated;
131
+ item.appendChild(contentDiv);
132
+
133
+ // Expandable atomic fact section (only if source differs from content)
134
+ if (hasSource && r.content !== r.source_content) {
135
+ var expandBtn = document.createElement('button');
136
+ expandBtn.className = 'btn btn-sm btn-outline-secondary mb-2';
137
+ expandBtn.textContent = 'Show matched fact + channels';
138
+ var factSection = document.createElement('div');
139
+ factSection.style.display = 'none';
140
+ factSection.className = 'border-top pt-2 mt-1';
141
+
142
+ var factLabel = document.createElement('small');
143
+ factLabel.className = 'text-muted d-block mb-1';
144
+ factLabel.textContent = 'Matched atomic fact:';
145
+ factSection.appendChild(factLabel);
146
+
147
+ var factContent = document.createElement('div');
148
+ factContent.className = 'small bg-light p-2 rounded mb-2';
149
+ factContent.textContent = r.content;
150
+ factSection.appendChild(factContent);
151
+
152
+ // Channel bars
153
+ factSection.appendChild(buildChannelBar('Semantic', channels.semantic || 0, maxChannel, 'primary'));
154
+ factSection.appendChild(buildChannelBar('BM25', channels.bm25 || 0, maxChannel, 'success'));
155
+ factSection.appendChild(buildChannelBar('Entity', channels.entity_graph || 0, maxChannel, 'info'));
156
+ factSection.appendChild(buildChannelBar('Temporal', channels.temporal || 0, maxChannel, 'warning'));
157
+
158
+ expandBtn.addEventListener('click', function() {
159
+ var visible = factSection.style.display !== 'none';
160
+ factSection.style.display = visible ? 'none' : 'block';
161
+ expandBtn.textContent = visible ? 'Show matched fact + channels' : 'Hide matched fact';
162
+ });
163
+
164
+ item.appendChild(expandBtn);
165
+ item.appendChild(factSection);
166
+ } else {
167
+ // No source_content — show channel bars inline
88
168
  var barsDiv = document.createElement('div');
89
- barsDiv.className = 'mt-2';
169
+ barsDiv.className = 'mt-1';
90
170
  barsDiv.appendChild(buildChannelBar('Semantic', channels.semantic || 0, maxChannel, 'primary'));
91
171
  barsDiv.appendChild(buildChannelBar('BM25', channels.bm25 || 0, maxChannel, 'success'));
92
172
  barsDiv.appendChild(buildChannelBar('Entity', channels.entity_graph || 0, maxChannel, 'info'));
93
173
  barsDiv.appendChild(buildChannelBar('Temporal', channels.temporal || 0, maxChannel, 'warning'));
94
174
  item.appendChild(barsDiv);
175
+ }
95
176
 
96
- listGroup.appendChild(item);
97
- });
98
- resultsDiv.appendChild(listGroup);
99
- }).catch(function(e) {
100
- resultsDiv.textContent = '';
101
- var errDiv = document.createElement('div');
102
- errDiv.className = 'alert alert-danger';
103
- errDiv.textContent = 'Error: ' + e.message;
104
- resultsDiv.appendChild(errDiv);
177
+ // Click for detail modal
178
+ item.style.cursor = 'pointer';
179
+ (function(result) {
180
+ contentDiv.addEventListener('click', function() {
181
+ if (typeof openMemoryDetail === 'function') {
182
+ openMemoryDetail({
183
+ id: result.fact_id,
184
+ memory_id: result.memory_id,
185
+ content: result.source_content || result.content,
186
+ score: result.score,
187
+ importance: Math.round((result.confidence || 0.5) * 10),
188
+ category: 'recall',
189
+ created_at: null,
190
+ trust_score: result.trust_score,
191
+ channel_scores: result.channel_scores
192
+ }, 'recall'); // source='recall': show View Original, hide Expand Neighbors
193
+ }
194
+ });
195
+ })(r);
196
+
197
+ listGroup.appendChild(item);
105
198
  });
106
- });
199
+ resultsDiv.appendChild(listGroup);
200
+
201
+ // Pagination
202
+ if (totalPages > 1) {
203
+ var nav = document.createElement('nav');
204
+ nav.className = 'mt-3';
205
+ var ul = document.createElement('ul');
206
+ ul.className = 'pagination justify-content-center';
207
+
208
+ var prevLi = document.createElement('li');
209
+ prevLi.className = 'page-item' + (recallLabState.page === 0 ? ' disabled' : '');
210
+ var prevA = document.createElement('a');
211
+ prevA.className = 'page-link';
212
+ prevA.href = '#';
213
+ prevA.textContent = 'Previous';
214
+ prevA.addEventListener('click', function(e) {
215
+ e.preventDefault();
216
+ if (recallLabState.page > 0) { recallLabState.page--; renderRecallPage(); }
217
+ });
218
+ prevLi.appendChild(prevA);
219
+ ul.appendChild(prevLi);
220
+
221
+ for (var p = 0; p < totalPages; p++) {
222
+ var li = document.createElement('li');
223
+ li.className = 'page-item' + (p === recallLabState.page ? ' active' : '');
224
+ var a = document.createElement('a');
225
+ a.className = 'page-link';
226
+ a.href = '#';
227
+ a.textContent = String(p + 1);
228
+ (function(pageNum) {
229
+ a.addEventListener('click', function(e) {
230
+ e.preventDefault();
231
+ recallLabState.page = pageNum;
232
+ renderRecallPage();
233
+ });
234
+ })(p);
235
+ li.appendChild(a);
236
+ ul.appendChild(li);
237
+ }
238
+
239
+ var nextLi = document.createElement('li');
240
+ nextLi.className = 'page-item' + (recallLabState.page >= totalPages - 1 ? ' disabled' : '');
241
+ var nextA = document.createElement('a');
242
+ nextA.className = 'page-link';
243
+ nextA.href = '#';
244
+ nextA.textContent = 'Next';
245
+ nextA.addEventListener('click', function(e) {
246
+ e.preventDefault();
247
+ if (recallLabState.page < totalPages - 1) { recallLabState.page++; renderRecallPage(); }
248
+ });
249
+ nextLi.appendChild(nextA);
250
+ ul.appendChild(nextLi);
251
+ nav.appendChild(ul);
252
+ resultsDiv.appendChild(nav);
253
+
254
+ var info = document.createElement('div');
255
+ info.className = 'text-center text-muted small';
256
+ info.textContent = 'Showing ' + (start + 1) + '-' + end + ' of ' + results.length + ' results';
257
+ resultsDiv.appendChild(info);
258
+ }
259
+ }
107
260
 
108
261
  function appendMetaField(parent, label, value) {
109
- var text = document.createTextNode(label);
110
- parent.appendChild(text);
262
+ parent.appendChild(document.createTextNode(label));
111
263
  var strong = document.createElement('strong');
112
264
  strong.textContent = value;
113
265
  parent.appendChild(strong);
@@ -115,32 +267,26 @@ function appendMetaField(parent, label, value) {
115
267
 
116
268
  function buildChannelBar(name, score, max, color) {
117
269
  var pct = max > 0 ? Math.round((score / max) * 100) : 0;
118
-
119
270
  var row = document.createElement('div');
120
271
  row.className = 'd-flex align-items-center mb-1';
121
-
122
272
  var label = document.createElement('span');
123
273
  label.className = 'me-2';
124
274
  label.style.width = '70px';
125
275
  label.style.fontSize = '0.75rem';
126
276
  label.textContent = name;
127
277
  row.appendChild(label);
128
-
129
278
  var progressWrap = document.createElement('div');
130
279
  progressWrap.className = 'progress flex-grow-1';
131
280
  progressWrap.style.height = '14px';
132
-
133
281
  var bar = document.createElement('div');
134
282
  bar.className = 'progress-bar bg-' + color;
135
283
  bar.style.width = pct + '%';
136
284
  bar.textContent = score.toFixed(3);
137
285
  progressWrap.appendChild(bar);
138
-
139
286
  row.appendChild(progressWrap);
140
287
  return row;
141
288
  }
142
289
 
143
- // Enter key support
144
290
  document.getElementById('recall-lab-query')?.addEventListener('keydown', function(e) {
145
291
  if (e.key === 'Enter') document.getElementById('recall-lab-search')?.click();
146
292
  });