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 -->