shared css classes for memory-dash and scheduler
3clyp50 committed
Dec 26, 2025 at 14:47 UTC
0d92bb793bf0ed2b3ca4e5468b5c49a3cf21dc74
4 files changed
+612
-698
webui/components/modals/scheduler/scheduler-task-list.html
+36
-285
@@ -11,7 +11,7 @@
11
x-show="!$store.schedulerStore.isCreating && !$store.schedulerStore.isEditing">
12
13
<!-- Filter Header -->
14
- <div class="scheduler-filters-header">
14
+ <div class="filters-header">
15
<div class="filter-group-inline filter-type">
16
<label>Type</label>
17
<select x-model="$store.schedulerStore.filterType">
@@ -40,7 +40,7 @@
40
</div>
41
42
<!-- Status Bar -->
43
- <div class="scheduler-status-bar" x-show="!$store.schedulerStore.isLoading">
43
+ <div class="data-status-bar" x-show="!$store.schedulerStore.isLoading">
44
<div class="status-info">
45
<span class="status-item">
46
<span class="material-symbols-outlined">schedule</span>
@@ -77,9 +77,9 @@
77
</div>
78
79
<!-- Empty State -->
80
- <div class="scheduler-empty"
80
+ <div class="data-empty-state"
81
x-show="!$store.schedulerStore.isLoading && $store.schedulerStore.filteredTasks.length === 0">
82
- <div class="scheduler-empty-text">No tasks found</div>
82
+ <div>No tasks found</div>
83
<button class="btn btn-ok" @click="$store.schedulerStore.startCreateTask()">
84
<span class="material-symbols-outlined">add</span>
85
Create your first task
@@ -87,29 +87,29 @@
87
</div>
88
89
<!-- Task Table -->
90
- <div class="scheduler-table-container"
90
+ <div class="data-table-container"
91
x-show="!$store.schedulerStore.isLoading && $store.schedulerStore.paginatedTasks.length > 0">
92
- <table class="scheduler-task-table">
92
+ <table class="data-table scheduler-task-table">
93
<thead>
94
<tr>
95
- <th class="col-name" @click="$store.schedulerStore.changeSort('name')">
95
+ <th class="col-name sortable" @click="$store.schedulerStore.changeSort('name')">
96
Name
97
- <span class="scheduler-sort-indicator"
97
+ <span class="sort-indicator"
98
x-show="$store.schedulerStore.sortField === 'name'"
99
- :class="{'scheduler-sort-desc': $store.schedulerStore.sortDirection === 'desc'}">↑</span>
99
+ :class="{'desc': $store.schedulerStore.sortDirection === 'desc'}">↑</span>
100
</th>
101
- <th class="col-state" @click="$store.schedulerStore.changeSort('state')">
101
+ <th class="col-state sortable" @click="$store.schedulerStore.changeSort('state')">
102
State
103
- <span class="scheduler-sort-indicator"
103
+ <span class="sort-indicator"
104
x-show="$store.schedulerStore.sortField === 'state'"
105
- :class="{'scheduler-sort-desc': $store.schedulerStore.sortDirection === 'desc'}">↑</span>
105
+ :class="{'desc': $store.schedulerStore.sortDirection === 'desc'}">↑</span>
106
</th>
107
<th class="col-project">Project</th>
108
- <th class="col-lastrun" @click="$store.schedulerStore.changeSort('last_run')">
108
+ <th class="col-lastrun sortable" @click="$store.schedulerStore.changeSort('last_run')">
109
Last Run
110
- <span class="scheduler-sort-indicator"
110
+ <span class="sort-indicator"
111
x-show="$store.schedulerStore.sortField === 'last_run'"
112
- :class="{'scheduler-sort-desc': $store.schedulerStore.sortDirection === 'desc'}">↑</span>
112
+ :class="{'desc': $store.schedulerStore.sortDirection === 'desc'}">↑</span>
113
</th>
114
<th class="col-actions">Actions</th>
115
</tr>
@@ -158,7 +158,7 @@
158
</div>
159
160
<!-- Bottom Status Bar (for pagination when table is long) -->
161
- <div class="scheduler-status-bar scheduler-status-bar-bottom"
161
+ <div class="data-status-bar data-status-bar-bottom"
162
x-show="!$store.schedulerStore.isLoading && $store.schedulerStore.paginatedTasks.length > 5 && $store.schedulerStore.totalPages > 1">
163
<div class="status-info">
164
<span class="status-item">
@@ -198,50 +198,14 @@
198
</div>
199
200
<style>
201
+ /* Scheduler-specific: Container layout */
202
.scheduler-list-container {
203
display: flex;
204
flex-direction: column;
205
gap: 0;
206
}
207
207
- /* Filter Header - matches memory-dashboard pattern */
208
- .scheduler-filters-header {
209
- display: flex;
210
- flex-wrap: wrap;
211
- gap: 0.75rem;
212
- align-items: center;
213
- padding: 1rem;
214
- background: var(--color-panel);
215
- border: 1px solid var(--color-border);
216
- border-radius: 8px;
217
- margin-bottom: 0;
218
- }
219
-
220
- .filter-group-inline {
221
- display: flex;
222
- align-items: center;
223
- gap: 0.5rem;
224
- }
225
-
226
- .filter-group-inline label {
227
- font-size: 0.85rem;
228
- font-weight: 600;
229
- color: var(--color-text);
230
- opacity: 0.8;
231
- white-space: nowrap;
232
- }
233
-
234
- .filter-group-inline select {
235
- border: 1px solid var(--color-border);
236
- min-width: 120px;
237
- }
238
-
239
- .filter-group-inline select:focus {
240
- outline: none;
241
- border-color: var(--color-primary);
242
- background: var(--color-input-focus);
243
- }
244
-
208
+ /* Scheduler-specific: New task button positioning */
209
.btn-new-task {
210
margin-left: auto;
211
display: inline-flex;
@@ -253,204 +217,19 @@
217
font-size: 20px;
218
}
219
256
- /* Status Bar - matches memory-dashboard pattern */
257
- .scheduler-status-bar {
258
- display: flex;
259
- justify-content: space-between;
260
- align-items: center;
261
- padding: 0.75rem 1rem;
262
- background: var(--color-panel);
263
- border: 1px solid var(--color-border);
264
- border-top: none;
265
- font-size: 0.9rem;
266
- }
267
-
268
- .scheduler-status-bar-bottom {
269
- border-top: 1px solid var(--color-border);
270
- border-radius: 0 0 8px 8px;
271
- }
272
-
273
- .status-info {
274
- display: flex;
275
- align-items: center;
276
- gap: 0.5rem;
277
- flex-wrap: wrap;
278
- }
279
-
280
- .status-item {
281
- display: flex;
282
- align-items: center;
283
- gap: 0.25rem;
284
- }
285
-
286
- .status-item strong {
287
- color: var(--color-primary);
288
- font-weight: 600;
289
- }
290
-
291
- .status-item .material-symbols-outlined {
292
- font-size: 18px;
293
- opacity: 0.6;
294
- }
295
-
296
- .status-separator {
297
- color: var(--color-border);
298
- margin: 0 0.25rem;
299
- }
300
-
301
- .status-pagination {
302
- display: flex;
303
- align-items: center;
304
- gap: 0.5rem;
305
- }
306
-
307
- .page-input-group {
308
- display: flex;
309
- align-items: center;
310
- gap: 0.5rem;
311
- }
312
-
313
- .page-input-group label {
314
- opacity: 0.8;
315
- }
316
-
317
- .page-total {
318
- color: var(--color-text);
319
- opacity: 0.8;
320
- font-size: 0.85rem;
321
- white-space: nowrap;
322
- }
323
-
324
- .page-total strong {
325
- color: var(--color-primary);
326
- font-weight: 600;
327
- }
328
-
329
- .page-input {
330
- width: 60px;
331
- padding: 0.25rem 0.5rem;
332
- text-align: center;
333
- border: 1px solid var(--color-border);
334
- background: var(--color-input);
335
- color: var(--color-text);
336
- border-radius: 4px;
337
- font-size: 0.85rem;
338
- height: 28px;
339
- }
340
-
341
- .page-input:focus {
342
- outline: none;
343
- border-color: var(--color-primary);
344
- background: var(--color-input-focus);
345
- }
346
-
347
- .page-input::-webkit-inner-spin-button,
348
- .page-input::-webkit-outer-spin-button {
349
- -webkit-appearance: none;
350
- margin: 0;
351
- }
352
-
353
- .page-input[type=number] {
354
- -moz-appearance: textfield;
355
- appearance: textfield;
356
- }
357
-
358
- /* Table Container */
359
- .scheduler-table-container {
360
- border: 1px solid var(--color-border);
361
- border-top: none;
362
- border-radius: 0 0 8px 8px;
363
- overflow: hidden;
364
- }
365
-
366
- .scheduler-task-table {
367
- width: 100%;
368
- border-collapse: collapse;
369
- table-layout: auto;
370
- font-size: 0.9rem;
371
- }
372
-
373
- .scheduler-task-table th,
374
- .scheduler-task-table td {
375
- padding: 0.75rem;
376
- text-align: left;
377
- border-bottom: 1px solid var(--color-border);
378
- vertical-align: middle;
379
- overflow: hidden;
380
- text-overflow: ellipsis;
381
- white-space: nowrap;
382
- }
383
-
384
- .scheduler-task-table th {
385
- background: var(--color-panel);
386
- font-weight: 600;
387
- cursor: pointer;
388
- user-select: none;
389
- }
390
-
391
- .scheduler-task-table th:hover {
392
- background: var(--color-bg-tertiary);
393
- }
394
-
395
- .scheduler-task-table tbody tr {
396
- cursor: pointer;
397
- transition: background-color 0.2s ease;
398
- }
399
-
400
- .scheduler-task-table tbody tr:hover {
401
- background-color: rgba(255, 255, 255, 0.03);
402
- }
403
-
404
- .light-mode .scheduler-task-table tbody tr:hover {
405
- background-color: rgba(0, 0, 0, 0.02);
406
- }
407
-
408
- .scheduler-task-table tbody tr:last-child td {
409
- border-bottom: none;
410
- }
411
-
412
- /* Column widths */
413
- .col-name { width: 25%; }
414
- .col-state { width: 12%; }
415
- .col-project { width: 20%; }
416
- .col-lastrun { width: 23%; }
417
- .col-actions { width: 20%; }
418
-
419
- /* Empty state */
420
- .scheduler-empty {
421
- text-align: center;
422
- padding: 3rem 1rem;
423
- color: var(--color-text);
424
- opacity: 0.8;
425
- background: var(--color-panel);
426
- border: 1px solid var(--color-border);
427
- border-top: none;
428
- border-radius: 0 0 8px 8px;
429
- }
430
-
431
- .scheduler-empty-text {
432
- margin-bottom: 1rem;
433
- font-size: 1rem;
434
- }
435
-
436
- .scheduler-empty .btn {
437
- display: inline-flex;
438
- align-items: center;
439
- gap: 0.5rem;
440
- }
441
-
442
- /* Sort indicator */
443
- .scheduler-sort-indicator {
444
- display: inline-block;
445
- margin-left: 4px;
446
- transition: transform 0.2s ease;
220
+ /* Scheduler-specific: Filter select min-width */
221
+ .scheduler-list-container .filter-group-inline select {
222
+ min-width: 120px;
223
}
224
449
- .scheduler-sort-desc {
450
- transform: rotate(180deg);
451
- }
225
+ /* Scheduler-specific: Column widths */
226
+ .scheduler-task-table .col-name { width: 25%; }
227
+ .scheduler-task-table .col-state { width: 12%; }
228
+ .scheduler-task-table .col-project { width: 20%; }
229
+ .scheduler-task-table .col-lastrun { width: 23%; }
230
+ .scheduler-task-table .col-actions { width: 20%; }
231
453
- /* Project color ball */
232
+ /* Scheduler-specific: Project color ball */
233
.project-color-ball {
234
display: inline-block;
235
width: 10px;
@@ -460,51 +239,23 @@
239
vertical-align: middle;
240
}
241
463
- /* Responsive */
242
+ /* Responsive - Scheduler-specific */
243
@media (max-width: 870px) {
465
- .scheduler-filters-header {
466
- flex-direction: column;
467
- align-items: stretch;
468
- }
469
-
470
- .filter-group-inline {
471
- width: 100%;
472
- }
473
-
474
- .filter-group-inline select {
475
- flex: 1;
476
- }
477
-
244
.btn-new-task {
245
margin-left: 0;
246
justify-content: center;
247
}
248
483
- .scheduler-status-bar {
484
- flex-direction: column;
485
- gap: 0.75rem;
486
- }
487
-
488
- .status-info {
489
- width: 100%;
490
- justify-content: center;
491
- }
492
-
493
- .status-pagination {
494
- width: 100%;
495
- justify-content: center;
496
- padding-top: 0.5rem;
497
- border-top: 1px solid var(--color-border);
498
- }
499
-
500
- .col-project,
501
- .col-lastrun {
249
+ /* Hide less important columns on mobile */
250
+ .scheduler-task-table .col-project,
251
+ .scheduler-task-table .col-lastrun {
252
display: none;
253
}
254
505
- .col-name { width: 45%; }
506
- .col-state { width: 20%; }
507
- .col-actions { width: 35%; }
255
+ /* Adjust remaining column widths */
256
+ .scheduler-task-table .col-name { width: 45%; }
257
+ .scheduler-task-table .col-state { width: 20%; }
258
+ .scheduler-task-table .col-actions { width: 35%; }
259
}
260
</style>
261
</body>
webui/components/settings/memory/memory-dashboard.html
+106
-413
@@ -14,7 +14,7 @@
14
class="memory-dashboard">
15
16
<!-- Search and Filters Section -->
17
- <div class="memory-filters-header">
17
+ <div class="filters-header 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"
@@ -81,10 +81,10 @@
81
82
<!-- Memory table -->
83
<div x-show="!$store.memoryDashboardStore.loading && !$store.memoryDashboardStore.error"
84
- class="memory-table-container">
84
+ class="data-table-container data-table-container-standalone memory-table-container">
85
86
<!-- Status Bar -->
87
- <div class="memory-status-bar" x-show="!$store.memoryDashboardStore.loading">
87
+ <div class="data-status-bar" x-show="!$store.memoryDashboardStore.loading">
88
<div class="status-info">
89
<span class="status-item">
90
<span class="material-symbols-outlined">database</span>
@@ -164,7 +164,7 @@
164
</div>
165
166
<div class="table-wrapper">
167
- <table class="memory-table">
167
+ <table class="data-table memory-table">
168
<thead>
169
<tr>
170
<th class="col-select">
@@ -288,7 +288,7 @@
288
289
290
<!-- Status Bar -->
291
- <div class="memory-status-bar" x-show="!$store.memoryDashboardStore.loading">
291
+ <div class="data-status-bar data-status-bar-bottom" x-show="!$store.memoryDashboardStore.loading">
292
<div class="status-info">
293
<span class="status-item">
294
<span class="material-symbols-outlined">database</span>
@@ -335,7 +335,7 @@
335
336
<!-- No memories message -->
337
<div x-show="$store.memoryDashboardStore.memories.length === 0 && !$store.memoryDashboardStore.message"
338
- class="no-memories">
338
+ class="data-empty-state">
339
No memories found matching the current filters.
340
</div>
341
@@ -355,6 +355,7 @@
355
</div>
356
357
<style>
358
+ /* Modal footer layout */
359
.modal-inner.modal-with-footer {
360
display: flex;
361
flex-direction: column;
@@ -366,17 +367,13 @@
367
.modal-inner.modal-with-footer .modal-footer {
368
flex-shrink: 0;
369
}
370
+
371
+ /* Memory-specific: Grid-based filter header layout */
372
.memory-filters-header {
373
display: grid;
374
grid-template-columns: 35% 25% 10% auto;
375
grid-template-rows: auto auto;
373
- gap: 0.75rem;
374
- align-items: center;
376
margin: 1rem 0;
376
- padding: 1rem;
377
- background: var(--color-panel);
378
- border: 1px solid var(--color-border);
379
- border-radius: 8px;
377
}
378
379
/* Grid positioning for desktop - 2 rows */
@@ -399,6 +396,15 @@
396
#search-input {
397
grid-column: 1 / 3;
398
grid-row: 2;
399
+ flex: 1;
400
+ border: 1px solid var(--color-border);
401
+ font-size: 0.9rem;
402
+ height: 38px;
403
+ }
404
+ #search-input:focus {
405
+ outline: none;
406
+ border-color: var(--color-primary);
407
+ background: var(--color-input-focus);
408
}
409
.btn-search {
410
grid-column: 3;
@@ -411,223 +417,45 @@
417
justify-self: end;
418
}
419
414
- .filter-group-inline {
415
- display: flex;
420
+ /* Memory-specific filter overrides */
421
+ .memory-filters-header .filter-group-inline {
422
flex: 1;
417
- align-items: center;
418
- gap: 0.5rem;
419
- }
420
- .filter-group-inline label {
421
- font-size: 0.85rem;
422
- font-weight: 600;
423
- color: var(--color-text);
424
- opacity: 0.8;
425
- white-space: nowrap;
423
}
427
- .filter-group-inline select,
428
- .filter-group-inline input {
429
- border: 1px solid var(--color-border);
424
+ .memory-filters-header .filter-group-inline select,
425
+ .memory-filters-header .filter-group-inline input {
426
flex: 0 auto;
431
- min-width: 0;
432
- }
433
- .filter-group-inline select:focus,
434
- .filter-group-inline input:focus {
435
- outline: none;
436
- border-color: var(--color-primary);
437
- background: var(--color-input-focus);
427
}
439
- .filter-group-inline.filter-limit {
428
+ .memory-filters-header .filter-group-inline.filter-limit {
429
flex: 0 0 auto;
430
}
431
.filter-limit input {
432
flex: 0 0 150%;
433
}
445
- #search-input {
446
- flex: 1;
447
- border: 1px solid var(--color-border);
448
- font-size: 0.9rem;
449
- height: 38px;
450
- }
451
- #search-input:focus {
452
- outline: none;
453
- border-color: var(--color-primary);
454
- background: var(--color-input-focus);
455
- }
456
- .filter-threshold label {
457
- font-size: 0.85rem;
458
- opacity: 0.8;
459
- }
460
-
461
- .memory-status-bar {
462
- display: flex;
463
- justify-content: space-between;
464
- align-items: center;
465
- padding: 0.75rem 1rem;
466
- background: var(--color-panel);
467
- border-bottom: 1px solid var(--color-border);
468
- border-radius: 8px;
469
- font-size: 0.9rem;
470
- }
471
- .status-info {
472
- display: flex;
473
- align-items: center;
474
- gap: 0.5rem;
475
- flex-wrap: wrap;
476
- }
477
- .status-item {
478
- display: flex;
479
- align-items: center;
480
- gap: 0.25rem;
481
- }
482
- .status-item strong {
483
- color: var(--color-primary);
484
- font-weight: 600;
485
- }
486
- .status-item .material-symbols-outlined {
487
- font-size: 18px;
488
- opacity: 0.6;
489
- }
490
- .status-separator {
491
- color: var(--color-border);
492
- margin: 0 0.25rem;
493
- }
494
- .status-pagination {
495
- display: flex;
496
- align-items: center;
497
- gap: 0.5rem;
498
- }
499
-
500
- .btn-icon {
501
- background: transparent;
502
- border: 1px solid var(--color-border);
503
- color: var(--color-text);
504
- padding: 0.25rem;
505
- display: flex;
506
- align-items: center;
507
- font-size: 0;
508
- }
509
- .btn-icon:hover:not(:disabled) {
510
- background: var(--color-panel);
511
- border-color: var(--color-primary);
512
- color: var(--color-text);
513
- }
514
- .btn-icon:disabled {
515
- opacity: 0.4;
516
- cursor: not-allowed;
517
- }
518
- .btn-icon .material-symbols-outlined {
519
- font-size: 20px;
520
- }
521
-
522
- .page-input-group {
523
- display: flex;
524
- align-items: center;
525
- gap: 0.5rem;
526
- }
527
- .page-input-group label {
528
- opacity: 0.8;
529
- }
530
- .page-total {
531
- color: var(--color-text);
532
- opacity: 0.8;
533
- font-size: 0.85rem;
534
- white-space: nowrap;
535
- }
536
- .page-total strong {
537
- color: var(--color-primary);
538
- font-weight: 600;
539
- }
540
- .page-input {
541
- width: 60px;
542
- padding: 0.25rem 0.5rem;
543
- text-align: center;
544
- border: 1px solid var(--color-border);
545
- background: var(--color-input);
546
- color: var(--color-text);
547
- border-radius: 4px;
548
- font-size: 0.85rem;
549
- height: 28px;
550
- }
551
- .page-input:focus {
552
- outline: none;
553
- border-color: var(--color-primary);
554
- background: var(--color-input-focus);
555
- }
556
-
557
- /* Remove spinner arrows */
558
- .page-input::-webkit-inner-spin-button,
559
- .page-input::-webkit-outer-spin-button {
560
- -webkit-appearance: none;
561
- margin: 0;
562
- }
563
- .page-input[type=number] {
564
- -moz-appearance: textfield;
565
- appearance: textfield;
566
- }
434
568
- .loading-state,
569
- .error-state,
570
- .no-memories,
571
- .init-message {
572
- text-align: center;
573
- padding: 2rem;
574
- color: var(--color-text);
575
- opacity: 0.8;
576
- background: var(--color-panel);
577
- border: 1px solid var(--color-border);
578
- border-radius: 8px;
579
- margin: 1rem 0;
580
- }
581
- .loading-state {
582
- display: grid;
583
- align-items: center;
435
+ /* Memory-specific: Table layout overrides */
436
+ .memory-table {
437
+ table-layout: fixed;
438
}
585
- .init-message {
586
- color: var(--color-primary);
587
- border-color: var(--color-primary);
439
+ .memory-table th,
440
+ .memory-table td {
441
+ padding: 1rem 0.75rem;
442
+ vertical-align: top;
443
}
589
- .error-state {
590
- color: var(--color-accent);
591
- border-color: var(--color-accent);
592
- }
593
-
594
- .loading-spinner {
595
- width: 24px;
596
- height: 24px;
597
- min-width: 24px;
598
- min-height: 24px;
599
- max-width: 24px;
600
- max-height: 24px;
601
- border: 3px solid var(--color-border);
602
- border-top-color: var(--color-primary);
603
- border-radius: 50%;
604
- animation: spin 1s linear infinite;
605
- margin: 0 auto 0.5rem;
606
- flex-shrink: 0;
607
- flex-grow: 0;
608
- display: inline-block;
609
- position: relative;
610
- box-sizing: border-box;
444
+ .memory-table th {
445
+ position: sticky;
446
+ top: 0;
447
+ z-index: 10;
448
+ border-bottom: 2px solid var(--color-border);
449
}
612
-
613
- @keyframes spin {
614
- to {
615
- transform: rotate(360deg);
616
- }
450
+ .memory-table tbody tr.memory-row > td {
451
+ height: 5em;
452
}
453
619
- .memory-table-container {
620
- border: 1px solid var(--color-border);
621
- border-radius: 8px;
622
- }
623
- .memory-table {
624
- width: 100%;
625
- border-collapse: collapse;
626
- table-layout: fixed;
627
- }
628
-
454
+ /* Memory-specific column widths */
455
.col-select {
456
width: 5%;
457
+ text-align: center;
458
+ padding: 0.65rem 0.5rem !important;
459
}
460
.col-metadata {
461
width: 10em;
@@ -635,16 +463,12 @@
463
.col-actions {
464
width: 4em;
465
}
638
-
639
- .memory-table th,
640
- .memory-table td {
641
- padding: 1rem 0.75rem;
642
- text-align: left;
643
- vertical-align: top;
644
- border-bottom: 1px solid var(--color-border);
645
- overflow: hidden;
466
+ .col-select input[type="checkbox"] {
467
+ cursor: pointer;
468
+ transform: scale(1.2);
469
}
647
-
470
+
471
+ /* Memory-specific: Metadata display */
472
.metadata-cell {
473
overflow: visible;
474
white-space: normal;
@@ -654,6 +478,8 @@
478
display: flex;
479
flex-direction: column;
480
gap: 0.5rem;
481
+ height: 5.5em;
482
+ overflow: hidden;
483
}
484
.metadata-row {
485
display: flex;
@@ -666,6 +492,7 @@
492
font-family: monospace;
493
}
494
495
+ /* Memory-specific: Area badge */
496
.area-badge {
497
color: white;
498
padding: 0.35rem 0.75rem;
@@ -674,95 +501,8 @@
501
font-weight: bold;
502
text-transform: uppercase;
503
}
677
- .select-cell,
678
- .col-select {
679
- text-align: center;
680
- padding: 0.65rem 0.5rem !important;
681
- }
682
- .select-cell input[type="checkbox"],
683
- .col-select input[type="checkbox"] {
684
- cursor: pointer;
685
- transform: scale(1.2);
686
- }
687
- .memory-row.selected {
688
- background: var(--color-panel);
689
- border-left: 3px solid var(--color-primary);
690
- }
691
- .mass-action-toolbar {
692
- display: flex;
693
- justify-content: space-between;
694
- align-items: center;
695
- padding: 1rem;
696
- background: var(--color-panel);
697
- margin: 0;
698
- animation: slideDown 0.3s ease;
699
- }
700
- .btn-mass {
701
- align-items: center;
702
- border: 1px solid var(--color-border);
703
- gap: 0.25rem;
704
- display: flex;
705
- background: var(--color-background);
706
- color: var(--color-text);
707
- }
708
- .btn-mass:hover {
709
- border-color: var(--color-primary);
710
- color: var(--color-primary);
711
- }
712
- .btn-mass.delete:hover {
713
- border-color: var(--color-accent);
714
- color: var(--color-accent);
715
- }
716
- .selection-info {
717
- font-weight: 600;
718
- }
719
- .mass-actions {
720
- display: flex;
721
- gap: 0.5rem;
722
- }
723
- .btn-mass .material-symbols-outlined {
724
- font-size: 18px;
725
- }
726
- .btn-mass:hover {
727
- border-color: var(--color-primary);
728
- background: var(--color-panel);
729
- }
730
- .btn-mass.delete:hover {
731
- border-color: var(--color-accent);
732
- color: var(--color-accent);
733
- }
734
-
735
- @keyframes slideDown {
736
- from {
737
- opacity: 0;
738
- transform: translateY(-10px);
739
- }
740
-
741
- to {
742
- opacity: 1;
743
- transform: translateY(0);
744
- }
745
- }
746
-
747
- .memory-table th {
748
- background: var(--color-panel);
749
- font-weight: 600;
750
- color: var(--color-text);
751
- position: sticky;
752
- top: 0;
753
- z-index: 10;
754
- border-bottom: 2px solid var(--color-border);
755
- }
756
- .memory-table tbody tr {
757
- border-bottom: 1px solid var(--color-border);
758
- }
759
- .memory-table tbody tr:last-child {
760
- border-bottom: none;
761
- }
762
- .memory-table tbody tr.memory-row>td {
763
- height: 5em;
764
- }
504
505
+ /* Memory-specific: Source type badge */
506
.source-type {
507
padding: 0.25rem 0.5rem;
508
border-radius: var(--border-radius);
@@ -773,13 +513,16 @@
513
white-space: nowrap;
514
flex-shrink: 0;
515
}
776
- .content-preview,
777
- .metadata-info {
516
+
517
+ /* Memory-specific: Content preview */
518
+ .content-preview {
519
height: 5.5em;
520
overflow: hidden;
521
word-wrap: break-word;
522
overflow-wrap: break-word;
523
}
524
+
525
+ /* Memory-specific: Tags */
526
.tags {
527
display: flex;
528
gap: 0.25rem;
@@ -795,70 +538,47 @@
538
color: var(--color-text);
539
opacity: 0.8;
540
}
541
+
542
+ /* Memory-specific: Selection state */
543
+ .memory-row.selected {
544
+ background: var(--color-panel);
545
+ border-left: 3px solid var(--color-primary);
546
+ }
547
+ .select-cell {
548
+ text-align: center;
549
+ padding: 0.65rem 0.5rem !important;
550
+ }
551
+ .select-cell input[type="checkbox"] {
552
+ cursor: pointer;
553
+ transform: scale(1.2);
554
+ }
555
+
556
+ /* Memory-specific: Actions cell vertical layout */
557
.actions-cell {
558
padding: 0 !important;
559
position: relative;
560
}
802
- .actions-wrapper {
561
+ .memory-table .actions-wrapper {
562
position: absolute;
563
top: 0;
564
left: 0;
565
right: 0;
566
bottom: 0;
808
- display: flex;
567
flex-direction: column;
810
- align-items: center;
568
justify-content: center;
569
}
570
814
- /* Table action buttons - extends .btn */
815
- .btn-action {
816
- background: none;
817
- border: 1px solid var(--color-border);
818
- padding: 0.25rem;
819
- margin: 0 0.1rem;
820
- color: var(--color-text);
821
- opacity: 0.7;
822
- display: inline-flex;
823
- }
824
- .btn-action .material-symbols-outlined {
825
- font-size: 18px;
826
- }
827
- .btn-action:hover {
828
- opacity: 1;
829
- background: var(--color-panel);
830
- border-color: var(--color-primary);
831
- color: var(--color-primary);
832
- transform: translateY(-1px);
833
- }
834
- .btn-action.view:hover {
835
- background: var(--color-panel);
836
- border-color: var(--color-primary);
837
- color: var(--color-primary);
838
- }
839
- .btn-action.copy:hover {
840
- background: var(--color-panel);
841
- border-color: var(--color-primary);
842
- color: var(--color-primary);
843
- }
844
- .btn-action.delete:hover {
845
- background: var(--color-panel);
846
- border-color: var(--color-accent);
847
- color: var(--color-accent);
848
- }
849
-
850
- /* Responsive design */
571
+ /* Responsive design - Memory-specific */
572
@media (max-width: 870px) {
573
.memory-dashboard {
574
padding: 0.5rem;
575
}
855
- .memory-filters-header {
856
- padding: 0.75rem;
857
- }
576
+
577
+ /* Switch to flex layout on mobile */
578
.memory-filters-header {
579
display: flex;
580
flex-wrap: wrap;
861
- gap: 0.75rem;
581
+ padding: 0.75rem;
582
}
583
.content-preview,
584
.metadata-info {
@@ -876,29 +596,8 @@
596
grid-column: auto;
597
grid-row: auto;
598
}
879
- .filter-group-inline {
880
- width: 100%;
881
- flex-direction: row;
882
- align-items: center;
883
- gap: 0.5rem;
884
- }
885
- .filter-group-inline label {
886
- font-size: 0.9rem;
887
- margin-bottom: 0.25rem;
888
- }
889
- .filter-group-inline select,
890
- .filter-group-inline input {
891
- height: 42px;
892
- font-size: 1rem;
893
- }
894
- .filter-group-inline.filter-limit {
895
- flex: 33%;
896
- }
897
- .filter-limit input {
898
- flex: 1;
899
- }
599
901
- /* Mobile order and layout - force items to specific widths */
600
+ /* Mobile order and layout */
601
.filter-memory-dir {
602
order: 1;
603
flex: 1 1 100%;
@@ -911,29 +610,6 @@
610
order: 3;
611
flex: 1 1 100%;
612
}
914
- .btn-clear {
915
- order: 6;
916
- flex: 40%;
917
- min-width: 80px;
918
- padding: 0;
919
- height: 42px;
920
- justify-content: center;
921
- }
922
- #search-input {
923
- order: 5;
924
- flex: auto;
925
- width: 100%;
926
- height: 42px;
927
- }
928
-
929
- /* Search button and threshold on same row */
930
- .btn-search {
931
- order: 7;
932
- flex: 40%;
933
- height: 42px;
934
- padding: 0 1rem;
935
- min-width: 80px;
936
- }
613
.filter-threshold {
614
order: 4;
615
flex: 40%;
@@ -951,24 +627,41 @@
627
.filter-threshold input[type="range"] {
628
width: 100%;
629
}
954
- .memory-status-bar {
955
- flex-direction: column;
956
- gap: 0.75rem;
957
- }
958
- .status-info {
630
+ #search-input {
631
+ order: 5;
632
+ flex: auto;
633
width: 100%;
634
+ height: 42px;
635
+ }
636
+ .btn-clear {
637
+ order: 6;
638
+ flex: 40%;
639
+ min-width: 80px;
640
+ padding: 0;
641
+ height: 42px;
642
justify-content: center;
961
- flex-wrap: wrap;
962
- gap: 0.5rem 1rem;
643
}
964
- .status-info .material-symbols-outlined {
965
- font-size: 18px;
644
+ .btn-search {
645
+ order: 7;
646
+ flex: 40%;
647
+ height: 42px;
648
+ padding: 0 1rem;
649
+ min-width: 80px;
650
}
967
- .status-pagination {
651
+
652
+ .memory-filters-header .filter-group-inline {
653
width: 100%;
969
- justify-content: center;
970
- padding-top: 0.5rem;
971
- border-top: 1px solid var(--color-border);
654
+ }
655
+ .memory-filters-header .filter-group-inline select,
656
+ .memory-filters-header .filter-group-inline input {
657
+ height: 42px;
658
+ font-size: 1rem;
659
+ }
660
+ .memory-filters-header .filter-group-inline.filter-limit {
661
+ flex: 33%;
662
+ }
663
+ .filter-limit input {
664
+ flex: 1;
665
}
666
667
.memory-table th,
webui/css/tables.css
new
+469
@@ -0,0 +1,469 @@
1
+/* ==========================================================================
2
+ Tables CSS - Shared styles for data tables and list views
3
+ Used by: Memory Dashboard, Task Scheduler, and similar components
4
+ ========================================================================== */
5
+
6
+/* ==========================================================================
7
+ FILTER HEADER
8
+ Flex-based filter layout for search/filter controls
9
+ ========================================================================== */
10
+.filters-header {
11
+ display: flex;
12
+ flex-wrap: wrap;
13
+ gap: 0.75rem;
14
+ align-items: center;
15
+ padding: 1rem;
16
+ background: var(--color-panel);
17
+ border: 1px solid var(--color-border);
18
+ border-radius: 8px;
19
+ margin-bottom: 0;
20
+}
21
+
22
+.filter-group-inline {
23
+ display: flex;
24
+ align-items: center;
25
+ gap: 0.5rem;
26
+}
27
+
28
+.filter-group-inline label {
29
+ font-size: 0.85rem;
30
+ font-weight: 600;
31
+ color: var(--color-text);
32
+ opacity: 0.8;
33
+ white-space: nowrap;
34
+}
35
+
36
+.filter-group-inline select,
37
+.filter-group-inline input {
38
+ border: 1px solid var(--color-border);
39
+ min-width: 0;
40
+}
41
+
42
+.filter-group-inline select:focus,
43
+.filter-group-inline input:focus {
44
+ outline: none;
45
+ border-color: var(--color-primary);
46
+ background: var(--color-input-focus);
47
+}
48
+
49
+/* Filter threshold (range slider with label) */
50
+.filter-threshold {
51
+ display: flex;
52
+ flex-direction: column;
53
+ gap: 0.25rem;
54
+}
55
+
56
+.filter-threshold label {
57
+ font-size: 0.85rem;
58
+ opacity: 0.8;
59
+}
60
+
61
+/* ==========================================================================
62
+ STATUS BAR
63
+ Status info and pagination controls
64
+ ========================================================================== */
65
+.data-status-bar {
66
+ display: flex;
67
+ justify-content: space-between;
68
+ align-items: center;
69
+ padding: 0.75rem 1rem;
70
+ background: var(--color-panel);
71
+ border: 1px solid var(--color-border);
72
+ border-top: none;
73
+ font-size: 0.9rem;
74
+}
75
+
76
+.data-status-bar-top {
77
+ border-top: 1px solid var(--color-border);
78
+ border-bottom: none;
79
+ border-radius: 8px 8px 0 0;
80
+}
81
+
82
+.data-status-bar-bottom {
83
+ border-top: 1px solid var(--color-border);
84
+ border-radius: 0 0 8px 8px;
85
+}
86
+
87
+.data-status-bar-standalone {
88
+ border: 1px solid var(--color-border);
89
+ border-radius: 8px;
90
+}
91
+
92
+.status-info {
93
+ display: flex;
94
+ align-items: center;
95
+ gap: 0.5rem;
96
+ flex-wrap: wrap;
97
+}
98
+
99
+.status-item {
100
+ display: flex;
101
+ align-items: center;
102
+ gap: 0.25rem;
103
+}
104
+
105
+.status-item strong {
106
+ color: var(--color-primary);
107
+ font-weight: 600;
108
+}
109
+
110
+.status-item .material-symbols-outlined {
111
+ font-size: 18px;
112
+ opacity: 0.6;
113
+}
114
+
115
+.status-separator {
116
+ color: var(--color-border);
117
+ margin: 0 0.25rem;
118
+}
119
+
120
+/* ==========================================================================
121
+ PAGINATION
122
+ Page navigation controls
123
+ ========================================================================== */
124
+.status-pagination {
125
+ display: flex;
126
+ align-items: center;
127
+ gap: 0.5rem;
128
+}
129
+
130
+.page-input-group {
131
+ display: flex;
132
+ align-items: center;
133
+ gap: 0.5rem;
134
+}
135
+
136
+.page-input-group label {
137
+ opacity: 0.8;
138
+}
139
+
140
+.page-total {
141
+ color: var(--color-text);
142
+ opacity: 0.8;
143
+ font-size: 0.85rem;
144
+ white-space: nowrap;
145
+}
146
+
147
+.page-total strong {
148
+ color: var(--color-primary);
149
+ font-weight: 600;
150
+}
151
+
152
+.page-input {
153
+ width: 60px;
154
+ padding: 0.25rem 0.5rem;
155
+ text-align: center;
156
+ border: 1px solid var(--color-border);
157
+ background: var(--color-input);
158
+ color: var(--color-text);
159
+ border-radius: 4px;
160
+ font-size: 0.85rem;
161
+ height: 28px;
162
+}
163
+
164
+.page-input:focus {
165
+ outline: none;
166
+ border-color: var(--color-primary);
167
+ background: var(--color-input-focus);
168
+}
169
+
170
+/* Remove spinner arrows from number input */
171
+.page-input::-webkit-inner-spin-button,
172
+.page-input::-webkit-outer-spin-button {
173
+ -webkit-appearance: none;
174
+ margin: 0;
175
+}
176
+
177
+.page-input[type=number] {
178
+ -moz-appearance: textfield;
179
+ appearance: textfield;
180
+}
181
+
182
+/* ==========================================================================
183
+ TABLE CONTAINER & BASE STYLES
184
+ Wrapper and base table styling
185
+ ========================================================================== */
186
+.data-table-container {
187
+ border: 1px solid var(--color-border);
188
+ border-top: none;
189
+ border-radius: 0 0 8px 8px;
190
+ overflow: hidden;
191
+}
192
+
193
+.data-table-container-standalone {
194
+ border-top: 1px solid var(--color-border);
195
+ border-radius: 8px;
196
+}
197
+
198
+.table-wrapper {
199
+ overflow-x: auto;
200
+}
201
+
202
+.data-table {
203
+ width: 100%;
204
+ border-collapse: collapse;
205
+ table-layout: auto;
206
+ font-size: 0.9rem;
207
+}
208
+
209
+.data-table th,
210
+.data-table td {
211
+ padding: 0.75rem;
212
+ text-align: left;
213
+ border-bottom: 1px solid var(--color-border);
214
+ vertical-align: middle;
215
+ overflow: hidden;
216
+ text-overflow: ellipsis;
217
+ white-space: nowrap;
218
+}
219
+
220
+.data-table th {
221
+ background: var(--color-panel);
222
+ font-weight: 600;
223
+ color: var(--color-text);
224
+}
225
+
226
+.data-table th.sortable {
227
+ cursor: pointer;
228
+ user-select: none;
229
+}
230
+
231
+.data-table th.sortable:hover {
232
+ background: var(--color-bg-tertiary);
233
+}
234
+
235
+.data-table tbody tr {
236
+ cursor: pointer;
237
+ transition: background-color 0.2s ease;
238
+}
239
+
240
+.data-table tbody tr:hover {
241
+ background-color: rgba(255, 255, 255, 0.03);
242
+}
243
+
244
+.light-mode .data-table tbody tr:hover {
245
+ background-color: rgba(0, 0, 0, 0.02);
246
+}
247
+
248
+.data-table tbody tr:last-child td {
249
+ border-bottom: none;
250
+}
251
+
252
+/* Sticky header for scrollable tables */
253
+.data-table th.sticky {
254
+ position: sticky;
255
+ top: 0;
256
+ z-index: 10;
257
+ border-bottom: 2px solid var(--color-border);
258
+}
259
+
260
+/* ==========================================================================
261
+ SORT INDICATOR
262
+ Arrow indicator for sortable columns
263
+ ========================================================================== */
264
+.sort-indicator {
265
+ display: inline-block;
266
+ margin-left: 4px;
267
+ transition: transform 0.2s ease;
268
+}
269
+
270
+.sort-indicator.desc {
271
+ transform: rotate(180deg);
272
+}
273
+
274
+/* ==========================================================================
275
+ EMPTY & LOADING STATES
276
+ Empty results and loading indicators
277
+ ========================================================================== */
278
+.data-empty-state {
279
+ text-align: center;
280
+ padding: 3rem 1rem;
281
+ color: var(--color-text);
282
+ opacity: 0.8;
283
+ background: var(--color-panel);
284
+ border: 1px solid var(--color-border);
285
+ border-top: none;
286
+ border-radius: 0 0 8px 8px;
287
+}
288
+
289
+.data-empty-state-standalone {
290
+ border-top: 1px solid var(--color-border);
291
+ border-radius: 8px;
292
+}
293
+
294
+.data-empty-state .btn {
295
+ display: inline-flex;
296
+ align-items: center;
297
+ gap: 0.5rem;
298
+ margin-top: 1rem;
299
+}
300
+
301
+.loading-state {
302
+ text-align: center;
303
+ padding: 2rem;
304
+ color: var(--color-text);
305
+ opacity: 0.8;
306
+ background: var(--color-panel);
307
+ border: 1px solid var(--color-border);
308
+ border-radius: 8px;
309
+ margin: 1rem 0;
310
+ display: grid;
311
+ align-items: center;
312
+}
313
+
314
+.error-state {
315
+ text-align: center;
316
+ padding: 2rem;
317
+ color: var(--color-accent);
318
+ border: 1px solid var(--color-accent);
319
+ background: var(--color-panel);
320
+ border-radius: 8px;
321
+ margin: 1rem 0;
322
+}
323
+
324
+.init-message {
325
+ text-align: center;
326
+ padding: 2rem;
327
+ color: var(--color-primary);
328
+ border: 1px solid var(--color-primary);
329
+ background: var(--color-panel);
330
+ border-radius: 8px;
331
+ margin: 1rem 0;
332
+}
333
+
334
+.loading-spinner {
335
+ width: 24px;
336
+ height: 24px;
337
+ min-width: 24px;
338
+ min-height: 24px;
339
+ max-width: 24px;
340
+ max-height: 24px;
341
+ border: 3px solid var(--color-border);
342
+ border-top-color: var(--color-primary);
343
+ border-radius: 50%;
344
+ animation: spin 1s linear infinite;
345
+ margin: 0 auto 0.5rem;
346
+ flex-shrink: 0;
347
+ flex-grow: 0;
348
+ display: inline-block;
349
+ position: relative;
350
+ box-sizing: border-box;
351
+}
352
+
353
+@keyframes spin {
354
+ to {
355
+ transform: rotate(360deg);
356
+ }
357
+}
358
+
359
+/* ==========================================================================
360
+ MASS ACTION TOOLBAR
361
+ Bulk action controls for selected items
362
+ ========================================================================== */
363
+.mass-action-toolbar {
364
+ display: flex;
365
+ justify-content: space-between;
366
+ align-items: center;
367
+ padding: 1rem;
368
+ background: var(--color-panel);
369
+ margin: 0;
370
+ animation: slideDown 0.3s ease;
371
+}
372
+
373
+.selection-info {
374
+ font-weight: 600;
375
+}
376
+
377
+.mass-actions {
378
+ display: flex;
379
+ gap: 0.5rem;
380
+}
381
+
382
+.btn-mass {
383
+ align-items: center;
384
+ border: 1px solid var(--color-border);
385
+ gap: 0.25rem;
386
+ display: flex;
387
+ background: var(--color-background);
388
+ color: var(--color-text);
389
+}
390
+
391
+.btn-mass .material-symbols-outlined {
392
+ font-size: 18px;
393
+}
394
+
395
+.btn-mass:hover {
396
+ border-color: var(--color-primary);
397
+ color: var(--color-primary);
398
+ background: var(--color-panel);
399
+}
400
+
401
+.btn-mass.delete:hover {
402
+ border-color: var(--color-accent);
403
+ color: var(--color-accent);
404
+}
405
+
406
+@keyframes slideDown {
407
+ from {
408
+ opacity: 0;
409
+ transform: translateY(-10px);
410
+ }
411
+ to {
412
+ opacity: 1;
413
+ transform: translateY(0);
414
+ }
415
+}
416
+
417
+/* ==========================================================================
418
+ RESPONSIVE STYLES
419
+ Mobile breakpoints for tables and status bars
420
+ ========================================================================== */
421
+@media (max-width: 870px) {
422
+ .filters-header {
423
+ flex-direction: column;
424
+ align-items: stretch;
425
+ }
426
+
427
+ .filter-group-inline {
428
+ width: 100%;
429
+ }
430
+
431
+ .filter-group-inline select,
432
+ .filter-group-inline input {
433
+ flex: 1;
434
+ }
435
+
436
+ .data-status-bar {
437
+ flex-direction: column;
438
+ gap: 0.75rem;
439
+ }
440
+
441
+ .status-info {
442
+ width: 100%;
443
+ justify-content: center;
444
+ }
445
+
446
+ .status-pagination {
447
+ width: 100%;
448
+ justify-content: center;
449
+ padding-top: 0.5rem;
450
+ border-top: 1px solid var(--color-border);
451
+ }
452
+
453
+ .data-table th,
454
+ .data-table td {
455
+ padding: 0.5rem 0.25rem;
456
+ font-size: 0.85rem;
457
+ }
458
+
459
+ .mass-action-toolbar {
460
+ flex-direction: column;
461
+ gap: 0.75rem;
462
+ }
463
+
464
+ .mass-actions {
465
+ flex-wrap: wrap;
466
+ justify-content: center;
467
+ }
468
+}
469
+
webui/index.html
+1
@@ -17,6 +17,7 @@
17
<link rel="stylesheet" href="css/scheduler.css">
18
<link rel="stylesheet" href="css/notification.css">
19
<link rel="stylesheet" href="css/buttons.css">
20
+ <link rel="stylesheet" href="css/tables.css">
21
22
<!-- Flatpickr for datetime picker -->
23
<link rel="stylesheet" href="vendor/flatpickr/flatpickr.min.css">