scheduler redesign
3clyp50 committed
Dec 26, 2025 at 14:37 UTC
76fd1b7d088509cbbc59161b2987aa16fd98c466
9 files changed
+892
-369
webui/components/modals/scheduler/scheduler-settings.html
+13
-9
@@ -7,17 +7,21 @@
7
<body>
8
<div x-data>
9
<template x-if="$store.schedulerStore">
10
- <div id="scheduler-settings-root">
11
- <div id="section-task-scheduler" class="section">
12
- <div class="section-title">Task Scheduler</div>
13
- <div class="section-description">Manage scheduled tasks and automated processes for Agent Zero.</div>
14
-
15
- <x-component path="modals/scheduler/scheduler-task-editor.html"></x-component>
16
- <x-component path="modals/scheduler/scheduler-task-list.html"></x-component>
17
- </div>
10
+ <div id="scheduler-settings-root" class="scheduler-dashboard">
11
+ <div class="scheduler-dashboard-description">Manage scheduled tasks and automated processes for Agent Zero.</div>
12
+ <x-component path="modals/scheduler/scheduler-task-editor.html"></x-component>
13
+ <x-component path="modals/scheduler/scheduler-task-list.html"></x-component>
14
</div>
15
</template>
16
</div>
17
+
18
+<style>
19
+ .scheduler-dashboard {
20
+ padding: 0;
21
+ }
22
+ .scheduler-dashboard-description {
23
+ padding: 1rem 1rem;
24
+ }
25
+</style>
26
</body>
27
</html>
23
-
webui/components/modals/scheduler/scheduler-store.js
+48
@@ -462,6 +462,10 @@ const schedulerStoreModel = {
462
viewMode: readPersistedViewMode(),
463
selectedTaskForDetail: null,
464
465
+ // Pagination ---------------------------------------------------------------
466
+ currentPage: 1,
467
+ pageSize: 10,
468
+
469
// Editor state -------------------------------------------------------------
470
isCreating: false,
471
isEditing: false,
@@ -493,6 +497,23 @@ const schedulerStoreModel = {
497
return this.sortTasks(filtered);
498
},
499
500
+ get totalPages() {
501
+ if (!Array.isArray(this.filteredTasks) || this.filteredTasks.length === 0) return 1;
502
+ return Math.ceil(this.filteredTasks.length / this.pageSize);
503
+ },
504
+
505
+ get paginatedTasks() {
506
+ if (!Array.isArray(this.filteredTasks)) return [];
507
+ // Ensure currentPage is within valid range
508
+ const maxPage = this.totalPages;
509
+ if (this.currentPage > maxPage) {
510
+ this.currentPage = Math.max(1, maxPage);
511
+ }
512
+ const start = (this.currentPage - 1) * this.pageSize;
513
+ const end = start + this.pageSize;
514
+ return this.filteredTasks.slice(start, end);
515
+ },
516
+
517
get attachmentsText() {
518
const attachments = Array.isArray(this.editingTask.attachments)
519
? this.editingTask.attachments
@@ -803,6 +824,33 @@ const schedulerStoreModel = {
824
this.sortField = field;
825
this.sortDirection = "asc";
826
}
827
+ // Reset to first page when sorting changes
828
+ this.currentPage = 1;
829
+ },
830
+
831
+ // Pagination methods --------------------------------------------------------
832
+ nextPage() {
833
+ if (this.currentPage < this.totalPages) {
834
+ this.currentPage++;
835
+ }
836
+ },
837
+
838
+ prevPage() {
839
+ if (this.currentPage > 1) {
840
+ this.currentPage--;
841
+ }
842
+ },
843
+
844
+ goToPage(page) {
845
+ const pageNum = parseInt(page, 10);
846
+ if (Number.isNaN(pageNum)) return;
847
+ if (pageNum < 1) {
848
+ this.currentPage = 1;
849
+ } else if (pageNum > this.totalPages) {
850
+ this.currentPage = this.totalPages;
851
+ } else {
852
+ this.currentPage = pageNum;
853
+ }
854
},
855
856
sortTasks(tasks) {
webui/components/modals/scheduler/scheduler-task-detail.html
+7
@@ -144,6 +144,13 @@
144
</div>
145
</template>
146
</div>
147
+
148
+<style>
149
+ .scheduler-detail-header-actions .btn-cancel {
150
+ margin-left: var(--spacing-sm);
151
+ }
152
+</style>
153
+
154
</body>
155
</html>
156
webui/components/modals/scheduler/scheduler-task-editor.html
+105
-40
@@ -13,11 +13,11 @@
13
<div class="scheduler-form-header">
14
<div class="scheduler-form-title">Create New Task</div>
15
<div class="scheduler-form-actions">
16
- <button class="btn btn-ok btn-field" @click="$store.schedulerStore.saveTask()">
17
- Save
16
+ <button class="btn btn-action-header cancel" @click="$store.schedulerStore.cancelEdit()" title="Cancel">
17
+ <span class="material-symbols-outlined">close</span>
18
</button>
19
- <button class="btn btn-cancel" @click="$store.schedulerStore.cancelEdit()">
20
- Cancel
19
+ <button class="btn btn-action-header confirm" @click="$store.schedulerStore.saveTask()" title="Save Task">
20
+ <span class="material-symbols-outlined">check</span>
21
</button>
22
</div>
23
</div>
@@ -142,12 +142,7 @@
142
<span x-text="$store.schedulerStore.formatDate(time)"></span>
143
<button @click.prevent="$store.schedulerStore.editingTask.plan.todo.splice(index, 1)"
144
class="scheduler-todo-remove">
145
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"
146
- fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
147
- stroke-linejoin="round">
148
- <line x1="18" y1="6" x2="6" y2="18"></line>
149
- <line x1="6" y1="6" x2="18" y2="18"></line>
150
- </svg>
145
+ <span class="material-symbols-outlined">close</span>
146
</button>
147
</div>
148
</template>
@@ -162,14 +157,8 @@
157
class="scheduler-flatpickr-input"
158
placeholder="Select date and time">
159
<button @click.prevent="$store.schedulerStore.addPlannedTime('create')"
165
- class="scheduler-add-todo-button">
166
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"
167
- fill="none" stroke="currentColor" stroke-width="2"
168
- stroke-linecap="round" stroke-linejoin="round"
169
- style="margin-right: 4px;">
170
- <line x1="12" y1="5" x2="12" y2="19"></line>
171
- <line x1="5" y1="12" x2="19" y2="12"></line>
172
- </svg>
160
+ class="btn btn-ok scheduler-add-todo-button">
161
+ <span class="material-symbols-outlined">add</span>
162
Add Time
163
</button>
164
</div>
@@ -187,8 +176,10 @@
176
<div class="input-group">
177
<input type="text" x-model="$store.schedulerStore.editingTask.token"
178
placeholder="Token for ad-hoc task">
190
- <button class="scheduler-task-action"
191
- @click="$store.schedulerStore.editingTask.token = $store.schedulerStore.generateRandomToken()">
179
+ <button class="btn btn-action generate-token"
180
+ @click="$store.schedulerStore.editingTask.token = $store.schedulerStore.generateRandomToken()"
181
+ title="Generate Token">
182
+ <span class="material-symbols-outlined">refresh</span>
183
Generate
184
</button>
185
</div>
@@ -228,11 +219,11 @@
219
<div class="scheduler-form-header">
220
<div class="scheduler-form-title">Edit Task</div>
221
<div class="scheduler-form-actions">
231
- <button class="btn btn-ok btn-field" @click="$store.schedulerStore.saveTask()">
232
- Save
222
+ <button class="btn btn-action-header cancel" @click="$store.schedulerStore.cancelEdit()" title="Cancel">
223
+ <span class="material-symbols-outlined">close</span>
224
</button>
234
- <button class="btn btn-cancel" @click="$store.schedulerStore.cancelEdit()">
235
- Cancel
225
+ <button class="btn btn-action-header confirm" @click="$store.schedulerStore.saveTask()" title="Save Task">
226
+ <span class="material-symbols-outlined">check</span>
227
</button>
228
</div>
229
</div>
@@ -352,12 +343,7 @@
343
<span x-text="$store.schedulerStore.formatDate(time)"></span>
344
<button @click.prevent="$store.schedulerStore.editingTask.plan.todo.splice(index, 1)"
345
class="scheduler-todo-remove">
355
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"
356
- fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
357
- stroke-linejoin="round">
358
- <line x1="18" y1="6" x2="6" y2="18"></line>
359
- <line x1="6" y1="6" x2="18" y2="18"></line>
360
- </svg>
346
+ <span class="material-symbols-outlined">close</span>
347
</button>
348
</div>
349
</template>
@@ -372,14 +358,8 @@
358
class="scheduler-flatpickr-input"
359
placeholder="Select date and time">
360
<button @click.prevent="$store.schedulerStore.addPlannedTime('edit')"
375
- class="scheduler-add-todo-button">
376
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"
377
- fill="none" stroke="currentColor" stroke-width="2"
378
- stroke-linecap="round" stroke-linejoin="round"
379
- style="margin-right: 4px;">
380
- <line x1="12" y1="5" x2="12" y2="19"></line>
381
- <line x1="5" y1="12" x2="19" y2="12"></line>
382
- </svg>
361
+ class="btn btn-ok scheduler-add-todo-button">
362
+ <span class="material-symbols-outlined">add</span>
363
Add Time
364
</button>
365
</div>
@@ -397,8 +377,10 @@
377
<div class="input-group">
378
<input type="text" x-model="$store.schedulerStore.editingTask.token"
379
placeholder="Token for ad-hoc task">
400
- <button class="scheduler-task-action"
401
- @click="$store.schedulerStore.editingTask.token = $store.schedulerStore.generateRandomToken()">
380
+ <button class="btn btn-action generate-token"
381
+ @click="$store.schedulerStore.editingTask.token = $store.schedulerStore.generateRandomToken()"
382
+ title="Generate Token">
383
+ <span class="material-symbols-outlined">refresh</span>
384
Generate
385
</button>
386
</div>
@@ -435,6 +417,89 @@
417
</div>
418
</template>
419
</div>
420
+
421
+<style>
422
+ /* Form header with icon buttons - matches memory-detail-modal pattern */
423
+ .scheduler-form-header {
424
+ display: flex;
425
+ justify-content: space-between;
426
+ align-items: center;
427
+ padding: 0.75rem 0;
428
+ margin-bottom: 1rem;
429
+ border-bottom: 1px solid var(--color-border);
430
+ }
431
+
432
+ .scheduler-form-title {
433
+ font-size: 1.2rem;
434
+ font-weight: 600;
435
+ color: var(--color-primary);
436
+ }
437
+
438
+ .scheduler-form-actions {
439
+ display: flex;
440
+ gap: 0.5rem;
441
+ }
442
+
443
+ /* Generate token button - extends .btn-action from buttons.css */
444
+ .btn-action.generate-token {
445
+ padding: 0.4rem 0.75rem;
446
+ white-space: nowrap;
447
+ }
448
+
449
+ /* Todo remove button with Material Symbol */
450
+ .scheduler-todo-remove {
451
+ background: none;
452
+ border: 1px solid var(--color-border);
453
+ padding: 0.15rem;
454
+ color: var(--color-text);
455
+ opacity: 0.7;
456
+ display: inline-flex;
457
+ align-items: center;
458
+ justify-content: center;
459
+ border-radius: 4px;
460
+ cursor: pointer;
461
+ }
462
+
463
+ .scheduler-todo-remove .material-symbols-outlined {
464
+ font-size: 16px;
465
+ }
466
+
467
+ .scheduler-todo-remove:hover {
468
+ opacity: 1;
469
+ border-color: var(--color-accent);
470
+ color: var(--color-accent);
471
+ }
472
+
473
+ /* Add todo button styling */
474
+ .scheduler-add-todo-button {
475
+ display: inline-flex;
476
+ align-items: center;
477
+ gap: 0.25rem;
478
+ }
479
+
480
+ .scheduler-add-todo-button .material-symbols-outlined {
481
+ font-size: 18px;
482
+ }
483
+
484
+ /* Project color ball */
485
+ .project-color-ball {
486
+ display: inline-block;
487
+ width: 10px;
488
+ height: 10px;
489
+ border-radius: 50%;
490
+ margin-right: 6px;
491
+ vertical-align: middle;
492
+ }
493
+
494
+ .project-display {
495
+ display: flex;
496
+ align-items: center;
497
+ padding: 0.5rem;
498
+ background: var(--color-background);
499
+ border: 1px solid var(--color-border);
500
+ border-radius: 4px;
501
+ }
502
+</style>
503
</body>
504
</html>
505
webui/components/modals/scheduler/scheduler-task-list.html
+474
-98
@@ -7,21 +7,14 @@
7
<body>
8
<div x-data>
9
<template x-if="$store.schedulerStore">
10
- <div class="scheduler-container"
10
+ <div class="scheduler-list-container"
11
x-show="!$store.schedulerStore.isCreating && !$store.schedulerStore.isEditing">
12
- <div class="scheduler-header">
13
- <h2>Task Management</h2>
14
- <div class="scheduler-actions">
15
- <button class="btn btn-ok" @click="$store.schedulerStore.startCreateTask()">
16
- New Task
17
- </button>
18
- </div>
19
- </div>
12
21
- <div class="scheduler-filters">
22
- <div class="scheduler-filter-group">
23
- <span class="scheduler-filter-label">Type:</span>
24
- <select class="scheduler-filter-select" x-model="$store.schedulerStore.filterType">
13
+ <!-- Filter Header -->
14
+ <div class="scheduler-filters-header">
15
+ <div class="filter-group-inline filter-type">
16
+ <label>Type</label>
17
+ <select x-model="$store.schedulerStore.filterType">
18
<option value="all">All Types</option>
19
<option value="scheduled">Scheduled</option>
20
<option value="adhoc">Ad-hoc</option>
@@ -29,9 +22,9 @@
22
</select>
23
</div>
24
32
- <div class="scheduler-filter-group">
33
- <span class="scheduler-filter-label">State:</span>
34
- <select class="scheduler-filter-select" x-model="$store.schedulerStore.filterState">
25
+ <div class="filter-group-inline filter-state">
26
+ <label>State</label>
27
+ <select x-model="$store.schedulerStore.filterState">
28
<option value="all">All States</option>
29
<option value="idle">Idle</option>
30
<option value="running">Running</option>
@@ -39,98 +32,481 @@
32
<option value="error">Error</option>
33
</select>
34
</div>
35
+
36
+ <button class="btn btn-ok btn-new-task" @click="$store.schedulerStore.startCreateTask()">
37
+ <span class="material-symbols-outlined">add</span>
38
+ New Task
39
+ </button>
40
</div>
41
42
+ <!-- Status Bar -->
43
+ <div class="scheduler-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>
47
+ Total: <strong x-text="$store.schedulerStore.tasks.length"></strong>
48
+ </span>
49
+ <span class="status-separator">•</span>
50
+ <span class="status-item">
51
+ Filtered: <strong x-text="$store.schedulerStore.filteredTasks.length"></strong>
52
+ </span>
53
+ </div>
54
+
55
+ <!-- Pagination -->
56
+ <div class="status-pagination" x-show="$store.schedulerStore.totalPages > 1">
57
+ <button class="btn btn-icon" @click="$store.schedulerStore.prevPage()"
58
+ :disabled="$store.schedulerStore.currentPage === 1" title="Previous Page">
59
+ <span class="material-symbols-outlined">chevron_left</span>
60
+ </button>
61
+ <div class="page-input-group">
62
+ <label>Page</label>
63
+ <input type="number" class="page-input"
64
+ x-model.number="$store.schedulerStore.currentPage"
65
+ @change="$store.schedulerStore.goToPage($store.schedulerStore.currentPage)"
66
+ @keyup.enter="$store.schedulerStore.goToPage($store.schedulerStore.currentPage)"
67
+ :min="1" :max="$store.schedulerStore.totalPages" />
68
+ <span class="page-total">of <strong
69
+ x-text="$store.schedulerStore.totalPages"></strong></span>
70
+ </div>
71
+ <button class="btn btn-icon" @click="$store.schedulerStore.nextPage()"
72
+ :disabled="$store.schedulerStore.currentPage === $store.schedulerStore.totalPages"
73
+ title="Next Page">
74
+ <span class="material-symbols-outlined">chevron_right</span>
75
+ </button>
76
+ </div>
77
+ </div>
78
+
79
+ <!-- Empty State -->
80
<div class="scheduler-empty"
45
- x-show="!$store.schedulerStore.isLoading && $store.schedulerStore.filteredTasks.length === 0"
46
- x-effect="$el.style.display = (!$store.schedulerStore.isLoading && $store.schedulerStore.filteredTasks.length === 0) ? '' : 'none'">
81
+ x-show="!$store.schedulerStore.isLoading && $store.schedulerStore.filteredTasks.length === 0">
82
<div class="scheduler-empty-text">No tasks found</div>
48
- <button class="btn btn-ok" @click="$store.schedulerStore.startCreateTask()">Create your first task</button>
83
+ <button class="btn btn-ok" @click="$store.schedulerStore.startCreateTask()">
84
+ <span class="material-symbols-outlined">add</span>
85
+ Create your first task
86
+ </button>
87
</div>
88
51
- <table class="scheduler-task-list"
52
- x-show="!$store.schedulerStore.isLoading && $store.schedulerStore.filteredTasks.length > 0"
53
- x-effect="$el.style.display = (!$store.schedulerStore.isLoading && $store.schedulerStore.filteredTasks.length > 0) ? '' : 'none'">
54
- <thead>
55
- <tr>
56
- <th class="col-name" @click="$store.schedulerStore.changeSort('name')">
57
- Name
58
- <span class="scheduler-sort-indicator"
59
- x-show="$store.schedulerStore.sortField === 'name'"
60
- :class="{'scheduler-sort-desc': $store.schedulerStore.sortDirection === 'desc'}">↑</span>
61
- </th>
62
- <th class="col-state" @click="$store.schedulerStore.changeSort('state')">
63
- State
64
- <span class="scheduler-sort-indicator"
65
- x-show="$store.schedulerStore.sortField === 'state'"
66
- :class="{'scheduler-sort-desc': $store.schedulerStore.sortDirection === 'desc'}">↑</span>
67
- </th>
68
- <th class="col-project">Project</th>
69
- <th class="col-lastrun" @click="$store.schedulerStore.changeSort('last_run')">
70
- Last Run
71
- <span class="scheduler-sort-indicator"
72
- x-show="$store.schedulerStore.sortField === 'last_run'"
73
- :class="{'scheduler-sort-desc': $store.schedulerStore.sortDirection === 'desc'}">↑</span>
74
- </th>
75
- <th class="col-actions">Actions</th>
76
- </tr>
77
- </thead>
78
- <tbody>
79
- <template x-for="task in $store.schedulerStore.filteredTasks" :key="task.uuid">
80
- <tr @click="$store.schedulerStore.showTaskDetail(task.uuid)">
81
- <td class="col-name">
82
- <span x-text="task.name"></span>
83
- </td>
84
- <td class="col-state">
85
- <span class="scheduler-status-badge"
86
- :class="$store.schedulerStore.getStateBadgeClass(task.state)"
87
- x-text="task.state"></span>
88
- </td>
89
- <td class="col-project">
90
- <span class="project-color-ball"
91
- :style="$store.schedulerStore.extractTaskProject(task)?.color ? { backgroundColor: $store.schedulerStore.extractTaskProject(task).color } : { border: '1px solid var(--color-border)' }"></span>
92
- <span x-text="$store.schedulerStore.formatTaskProject(task)"></span>
93
- </td>
94
- <td class="col-lastrun" x-text="$store.schedulerStore.formatDate(task.last_run)"></td>
95
- <td class="col-actions" @click.stop>
96
- <div class="scheduler-task-actions">
97
- <button class="scheduler-task-action"
98
- @click="$store.schedulerStore.runTask(task.uuid)" title="Run Task">
99
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16"
100
- height="16">
101
- <path d="M8 5v14l11-7z"/>
102
- </svg>
103
- </button>
104
- <button class="scheduler-task-action"
105
- @click="$store.schedulerStore.resetTaskState(task.uuid)" title="Reset State">
106
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16"
107
- height="16">
108
- <path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/>
109
- </svg>
110
- </button>
111
- <button class="scheduler-task-action"
112
- @click="$store.schedulerStore.startEditTask(task.uuid)" title="Edit Task">
113
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16"
114
- height="16">
115
- <path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>
116
- </svg>
117
- </button>
118
- <button class="scheduler-task-action"
119
- @click="$store.schedulerStore.deleteTask(task.uuid)" title="Delete Task">
120
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16"
121
- height="16">
122
- <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>
123
- </svg>
124
- </button>
125
- </div>
126
- </td>
89
+ <!-- Task Table -->
90
+ <div class="scheduler-table-container"
91
+ x-show="!$store.schedulerStore.isLoading && $store.schedulerStore.paginatedTasks.length > 0">
92
+ <table class="scheduler-task-table">
93
+ <thead>
94
+ <tr>
95
+ <th class="col-name" @click="$store.schedulerStore.changeSort('name')">
96
+ Name
97
+ <span class="scheduler-sort-indicator"
98
+ x-show="$store.schedulerStore.sortField === 'name'"
99
+ :class="{'scheduler-sort-desc': $store.schedulerStore.sortDirection === 'desc'}">↑</span>
100
+ </th>
101
+ <th class="col-state" @click="$store.schedulerStore.changeSort('state')">
102
+ State
103
+ <span class="scheduler-sort-indicator"
104
+ x-show="$store.schedulerStore.sortField === 'state'"
105
+ :class="{'scheduler-sort-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')">
109
+ Last Run
110
+ <span class="scheduler-sort-indicator"
111
+ x-show="$store.schedulerStore.sortField === 'last_run'"
112
+ :class="{'scheduler-sort-desc': $store.schedulerStore.sortDirection === 'desc'}">↑</span>
113
+ </th>
114
+ <th class="col-actions">Actions</th>
115
</tr>
128
- </template>
129
- </tbody>
130
- </table>
116
+ </thead>
117
+ <tbody>
118
+ <template x-for="task in $store.schedulerStore.paginatedTasks" :key="task.uuid">
119
+ <tr @click="$store.schedulerStore.showTaskDetail(task.uuid)">
120
+ <td class="col-name">
121
+ <span x-text="task.name"></span>
122
+ </td>
123
+ <td class="col-state">
124
+ <span class="scheduler-status-badge"
125
+ :class="$store.schedulerStore.getStateBadgeClass(task.state)"
126
+ x-text="task.state"></span>
127
+ </td>
128
+ <td class="col-project">
129
+ <span class="project-color-ball"
130
+ :style="$store.schedulerStore.extractTaskProject(task)?.color ? { backgroundColor: $store.schedulerStore.extractTaskProject(task).color } : { border: '1px solid var(--color-border)' }"></span>
131
+ <span x-text="$store.schedulerStore.formatTaskProject(task)"></span>
132
+ </td>
133
+ <td class="col-lastrun" x-text="$store.schedulerStore.formatDate(task.last_run)"></td>
134
+ <td class="col-actions" @click.stop>
135
+ <div class="actions-wrapper">
136
+ <button class="btn btn-action"
137
+ @click="$store.schedulerStore.runTask(task.uuid)" title="Run Task">
138
+ <span class="material-symbols-outlined">play_arrow</span>
139
+ </button>
140
+ <button class="btn btn-action"
141
+ @click="$store.schedulerStore.resetTaskState(task.uuid)" title="Reset State">
142
+ <span class="material-symbols-outlined">refresh</span>
143
+ </button>
144
+ <button class="btn btn-action"
145
+ @click="$store.schedulerStore.startEditTask(task.uuid)" title="Edit Task">
146
+ <span class="material-symbols-outlined">edit</span>
147
+ </button>
148
+ <button class="btn btn-action delete"
149
+ @click="$store.schedulerStore.deleteTask(task.uuid)" title="Delete Task">
150
+ <span class="material-symbols-outlined">delete</span>
151
+ </button>
152
+ </div>
153
+ </td>
154
+ </tr>
155
+ </template>
156
+ </tbody>
157
+ </table>
158
+ </div>
159
+
160
+ <!-- Bottom Status Bar (for pagination when table is long) -->
161
+ <div class="scheduler-status-bar scheduler-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">
165
+ <span class="material-symbols-outlined">schedule</span>
166
+ Total: <strong x-text="$store.schedulerStore.tasks.length"></strong>
167
+ </span>
168
+ <span class="status-separator">•</span>
169
+ <span class="status-item">
170
+ Filtered: <strong x-text="$store.schedulerStore.filteredTasks.length"></strong>
171
+ </span>
172
+ </div>
173
+
174
+ <div class="status-pagination">
175
+ <button class="btn btn-icon" @click="$store.schedulerStore.prevPage()"
176
+ :disabled="$store.schedulerStore.currentPage === 1" title="Previous Page">
177
+ <span class="material-symbols-outlined">chevron_left</span>
178
+ </button>
179
+ <div class="page-input-group">
180
+ <label>Page</label>
181
+ <input type="number" class="page-input"
182
+ x-model.number="$store.schedulerStore.currentPage"
183
+ @change="$store.schedulerStore.goToPage($store.schedulerStore.currentPage)"
184
+ @keyup.enter="$store.schedulerStore.goToPage($store.schedulerStore.currentPage)"
185
+ :min="1" :max="$store.schedulerStore.totalPages" />
186
+ <span class="page-total">of <strong
187
+ x-text="$store.schedulerStore.totalPages"></strong></span>
188
+ </div>
189
+ <button class="btn btn-icon" @click="$store.schedulerStore.nextPage()"
190
+ :disabled="$store.schedulerStore.currentPage === $store.schedulerStore.totalPages"
191
+ title="Next Page">
192
+ <span class="material-symbols-outlined">chevron_right</span>
193
+ </button>
194
+ </div>
195
+ </div>
196
</div>
197
</template>
198
</div>
199
+
200
+<style>
201
+ .scheduler-list-container {
202
+ display: flex;
203
+ flex-direction: column;
204
+ gap: 0;
205
+ }
206
+
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
+
245
+ .btn-new-task {
246
+ margin-left: auto;
247
+ display: inline-flex;
248
+ align-items: center;
249
+ gap: 0.5rem;
250
+ }
251
+
252
+ .btn-new-task .material-symbols-outlined {
253
+ font-size: 20px;
254
+ }
255
+
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;
447
+ }
448
+
449
+ .scheduler-sort-desc {
450
+ transform: rotate(180deg);
451
+ }
452
+
453
+ /* Project color ball */
454
+ .project-color-ball {
455
+ display: inline-block;
456
+ width: 10px;
457
+ height: 10px;
458
+ border-radius: 50%;
459
+ margin-right: 6px;
460
+ vertical-align: middle;
461
+ }
462
+
463
+ /* Responsive */
464
+ @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
+
478
+ .btn-new-task {
479
+ margin-left: 0;
480
+ justify-content: center;
481
+ }
482
+
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 {
502
+ display: none;
503
+ }
504
+
505
+ .col-name { width: 45%; }
506
+ .col-state { width: 20%; }
507
+ .col-actions { width: 35%; }
508
+ }
509
+</style>
510
</body>
511
</html>
512
webui/components/settings/memory/memory-detail-modal.html
+2
@@ -172,6 +172,8 @@
172
gap: 0.25rem;
173
background: var(--color-message-bg);
174
color: var(--color-text);
175
+ border: 1px solid var(--color-border);
176
+ cursor: pointer;
177
}
178
179
.btn-action-header .material-symbols-outlined {
webui/css/buttons.css
+116
@@ -61,3 +61,119 @@
61
padding-left: 0.75rem;
62
padding-right: 0.75rem;
63
}
64
+
65
+/* Action Header Buttons - for modal headers with confirm/cancel/edit/delete actions */
66
+.btn-action-header {
67
+ padding: 0.4rem 0.6rem;
68
+ display: flex;
69
+ align-items: center;
70
+ gap: 0.25rem;
71
+ background: var(--color-message-bg);
72
+ color: var(--color-text);
73
+ border: 1px solid var(--color-border);
74
+ border-radius: 4px;
75
+ cursor: pointer;
76
+ transition: all 0.15s ease;
77
+}
78
+
79
+.btn-action-header .material-symbols-outlined {
80
+ font-size: 18px;
81
+}
82
+
83
+.btn-action-header:hover {
84
+ border-color: var(--color-primary);
85
+ color: var(--color-primary);
86
+}
87
+
88
+.btn-action-header.confirm:hover {
89
+ border-color: #4CAF50;
90
+ color: #4CAF50;
91
+ background: var(--color-background);
92
+}
93
+
94
+.btn-action-header.cancel:hover {
95
+ border-color: var(--color-accent);
96
+ color: var(--color-accent);
97
+ background: var(--color-background);
98
+}
99
+
100
+.btn-action-header.delete:hover {
101
+ border-color: var(--color-accent);
102
+ color: var(--color-accent);
103
+}
104
+
105
+.btn-action-header.edit:hover,
106
+.btn-action-header.copy:hover,
107
+.btn-action-header.copy-all:hover,
108
+.btn-action-header.copy-content:hover {
109
+ border-color: var(--color-primary);
110
+ color: var(--color-primary);
111
+}
112
+
113
+/* Table/Row Action Buttons - for inline row actions (run, edit, delete, etc.) */
114
+.btn-action {
115
+ background: none;
116
+ border: 1px solid var(--color-border);
117
+ padding: 0.25rem;
118
+ color: var(--color-text);
119
+ opacity: 0.7;
120
+ display: inline-flex;
121
+ align-items: center;
122
+ justify-content: center;
123
+ border-radius: 4px;
124
+ cursor: pointer;
125
+ transition: all 0.15s ease;
126
+}
127
+
128
+.btn-action .material-symbols-outlined {
129
+ font-size: 18px;
130
+}
131
+
132
+.btn-action:hover {
133
+ opacity: 1;
134
+ background: var(--color-panel);
135
+ border-color: var(--color-primary);
136
+ color: var(--color-primary);
137
+ transform: translateY(-1px);
138
+}
139
+
140
+.btn-action.delete:hover {
141
+ background: var(--color-panel);
142
+ border-color: var(--color-accent);
143
+ color: var(--color-accent);
144
+}
145
+
146
+/* Icon Button - minimal padding for icon-only buttons */
147
+.btn-icon {
148
+ background: transparent;
149
+ border: 1px solid var(--color-border);
150
+ color: var(--color-text);
151
+ padding: 0.25rem;
152
+ display: flex;
153
+ align-items: center;
154
+ border-radius: 4px;
155
+ cursor: pointer;
156
+ transition: all 0.15s ease;
157
+}
158
+
159
+.btn-icon:hover:not(:disabled) {
160
+ background: var(--color-panel);
161
+ border-color: var(--color-primary);
162
+ color: var(--color-text);
163
+}
164
+
165
+.btn-icon:disabled {
166
+ opacity: 0.4;
167
+ cursor: not-allowed;
168
+}
169
+
170
+.btn-icon .material-symbols-outlined {
171
+ font-size: 20px;
172
+}
173
+
174
+/* Actions wrapper for grouping action buttons */
175
+.actions-wrapper {
176
+ display: flex;
177
+ align-items: center;
178
+ gap: 0.25rem;
179
+}
webui/css/scheduler.css
+122
-221
@@ -1,91 +1,4 @@
1
-.scheduler-header {
2
- display: flex;
3
- justify-content: space-between;
4
- align-items: center;
5
- margin-bottom: 20px;
6
-}
7
-
8
-.scheduler-header h2 {
9
- margin: 0;
10
- font-size: 1.2rem;
11
- font-weight: 500;
12
-}
13
-
14
-.scheduler-filters {
15
- display: flex;
16
- gap: 20px;
17
- margin-bottom: 20px;
18
- flex-wrap: wrap;
19
-}
20
-
21
-.scheduler-filter-group {
22
- display: flex;
23
- align-items: center;
24
- gap: 8px;
25
-}
26
-
27
-.scheduler-filter-label {
28
- font-weight: 500;
29
- color: var(--color-text-secondary);
30
-}
31
-
32
-.scheduler-filter-select {
33
- padding: 6px 8px;
34
- border-radius: 4px;
35
- border: 1px solid var(--color-border);
36
- background-color: var(--color-bg-secondary);
37
- color: var(--color-text);
38
-}
39
-
40
-.scheduler-task-list {
41
- width: 100%;
42
- border-collapse: collapse;
43
- margin: 1rem 0;
44
- font-size: 0.9rem;
45
- table-layout: fixed;
46
-}
47
-
48
-.scheduler-task-list th,
49
-.scheduler-task-list td {
50
- padding: 0.75rem;
51
- text-align: left;
52
- border-bottom: 1px solid var(--color-border);
53
- vertical-align: middle;
54
- overflow: hidden;
55
- text-overflow: ellipsis;
56
- white-space: nowrap;
57
-}
58
-
59
-.scheduler-task-list th {
60
- background-color: var(--color-bg-secondary);
61
- font-weight: 500;
62
- cursor: pointer;
63
- user-select: none;
64
-}
65
-
66
-.scheduler-task-list th:hover {
67
- background-color: var(--color-bg-tertiary);
68
-}
69
-
70
-.scheduler-task-actions {
71
- display: flex;
72
- gap: 8px;
73
-}
74
-
75
-.scheduler-task-action {
76
- padding: 4px;
77
- background: none;
78
- border: none;
79
- color: var(--color-text-secondary);
80
- cursor: pointer;
81
- border-radius: 4px;
82
-}
83
-
84
-.scheduler-task-action:hover {
85
- background-color: var(--color-bg-tertiary);
86
- color: var(--color-text);
87
-}
88
-
1
+/* Scheduler Status Badges */
2
.scheduler-status-badge {
3
display: inline-block;
4
padding: 4px 8px;
@@ -94,104 +7,61 @@
7
font-weight: 500;
8
text-transform: capitalize;
9
white-space: nowrap;
10
+ cursor: pointer;
11
}
12
13
.scheduler-status-idle {
14
background-color: rgba(0, 100, 0, 0.2);
101
- color: #2a9d8f; /* Dark green that works with both light and dark themes */
15
+ color: #2a9d8f;
16
border: 1px solid rgba(42, 157, 143, 0.3);
17
}
18
19
.scheduler-status-running {
20
background-color: rgba(0, 60, 120, 0.2);
107
- color: #4361ee; /* Dark blue that works with both light and dark themes */
21
+ color: #4361ee;
22
border: 1px solid rgba(67, 97, 238, 0.3);
23
}
24
25
.scheduler-status-disabled {
26
background-color: rgba(70, 70, 70, 0.2);
113
- color: #6c757d; /* Dark grey that works with both light and dark themes */
27
+ color: #6c757d;
28
border: 1px solid rgba(108, 117, 125, 0.3);
29
}
30
31
.scheduler-status-error {
32
background-color: rgba(120, 0, 0, 0.2);
119
- color: #e63946; /* Dark red that works with both light and dark themes */
33
+ color: #e63946;
34
border: 1px solid rgba(230, 57, 70, 0.3);
35
}
36
37
+/* Status badge selected state for editor */
38
+.scheduler-status-selected {
39
+ outline: 2px solid var(--color-primary);
40
+ outline-offset: 2px;
41
+}
42
+
43
/* Light mode adjustments */
44
.light-mode .scheduler-status-idle {
45
background-color: rgba(42, 157, 143, 0.1);
126
- color: #1a6f65; /* Darker green for light mode */
46
+ color: #1a6f65;
47
}
48
49
.light-mode .scheduler-status-running {
50
background-color: rgba(67, 97, 238, 0.1);
131
- color: #2540b3; /* Darker blue for light mode */
51
+ color: #2540b3;
52
}
53
54
.light-mode .scheduler-status-disabled {
55
background-color: rgba(108, 117, 125, 0.1);
136
- color: #495057; /* Darker grey for light mode */
56
+ color: #495057;
57
}
58
59
.light-mode .scheduler-status-error {
60
background-color: rgba(230, 57, 70, 0.1);
141
- color: #c5283d; /* Darker red for light mode */
142
-}
143
-
144
-.scheduler-empty {
145
- text-align: center;
146
- padding: 40px 0;
147
- color: var(--color-text-secondary);
148
-}
149
-
150
-.scheduler-empty-icon {
151
- font-size: 32px;
152
- margin-bottom: 10px;
153
-}
154
-
155
-.scheduler-empty-text {
156
- margin-bottom: 20px;
157
-}
158
-
159
-.scheduler-loading {
160
- text-align: center;
161
- padding: 40px 0;
162
- color: var(--color-text-secondary);
163
-}
164
-
165
-.scheduler-task-details {
166
- padding: 16px;
167
- background-color: var(--color-bg-secondary);
168
- border-radius: 4px;
169
-}
170
-
171
-.scheduler-details-grid {
172
- display: grid;
173
- grid-template-columns: 120px 1fr;
174
- gap: 8px 16px;
175
- margin-bottom: 16px;
176
-}
177
-
178
-.scheduler-details-label {
179
- font-weight: 500;
180
- color: var(--color-text-secondary);
181
- display: flex;
182
- align-items: center;
183
-}
184
-
185
-.scheduler-details-value {
186
- color: var(--color-text);
187
- word-break: break-word;
188
-}
189
-
190
-.scheduler-details-actions {
191
- display: flex;
192
- justify-content: flex-end;
61
+ color: #c5283d;
62
}
63
64
+/* Scheduler Form Styles */
65
.scheduler-form {
66
background-color: var(--color-bg-secondary);
67
border-radius: 4px;
@@ -199,14 +69,6 @@
69
margin-bottom: 20px;
70
}
71
202
-.scheduler-form-title {
203
- font-size: 1.2rem;
204
- font-weight: 500;
205
- margin-bottom: 20px;
206
- padding-bottom: 10px;
207
- border-bottom: 1px solid var(--color-border);
208
-}
209
-
72
.scheduler-form-grid {
73
display: grid;
74
grid-template-columns: 1fr 1fr;
@@ -233,12 +95,26 @@
95
color: var(--color-text-secondary);
96
}
97
236
-.scheduler-form-actions {
98
+.label-help-wrapper {
99
display: flex;
238
- justify-content: flex-end;
239
- gap: 12px;
100
+ flex-direction: column;
101
+ gap: 2px;
102
+}
103
+
104
+/* State selector in form */
105
+.scheduler-state-selector {
106
+ display: flex;
107
+ gap: 8px;
108
+ flex-wrap: wrap;
109
+ margin-bottom: 8px;
110
+}
111
+
112
+.scheduler-state-explanation {
113
+ font-size: 12px;
114
+ color: var(--color-text-secondary);
115
}
116
117
+/* Schedule Builder */
118
.scheduler-schedule-builder {
119
display: grid;
120
grid-template-columns: repeat(5, 1fr);
@@ -249,13 +125,13 @@
125
display: flex;
126
flex-direction: column;
127
gap: 4px;
252
- max-width: 70px; /* Limit width of schedule fields */
128
+ max-width: 70px;
129
}
130
131
.scheduler-schedule-field input {
132
width: 100%;
257
- min-width: 0; /* Allow shrinking below content size */
258
- font-size: 0.9rem; /* Slightly reduce font size for better fit */
133
+ min-width: 0;
134
+ font-size: 0.9rem;
135
}
136
137
.scheduler-schedule-label {
@@ -263,80 +139,65 @@
139
color: var(--color-text-secondary);
140
}
141
266
-.input-group {
142
+/* Plan Builder */
143
+.scheduler-plan-builder {
144
display: flex;
268
- gap: 8px;
269
-}
270
-
271
-/* Sort indicators */
272
-.scheduler-sort-indicator {
273
- display: inline-block;
274
- margin-left: 4px;
275
- transition: transform 0.2s ease;
145
+ flex-direction: column;
146
+ gap: 12px;
147
}
148
278
-.scheduler-sort-desc {
279
- transform: rotate(180deg);
149
+.scheduler-plan-todo {
150
+ display: flex;
151
+ flex-direction: column;
152
+ gap: 8px;
153
}
154
282
-/* Responsive adjustments */
283
-@media (max-width: 768px) {
284
- .scheduler-form-grid {
285
- grid-template-columns: 1fr;
286
- }
287
-
288
- .scheduler-schedule-builder {
289
- grid-template-columns: 1fr 1fr;
290
- }
291
-
292
- .scheduler-filters {
293
- flex-direction: column;
294
- gap: 12px;
295
- }
296
-
297
- .scheduler-task-actions {
298
- flex-wrap: wrap;
299
- }
155
+.scheduler-plan-label {
156
+ font-weight: 500;
157
+ font-size: 0.9rem;
158
}
159
302
-@media (max-width: 480px) {
303
- nav ul li a {
304
- flex-direction: row;
305
- justify-content: flex-start;
306
- gap: 1rem;
307
- padding: 0.75rem 1rem;
308
- }
309
-
310
- nav ul li a img {
311
- margin-bottom: 0;
312
- width: 30px;
313
- height: 30px;
314
- }
160
+.scheduler-todo-list {
161
+ display: flex;
162
+ flex-direction: column;
163
+ gap: 8px;
164
+ padding: 12px;
165
+ background: var(--color-background);
166
+ border: 1px solid var(--color-border);
167
+ border-radius: 4px;
168
}
169
317
-/* Add row hover effect for task list rows matching left panel hover */
318
-.scheduler-task-list tbody tr {
319
- cursor: pointer;
320
- transition: background-color 0.2s ease;
170
+.scheduler-todo-item {
171
+ display: flex;
172
+ justify-content: space-between;
173
+ align-items: center;
174
+ padding: 8px;
175
+ background: var(--color-panel);
176
+ border: 1px solid var(--color-border);
177
+ border-radius: 4px;
178
}
179
323
-.scheduler-task-list tbody tr:hover {
324
- background-color: rgba(255, 255, 255, 0.03);
180
+.scheduler-empty-plan {
181
+ color: var(--color-text-secondary);
182
+ font-style: italic;
183
+ text-align: center;
184
+ padding: 12px;
185
}
186
327
-.light-mode .scheduler-task-list tbody tr:hover {
328
- background-color: rgba(0, 0, 0, 0.02);
187
+.scheduler-add-todo {
188
+ display: flex;
189
+ gap: 8px;
190
+ margin-top: 8px;
191
}
192
331
-.scheduler-task-list th {
332
- background-color: var(--color-bg-secondary);
333
- font-weight: 500;
334
- cursor: pointer;
335
- user-select: none;
193
+.scheduler-add-todo input {
194
+ flex: 1;
195
}
196
338
-.scheduler-task-list th:hover {
339
- background-color: var(--color-bg-tertiary);
197
+/* Input group for token field */
198
+.input-group {
199
+ display: flex;
200
+ gap: 8px;
201
}
202
203
/* Task detail view styling */
@@ -350,7 +211,7 @@
211
212
.scheduler-detail-header {
213
display: flex;
353
- justify-content: flex-start;
214
+ justify-content: space-between;
215
align-items: center;
216
margin-bottom: 20px;
217
padding-bottom: 10px;
@@ -374,8 +235,32 @@
235
margin-bottom: 20px;
236
}
237
377
-/* Task Scheduler Styles */
238
+.scheduler-details-grid {
239
+ display: grid;
240
+ grid-template-columns: 120px 1fr;
241
+ gap: 8px 16px;
242
+ margin-bottom: 16px;
243
+}
244
245
+.scheduler-details-label {
246
+ font-weight: 500;
247
+ color: var(--color-text-secondary);
248
+ display: flex;
249
+ align-items: center;
250
+}
251
+
252
+.scheduler-details-value {
253
+ color: var(--color-text);
254
+ word-break: break-word;
255
+}
256
+
257
+.scheduler-details-actions {
258
+ display: flex;
259
+ justify-content: flex-end;
260
+ gap: 8px;
261
+}
262
+
263
+/* Misc */
264
.scheduler-no-schedule {
265
color: var(--color-text-secondary);
266
opacity: 0.7;
@@ -389,7 +274,6 @@
274
gap: 6px;
275
}
276
392
-/* Smaller status badge for task list */
277
.scheduler-status-badge-small {
278
font-size: 10px;
279
padding: 2px 6px;
@@ -397,3 +281,20 @@
281
min-width: 40px;
282
text-align: center;
283
}
284
+
285
+.scheduler-loading {
286
+ text-align: center;
287
+ padding: 40px 0;
288
+ color: var(--color-text-secondary);
289
+}
290
+
291
+/* Responsive adjustments */
292
+@media (max-width: 768px) {
293
+ .scheduler-form-grid {
294
+ grid-template-columns: 1fr;
295
+ }
296
+
297
+ .scheduler-schedule-builder {
298
+ grid-template-columns: 1fr 1fr;
299
+ }
300
+}
webui/css/settings.css
+5
-1
@@ -653,13 +653,17 @@ nav ul li a img {
653
padding: 0.4rem 0.6rem;
654
display: flex;
655
align-items: center;
656
+ justify-content: space-between;
657
background: var(--color-bg-secondary);
658
border: 1px solid var(--color-border);
659
color: var(--color-text);
659
- border-radius: var(--border-radius);
660
cursor: pointer;
661
}
662
663
+.scheduler-detail-header .btn-action-header .btn-cancel {
664
+ margin-left: var(--spacing-sm);
665
+}
666
+
667
.scheduler-detail-header .btn-action-header:hover {
668
border-color: var(--color-primary);
669
color: var(--color-primary);