memory dashboard design
Alessandro committed
Sep 25, 2025 at 21:17 UTC
a6ce9c440489ddbef0052aa267b216af20636114
2 files changed
+479
-355
webui/components/settings/memory/memory-dashboard.html
+416
-294
@@ -13,11 +13,12 @@
13
<div x-create="$store.memoryDashboardStore.onOpen()"
14
x-destroy="$store.memoryDashboardStore.cleanup()" class="memory-dashboard">
15
16
- <!-- Search and Filters -->
17
- <div class="filters-section">
18
- <div class="filter-row">
19
- <div class="filter-group">
20
- <label for="memory-subdir-select">Memory Directory:</label>
16
+ <!-- Unified Filter Section -->
17
+ <div class="memory-filters-header">
18
+ <div class="filter-container-unified">
19
+ <!-- All elements in single container for flexible layout -->
20
+ <div class="filter-group-inline filter-memory-dir">
21
+ <label for="memory-subdir-select">Memory Directory</label>
22
<select id="memory-subdir-select" x-model="$store.memoryDashboardStore.selectedMemorySubdir"
23
@change="$store.memoryDashboardStore.onMemorySubdirChange()"
24
:disabled="$store.memoryDashboardStore.loadingSubdirs">
@@ -27,12 +28,10 @@
28
</option>
29
</template>
30
</select>
30
- <span x-show="$store.memoryDashboardStore.loadingSubdirs"
31
- class="loading-text">Loading...</span>
31
</div>
32
34
- <div class="filter-group">
35
- <label for="area-filter">Area:</label>
33
+ <div class="filter-group-inline filter-area">
34
+ <label for="area-filter">Area</label>
35
<select id="area-filter" x-model="$store.memoryDashboardStore.areaFilter">
36
<option value="">All Areas</option>
37
<option value="main">Main</option>
@@ -42,36 +41,31 @@
41
</select>
42
</div>
43
45
- <div class="filter-group">
46
- <label for="search-input">Search:</label>
47
- <input type="text" id="search-input" x-model="$store.memoryDashboardStore.searchQuery"
48
- placeholder="Search memory content..."
49
- @keyup.enter="$store.memoryDashboardStore.searchMemories()" />
50
- </div>
51
-
52
- <div class="filter-group">
44
+ <div class="filter-group-inline filter-limit">
45
<label for="limit-input">Limit:</label>
46
<input type="number" id="limit-input" x-model.number="$store.memoryDashboardStore.limit"
55
- min="10" max="1000" step="10" style="min-width: 100px;" />
47
+ min="10" max="1000" step="10" />
48
</div>
49
58
- <div class="filter-group">
59
- <label for="threshold-input">Threshold: <span x-text="$store.memoryDashboardStore.threshold.toFixed(2)"></span></label>
50
+ <div class="filter-threshold">
51
+ <label for="threshold-input">Threshold: <strong x-text="$store.memoryDashboardStore.threshold.toFixed(2)"></strong></label>
52
<input type="range" id="threshold-input" x-model.number="$store.memoryDashboardStore.threshold"
61
- min="0" max="1" step="0.01" style="min-width: 10em;" />
53
+ min="0" max="1" step="0.01" />
54
</div>
55
64
- <div class="filter-actions">
65
- <button class="btn primary slim" @click="$store.memoryDashboardStore.searchMemories()"
66
- :disabled="$store.memoryDashboardStore.loading || $store.memoryDashboardStore.loadingSubdirs">
67
- <span x-show="!$store.memoryDashboardStore.initializingMemory">Search</span>
68
- <span x-show="$store.memoryDashboardStore.initializingMemory">Initializing
69
- Memory...</span>
70
- </button>
71
- <button class="btn slim" @click="$store.memoryDashboardStore.clearSearch()">
72
- Clear
73
- </button>
74
- </div>
56
+ <input type="text" id="search-input" x-model="$store.memoryDashboardStore.searchQuery"
57
+ placeholder="Search memory content..."
58
+ @keyup.enter="$store.memoryDashboardStore.searchMemories()" />
59
+
60
+ <button class="btn primary btn-search" @click="$store.memoryDashboardStore.searchMemories()"
61
+ :disabled="$store.memoryDashboardStore.loading || $store.memoryDashboardStore.loadingSubdirs">
62
+ <span x-show="!$store.memoryDashboardStore.initializingMemory">Search</span>
63
+ <span x-show="$store.memoryDashboardStore.initializingMemory">Init...</span>
64
+ </button>
65
+
66
+ <button class="btn btn-clear" @click="$store.memoryDashboardStore.clearSearch()">
67
+ Clear
68
+ </button>
69
</div>
70
</div>
71
@@ -91,56 +85,49 @@
85
<div x-show="!$store.memoryDashboardStore.loading && !$store.memoryDashboardStore.error"
86
class="memory-table-container">
87
94
- <!-- Combined stats and pagination header -->
95
- <div class="stats-pagination-header" x-show="!$store.memoryDashboardStore.loading">
96
- <!-- Statistics -->
97
- <div class="memory-stats-compact">
98
- <div class="stat-item">
99
- <span class="stat-label">DB Total:</span>
100
- <span class="stat-value" x-text="$store.memoryDashboardStore.totalDbCount"></span>
101
- </div>
102
- <div class="stat-item">
103
- <span class="stat-label">Filtered:</span>
104
- <span class="stat-value" x-text="$store.memoryDashboardStore.totalCount"></span>
105
- </div>
106
- <div class="stat-item">
107
- <span class="stat-label">Knowledge:</span>
108
- <span class="stat-value" x-text="$store.memoryDashboardStore.knowledgeCount"></span>
109
- </div>
110
- <div class="stat-item">
111
- <span class="stat-label">Conversation:</span>
112
- <span class="stat-value" x-text="$store.memoryDashboardStore.conversationCount"></span>
113
- </div>
88
+ <!-- Compact Status Bar -->
89
+ <div class="memory-status-bar" x-show="!$store.memoryDashboardStore.loading">
90
+ <div class="status-info">
91
+ <span class="status-item">
92
+ <span class="material-symbols-outlined">database</span>
93
+ Total: <strong x-text="$store.memoryDashboardStore.totalDbCount"></strong>
94
+ </span>
95
+ <span class="status-separator">•</span>
96
+ <span class="status-item">
97
+ Filtered: <strong x-text="$store.memoryDashboardStore.totalCount"></strong>
98
+ </span>
99
+ <span class="status-separator">•</span>
100
+ <span class="status-item">
101
+ Knowledge: <strong x-text="$store.memoryDashboardStore.knowledgeCount"></strong>
102
+ </span>
103
+ <span class="status-separator">•</span>
104
+ <span class="status-item">
105
+ Conversation: <strong x-text="$store.memoryDashboardStore.conversationCount"></strong>
106
+ </span>
107
</div>
108
116
- <!-- Pagination controls -->
117
- <div class="pagination-controls-compact" x-show="$store.memoryDashboardStore.totalPages > 1">
118
- <div class="pagination-info-inline">
119
- <span>Page <span x-text="$store.memoryDashboardStore.currentPage"></span>
120
- of <span x-text="$store.memoryDashboardStore.totalPages"></span>
121
- </span>
122
- </div>
123
-
124
- <div class="pagination-controls">
125
- <button class="btn slim" @click="$store.memoryDashboardStore.prevPage()"
126
- :disabled="$store.memoryDashboardStore.currentPage === 1">
127
- Previous
128
- </button>
129
-
130
- <select class="page-select" x-model.number="$store.memoryDashboardStore.currentPage"
131
- @change="$store.memoryDashboardStore.goToPage($store.memoryDashboardStore.currentPage)">
132
- <template
133
- x-for="page in Array.from({length: $store.memoryDashboardStore.totalPages}, (_, i) => i + 1)"
134
- :key="page">
135
- <option :value="page" x-text="'Page ' + page"></option>
136
- </template>
137
- </select>
138
-
139
- <button class="btn slim" @click="$store.memoryDashboardStore.nextPage()"
140
- :disabled="$store.memoryDashboardStore.currentPage === $store.memoryDashboardStore.totalPages">
141
- Next
142
- </button>
109
+ <!-- Compact Pagination -->
110
+ <div class="status-pagination" x-show="$store.memoryDashboardStore.totalPages > 1">
111
+ <button class="btn-icon" @click="$store.memoryDashboardStore.prevPage()"
112
+ :disabled="$store.memoryDashboardStore.currentPage === 1"
113
+ title="Previous Page">
114
+ <span class="material-symbols-outlined">chevron_left</span>
115
+ </button>
116
+ <div class="page-input-group">
117
+ <label>Page</label>
118
+ <input type="number"
119
+ class="page-input"
120
+ x-model.number="$store.memoryDashboardStore.currentPage"
121
+ @change="$store.memoryDashboardStore.goToPage($store.memoryDashboardStore.currentPage)"
122
+ @keyup.enter="$store.memoryDashboardStore.goToPage($store.memoryDashboardStore.currentPage)"
123
+ :min="1"
124
+ :max="$store.memoryDashboardStore.totalPages" />
125
</div>
126
+ <button class="btn-icon" @click="$store.memoryDashboardStore.nextPage()"
127
+ :disabled="$store.memoryDashboardStore.currentPage === $store.memoryDashboardStore.totalPages"
128
+ title="Next Page">
129
+ <span class="material-symbols-outlined">chevron_right</span>
130
+ </button>
131
</div>
132
</div>
133
@@ -153,46 +140,25 @@
140
<div class="mass-actions">
141
<button class="btn-mass copy" @click="$store.memoryDashboardStore.bulkCopyMemories()"
142
title="Copy Selected Content">
156
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
157
- stroke-width="2">
158
- <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
159
- <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
160
- </svg>
143
+ <span class="material-symbols-outlined">content_copy</span>
144
Copy
145
</button>
146
147
<button class="btn-mass export" @click="$store.memoryDashboardStore.bulkExportMemories()"
148
title="Export Selected Memories">
166
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
167
- stroke-width="2">
168
- <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
169
- <polyline points="7,10 12,15 17,10"></polyline>
170
- <line x1="12" y1="15" x2="12" y2="3"></line>
171
- </svg>
149
+ <span class="material-symbols-outlined">download</span>
150
Export
151
</button>
152
153
<button class="btn-mass delete" @click="$store.memoryDashboardStore.bulkDeleteMemories()"
154
title="Delete Selected Memories">
177
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
178
- stroke-width="2">
179
- <polyline points="3,6 5,6 21,6"></polyline>
180
- <path
181
- d="M19,6V20a2,2,0,0,1-2,2H7a2,2,0,0,1-2-2V6m3,0V4a2,2,0,0,1,2-2h4a2,2,0,0,1,2,2V6">
182
- </path>
183
- <line x1="10" y1="11" x2="10" y2="17"></line>
184
- <line x1="14" y1="11" x2="14" y2="17"></line>
185
- </svg>
155
+ <span class="material-symbols-outlined">delete</span>
156
Delete
157
</button>
158
159
<button class="btn-mass clear" @click="$store.memoryDashboardStore.clearSelection()"
160
title="Clear Selection">
191
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
192
- stroke-width="2">
193
- <line x1="18" y1="6" x2="6" y2="18"></line>
194
- <line x1="6" y1="6" x2="18" y2="18"></line>
195
- </svg>
161
+ <span class="material-symbols-outlined">close</span>
162
Clear
163
</button>
164
</div>
@@ -264,27 +230,13 @@
230
<button class="btn-action copy"
231
@click="$store.memoryDashboardStore.copyToClipboard($store.memoryDashboardStore.formatMemoryForCopy(memory))"
232
title="Copy Memory">
267
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none"
268
- stroke="currentColor" stroke-width="2">
269
- <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
270
- <path
271
- d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">
272
- </path>
273
- </svg>
233
+ <span class="material-symbols-outlined">content_copy</span>
234
</button>
235
236
<button class="btn-action delete"
237
@click="$store.memoryDashboardStore.deleteMemory(memory)"
238
title="Delete Memory">
279
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none"
280
- stroke="currentColor" stroke-width="2">
281
- <polyline points="3,6 5,6 21,6"></polyline>
282
- <path
283
- d="M19,6V20a2,2,0,0,1-2,2H7a2,2,0,0,1-2-2V6M8,6V4a2,2,0,0,1,2-2h4a2,2,0,0,1,2,2V6">
284
- </path>
285
- <line x1="10" y1="11" x2="10" y2="17"></line>
286
- <line x1="14" y1="11" x2="14" y2="17"></line>
287
- </svg>
239
+ <span class="material-symbols-outlined">delete</span>
240
</button>
241
</div>
242
</td>
@@ -312,7 +264,7 @@
264
265
<!-- Export button -->
266
<div class="export-section">
315
- <button class="btn slim" @click="$store.memoryDashboardStore.exportMemories()">
267
+ <button class="btn primary" @click="$store.memoryDashboardStore.exportMemories()">
268
Export Memories (JSON)
269
</button>
270
</div>
@@ -331,137 +283,268 @@
283
284
.memory-dashboard {
285
padding: 1rem;
334
- max-width: 100%;
286
+ margin: 0 auto;
287
background: var(--color-background);
288
color: var(--color-text);
289
}
290
339
- .memory-dashboard h3 {
340
- color: var(--color-text);
341
- margin: 0;
342
- font-size: 1.5rem;
343
- font-weight: 600;
344
- }
345
-
346
-
347
-
348
- .stats-pagination-header {
349
- display: flex;
350
- justify-content: space-between;
351
- align-items: center;
291
+ /* Compact Linear Filter Layout */
292
+ .memory-filters-header {
293
+ margin-bottom: 1rem;
294
padding: 1rem;
295
background: var(--color-panel);
296
border: 1px solid var(--color-border);
297
border-radius: 8px;
356
- border-bottom: none;
357
- border-bottom-left-radius: 0;
358
- border-bottom-right-radius: 0;
298
}
299
361
- .memory-stats-compact {
362
- display: flex;
363
- gap: 1rem;
364
- flex-wrap: wrap;
300
+ /* Unified container using CSS Grid for desktop layout */
301
+ .filter-container-unified {
302
+ display: grid;
303
+ grid-template-columns: 35% 25% 10% auto;
304
+ grid-template-rows: auto auto;
305
+ gap: 0.75rem;
306
+ align-items: center;
307
}
308
367
- .pagination-controls-compact {
368
- display: flex;
369
- align-items: center;
370
- gap: 1rem;
309
+ /* Grid positioning for desktop - 2 rows */
310
+ .filter-memory-dir { grid-column: 1; grid-row: 1; }
311
+ .filter-area { grid-column: 2; grid-row: 1; }
312
+ .filter-limit { grid-column: 3; grid-row: 1; }
313
+ .filter-threshold { grid-column: 4; grid-row: 2; }
314
+
315
+ #search-input { grid-column: 1 / 3; grid-row: 2; }
316
+ .btn-search { grid-column: 3; grid-row: 2; }
317
+ .btn-clear {
318
+ grid-column: 4;
319
+ grid-row: 1;
320
+ justify-self: end;
321
}
322
373
- .stat-item {
323
+ .filter-group-inline {
324
display: flex;
375
- flex-direction: column;
325
align-items: center;
377
- padding: 0.5rem;
378
- background: var(--color-panel);
326
+ gap: 0.5rem;
327
+ flex: 1;
328
+ }
329
+
330
+ .filter-group-inline label {
331
+ font-size: 0.85rem;
332
+ font-weight: 600;
333
+ color: var(--color-text);
334
+ opacity: 0.8;
335
+ white-space: nowrap;
336
+ }
337
+
338
+ .filter-group-inline select,
339
+ .filter-group-inline input {
340
+ padding: 0.4rem 0.6rem;
341
border: 1px solid var(--color-border);
380
- border-radius: 8px;
381
- min-width: 80px;
342
+ background: var(--color-input);
343
+ color: var(--color-text);
344
+ border-radius: 4px;
345
+ font-size: 0.9rem;
346
+ height: 36px;
347
+ flex: 0 auto;
348
+ min-width: 0;
349
}
350
384
- .stat-label {
385
- font-size: 0.8rem;
351
+ .filter-group-inline select:focus,
352
+ .filter-group-inline input:focus {
353
+ outline: none;
354
+ border-color: var(--color-primary);
355
+ background: var(--color-input-focus);
356
+ }
357
+
358
+ .filter-group-inline.filter-limit {
359
+ flex: 0 0 auto;
360
+ }
361
+
362
+ .filter-limit input {
363
+ flex: 0 0 200%;
364
+ }
365
+
366
+
367
+ #search-input {
368
+ flex: 1;
369
+ padding: 0.5rem 0.75rem;
370
+ border: 1px solid var(--color-border);
371
+ background: var(--color-input);
372
color: var(--color-text);
387
- opacity: 0.7;
388
- margin-bottom: 0.25rem;
373
+ border-radius: 4px;
374
+ font-size: 0.9rem;
375
+ height: 38px;
376
}
377
391
- .light-mode .stat-label {
378
+ #search-input:focus {
379
+ outline: none;
380
+ border-color: var(--color-primary);
381
+ background: var(--color-input-focus);
382
+ }
383
+
384
+ .filter-threshold {
385
+ display: flex;
386
+ align-items: center;
387
+ gap: 0.75rem;
388
+ padding: 0 0.5rem;
389
+ }
390
+
391
+ .filter-threshold label {
392
+ font-size: 0.85rem;
393
+ color: var(--color-text);
394
opacity: 0.8;
395
+ white-space: nowrap;
396
}
397
395
- .stat-value {
396
- font-size: 1.1rem;
397
- font-weight: bold;
398
+ .filter-threshold strong {
399
color: var(--color-primary);
400
+ font-weight: 600;
401
+ min-width: 3ch;
402
+ display: inline-block;
403
}
404
401
- .filters-section {
402
- margin-bottom: 1.5rem;
403
- padding: 1rem;
405
+ .filter-threshold input[type="range"] {
406
+ height: 4px;
407
+ -webkit-appearance: none;
408
+ appearance: none;
409
+ background: var(--color-border);
410
+ border-radius: 2px;
411
+ outline: none;
412
+ }
413
+
414
+ .filter-threshold input[type="range"]::-webkit-slider-thumb {
415
+ -webkit-appearance: none;
416
+ appearance: none;
417
+ width: 16px;
418
+ height: 16px;
419
+ background: var(--color-primary);
420
+ border-radius: 50%;
421
+ cursor: pointer;
422
+ }
423
+
424
+ .filter-threshold input[type="range"]::-moz-range-thumb {
425
+ width: 16px;
426
+ height: 16px;
427
+ background: var(--color-primary);
428
+ border-radius: 50%;
429
+ cursor: pointer;
430
+ border: none;
431
+ }
432
+
433
+ /* Compact Status Bar */
434
+ .memory-status-bar {
435
+ display: flex;
436
+ justify-content: space-between;
437
+ align-items: center;
438
+ padding: 0.75rem 1rem;
439
background: var(--color-panel);
440
border: 1px solid var(--color-border);
441
border-radius: 8px;
442
+ margin-bottom: 1rem;
443
+ font-size: 0.9rem;
444
}
445
409
- .filter-row {
446
+ .status-info {
447
display: flex;
411
- gap: 1rem;
412
- align-items: end;
448
+ align-items: center;
449
+ gap: 0.5rem;
450
flex-wrap: wrap;
451
}
452
416
- .filter-group {
453
+ .status-item {
454
display: flex;
418
- flex-direction: column;
455
+ align-items: center;
456
gap: 0.25rem;
457
+ color: var(--color-text);
458
+ opacity: 0.8;
459
}
460
422
- .filter-group label {
423
- font-size: 0.9rem;
424
- font-weight: 500;
425
- color: var(--color-text);
461
+ .status-item strong {
462
+ color: var(--color-primary);
463
+ font-weight: 600;
464
}
465
428
- .filter-group input,
429
- .filter-group select {
430
- padding: 0.5rem;
466
+ .status-item .material-symbols-outlined {
467
+ font-size: 18px;
468
+ opacity: 0.6;
469
+ }
470
+
471
+ .status-separator {
472
+ color: var(--color-border);
473
+ margin: 0 0.25rem;
474
+ }
475
+
476
+ .status-pagination {
477
+ display: flex;
478
+ align-items: center;
479
+ gap: 0.5rem;
480
+ }
481
+
482
+ .btn-icon {
483
+ background: transparent;
484
border: 1px solid var(--color-border);
432
- background: var(--color-background);
485
+ border-radius: 4px;
486
+ padding: 0.25rem;
487
+ cursor: pointer;
488
color: var(--color-text);
434
- border-radius: 6px;
435
- font-size: 0.9rem;
436
- min-width: 200px;
437
- height: 40px;
438
- box-sizing: border-box;
489
+ display: flex;
490
+ align-items: center;
491
+ transition: all 0.2s;
492
}
493
441
- .filter-group input:focus,
442
- .filter-group select:focus {
443
- outline: none;
494
+ .btn-icon:hover:not(:disabled) {
495
+ background: var(--color-panel);
496
border-color: var(--color-primary);
445
- box-shadow: 0 0 0 2px rgba(115, 122, 129, 0.2);
446
- background: var(--color-input-focus);
497
}
498
449
- .light-mode .filter-group input:focus,
450
- .light-mode .filter-group select:focus {
451
- box-shadow: 0 0 0 2px rgba(56, 70, 83, 0.1);
499
+ .btn-icon:disabled {
500
+ opacity: 0.4;
501
+ cursor: not-allowed;
502
}
503
454
- .filter-actions {
455
- display: flex;
456
- gap: 0.5rem;
457
- align-items: stretch;
504
+ .btn-icon .material-symbols-outlined {
505
+ font-size: 20px;
506
}
507
460
- .filter-actions .btn {
461
- height: 40px;
508
+ .page-input-group {
509
display: flex;
510
align-items: center;
464
- justify-content: center;
511
+ gap: 0.5rem;
512
+ font-size: 0.85rem;
513
+ color: var(--color-text);
514
+ }
515
+
516
+ .page-input-group label {
517
+ opacity: 0.8;
518
+ }
519
+
520
+ .page-input {
521
+ width: 60px;
522
+ padding: 0.25rem 0.5rem;
523
+ text-align: center;
524
+ border: 1px solid var(--color-border);
525
+ background: var(--color-input);
526
+ color: var(--color-text);
527
+ border-radius: 4px;
528
+ font-size: 0.85rem;
529
+ height: 28px;
530
+ }
531
+
532
+ .page-input:focus {
533
+ outline: none;
534
+ border-color: var(--color-primary);
535
+ background: var(--color-input-focus);
536
+ }
537
+
538
+ /* Remove spinner arrows for cleaner look */
539
+ .page-input::-webkit-inner-spin-button,
540
+ .page-input::-webkit-outer-spin-button {
541
+ -webkit-appearance: none;
542
+ margin: 0;
543
+ }
544
+
545
+ .page-input[type=number] {
546
+ -moz-appearance: textfield;
547
+ appearance: textfield;
548
}
549
550
.loading-text {
@@ -487,24 +570,14 @@
570
571
.init-message {
572
color: var(--color-primary);
490
- background: rgba(115, 122, 129, 0.2);
573
border-color: var(--color-primary);
574
}
575
494
- .light-mode .init-message {
495
- background: rgba(56, 70, 83, 0.1);
496
- }
497
-
576
.error-state {
577
color: var(--color-accent);
500
- background: rgba(207, 102, 121, 0.2);
578
border-color: var(--color-accent);
579
}
580
504
- .light-mode .error-state {
505
- background: rgba(176, 0, 32, 0.1);
506
- }
507
-
581
.loading-spinner {
582
width: 24px;
583
height: 24px;
@@ -625,9 +698,6 @@
698
border-left: 3px solid var(--color-primary);
699
}
700
628
- .light-mode .memory-row.selected {
629
- background: rgba(56, 70, 83, 0.1);
630
- }
701
702
/* Mass action toolbar */
703
.mass-action-toolbar {
@@ -642,9 +712,6 @@
712
animation: slideDown 0.3s ease;
713
}
714
645
- .light-mode .mass-action-toolbar {
646
- background: rgba(56, 70, 83, 0.1);
647
- }
715
716
.selection-info {
717
font-weight: 600;
@@ -659,18 +726,22 @@
726
.btn-mass {
727
display: flex;
728
align-items: center;
662
- gap: 0.5rem;
663
- padding: 0.5rem 1rem;
729
+ gap: 0.25rem;
730
+ padding: 0.4rem 0.8rem;
731
border: 1px solid var(--color-border);
732
background: var(--color-background);
733
color: var(--color-text);
667
- border-radius: 6px;
734
+ border-radius: 4px;
735
cursor: pointer;
669
- font-size: 0.9rem;
736
+ font-size: 0.85rem;
737
font-weight: 500;
738
transition: all 0.2s ease;
739
}
740
741
+ .btn-mass .material-symbols-outlined {
742
+ font-size: 18px;
743
+ }
744
+
745
.btn-mass:hover {
746
border-color: var(--color-primary);
747
background: var(--color-panel);
@@ -745,10 +816,6 @@
816
border: 1px solid rgba(255, 255, 255, 0.1);
817
}
818
748
- .light-mode .area-badge {
749
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
750
- border: 1px solid rgba(0, 0, 0, 0.1);
751
- }
819
820
.timestamp-cell {
821
font-size: 0.85rem;
@@ -784,10 +851,6 @@
851
border-color: var(--color-primary);
852
}
853
787
- .light-mode .source-type.knowledge {
788
- background: rgba(56, 70, 83, 0.1);
789
- color: var(--color-primary);
790
- }
854
855
.source-type.conversation {
856
background: rgba(101, 101, 101, 0.2);
@@ -796,11 +859,6 @@
859
border-color: var(--color-border);
860
}
861
799
- .light-mode .source-type.conversation {
800
- background: rgba(232, 234, 246, 0.5);
801
- color: var(--color-text);
802
- opacity: 0.9;
803
- }
862
863
.source-file {
864
font-size: 0.7rem;
@@ -875,18 +933,22 @@
933
.btn-action {
934
background: none;
935
border: 1px solid var(--color-border);
878
- padding: 0.3rem;
936
+ padding: 0.25rem;
937
margin: 0 0.1rem;
938
cursor: pointer;
881
- border-radius: 6px;
939
+ border-radius: 4px;
940
color: var(--color-text);
941
opacity: 0.7;
942
transition: all 0.2s ease;
943
display: inline-flex;
944
align-items: center;
945
justify-content: center;
888
- min-width: 32px;
889
- height: 32px;
946
+ min-width: 28px;
947
+ height: 28px;
948
+ }
949
+
950
+ .btn-action .material-symbols-outlined {
951
+ font-size: 18px;
952
}
953
954
.btn-action:hover {
@@ -947,9 +1009,6 @@
1009
background: var(--color-input-focus);
1010
}
1011
950
- .light-mode .page-select:focus {
951
- box-shadow: 0 0 0 2px rgba(56, 70, 83, 0.1);
952
- }
1012
1013
1014
@@ -963,23 +1022,6 @@
1022
border-top: 1px solid var(--color-border);
1023
}
1024
966
- /* Enhanced Modal Styles */
967
- .modal-overlay {
968
- position: fixed;
969
- top: 0;
970
- left: 0;
971
- right: 0;
972
- bottom: 0;
973
- background: rgba(0, 0, 0, 0.6);
974
- backdrop-filter: blur(4px);
975
- display: flex;
976
- align-items: flex-start;
977
- justify-content: center;
978
- z-index: 1000;
979
- overflow-y: auto;
980
- padding: 2rem 0;
981
- }
982
-
1025
.modal-enter {
1026
transition: all 0.3s ease;
1027
}
@@ -1032,15 +1074,6 @@
1074
}
1075
}
1076
1035
- .modal-header-enhanced {
1036
- background: var(--color-panel);
1037
- padding: 1.5rem 2rem;
1038
- border-bottom: 2px solid var(--color-border);
1039
- display: flex;
1040
- justify-content: space-between;
1041
- align-items: flex-start;
1042
- }
1043
-
1077
.header-left {
1078
flex: 1;
1079
}
@@ -1284,39 +1317,143 @@
1317
1318
/* Responsive design */
1319
@media (max-width: 768px) {
1287
- .modal-overlay {
1288
- padding: 1rem 0;
1289
- }
1290
-
1320
.memory-dashboard {
1321
padding: 0.5rem;
1322
}
1323
1295
- .stats-pagination-header {
1324
+ .memory-filters-header {
1325
+ padding: 0.75rem;
1326
+ }
1327
+
1328
+ /* Transform grid to flex on mobile for reordering */
1329
+ .filter-container-unified {
1330
+ display: flex;
1331
+ flex-wrap: wrap;
1332
+ gap: 0.75rem;
1333
+ }
1334
+
1335
+ /* Reset grid positioning */
1336
+ .filter-memory-dir,
1337
+ .filter-area,
1338
+ .filter-limit,
1339
+ .filter-threshold,
1340
+ #search-input,
1341
+ .btn-search,
1342
+ .btn-clear {
1343
+ grid-column: auto;
1344
+ grid-row: auto;
1345
+ }
1346
+
1347
+ .filter-group-inline {
1348
+ width: 100%;
1349
+ flex-direction: row;
1350
+ align-items: center;
1351
+ gap: 0.5rem;
1352
+ }
1353
+
1354
+ .filter-group-inline label {
1355
+ font-size: 0.9rem;
1356
+ margin-bottom: 0.25rem;
1357
+ }
1358
+
1359
+ .filter-group-inline select,
1360
+ .filter-group-inline input {
1361
+ width: 100%;
1362
+ max-width: 100%;
1363
+ height: 42px;
1364
+ font-size: 1rem;
1365
+ }
1366
+
1367
+ .filter-group-inline.filter-limit {
1368
+ flex: 33%;
1369
+ }
1370
+
1371
+ .filter-limit input {
1372
+ width: 100%;
1373
+ flex: 1;
1374
+ }
1375
+
1376
+ /* Mobile order and layout - force items to specific widths */
1377
+ .filter-memory-dir {
1378
+ order: 1;
1379
+ flex: 1 1 100%;
1380
+ }
1381
+ .filter-area {
1382
+ order: 2;
1383
+ flex: 1 1 100%;
1384
+ }
1385
+ .filter-limit {
1386
+ order: 3;
1387
+ flex: 1 1 100%;
1388
+ }
1389
+ .btn-clear {
1390
+ order: 6;
1391
+ flex: 40%;
1392
+ min-width: 80px;
1393
+ padding: 0;
1394
+ height: 42px;
1395
+ justify-content: center;
1396
+ }
1397
+ #search-input {
1398
+ order: 5;
1399
+ flex: auto;
1400
+ width: 100%;
1401
+ height: 42px;
1402
+ font-size: 1rem;
1403
+ }
1404
+
1405
+ /* Search button and threshold on same row */
1406
+ .btn-search {
1407
+ order: 7;
1408
+ flex: 40%;
1409
+ height: 42px;
1410
+ padding: 0 1rem;
1411
+ min-width: 80px;
1412
+ }
1413
+
1414
+ .filter-threshold {
1415
+ order: 4;
1416
+ flex: 40%;
1417
flex-direction: column;
1297
- align-items: flex-start;
1298
- gap: 1rem;
1418
+ align-items: stretch;
1419
+ gap: 0.5rem;
1420
+ padding: 0;
1421
}
1422
1301
- .memory-stats-compact {
1423
+ .filter-threshold label {
1424
+ font-size: 0.9rem;
1425
width: 100%;
1426
+ display: flex;
1427
justify-content: space-between;
1428
}
1429
1306
- .pagination-controls-compact {
1307
- align-self: stretch;
1308
- justify-content: center;
1430
+ .filter-threshold input[type="range"] {
1431
+ width: 100%;
1432
}
1433
1311
- .filter-row {
1434
+ .memory-status-bar {
1435
flex-direction: column;
1313
- align-items: stretch;
1436
+ gap: 0.75rem;
1437
+ padding: 0.75rem;
1438
+ text-align: center;
1439
}
1440
1316
- .filter-group input,
1317
- .filter-group select {
1318
- min-width: unset;
1441
+ .status-info {
1442
width: 100%;
1443
+ justify-content: center;
1444
+ flex-wrap: wrap;
1445
+ gap: 0.5rem 1rem;
1446
+ }
1447
+
1448
+ .status-info .material-symbols-outlined {
1449
+ font-size: 18px;
1450
+ }
1451
+
1452
+ .status-pagination {
1453
+ width: 100%;
1454
+ justify-content: center;
1455
+ padding-top: 0.5rem;
1456
+ border-top: 1px solid var(--color-border);
1457
}
1458
1459
.memory-table th,
@@ -1592,22 +1729,6 @@
1729
font-size: 0.9rem;
1730
}
1731
1595
- /* Light mode adjustments for modal */
1596
- .light-mode .modal-header-enhanced {
1597
- background: linear-gradient(135deg, rgba(240, 240, 240, 0.8) 0%, rgba(255, 255, 255, 0.9) 100%);
1598
- }
1599
-
1600
- .light-mode .memory-content-display {
1601
- background: rgba(248, 248, 248, 0.8);
1602
- }
1603
-
1604
- .light-mode .metadata-sidebar {
1605
- background: rgba(248, 248, 248, 0.8);
1606
- }
1607
-
1608
- .light-mode .metadata-item {
1609
- background: rgba(255, 255, 255, 0.9);
1610
- }
1732
1733
/* Responsive design for modal */
1734
@media (max-width: 768px) {
@@ -1644,4 +1765,5 @@
1765
</div>
1766
</body>
1767
1768
+</html>
1769
</html>
\ No newline at end of file
webui/components/settings/memory/memory-detail-modal.html
+63
-61
@@ -5,61 +5,49 @@
5
</head>
6
7
<style>
8
- /* Memory Detail Modal Styles */
9
- .modal-header-enhanced {
8
+ /* Compact Modal Header */
9
+ .modal-header-compact {
10
display: flex;
11
justify-content: space-between;
12
- align-items: flex-start;
13
- padding: 1.5rem;
14
- border-bottom: 2px solid var(--color-border);
12
+ align-items: center;
13
+ padding: 1rem 1.5rem;
14
+ border-bottom: 1px solid var(--color-border);
15
background: var(--color-panel);
16
+ position: sticky;
17
+ top: 0;
18
+ z-index: 100;
19
}
20
18
- .memory-title {
21
+ .header-info {
22
display: flex;
23
align-items: center;
21
- gap: 1rem;
22
- margin-bottom: 0.5rem;
24
+ gap: 0.5rem;
25
+ font-size: 0.9rem;
26
}
27
25
- .area-badge-large {
28
+ .area-badge {
29
color: white;
27
- padding: 0.5rem 1rem;
28
- border-radius: 20px;
29
- font-size: 0.8rem;
30
- font-weight: bold;
31
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
32
- }
33
-
34
- .memory-meta-quick {
35
- display: flex;
36
- gap: 1rem;
37
- align-items: center;
38
- }
39
-
40
- .timestamp-badge, .source-badge {
41
- padding: 0.25rem 0.75rem;
42
- border-radius: 12px;
30
+ padding: 0.35rem 0.75rem;
31
+ border-radius: 16px;
32
font-size: 0.75rem;
44
- font-weight: 500;
45
- background: var(--color-panel);
46
- border: 1px solid var(--color-border);
47
- color: var(--color-text);
48
- opacity: 0.8;
33
+ font-weight: bold;
34
+ text-transform: uppercase;
35
}
36
51
- .source-badge.knowledge {
52
- background: rgba(115, 122, 129, 0.1);
53
- color: var(--color-primary);
54
- border-color: var(--color-primary);
55
- opacity: 1;
37
+ .header-separator {
38
+ color: var(--color-border);
39
+ opacity: 0.6;
40
}
41
58
- .source-badge.conversation {
59
- background: rgba(101, 101, 101, 0.1);
42
+ .timestamp-text,
43
+ .source-text {
44
color: var(--color-text);
45
opacity: 0.8;
62
- border-color: var(--color-border);
46
+ font-size: 0.75rem;
47
+ padding: 0.25rem 0.6rem;
48
+ background: var(--color-background);
49
+ border: 1px solid var(--color-border);
50
+ border-radius: 12px;
51
}
52
53
.header-actions {
@@ -68,13 +56,17 @@
56
}
57
58
.btn-action-header {
71
- background: var(--color-background);
59
+ background: transparent;
60
border: 1px solid var(--color-border);
61
color: var(--color-text);
74
- padding: 0.5rem;
75
- border-radius: 8px;
62
+ padding: 0.4rem 0.6rem;
63
+ border-radius: 4px;
64
cursor: pointer;
65
transition: all 0.2s ease;
66
+ display: flex;
67
+ align-items: center;
68
+ gap: 0.25rem;
69
+ font-size: 0.85rem;
70
}
71
72
.btn-action-header:hover {
@@ -82,6 +74,15 @@
74
background: var(--color-panel);
75
}
76
77
+ .btn-action-header .material-symbols-outlined {
78
+ font-size: 18px;
79
+ }
80
+
81
+ .btn-action-header.delete:hover {
82
+ border-color: var(--color-accent);
83
+ color: var(--color-accent);
84
+ }
85
+
86
.modal-body-enhanced {
87
display: flex;
88
min-height: 60vh;
@@ -214,6 +215,11 @@
215
font-size: 0.9rem;
216
}
217
218
+ /* Wider modal for better readability */
219
+ .modal-inner.modal-wide {
220
+ max-width: 1200px;
221
+ }
222
+
223
/* Responsive design for modal */
224
@media (max-width: 768px) {
225
.modal-body-enhanced {
@@ -228,14 +234,14 @@
234
border-top: 1px solid var(--color-border);
235
}
236
231
- .modal-header-enhanced {
232
- padding: 1rem;
237
+ .modal-header-compact {
238
+ padding: 0.75rem 1rem;
239
+ flex-wrap: wrap;
240
+ gap: 0.75rem;
241
}
242
235
- .memory-title {
236
- flex-direction: column;
237
- align-items: flex-start;
238
- gap: 0.5rem;
243
+ .header-info {
244
+ flex-wrap: wrap;
245
}
246
247
.content-section-main,
@@ -249,20 +255,16 @@
255
<div x-data>
256
<template x-if="$store.memoryDashboardStore && $store.memoryDashboardStore.detailMemory">
257
<div>
252
- <!-- Modal Header with Memory Info -->
253
- <div class="modal-header-enhanced">
254
- <div class="header-left">
255
- <div class="memory-title">
256
- <span class="area-badge-large"
257
- :style="`background-color: ${$store.memoryDashboardStore.getAreaColor($store.memoryDashboardStore.detailMemory?.area)}`"
258
- x-text="$store.memoryDashboardStore.detailMemory?.area?.toUpperCase() || 'UNKNOWN'"></span>
259
- </div>
260
- <div class="memory-meta-quick">
261
- <span class="timestamp-badge" x-text="$store.memoryDashboardStore.formatTimestamp($store.memoryDashboardStore.detailMemory?.timestamp, true)"></span>
262
- <span class="source-badge"
263
- :class="$store.memoryDashboardStore.detailMemory?.knowledge_source ? 'knowledge' : 'conversation'"
264
- x-text="$store.memoryDashboardStore.detailMemory?.knowledge_source ? 'Knowledge' : 'Conversation'"></span>
265
- </div>
258
+ <!-- Compact Modal Header -->
259
+ <div class="modal-header-compact">
260
+ <div class="header-info">
261
+ <span class="area-badge"
262
+ :style="`background-color: ${$store.memoryDashboardStore.getAreaColor($store.memoryDashboardStore.detailMemory?.area)}`"
263
+ x-text="$store.memoryDashboardStore.detailMemory?.area?.toUpperCase() || 'UNKNOWN'"></span>
264
+ <span class="header-separator">•</span>
265
+ <span class="timestamp-text" x-text="$store.memoryDashboardStore.formatTimestamp($store.memoryDashboardStore.detailMemory?.timestamp, true)"></span>
266
+ <span class="header-separator">•</span>
267
+ <span class="source-text" x-text="$store.memoryDashboardStore.detailMemory?.knowledge_source ? 'Knowledge' : 'Conversation'"></span>
268
</div>
269
<div class="header-actions">
270
<!-- View Mode Buttons -->