memory dashboard polishing
frdel committed
Sep 29, 2025 at 21:39 UTC
bd09a89beff531e63521452a63bd998d13424b94
2 files changed
+150
-120
python/api/memory_dashboard.py
+2
-2
@@ -229,8 +229,8 @@ class MemoryDashboard(ApiHandler):
229
"id": metadata.get("id", "unknown"),
230
"area": metadata.get("area", "unknown"),
231
"timestamp": metadata.get("timestamp", "unknown"),
232
- "content_preview": m.page_content[:200]
233
- + ("..." if len(m.page_content) > 200 else ""),
232
+ # "content_preview": m.page_content[:200]
233
+ # + ("..." if len(m.page_content) > 200 else ""),
234
"content_full": m.page_content,
235
"knowledge_source": metadata.get("knowledge_source", False),
236
"source_file": metadata.get("source_file", ""),
webui/components/settings/memory/memory-dashboard.html
+148
-118
@@ -10,60 +10,61 @@
10
<body>
11
<div x-data>
12
<template x-if="$store.memoryDashboardStore">
13
- <div x-create="$store.memoryDashboardStore.onOpen()"
14
- x-destroy="$store.memoryDashboardStore.cleanup()" class="memory-dashboard">
13
+ <div x-create="$store.memoryDashboardStore.onOpen()" x-destroy="$store.memoryDashboardStore.cleanup()"
14
+ class="memory-dashboard">
15
16
<!-- Search and Filters Section -->
17
<div class="memory-filters-header">
18
- <div class="filter-group-inline filter-memory-dir">
19
- <label for="memory-subdir-select">Memory Directory</label>
20
- <select id="memory-subdir-select" x-model="$store.memoryDashboardStore.selectedMemorySubdir"
21
- @change="$store.memoryDashboardStore.onMemorySubdirChange()"
22
- :disabled="$store.memoryDashboardStore.loadingSubdirs">
23
- <template x-for="subdir in $store.memoryDashboardStore.memorySubdirs" :key="subdir">
24
- <option :value="subdir" x-text="subdir"
25
- :selected="subdir === $store.memoryDashboardStore.selectedMemorySubdir">
26
- </option>
27
- </template>
28
- </select>
29
- </div>
18
+ <div class="filter-group-inline filter-memory-dir">
19
+ <label for="memory-subdir-select">Memory Directory</label>
20
+ <select id="memory-subdir-select" x-model="$store.memoryDashboardStore.selectedMemorySubdir"
21
+ @change="$store.memoryDashboardStore.onMemorySubdirChange()"
22
+ :disabled="$store.memoryDashboardStore.loadingSubdirs">
23
+ <template x-for="subdir in $store.memoryDashboardStore.memorySubdirs" :key="subdir">
24
+ <option :value="subdir" x-text="subdir"
25
+ :selected="subdir === $store.memoryDashboardStore.selectedMemorySubdir">
26
+ </option>
27
+ </template>
28
+ </select>
29
+ </div>
30
31
- <div class="filter-group-inline filter-area">
32
- <label for="area-filter">Area</label>
33
- <select id="area-filter" x-model="$store.memoryDashboardStore.areaFilter">
34
- <option value="">All Areas</option>
35
- <option value="main">Main</option>
36
- <option value="fragments">Fragments</option>
37
- <option value="solutions">Solutions</option>
38
- <option value="instruments">Instruments</option>
39
- </select>
40
- </div>
31
+ <div class="filter-group-inline filter-area">
32
+ <label for="area-filter">Area</label>
33
+ <select id="area-filter" x-model="$store.memoryDashboardStore.areaFilter">
34
+ <option value="">All Areas</option>
35
+ <option value="main">Main</option>
36
+ <option value="fragments">Fragments</option>
37
+ <option value="solutions">Solutions</option>
38
+ <option value="instruments">Instruments</option>
39
+ </select>
40
+ </div>
41
42
- <div class="filter-group-inline filter-limit">
43
- <label for="limit-input">Limit:</label>
44
- <input type="number" id="limit-input" x-model.number="$store.memoryDashboardStore.limit"
45
- min="10" max="1000" step="10" />
46
- </div>
42
+ <div class="filter-group-inline filter-limit">
43
+ <label for="limit-input">Limit:</label>
44
+ <input type="number" id="limit-input" x-model.number="$store.memoryDashboardStore.limit"
45
+ min="10" max="1000" step="10" />
46
+ </div>
47
48
- <div class="filter-threshold">
49
- <label for="threshold-input">Threshold: <strong x-text="$store.memoryDashboardStore.threshold.toFixed(2)"></strong></label>
50
- <input type="range" id="threshold-input" x-model.number="$store.memoryDashboardStore.threshold"
51
- min="0" max="1" step="0.01" />
52
- </div>
48
+ <div class="filter-threshold">
49
+ <label for="threshold-input">Threshold: <strong
50
+ x-text="$store.memoryDashboardStore.threshold.toFixed(2)"></strong></label>
51
+ <input type="range" id="threshold-input" x-model.number="$store.memoryDashboardStore.threshold"
52
+ min="0" max="1" step="0.01" />
53
+ </div>
54
+
55
+ <input type="text" id="search-input" x-model="$store.memoryDashboardStore.searchQuery"
56
+ placeholder="Search memory content..."
57
+ @keyup.enter="$store.memoryDashboardStore.searchMemories()" />
58
54
- <input type="text" id="search-input" x-model="$store.memoryDashboardStore.searchQuery"
55
- placeholder="Search memory content..."
56
- @keyup.enter="$store.memoryDashboardStore.searchMemories()" />
57
-
58
- <button class="btn btn-ok btn-search" @click="$store.memoryDashboardStore.searchMemories()"
59
- :disabled="$store.memoryDashboardStore.loading || $store.memoryDashboardStore.loadingSubdirs">
60
- <span x-show="!$store.memoryDashboardStore.initializingMemory">Search</span>
61
- <span x-show="$store.memoryDashboardStore.initializingMemory">Init...</span>
62
- </button>
63
-
64
- <button class="btn btn-cancel btn-clear" @click="$store.memoryDashboardStore.clearSearch()">
65
- Clear
66
- </button>
59
+ <button class="btn btn-ok btn-search" @click="$store.memoryDashboardStore.searchMemories()"
60
+ :disabled="$store.memoryDashboardStore.loading || $store.memoryDashboardStore.loadingSubdirs">
61
+ <span x-show="!$store.memoryDashboardStore.initializingMemory">Search</span>
62
+ <span x-show="$store.memoryDashboardStore.initializingMemory">Init...</span>
63
+ </button>
64
+
65
+ <button class="btn btn-cancel btn-clear" @click="$store.memoryDashboardStore.clearSearch()">
66
+ Clear
67
+ </button>
68
</div>
69
70
<!-- Loading state -->
@@ -106,20 +107,18 @@
107
<!-- Pagination -->
108
<div class="status-pagination" x-show="$store.memoryDashboardStore.totalPages > 1">
109
<button class="btn btn-icon" @click="$store.memoryDashboardStore.prevPage()"
109
- :disabled="$store.memoryDashboardStore.currentPage === 1"
110
- title="Previous Page">
110
+ :disabled="$store.memoryDashboardStore.currentPage === 1" title="Previous Page">
111
<span class="material-symbols-outlined">chevron_left</span>
112
</button>
113
<div class="page-input-group">
114
<label>Page</label>
115
- <input type="number"
116
- class="page-input"
117
- x-model.number="$store.memoryDashboardStore.currentPage"
118
- @change="$store.memoryDashboardStore.goToPage($store.memoryDashboardStore.currentPage)"
119
- @keyup.enter="$store.memoryDashboardStore.goToPage($store.memoryDashboardStore.currentPage)"
120
- :min="1"
121
- :max="$store.memoryDashboardStore.totalPages" />
122
- <span class="page-total">of <strong x-text="$store.memoryDashboardStore.totalPages"></strong></span>
115
+ <input type="number" class="page-input"
116
+ x-model.number="$store.memoryDashboardStore.currentPage"
117
+ @change="$store.memoryDashboardStore.goToPage($store.memoryDashboardStore.currentPage)"
118
+ @keyup.enter="$store.memoryDashboardStore.goToPage($store.memoryDashboardStore.currentPage)"
119
+ :min="1" :max="$store.memoryDashboardStore.totalPages" />
120
+ <span class="page-total">of <strong
121
+ x-text="$store.memoryDashboardStore.totalPages"></strong></span>
122
</div>
123
<button class="btn btn-icon" @click="$store.memoryDashboardStore.nextPage()"
124
:disabled="$store.memoryDashboardStore.currentPage === $store.memoryDashboardStore.totalPages"
@@ -142,13 +141,15 @@
141
Copy
142
</button>
143
145
- <button class="btn btn-mass export" @click="$store.memoryDashboardStore.bulkExportMemories()"
144
+ <button class="btn btn-mass export"
145
+ @click="$store.memoryDashboardStore.bulkExportMemories()"
146
title="Export Selected Memories">
147
<span class="material-symbols-outlined">download</span>
148
Export
149
</button>
150
151
- <button class="btn btn-mass delete" @click="$store.memoryDashboardStore.bulkDeleteMemories()"
151
+ <button class="btn btn-mass delete"
152
+ @click="$store.memoryDashboardStore.bulkDeleteMemories()"
153
title="Delete Selected Memories">
154
<span class="material-symbols-outlined">delete</span>
155
Delete
@@ -196,11 +197,6 @@
197
:style="`background-color: ${$store.memoryDashboardStore.getAreaColor(memory.area)}`"
198
x-text="(memory.area || 'UNKNOWN').toUpperCase()"></span>
199
</div>
199
- <div class="metadata-row metadata-timestamp">
200
- <span
201
- x-text="$store.memoryDashboardStore.formatTimestamp(memory.timestamp, true)"
202
- :title="$store.memoryDashboardStore.formatTimestamp(memory.timestamp, false)"></span>
203
- </div>
200
<div class="metadata-row">
201
<template x-if="memory.knowledge_source">
202
<span class="source-type knowledge">Knowledge</span>
@@ -209,12 +205,20 @@
205
<span class="source-type conversation">Conversation</span>
206
</template>
207
</div>
208
+ <div class="metadata-row metadata-timestamp">
209
+ <span
210
+ x-text="$store.memoryDashboardStore.formatTimestamp(memory.timestamp, true)"
211
+ :title="$store.memoryDashboardStore.formatTimestamp(memory.timestamp, false)"></span>
212
+ </div>
213
+
214
</div>
215
</td>
216
217
<!-- Content preview -->
218
<td class="preview-cell">
217
- <div class="content-preview" x-text="memory.content_preview"></div>
219
+ <div class="content-preview"
220
+ x-text="memory.content_full.substring(0, 200) + (memory.content_full.length > 200 ? '...' : '')">
221
+ </div>
222
<div class="tags" x-show="memory.tags && memory.tags.length > 0">
223
<template x-for="tag in memory.tags" :key="tag">
224
<span class="tag" x-text="tag"></span>
@@ -260,13 +264,15 @@
264
Copy
265
</button>
266
263
- <button class="btn btn-mass export" @click="$store.memoryDashboardStore.bulkExportMemories()"
267
+ <button class="btn btn-mass export"
268
+ @click="$store.memoryDashboardStore.bulkExportMemories()"
269
title="Export Selected Memories">
270
<span class="material-symbols-outlined">download</span>
271
Export
272
</button>
273
269
- <button class="btn btn-mass delete" @click="$store.memoryDashboardStore.bulkDeleteMemories()"
274
+ <button class="btn btn-mass delete"
275
+ @click="$store.memoryDashboardStore.bulkDeleteMemories()"
276
title="Delete Selected Memories">
277
<span class="material-symbols-outlined">delete</span>
278
Delete
@@ -305,20 +311,18 @@
311
<!-- Pagination -->
312
<div class="status-pagination" x-show="$store.memoryDashboardStore.totalPages > 1">
313
<button class="btn btn-icon" @click="$store.memoryDashboardStore.prevPage()"
308
- :disabled="$store.memoryDashboardStore.currentPage === 1"
309
- title="Previous Page">
314
+ :disabled="$store.memoryDashboardStore.currentPage === 1" title="Previous Page">
315
<span class="material-symbols-outlined">chevron_left</span>
316
</button>
317
<div class="page-input-group">
318
<label>Page</label>
314
- <input type="number"
315
- class="page-input"
316
- x-model.number="$store.memoryDashboardStore.currentPage"
317
- @change="$store.memoryDashboardStore.goToPage($store.memoryDashboardStore.currentPage)"
318
- @keyup.enter="$store.memoryDashboardStore.goToPage($store.memoryDashboardStore.currentPage)"
319
- :min="1"
320
- :max="$store.memoryDashboardStore.totalPages" />
321
- <span class="page-total">of <strong x-text="$store.memoryDashboardStore.totalPages"></strong></span>
319
+ <input type="number" class="page-input"
320
+ x-model.number="$store.memoryDashboardStore.currentPage"
321
+ @change="$store.memoryDashboardStore.goToPage($store.memoryDashboardStore.currentPage)"
322
+ @keyup.enter="$store.memoryDashboardStore.goToPage($store.memoryDashboardStore.currentPage)"
323
+ :min="1" :max="$store.memoryDashboardStore.totalPages" />
324
+ <span class="page-total">of <strong
325
+ x-text="$store.memoryDashboardStore.totalPages"></strong></span>
326
</div>
327
<button class="btn btn-icon" @click="$store.memoryDashboardStore.nextPage()"
328
:disabled="$store.memoryDashboardStore.currentPage === $store.memoryDashboardStore.totalPages"
@@ -355,12 +359,12 @@
359
display: flex;
360
flex-direction: column;
361
}
358
-
362
+
363
.modal-inner.modal-with-footer .modal-scroll {
364
flex: 1;
365
min-height: 0;
366
}
363
-
367
+
368
.modal-inner.modal-with-footer .modal-footer {
369
flex-shrink: 0;
370
}
@@ -379,15 +383,39 @@
383
}
384
385
/* Grid positioning for desktop - 2 rows */
382
- .filter-memory-dir { grid-column: 1; grid-row: 1; }
383
- .filter-area { grid-column: 2; grid-row: 1; }
384
- .filter-limit { grid-column: 3; grid-row: 1; }
385
- .filter-threshold { grid-column: 4; grid-row: 2; }
386
-
387
- #search-input { grid-column: 1 / 3; grid-row: 2; }
388
- .btn-search { grid-column: 3; grid-row: 2; justify-content: center; }
389
- .btn-clear {
390
- grid-column: 4;
386
+ .filter-memory-dir {
387
+ grid-column: 1;
388
+ grid-row: 1;
389
+ }
390
+
391
+ .filter-area {
392
+ grid-column: 2;
393
+ grid-row: 1;
394
+ }
395
+
396
+ .filter-limit {
397
+ grid-column: 3;
398
+ grid-row: 1;
399
+ }
400
+
401
+ .filter-threshold {
402
+ grid-column: 4;
403
+ grid-row: 2;
404
+ }
405
+
406
+ #search-input {
407
+ grid-column: 1 / 3;
408
+ grid-row: 2;
409
+ }
410
+
411
+ .btn-search {
412
+ grid-column: 3;
413
+ grid-row: 2;
414
+ justify-content: center;
415
+ }
416
+
417
+ .btn-clear {
418
+ grid-column: 4;
419
grid-row: 1;
420
justify-self: end;
421
}
@@ -424,7 +452,7 @@
452
.filter-group-inline.filter-limit {
453
flex: 0 0 auto;
454
}
427
-
455
+
456
.filter-limit input {
457
flex: 0 0 150%;
458
}
@@ -641,15 +669,13 @@
669
}
670
671
.col-metadata {
644
- width: 25%;
672
+ width: 10em;
673
}
674
647
- .col-preview {
648
- width: 60%;
649
- }
675
+ .col-preview {}
676
677
.col-actions {
652
- width: 10%;
678
+ width: 4em;
679
}
680
681
.memory-table th,
@@ -686,13 +712,13 @@
712
}
713
714
.area-badge {
689
- color: white;
690
- padding: 0.35rem 0.75rem;
691
- border-radius: 16px;
692
- font-size: 0.75rem;
693
- font-weight: bold;
694
- text-transform: uppercase;
695
- }
715
+ color: white;
716
+ padding: 0.35rem 0.75rem;
717
+ border-radius: 16px;
718
+ font-size: 0.75rem;
719
+ font-weight: bold;
720
+ text-transform: uppercase;
721
+ }
722
723
.select-cell,
724
.col-select {
@@ -720,7 +746,7 @@
746
/* border: 1px solid var(--color-primary); */
747
/* border-radius: 8px; */
748
/* margin: 1rem 0; */
723
- margin:0;
749
+ margin: 0;
750
animation: slideDown 0.3s ease;
751
}
752
@@ -771,6 +797,7 @@
797
opacity: 0;
798
transform: translateY(-10px);
799
}
800
+
801
to {
802
opacity: 1;
803
transform: translateY(0);
@@ -795,8 +822,8 @@
822
border-bottom: none;
823
}
824
798
- .memory-table tbody tr.memory-row {
799
- background: var(--color-panel);
825
+ .memory-table tbody tr.memory-row>td {
826
+ height: 5em;
827
}
828
829
.source-type {
@@ -810,12 +837,12 @@
837
flex-shrink: 0;
838
}
839
813
- .content-preview {
814
- line-height: 1.4;
840
+ .content-preview,
841
+ .metadata-info {
842
+ height: 5.5em;
843
+ overflow: hidden;
844
word-wrap: break-word;
845
overflow-wrap: break-word;
817
- overflow: hidden;
818
- text-overflow: ellipsis;
846
}
847
848
.tags {
@@ -908,7 +935,7 @@
935
flex-wrap: wrap;
936
gap: 0.75rem;
937
}
911
-
938
+
939
/* Reset grid positioning */
940
.filter-memory-dir,
941
.filter-area,
@@ -948,19 +975,22 @@
975
}
976
977
/* Mobile order and layout - force items to specific widths */
951
- .filter-memory-dir {
978
+ .filter-memory-dir {
979
order: 1;
980
flex: 1 1 100%;
981
}
955
- .filter-area {
982
+
983
+ .filter-area {
984
order: 2;
985
flex: 1 1 100%;
986
}
959
- .filter-limit {
987
+
988
+ .filter-limit {
989
order: 3;
990
flex: 1 1 100%;
991
}
963
- .btn-clear {
992
+
993
+ .btn-clear {
994
order: 6;
995
flex: 40%;
996
min-width: 80px;
@@ -968,22 +998,23 @@
998
height: 42px;
999
justify-content: center;
1000
}
971
- #search-input {
1001
+
1002
+ #search-input {
1003
order: 5;
1004
flex: auto;
1005
width: 100%;
1006
height: 42px;
1007
}
977
-
1008
+
1009
/* Search button and threshold on same row */
979
- .btn-search {
1010
+ .btn-search {
1011
order: 7;
1012
flex: 40%;
1013
height: 42px;
1014
padding: 0 1rem;
1015
min-width: 80px;
1016
}
986
-
1017
+
1018
.filter-threshold {
1019
order: 4;
1020
flex: 40%;
@@ -1054,7 +1085,6 @@
1085
width: 10%;
1086
}
1087
}
1057
-
1088
</style>
1089
1090
</template>