main
html 183 lines 8.21 KB
Raw
1 <html>
2 <head>
3 <script type="module">
4 import { store } from "/components/sidebar/tasks/tasks-store.js";
5 </script>
6 </head>
7 <body>
8 <div class="tasks-list-root">
9 <template x-if="$store.tasks">
10 <div x-data class="tasks-list-inner">
11 <x-extension id="sidebar-tasks-list-start"></x-extension>
12 <h3 class="section-header section-header-collapsible"
13 data-bs-toggle="collapse"
14 @click="$store.sidebar.toggleSection('tasks')"
15 x-effect="!$store.sidebar.isSectionOpen('tasks') ? $el.classList.add('collapsed') : $el.classList.remove('collapsed')">
16 Tasks
17 <svg class="arrow-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16">
18 <path d="M8 4l8 8-8 8" />
19 </svg>
20 </h3>
21 <div class="collapse" id="tasks-collapse"
22 x-effect="(() => { const c = bootstrap.Collapse.getOrCreateInstance($el, { toggle: false }); $store.sidebar.isSectionOpen('tasks') ? c.show() : c.hide(); })()">
23 <div class="tasks-list-body">
24 <div class="tasks-list-container" x-data>
25 <ul class="config-list tasks-config-list no-scrollbar" x-show="$store.tasks.visibleTasks().length > 0">
26 <template x-for="(task, taskIndex) in $store.tasks.visibleTasks()" :key="task.id">
27 <li class="task-list-item"
28 :class="{ 'sidebar-list-divider': $store.sidebar.hasRowDividerBefore('task', task, taskIndex, $store.tasks.visibleTasks()) }">
29 <div class="task-container" :class="{'task-selected': task.id === $store.tasks.selected}">
30 <div class="chat-list-button" @click="$store.tasks.selectTask(task.id)">
31 <div class="task-container-vertical">
32 <div class="task-title-line">
33 <span :class="{'project-color-ball': true, 'heartbeat': task.running}" :style="task.project?.color ? { backgroundColor: task.project.color } : { border: '1px solid var(--color-border)' }"></span>
34 <span class="task-name"
35 x-text="(task.task_name || `Task #${task.no}`)"
36 :data-task-id="task.id"></span>
37 </div>
38 <div class="task-info-line">
39 <span class="scheduler-status-badge scheduler-status-badge-small"
40 :class="task.state ? `scheduler-status-${task.state}` : 'scheduler-status-idle'"
41 x-text="task.state || 'idle'"></span>
42 <div class="task-actions">
43 <button class="btn-icon-action"
44 @click.stop="$store.tasks.openDetail(task.id)"
45 title="View task details">
46 <x-icon name="info"></x-icon>
47 </button>
48 <button class="btn-icon-action" title="Clear task chat"
49 @click.stop="$confirmClick($event, () => $store.tasks.reset(task.id))">
50 <x-icon name="delete_sweep"></x-icon>
51 </button>
52 <button class="btn-icon-action" title="Delete task" @click.stop="$confirmClick($event, () => $store.tasks.deleteTask(task.id))">
53 <x-icon name="close"></x-icon>
54 </button>
55 <button type="button" class="btn-icon-action" title="More task actions"
56 aria-label="More task actions" aria-haspopup="menu"
57 :aria-expanded="($store.sidebar.rowMenuOpenId === `task:${task.id}`).toString()"
58 @click.stop="$store.sidebar.rowMenuToggle(`task:${task.id}`, 'task', $event.currentTarget)">
59 <x-icon name="more_vert"></x-icon>
60 </button>
61 </div>
62 </div>
63 </div>
64 </div>
65 </div>
66 </li>
67 </template>
68 </ul>
69 <div class="empty-list-message" x-show="$store.tasks.visibleTasks().length === 0">
70 <p><i>No tasks to list.</i></p>
71 </div>
72 </div>
73 </div>
74 </div>
75 <x-extension id="sidebar-tasks-list-end"></x-extension>
76 </div>
77 </template>
78 </div>
79 </template>
80 </div>
81
82 <style>
83 /* Root wrappers provide flex context when embedded */
84 .tasks-list-root,
85 .tasks-list-inner {
86 display: contents;
87 }
88
89 /* tasks-list-body is the flex container inside the collapse wrapper */
90 .tasks-list-body {
91 display: flex;
92 flex-direction: column;
93 flex: 1 1 auto;
94 min-height: 0;
95 }
96 .tasks-list-container .section-header {
97 position: sticky;
98 top: 0;
99 flex-shrink: 0;
100 z-index: 10;
101 }
102
103 .tasks-list-container {
104 display: flex;
105 flex-direction: column;
106 flex: 1;
107 min-height: 0;
108 overflow: hidden;
109 position: relative;
110 }
111
112 /* Scrollable tasks config list */
113 .tasks-config-list {
114 flex: 1;
115 min-height: 0;
116 overflow-y: auto;
117 -webkit-overflow-scrolling: touch; /* smooth scrolling on iOS */
118 scroll-behavior: smooth;
119 overscroll-behavior: contain; /* avoid scroll chaining */
120 }
121
122 .task-title-line {
123 display: flex;
124 align-items: center;
125 gap: 0.5em;
126 }
127
128 .project-color-ball {
129 width: 0.6em;
130 height: 0.6em;
131 border-radius: 50%;
132 display: inline-block;
133 box-sizing: border-box;
134 flex-shrink: 0;
135 }
136
137 .task-name { display: block; width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 3px 0; cursor: pointer; border-radius: 4px; transition: background-color 0.2s; font-size: var(--font-size-small); margin-bottom: 2px; }
138
139 .task-info-line { display: flex; justify-content: space-between; align-items: center; width: 100%; margin-top: 2px; }
140 .task-actions { display: flex; gap: 0.375rem; margin-left: auto; }
141
142 .task-container {
143 width: 100%;
144 display: flex;
145 align-items: stretch;
146 position: relative;
147 border-radius: 4px;
148 overflow: hidden;
149 }
150
151 .task-container:hover {
152 background-color: var(--color-background-hover);
153 }
154
155
156 .task-container-vertical { display: flex; flex-direction: column; width: 100%; gap: 6px; }
157
158 /* Selected task accent */
159 .task-selected {
160 background-color: var(--color-background-hover);
161 }
162
163 .task-selected .task-name {
164 font-weight: bold;
165 }
166
167 /* Scheduler badges (subset used in sidebar) */
168 .scheduler-status-badge { display: inline-block; padding: 4px 8px; border-radius: 4px; font-size: 12px; font-weight: 500; text-transform: capitalize; white-space: nowrap; }
169 .scheduler-status-badge-small { font-size: 10px; padding: 2px 6px; margin-right: 5px; min-width: 40px; text-align: center; }
170 .scheduler-status-idle { background-color: rgba(0, 100, 0, 0.2); color: #2a9d8f; border: 1px solid rgba(42, 157, 143, 0.3); }
171 .scheduler-status-running { background-color: rgba(0, 60, 120, 0.2); color: #4361ee; border: 1px solid rgba(67, 97, 238, 0.3); }
172 .scheduler-status-disabled { background-color: rgba(70, 70, 70, 0.2); color: #6c757d; border: 1px solid rgba(108, 117, 125, 0.3); }
173 .scheduler-status-error { background-color: rgba(120, 0, 0, 0.2); color: #e63946; border: 1px solid rgba(230, 57, 70, 0.3); }
174 .light-mode .scheduler-status-idle { background-color: rgba(42, 157, 143, 0.1); color: #1a6f65; }
175 .light-mode .scheduler-status-running { background-color: rgba(67, 97, 238, 0.1); color: #2540b3; }
176 .light-mode .scheduler-status-disabled { background-color: rgba(108, 117, 125, 0.1); color: #495057; }
177 .light-mode .scheduler-status-error { background-color: rgba(230, 57, 70, 0.1); color: #c5283d; }
178
179 .empty-list-message { display: flex; justify-content: center; align-items: center; height: 100px; color: var(--color-secondary); text-align: center; opacity: 0.7; font-style: italic; }
180 .light-mode .empty-list-message { color: var(--color-secondary-light); }
181 </style>
182 </body>
183 </html>