standalone scheduler modal
3clyp50 committed
Dec 26, 2025 at 13:52 UTC
a6ea619568ad88624e496c4119b386dd0d0daf84
15 files changed
+505
-490
webui/components/modals/scheduler/scheduler-modal-store.js
new
+35
@@ -0,0 +1,35 @@
1
+import { createStore } from "/js/AlpineStore.js";
2
+import { store as schedulerStore } from "/components/modals/scheduler/scheduler-store.js";
3
+
4
+const model = {
5
+ closePromise: null,
6
+
7
+ async open(options = {}) {
8
+ // Open modal
9
+ this.closePromise = window.openModal("modals/scheduler/scheduler-modal.html");
10
+
11
+ // Setup cleanup on modal close
12
+ if (this.closePromise?.then) {
13
+ this.closePromise.then(() => this.cleanup());
14
+ }
15
+
16
+ // Notify scheduler store that modal opened
17
+ schedulerStore.onTabActivated?.();
18
+
19
+ // If taskId provided, show detail after load
20
+ if (options.taskId) {
21
+ setTimeout(async () => {
22
+ await schedulerStore.fetchTasks({ manual: true });
23
+ schedulerStore.showTaskDetail(options.taskId);
24
+ }, 200);
25
+ }
26
+ },
27
+
28
+ cleanup() {
29
+ schedulerStore.onModalClosed?.();
30
+ this.closePromise = null;
31
+ },
32
+};
33
+
34
+export const store = createStore("schedulerModal", model);
35
+
webui/components/modals/scheduler/scheduler-modal.html
new
+27
@@ -0,0 +1,27 @@
1
+<html>
2
+<head>
3
+ <title>Task Scheduler</title>
4
+ <script type="module">
5
+ import { store } from "/components/modals/scheduler/scheduler-modal-store.js";
6
+ </script>
7
+</head>
8
+<body>
9
+ <div x-data>
10
+ <template x-if="$store.schedulerModal">
11
+ <div class="scheduler-modal-content">
12
+ <x-component path="modals/scheduler/scheduler-settings.html"></x-component>
13
+ </div>
14
+ </template>
15
+ </div>
16
+
17
+ <style>
18
+ .scheduler-modal-content {
19
+ display: flex;
20
+ flex-direction: column;
21
+ height: 100%;
22
+ min-height: 400px;
23
+ }
24
+ </style>
25
+</body>
26
+</html>
27
+
webui/components/modals/scheduler/scheduler-settings.html
new
+23
@@ -0,0 +1,23 @@
1
+<html>
2
+<head>
3
+ <script type="module">
4
+ import { store } from "/components/modals/scheduler/scheduler-store.js";
5
+ </script>
6
+</head>
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>
18
+ </div>
19
+ </template>
20
+</div>
21
+</body>
22
+</html>
23
+
webui/components/modals/scheduler/scheduler-store.js
renamed
+1
-1
@@ -900,7 +900,7 @@ const schedulerStoreModel = {
900
}
901
902
this.selectedTaskForDetail = snapshot;
903
- const closePromise = window.openModal("settings/scheduler/scheduler-task-detail.html");
903
+ const closePromise = window.openModal("modals/scheduler/scheduler-task-detail.html");
904
if (closePromise && typeof closePromise.then === "function") {
905
closePromise.then(() => {
906
if (this.selectedTaskForDetail?.uuid === snapshot.uuid) {
webui/components/modals/scheduler/scheduler-task-detail.html
renamed
+2
-1
@@ -2,7 +2,7 @@
2
<head>
3
<title>Task Details</title>
4
<script type="module">
5
- import { store } from "/components/settings/scheduler/scheduler-store.js";
5
+ import { store } from "/components/modals/scheduler/scheduler-store.js";
6
</script>
7
</head>
8
<body>
@@ -146,3 +146,4 @@
146
</div>
147
</body>
148
</html>
149
+
webui/components/modals/scheduler/scheduler-task-editor.html
renamed
+2
-1
@@ -1,7 +1,7 @@
1
<html>
2
<head>
3
<script type="module">
4
- import { store } from "/components/settings/scheduler/scheduler-store.js";
4
+ import { store } from "/components/modals/scheduler/scheduler-store.js";
5
</script>
6
</head>
7
<body>
@@ -437,3 +437,4 @@
437
</div>
438
</body>
439
</html>
440
+
webui/components/modals/scheduler/scheduler-task-list.html
renamed
+2
-1
@@ -1,7 +1,7 @@
1
<html>
2
<head>
3
<script type="module">
4
- import { store } from "/components/settings/scheduler/scheduler-store.js";
4
+ import { store } from "/components/modals/scheduler/scheduler-store.js";
5
</script>
6
</head>
7
<body>
@@ -133,3 +133,4 @@
133
</div>
134
</body>
135
</html>
136
+
webui/components/settings/scheduler/scheduler-settings.html
deleted
-33
@@ -1,33 +0,0 @@
1
-<html>
2
-<head>
3
- <script type="module">
4
- import { store } from "/components/settings/scheduler/scheduler-store.js";
5
- </script>
6
-</head>
7
-<body>
8
-<div x-data>
9
- <template x-if="$store.schedulerStore">
10
- <div id="scheduler-settings-root">
11
- <nav>
12
- <ul>
13
- <li>
14
- <a href="#section-task-scheduler">
15
- <img src="/public/task_scheduler.svg" alt="Task Scheduler">
16
- <span>Task Scheduler</span>
17
- </a>
18
- </li>
19
- </ul>
20
- </nav>
21
-
22
- <div id="section-task-scheduler" class="section">
23
- <div class="section-title">Task Scheduler</div>
24
- <div class="section-description">Manage scheduled tasks and automated processes for Agent Zero.</div>
25
-
26
- <x-component path="settings/scheduler/scheduler-task-editor.html"></x-component>
27
- <x-component path="settings/scheduler/scheduler-task-list.html"></x-component>
28
- </div>
29
- </div>
30
- </template>
31
-</div>
32
-</body>
33
-</html>
webui/components/settings/settings-store.js
-36
@@ -1,7 +1,6 @@
1
import { createStore } from "/js/AlpineStore.js";
2
import * as API from "/js/api.js";
3
import { store as notificationStore } from "/components/notifications/notification-store.js";
4
-import { store as schedulerStore } from "/components/settings/scheduler/scheduler-store.js";
4
5
// Constants
6
const VIEW_MODE_STORAGE_KEY = "settingsActiveTab";
@@ -65,12 +64,6 @@ const model = {
64
},
65
66
cleanup() {
68
- // Notify scheduler if it was active
69
- if (this._activeTab === "scheduler") {
70
- schedulerStore.onTabDeactivated?.();
71
- }
72
- schedulerStore.onModalClosed?.();
73
-
67
this.settings = null;
68
this.additional = null;
69
this.error = null;
@@ -83,14 +76,6 @@ const model = {
76
try {
77
localStorage.setItem(VIEW_MODE_STORAGE_KEY, current);
78
} catch {}
86
-
87
- // Scheduler lifecycle
88
- if (previous === "scheduler" && current !== "scheduler") {
89
- schedulerStore.onTabDeactivated?.();
90
- }
91
- if (current === "scheduler" && previous !== "scheduler") {
92
- schedulerStore.onTabActivated?.();
93
- }
79
},
80
81
switchTab(tabName) {
@@ -183,27 +168,6 @@ const model = {
168
}
169
await window.openModal("settings/settings.html");
170
},
186
-
187
- // Scheduler integration: open Settings modal, switch to scheduler tab, show task detail
188
- async openSchedulerTaskDetail(taskId) {
189
- // Set tab to scheduler before opening
190
- this._activeTab = "scheduler";
191
-
192
- // Open the modal (do NOT await - openModal resolves on close)
193
- window.openModal("settings/settings.html");
194
-
195
- // Ensure scheduler tasks are loaded, then show the detail modal
196
- setTimeout(async () => {
197
- try {
198
- if (typeof schedulerStore.fetchTasks === "function") {
199
- await schedulerStore.fetchTasks({ manual: true });
200
- }
201
- schedulerStore.showTaskDetail?.(taskId);
202
- } catch (error) {
203
- console.warn("[settings-store] openSchedulerTaskDetail failed", error);
204
- }
205
- }, 200);
206
- },
171
};
172
173
const store = createStore("settingsStore", model);
webui/components/settings/settings.html
+1
-12
@@ -46,10 +46,6 @@
46
:class="{'active': $store.settingsStore.activeTab === 'developer'}"
47
@click="$store.settingsStore.switchTab('developer')"
48
title="Developer">Developer</div>
49
- <div class="settings-tab"
50
- :class="{'active': $store.settingsStore.activeTab === 'scheduler'}"
51
- @click="$store.settingsStore.switchTab('scheduler')"
52
- title="Task Scheduler">Task Scheduler</div>
49
<div class="settings-tab"
50
:class="{'active': $store.settingsStore.activeTab === 'backup'}"
51
@click="$store.settingsStore.switchTab('backup')"
@@ -58,7 +54,7 @@
54
</div>
55
56
<!-- Settings sections for agent, external, developer, mcp, backup tabs -->
61
- <div id="settings-sections" x-show="$store.settingsStore.activeTab !== 'scheduler'">
57
+ <div id="settings-sections">
58
<template x-if="$store.settingsStore.activeTab === 'agent'">
59
<x-component path="settings/agent/agent-settings.html"></x-component>
60
</template>
@@ -75,13 +71,6 @@
71
<x-component path="settings/backup/backup-settings.html"></x-component>
72
</template>
73
</div>
78
-
79
- <!-- Task Scheduler Tab Content -->
80
- <template x-if="$store.settingsStore.activeTab === 'scheduler'">
81
- <div id="scheduler-tab-content">
82
- <x-component path="settings/scheduler/scheduler-settings.html"></x-component>
83
- </div>
84
- </template>
74
</div>
75
76
<!-- Footer -->
webui/components/sidebar/tasks/tasks-store.js
+4
-5
@@ -1,7 +1,6 @@
1
import { createStore } from "/js/AlpineStore.js";
2
import { store as chatsStore } from "/components/sidebar/chats/chats-store.js";
3
-import { store as schedulerStore } from "/components/settings/scheduler/scheduler-store.js";
4
-import { store as settingsStore } from "/components/settings/settings-store.js";
3
+import { store as schedulerStore } from "/components/modals/scheduler/scheduler-store.js";
4
5
// Tasks sidebar store: tasks list and selected task id
6
const model = {
@@ -52,9 +51,9 @@ const model = {
51
},
52
53
openDetail(taskId) {
55
- // Use the new settings modal store to open scheduler task detail
56
- if (settingsStore?.openSchedulerTaskDetail) {
57
- settingsStore.openSchedulerTaskDetail(taskId);
54
+ // Open lightweight task detail popup directly
55
+ if (schedulerStore?.showTaskDetail) {
56
+ schedulerStore.showTaskDetail(taskId);
57
}
58
},
59
webui/components/sidebar/top-section/quick-actions.html
+8
-1
@@ -4,8 +4,10 @@
4
<script type="module">
5
import { store } from "/components/sidebar/chats/chats-store.js";
6
import { store as settingsStore } from "/components/settings/settings-store.js";
7
+ import { store as schedulerModalStore } from "/components/modals/scheduler/scheduler-modal-store.js";
8
// Expose for Alpine template access
9
globalThis.openSettingsModal = () => settingsStore.open();
10
+ globalThis.openSchedulerModal = () => schedulerModalStore.open();
11
</script>
12
</head>
13
@@ -26,6 +28,10 @@
28
d="m50.301 66.5c-9 0-16.398-7.3008-16.398-16.398 0-9.1016 7.3008-16.398 16.398-16.398 9.1016 0 16.398 7.3008 16.398 16.398 0 9.0977-7.3984 16.398-16.398 16.398zm0-25.5c-5 0-9.1016 4.1016-9.1016 9.1016s4.1016 9.1016 9.1016 9.1016 9.1016-4.1016 9.1016-9.1016c-0.003906-5-4.1016-9.1016-9.1016-9.1016z">
29
</path>
30
</svg>Settings</button>
31
+ <button class="config-button" id="scheduler" @click="openSchedulerModal()">
32
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
33
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67V7z"/>
34
+ </svg>Scheduler</button>
35
<button class="config-button" id="memory-dash"
36
@click="openModal('settings/memory/memory-dashboard.html');">Memory</button>
37
<button class="config-button" id="dashboard" @click="deselectChat()">Dashboard</button>
@@ -61,7 +67,8 @@
67
opacity: 0.5;
68
}
69
64
- #settings {
70
+ #settings,
71
+ #scheduler {
72
display: flex;
73
align-items: center;
74
}
webui/css/scheduler.css
new
+399
@@ -0,0 +1,399 @@
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
+
89
+.scheduler-status-badge {
90
+ display: inline-block;
91
+ padding: 4px 8px;
92
+ border-radius: 4px;
93
+ font-size: 12px;
94
+ font-weight: 500;
95
+ text-transform: capitalize;
96
+ white-space: nowrap;
97
+}
98
+
99
+.scheduler-status-idle {
100
+ background-color: rgba(0, 100, 0, 0.2);
101
+ color: #2a9d8f; /* Dark green that works with both light and dark themes */
102
+ border: 1px solid rgba(42, 157, 143, 0.3);
103
+}
104
+
105
+.scheduler-status-running {
106
+ background-color: rgba(0, 60, 120, 0.2);
107
+ color: #4361ee; /* Dark blue that works with both light and dark themes */
108
+ border: 1px solid rgba(67, 97, 238, 0.3);
109
+}
110
+
111
+.scheduler-status-disabled {
112
+ background-color: rgba(70, 70, 70, 0.2);
113
+ color: #6c757d; /* Dark grey that works with both light and dark themes */
114
+ border: 1px solid rgba(108, 117, 125, 0.3);
115
+}
116
+
117
+.scheduler-status-error {
118
+ background-color: rgba(120, 0, 0, 0.2);
119
+ color: #e63946; /* Dark red that works with both light and dark themes */
120
+ border: 1px solid rgba(230, 57, 70, 0.3);
121
+}
122
+
123
+/* Light mode adjustments */
124
+.light-mode .scheduler-status-idle {
125
+ background-color: rgba(42, 157, 143, 0.1);
126
+ color: #1a6f65; /* Darker green for light mode */
127
+}
128
+
129
+.light-mode .scheduler-status-running {
130
+ background-color: rgba(67, 97, 238, 0.1);
131
+ color: #2540b3; /* Darker blue for light mode */
132
+}
133
+
134
+.light-mode .scheduler-status-disabled {
135
+ background-color: rgba(108, 117, 125, 0.1);
136
+ color: #495057; /* Darker grey for light mode */
137
+}
138
+
139
+.light-mode .scheduler-status-error {
140
+ 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;
193
+}
194
+
195
+.scheduler-form {
196
+ background-color: var(--color-bg-secondary);
197
+ border-radius: 4px;
198
+ padding: 20px;
199
+ margin-bottom: 20px;
200
+}
201
+
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
+
210
+.scheduler-form-grid {
211
+ display: grid;
212
+ grid-template-columns: 1fr 1fr;
213
+ gap: 16px;
214
+ margin-bottom: 20px;
215
+}
216
+
217
+.scheduler-form-field {
218
+ display: flex;
219
+ flex-direction: column;
220
+ gap: 6px;
221
+}
222
+
223
+.full-width {
224
+ grid-column: 1 / -1;
225
+}
226
+
227
+.scheduler-form-label {
228
+ font-weight: 500;
229
+}
230
+
231
+.scheduler-form-help {
232
+ font-size: 12px;
233
+ color: var(--color-text-secondary);
234
+}
235
+
236
+.scheduler-form-actions {
237
+ display: flex;
238
+ justify-content: flex-end;
239
+ gap: 12px;
240
+}
241
+
242
+.scheduler-schedule-builder {
243
+ display: grid;
244
+ grid-template-columns: repeat(5, 1fr);
245
+ gap: 12px;
246
+}
247
+
248
+.scheduler-schedule-field {
249
+ display: flex;
250
+ flex-direction: column;
251
+ gap: 4px;
252
+ max-width: 70px; /* Limit width of schedule fields */
253
+}
254
+
255
+.scheduler-schedule-field input {
256
+ width: 100%;
257
+ min-width: 0; /* Allow shrinking below content size */
258
+ font-size: 0.9rem; /* Slightly reduce font size for better fit */
259
+}
260
+
261
+.scheduler-schedule-label {
262
+ font-size: 12px;
263
+ color: var(--color-text-secondary);
264
+}
265
+
266
+.input-group {
267
+ 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;
276
+}
277
+
278
+.scheduler-sort-desc {
279
+ transform: rotate(180deg);
280
+}
281
+
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
+ }
300
+}
301
+
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
+ }
315
+}
316
+
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;
321
+}
322
+
323
+.scheduler-task-list tbody tr:hover {
324
+ background-color: rgba(255, 255, 255, 0.03);
325
+}
326
+
327
+.light-mode .scheduler-task-list tbody tr:hover {
328
+ background-color: rgba(0, 0, 0, 0.02);
329
+}
330
+
331
+.scheduler-task-list th {
332
+ background-color: var(--color-bg-secondary);
333
+ font-weight: 500;
334
+ cursor: pointer;
335
+ user-select: none;
336
+}
337
+
338
+.scheduler-task-list th:hover {
339
+ background-color: var(--color-bg-tertiary);
340
+}
341
+
342
+/* Task detail view styling */
343
+.scheduler-detail-view {
344
+ background-color: var(--color-bg-secondary);
345
+ border-radius: 4px;
346
+ padding: 20px;
347
+ margin-bottom: 20px;
348
+ animation: fadeIn 0.3s ease;
349
+}
350
+
351
+.scheduler-detail-header {
352
+ display: flex;
353
+ justify-content: flex-start;
354
+ align-items: center;
355
+ margin-bottom: 20px;
356
+ padding-bottom: 10px;
357
+ border-bottom: 1px solid var(--color-border);
358
+ flex-wrap: wrap;
359
+ gap: 10px;
360
+}
361
+
362
+.scheduler-detail-header .scheduler-detail-title {
363
+ font-size: 1.4rem;
364
+ font-weight: 500;
365
+ margin: 0;
366
+ margin-right: auto;
367
+}
368
+
369
+.scheduler-detail-header .scheduler-status-badge {
370
+ margin-right: 10px;
371
+}
372
+
373
+.scheduler-detail-content {
374
+ margin-bottom: 20px;
375
+}
376
+
377
+/* Task Scheduler Styles */
378
+
379
+.scheduler-no-schedule {
380
+ color: var(--color-text-secondary);
381
+ opacity: 0.7;
382
+ font-style: italic;
383
+}
384
+
385
+.task-container-vertical {
386
+ display: flex;
387
+ flex-direction: column;
388
+ width: 100%;
389
+ gap: 6px;
390
+}
391
+
392
+/* Smaller status badge for task list */
393
+.scheduler-status-badge-small {
394
+ font-size: 10px;
395
+ padding: 2px 6px;
396
+ margin-right: 5px;
397
+ min-width: 40px;
398
+ text-align: center;
399
+}
webui/index.css
-399
@@ -1166,405 +1166,6 @@ nav ul li a img {
1166
color: var(--color-text-secondary);
1167
}
1168
1169
-.scheduler-header {
1170
- display: flex;
1171
- justify-content: space-between;
1172
- align-items: center;
1173
- margin-bottom: 20px;
1174
-}
1175
-
1176
-.scheduler-header h2 {
1177
- margin: 0;
1178
- font-size: 1.2rem;
1179
- font-weight: 500;
1180
-}
1181
-
1182
-.scheduler-filters {
1183
- display: flex;
1184
- gap: 20px;
1185
- margin-bottom: 20px;
1186
- flex-wrap: wrap;
1187
-}
1188
-
1189
-.scheduler-filter-group {
1190
- display: flex;
1191
- align-items: center;
1192
- gap: 8px;
1193
-}
1194
-
1195
-.scheduler-filter-label {
1196
- font-weight: 500;
1197
- color: var(--color-text-secondary);
1198
-}
1199
-
1200
-.scheduler-filter-select {
1201
- padding: 6px 8px;
1202
- border-radius: 4px;
1203
- border: 1px solid var(--color-border);
1204
- background-color: var(--color-bg-secondary);
1205
- color: var(--color-text);
1206
-}
1207
-
1208
-.scheduler-task-list {
1209
- width: 100%;
1210
- border-collapse: collapse;
1211
- margin: 1rem 0;
1212
- font-size: 0.9rem;
1213
- table-layout: fixed;
1214
-}
1215
-
1216
-.scheduler-task-list th,
1217
-.scheduler-task-list td {
1218
- padding: 0.75rem;
1219
- text-align: left;
1220
- border-bottom: 1px solid var(--color-border);
1221
- vertical-align: middle;
1222
- overflow: hidden;
1223
- text-overflow: ellipsis;
1224
- white-space: nowrap;
1225
-}
1226
-
1227
-.scheduler-task-list th {
1228
- background-color: var(--color-bg-secondary);
1229
- font-weight: 500;
1230
- cursor: pointer;
1231
- user-select: none;
1232
-}
1233
-
1234
-.scheduler-task-list th:hover {
1235
- background-color: var(--color-bg-tertiary);
1236
-}
1237
-
1238
-.scheduler-task-actions {
1239
- display: flex;
1240
- gap: 8px;
1241
-}
1242
-
1243
-.scheduler-task-action {
1244
- padding: 4px;
1245
- background: none;
1246
- border: none;
1247
- color: var(--color-text-secondary);
1248
- cursor: pointer;
1249
- border-radius: 4px;
1250
-}
1251
-
1252
-.scheduler-task-action:hover {
1253
- background-color: var(--color-bg-tertiary);
1254
- color: var(--color-text);
1255
-}
1256
-
1257
-.scheduler-status-badge {
1258
- display: inline-block;
1259
- padding: 4px 8px;
1260
- border-radius: 4px;
1261
- font-size: 12px;
1262
- font-weight: 500;
1263
- text-transform: capitalize;
1264
- white-space: nowrap;
1265
-}
1266
-
1267
-.scheduler-status-idle {
1268
- background-color: rgba(0, 100, 0, 0.2);
1269
- color: #2a9d8f; /* Dark green that works with both light and dark themes */
1270
- border: 1px solid rgba(42, 157, 143, 0.3);
1271
-}
1272
-
1273
-.scheduler-status-running {
1274
- background-color: rgba(0, 60, 120, 0.2);
1275
- color: #4361ee; /* Dark blue that works with both light and dark themes */
1276
- border: 1px solid rgba(67, 97, 238, 0.3);
1277
-}
1278
-
1279
-.scheduler-status-disabled {
1280
- background-color: rgba(70, 70, 70, 0.2);
1281
- color: #6c757d; /* Dark grey that works with both light and dark themes */
1282
- border: 1px solid rgba(108, 117, 125, 0.3);
1283
-}
1284
-
1285
-.scheduler-status-error {
1286
- background-color: rgba(120, 0, 0, 0.2);
1287
- color: #e63946; /* Dark red that works with both light and dark themes */
1288
- border: 1px solid rgba(230, 57, 70, 0.3);
1289
-}
1290
-
1291
-/* Light mode adjustments */
1292
-.light-mode .scheduler-status-idle {
1293
- background-color: rgba(42, 157, 143, 0.1);
1294
- color: #1a6f65; /* Darker green for light mode */
1295
-}
1296
-
1297
-.light-mode .scheduler-status-running {
1298
- background-color: rgba(67, 97, 238, 0.1);
1299
- color: #2540b3; /* Darker blue for light mode */
1300
-}
1301
-
1302
-.light-mode .scheduler-status-disabled {
1303
- background-color: rgba(108, 117, 125, 0.1);
1304
- color: #495057; /* Darker grey for light mode */
1305
-}
1306
-
1307
-.light-mode .scheduler-status-error {
1308
- background-color: rgba(230, 57, 70, 0.1);
1309
- color: #c5283d; /* Darker red for light mode */
1310
-}
1311
-
1312
-.scheduler-empty {
1313
- text-align: center;
1314
- padding: 40px 0;
1315
- color: var(--color-text-secondary);
1316
-}
1317
-
1318
-.scheduler-empty-icon {
1319
- font-size: 32px;
1320
- margin-bottom: 10px;
1321
-}
1322
-
1323
-.scheduler-empty-text {
1324
- margin-bottom: 20px;
1325
-}
1326
-
1327
-.scheduler-loading {
1328
- text-align: center;
1329
- padding: 40px 0;
1330
- color: var(--color-text-secondary);
1331
-}
1332
-
1333
-.scheduler-task-details {
1334
- padding: 16px;
1335
- background-color: var(--color-bg-secondary);
1336
- border-radius: 4px;
1337
-}
1338
-
1339
-.scheduler-details-grid {
1340
- display: grid;
1341
- grid-template-columns: 120px 1fr;
1342
- gap: 8px 16px;
1343
- margin-bottom: 16px;
1344
-}
1345
-
1346
-.scheduler-details-label {
1347
- font-weight: 500;
1348
- color: var(--color-text-secondary);
1349
- display: flex;
1350
- align-items: center;
1351
-}
1352
-
1353
-.scheduler-details-value {
1354
- color: var(--color-text);
1355
- word-break: break-word;
1356
-}
1357
-
1358
-.scheduler-details-actions {
1359
- display: flex;
1360
- justify-content: flex-end;
1361
-}
1362
-
1363
-.scheduler-form {
1364
- background-color: var(--color-bg-secondary);
1365
- border-radius: 4px;
1366
- padding: 20px;
1367
- margin-bottom: 20px;
1368
-}
1369
-
1370
-.scheduler-form-title {
1371
- font-size: 1.2rem;
1372
- font-weight: 500;
1373
- margin-bottom: 20px;
1374
- padding-bottom: 10px;
1375
- border-bottom: 1px solid var(--color-border);
1376
-}
1377
-
1378
-.scheduler-form-grid {
1379
- display: grid;
1380
- grid-template-columns: 1fr 1fr;
1381
- gap: 16px;
1382
- margin-bottom: 20px;
1383
-}
1384
-
1385
-.scheduler-form-field {
1386
- display: flex;
1387
- flex-direction: column;
1388
- gap: 6px;
1389
-}
1390
-
1391
-.full-width {
1392
- grid-column: 1 / -1;
1393
-}
1394
-
1395
-.scheduler-form-label {
1396
- font-weight: 500;
1397
-}
1398
-
1399
-.scheduler-form-help {
1400
- font-size: 12px;
1401
- color: var(--color-text-secondary);
1402
-}
1403
-
1404
-.scheduler-form-actions {
1405
- display: flex;
1406
- justify-content: flex-end;
1407
- gap: 12px;
1408
-}
1409
-
1410
-.scheduler-schedule-builder {
1411
- display: grid;
1412
- grid-template-columns: repeat(5, 1fr);
1413
- gap: 12px;
1414
-}
1415
-
1416
-.scheduler-schedule-field {
1417
- display: flex;
1418
- flex-direction: column;
1419
- gap: 4px;
1420
- max-width: 70px; /* Limit width of schedule fields */
1421
-}
1422
-
1423
-.scheduler-schedule-field input {
1424
- width: 100%;
1425
- min-width: 0; /* Allow shrinking below content size */
1426
- font-size: 0.9rem; /* Slightly reduce font size for better fit */
1427
-}
1428
-
1429
-.scheduler-schedule-label {
1430
- font-size: 12px;
1431
- color: var(--color-text-secondary);
1432
-}
1433
-
1434
-.input-group {
1435
- display: flex;
1436
- gap: 8px;
1437
-}
1438
-
1439
-/* Sort indicators */
1440
-.scheduler-sort-indicator {
1441
- display: inline-block;
1442
- margin-left: 4px;
1443
- transition: transform 0.2s ease;
1444
-}
1445
-
1446
-.scheduler-sort-desc {
1447
- transform: rotate(180deg);
1448
-}
1449
-
1450
-/* Responsive adjustments */
1451
-@media (max-width: 768px) {
1452
- .scheduler-form-grid {
1453
- grid-template-columns: 1fr;
1454
- }
1455
-
1456
- .scheduler-schedule-builder {
1457
- grid-template-columns: 1fr 1fr;
1458
- }
1459
-
1460
- .scheduler-filters {
1461
- flex-direction: column;
1462
- gap: 12px;
1463
- }
1464
-
1465
- .scheduler-task-actions {
1466
- flex-wrap: wrap;
1467
- }
1468
-}
1469
-
1470
-@media (max-width: 480px) {
1471
- nav ul li a {
1472
- flex-direction: row;
1473
- justify-content: flex-start;
1474
- gap: 1rem;
1475
- padding: 0.75rem 1rem;
1476
- }
1477
-
1478
- nav ul li a img {
1479
- margin-bottom: 0;
1480
- width: 30px;
1481
- height: 30px;
1482
- }
1483
-}
1484
-
1485
-/* Add row hover effect for task list rows matching left panel hover */
1486
-.scheduler-task-list tbody tr {
1487
- cursor: pointer;
1488
- transition: background-color 0.2s ease;
1489
-}
1490
-
1491
-.scheduler-task-list tbody tr:hover {
1492
- background-color: rgba(255, 255, 255, 0.03);
1493
-}
1494
-
1495
-.light-mode .scheduler-task-list tbody tr:hover {
1496
- background-color: rgba(0, 0, 0, 0.02);
1497
-}
1498
-
1499
-.scheduler-task-list th {
1500
- background-color: var(--color-bg-secondary);
1501
- font-weight: 500;
1502
- cursor: pointer;
1503
- user-select: none;
1504
-}
1505
-
1506
-.scheduler-task-list th:hover {
1507
- background-color: var(--color-bg-tertiary);
1508
-}
1509
-
1510
-/* Task detail view styling */
1511
-.scheduler-detail-view {
1512
- background-color: var(--color-bg-secondary);
1513
- border-radius: 4px;
1514
- padding: 20px;
1515
- margin-bottom: 20px;
1516
- animation: fadeIn 0.3s ease;
1517
-}
1518
-
1519
-.scheduler-detail-header {
1520
- display: flex;
1521
- justify-content: flex-start;
1522
- align-items: center;
1523
- margin-bottom: 20px;
1524
- padding-bottom: 10px;
1525
- border-bottom: 1px solid var(--color-border);
1526
- flex-wrap: wrap;
1527
- gap: 10px;
1528
-}
1529
-
1530
-.scheduler-detail-header .scheduler-detail-title {
1531
- font-size: 1.4rem;
1532
- font-weight: 500;
1533
- margin: 0;
1534
- margin-right: auto;
1535
-}
1536
-
1537
-.scheduler-detail-header .scheduler-status-badge {
1538
- margin-right: 10px;
1539
-}
1540
-
1541
-.scheduler-detail-content {
1542
- margin-bottom: 20px;
1543
-}
1544
-
1545
-/* Task Scheduler Styles */
1546
-
1547
-.scheduler-no-schedule {
1548
- color: var(--color-text-secondary);
1549
- opacity: 0.7;
1550
- font-style: italic;
1551
-}
1552
-
1553
-.task-container-vertical {
1554
- display: flex;
1555
- flex-direction: column;
1556
- width: 100%;
1557
- gap: 6px;
1558
-}
1559
-
1560
-/* Smaller status badge for task list */
1561
-.scheduler-status-badge-small {
1562
- font-size: 10px;
1563
- padding: 2px 6px;
1564
- margin-right: 5px;
1565
- min-width: 40px;
1566
- text-align: center;
1567
-}
1169
.icon {
1170
vertical-align: middle;
1171
}
webui/index.html
+1
@@ -14,6 +14,7 @@
14
<link rel="stylesheet" href="css/modals.css">
15
<link rel="stylesheet" href="css/speech.css">
16
<link rel="stylesheet" href="css/scheduler-datepicker.css">
17
+ <link rel="stylesheet" href="css/scheduler.css">
18
<link rel="stylesheet" href="css/notification.css">
19
<link rel="stylesheet" href="css/buttons.css">
20