@yemi33/minions 0.1.123 → 0.1.124
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dashboard/js/render-schedules.js +129 -40
- package/dashboard/styles.css +8 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -179,6 +179,89 @@ async function _parseNaturalCron() {
|
|
|
179
179
|
|
|
180
180
|
let _schedPage = 0;
|
|
181
181
|
const SCHED_PER_PAGE = 15;
|
|
182
|
+
let _schedViewMode = 'list';
|
|
183
|
+
|
|
184
|
+
const _SLOT_COLORS = {
|
|
185
|
+
implement: { bg: 'rgba(88,166,255,0.15)', border: 'var(--blue)', text: 'var(--blue)' },
|
|
186
|
+
review: { bg: 'rgba(188,140,255,0.15)', border: 'var(--purple)', text: 'var(--purple)' },
|
|
187
|
+
fix: { bg: 'rgba(210,153,34,0.15)', border: 'var(--yellow)', text: 'var(--yellow)' },
|
|
188
|
+
explore: { bg: 'rgba(139,148,158,0.15)', border: 'var(--muted)', text: 'var(--muted)' },
|
|
189
|
+
test: { bg: 'rgba(227,179,65,0.15)', border: 'var(--orange)', text: 'var(--orange)' },
|
|
190
|
+
ask: { bg: 'rgba(63,185,80,0.15)', border: 'var(--green)', text: 'var(--green)' },
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
function _renderViewToggle() {
|
|
194
|
+
const isList = _schedViewMode === 'list';
|
|
195
|
+
return '<div style="display:flex;gap:4px;margin-bottom:8px">' +
|
|
196
|
+
'<button class="pr-pager-btn" style="font-size:9px;padding:2px 8px;' + (isList ? 'background:var(--blue);color:#fff;border-color:var(--blue)' : '') + '" onclick="_schedSetView(\'list\')">List</button>' +
|
|
197
|
+
'<button class="pr-pager-btn" style="font-size:9px;padding:2px 8px;' + (!isList ? 'background:var(--blue);color:#fff;border-color:var(--blue)' : '') + '" onclick="_schedSetView(\'calendar\')">Calendar</button>' +
|
|
198
|
+
'</div>';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function _schedSetView(mode) {
|
|
202
|
+
_schedViewMode = mode;
|
|
203
|
+
renderSchedules(window._lastSchedules || []);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function _renderScheduleCalendar(schedules) {
|
|
207
|
+
// Parse each schedule into day+hour slots
|
|
208
|
+
const slots = []; // { schedule, day, hour, minute }
|
|
209
|
+
for (const s of schedules) {
|
|
210
|
+
const p = _parseCronToPicker(s.cron || '');
|
|
211
|
+
for (const day of p.days) {
|
|
212
|
+
slots.push({ schedule: s, day: day, hour: p.hour, minute: p.minute });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Find hours that have slots (compact — skip empty hours)
|
|
217
|
+
const hoursUsed = new Set();
|
|
218
|
+
for (const sl of slots) hoursUsed.add(sl.hour);
|
|
219
|
+
const hours = [...hoursUsed].sort(function(a, b) { return a - b; });
|
|
220
|
+
|
|
221
|
+
if (hours.length === 0) {
|
|
222
|
+
return '<p class="empty">No schedules to show in calendar view.</p>';
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Build grid: header row + one row per hour
|
|
226
|
+
// Columns: Mon(1) Tue(2) Wed(3) Thu(4) Fri(5) Sat(6) Sun(0)
|
|
227
|
+
var dayOrder = [1, 2, 3, 4, 5, 6, 0];
|
|
228
|
+
var dayLabels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
|
229
|
+
|
|
230
|
+
var html = '<div class="sched-cal">';
|
|
231
|
+
// Header row
|
|
232
|
+
html += '<div class="sched-cal-header"></div>'; // empty top-left
|
|
233
|
+
for (var d = 0; d < 7; d++) {
|
|
234
|
+
html += '<div class="sched-cal-header">' + dayLabels[d] + '</div>';
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// One row per hour
|
|
238
|
+
for (var hi = 0; hi < hours.length; hi++) {
|
|
239
|
+
var hour = hours[hi];
|
|
240
|
+
html += '<div class="sched-cal-hour">' + String(hour).padStart(2, '0') + ':00</div>';
|
|
241
|
+
for (var di = 0; di < 7; di++) {
|
|
242
|
+
var dayNum = dayOrder[di];
|
|
243
|
+
var cellSlots = slots.filter(function(sl) { return sl.hour === hour && sl.day === dayNum; });
|
|
244
|
+
html += '<div class="sched-cal-cell">';
|
|
245
|
+
for (var si = 0; si < cellSlots.length; si++) {
|
|
246
|
+
var sl = cellSlots[si];
|
|
247
|
+
var s = sl.schedule;
|
|
248
|
+
var colors = _SLOT_COLORS[s.type || 'implement'] || _SLOT_COLORS.implement;
|
|
249
|
+
var opacity = s.enabled === false ? '0.4' : '1';
|
|
250
|
+
var strikeStyle = s.enabled === false ? 'text-decoration:line-through;' : '';
|
|
251
|
+
var timeLabel = String(sl.hour).padStart(2, '0') + ':' + String(sl.minute).padStart(2, '0');
|
|
252
|
+
html += '<div class="sched-cal-slot" style="background:' + colors.bg + ';border-left-color:' + colors.border + ';color:' + colors.text + ';opacity:' + opacity + '" ' +
|
|
253
|
+
'onclick="openScheduleDetail(\'' + escHtml(s.id) + '\')" title="' + escHtml(s.title + ' — ' + timeLabel + ' — ' + (s.type || 'implement')) + '">' +
|
|
254
|
+
'<span style="font-weight:600;' + strikeStyle + '">' + escHtml((s.title || s.id).slice(0, 25)) + '</span>' +
|
|
255
|
+
'<span style="font-size:9px;opacity:0.7"> ' + timeLabel + '</span>' +
|
|
256
|
+
'</div>';
|
|
257
|
+
}
|
|
258
|
+
if (cellSlots.length === 0) html += ' ';
|
|
259
|
+
html += '</div>';
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
html += '</div>';
|
|
263
|
+
return html;
|
|
264
|
+
}
|
|
182
265
|
|
|
183
266
|
function renderSchedules(schedules) {
|
|
184
267
|
schedules = schedules.filter(function(s) { return !isDeleted('sched:' + s.id); });
|
|
@@ -191,45 +274,51 @@ function renderSchedules(schedules) {
|
|
|
191
274
|
return;
|
|
192
275
|
}
|
|
193
276
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
'<
|
|
211
|
-
|
|
212
|
-
'<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
'<
|
|
218
|
-
'<
|
|
219
|
-
'<
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
'
|
|
232
|
-
|
|
277
|
+
let html = _renderViewToggle();
|
|
278
|
+
|
|
279
|
+
if (_schedViewMode === 'calendar') {
|
|
280
|
+
html += _renderScheduleCalendar(schedules);
|
|
281
|
+
} else {
|
|
282
|
+
const totalPages = Math.ceil(schedules.length / SCHED_PER_PAGE);
|
|
283
|
+
if (_schedPage >= totalPages) _schedPage = totalPages - 1;
|
|
284
|
+
const start = _schedPage * SCHED_PER_PAGE;
|
|
285
|
+
const pageItems = schedules.slice(start, start + SCHED_PER_PAGE);
|
|
286
|
+
|
|
287
|
+
html += '<div class="pr-table-wrap"><table class="pr-table"><thead><tr><th>ID</th><th>Title</th><th>Schedule</th><th>Type</th><th>Project</th><th>Agent</th><th>Enabled</th><th>Last Run</th><th></th></tr></thead><tbody>';
|
|
288
|
+
for (const s of pageItems) {
|
|
289
|
+
const enabledBadge = s.enabled
|
|
290
|
+
? '<span class="pr-badge approved">enabled</span>'
|
|
291
|
+
: '<span class="pr-badge rejected">disabled</span>';
|
|
292
|
+
const lastRun = s._lastRun ? timeAgo(s._lastRun) : 'never';
|
|
293
|
+
const typeBadge = '<span class="dispatch-type ' + escHtml(s.type || 'implement') + '">' + escHtml(s.type || 'implement') + '</span>';
|
|
294
|
+
const humanCron = _cronToHuman(s.cron || '');
|
|
295
|
+
html += '<tr style="cursor:pointer" onclick="openScheduleDetail(\'' + escHtml(s.id) + '\')">' +
|
|
296
|
+
'<td><span class="pr-id">' + escHtml(s.id || '') + '</span></td>' +
|
|
297
|
+
'<td style="max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + escHtml(s.title || '') + '">' + escHtml(s.title || '') + '</td>' +
|
|
298
|
+
'<td><span title="' + escHtml(s.cron || '') + '" style="font-size:11px;color:var(--blue)">' + escHtml(humanCron) + '</span></td>' +
|
|
299
|
+
'<td>' + typeBadge + '</td>' +
|
|
300
|
+
'<td><span style="font-size:10px;color:var(--muted)">' + escHtml(s.project || '') + '</span></td>' +
|
|
301
|
+
'<td><span class="pr-agent">' + escHtml(s.agent || 'auto') + '</span></td>' +
|
|
302
|
+
'<td>' + enabledBadge + '</td>' +
|
|
303
|
+
'<td><span class="pr-date">' + escHtml(lastRun) + '</span></td>' +
|
|
304
|
+
'<td style="white-space:nowrap">' +
|
|
305
|
+
'<button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:' + (s.enabled ? 'var(--yellow)' : 'var(--green)') + ';border-color:' + (s.enabled ? 'var(--yellow)' : 'var(--green)') + ';margin-right:4px" onclick="event.stopPropagation();toggleScheduleEnabled(\'' + escHtml(s.id) + '\',' + !s.enabled + ')" title="' + (s.enabled ? 'Disable' : 'Enable') + '">' + (s.enabled ? '⏸' : '▶') + '</button>' +
|
|
306
|
+
'<button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:var(--blue);border-color:var(--blue);margin-right:4px" onclick="event.stopPropagation();openEditScheduleModal(\'' + escHtml(s.id) + '\')" title="Edit">✎</button>' +
|
|
307
|
+
'<button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:var(--red);border-color:var(--red)" onclick="event.stopPropagation();deleteSchedule(\'' + escHtml(s.id) + '\')" title="Delete">✕</button>' +
|
|
308
|
+
'</td>' +
|
|
309
|
+
'</tr>';
|
|
310
|
+
}
|
|
311
|
+
html += '</tbody></table></div>';
|
|
312
|
+
|
|
313
|
+
if (schedules.length > SCHED_PER_PAGE) {
|
|
314
|
+
html += '<div class="pr-pager">' +
|
|
315
|
+
'<span class="pr-page-info">Showing ' + (start+1) + ' to ' + Math.min(start+SCHED_PER_PAGE, schedules.length) + ' of ' + schedules.length + '</span>' +
|
|
316
|
+
'<div class="pr-pager-btns">' +
|
|
317
|
+
'<button class="pr-pager-btn ' + (_schedPage === 0 ? 'disabled' : '') + '" onclick="_schedPrev()">Prev</button>' +
|
|
318
|
+
'<button class="pr-pager-btn ' + (_schedPage >= totalPages-1 ? 'disabled' : '') + '" onclick="_schedNext()">Next</button>' +
|
|
319
|
+
'</div>' +
|
|
320
|
+
'</div>';
|
|
321
|
+
}
|
|
233
322
|
}
|
|
234
323
|
|
|
235
324
|
el.innerHTML = html;
|
|
@@ -463,4 +552,4 @@ async function deleteSchedule(id) {
|
|
|
463
552
|
// Expose _generateScheduleId globally for the inline oninput handler
|
|
464
553
|
window._generateScheduleId = _generateScheduleId;
|
|
465
554
|
|
|
466
|
-
window.MinionsSchedules = { renderSchedules, openCreateScheduleModal, openEditScheduleModal, openScheduleDetail, submitSchedule, toggleScheduleEnabled, deleteSchedule, _cronToHuman, _parseNaturalCron, _toggleCronMode, _quickSelectDays, _toggleDayPill, _updateCronPreview, _schedPrev, _schedNext };
|
|
555
|
+
window.MinionsSchedules = { renderSchedules, openCreateScheduleModal, openEditScheduleModal, openScheduleDetail, submitSchedule, toggleScheduleEnabled, deleteSchedule, _cronToHuman, _parseNaturalCron, _toggleCronMode, _quickSelectDays, _toggleDayPill, _updateCronPreview, _schedPrev, _schedNext, _schedSetView };
|
package/dashboard/styles.css
CHANGED
|
@@ -131,6 +131,14 @@
|
|
|
131
131
|
.token-bar:hover .token-bar-tip { display: block; }
|
|
132
132
|
.token-chart-labels { display: flex; gap: 3px; }
|
|
133
133
|
.token-chart-labels span { flex: 1; min-width: 8px; max-width: 24px; font-size: 8px; color: var(--muted); text-align: center; overflow: hidden; }
|
|
134
|
+
/* Schedule calendar */
|
|
135
|
+
.sched-cal { display: grid; grid-template-columns: 50px repeat(7, 1fr); gap: 1px; background: var(--border); border-radius: var(--radius-md); overflow: hidden; }
|
|
136
|
+
.sched-cal-header { background: var(--surface2); padding: 6px 4px; text-align: center; font-size: 10px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
|
|
137
|
+
.sched-cal-hour { background: var(--surface); padding: 6px 6px 6px 0; font-size: 10px; color: var(--muted); text-align: right; font-variant-numeric: tabular-nums; display: flex; align-items: flex-start; justify-content: flex-end; }
|
|
138
|
+
.sched-cal-cell { background: var(--surface); padding: 3px; min-height: 44px; display: flex; flex-direction: column; gap: 2px; }
|
|
139
|
+
.sched-cal-slot { padding: 3px 6px; border-radius: 3px; font-size: 10px; cursor: pointer; border-left: 3px solid; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; transition: filter 0.15s; }
|
|
140
|
+
.sched-cal-slot:hover { filter: brightness(1.2); }
|
|
141
|
+
|
|
134
142
|
.token-agent-table { width: 100%; margin-top: var(--space-5); }
|
|
135
143
|
.token-agent-table th { text-align: right; font-size: var(--text-sm); color: var(--muted); font-weight: 500; padding: var(--space-2) var(--space-4); border-bottom: 1px solid var(--border); white-space: nowrap; }
|
|
136
144
|
.token-agent-table th:first-child { text-align: left; }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.124",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|