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