@yemi33/minions 0.1.2109 โ 0.1.2111
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/dashboard/js/command-center.js +36 -36
- package/dashboard/js/detail-panel.js +12 -12
- package/dashboard/js/fre.js +9 -9
- package/dashboard/js/live-stream.js +2 -2
- package/dashboard/js/modal-qa.js +17 -17
- package/dashboard/js/qa.js +3 -3
- package/dashboard/js/refresh.js +8 -8
- package/dashboard/js/render-agents.js +12 -12
- package/dashboard/js/render-dispatch.js +8 -8
- package/dashboard/js/render-inbox.js +5 -5
- package/dashboard/js/render-managed.js +16 -16
- package/dashboard/js/render-meetings.js +36 -36
- package/dashboard/js/render-other.js +23 -23
- package/dashboard/js/render-pinned.js +1 -1
- package/dashboard/js/render-pipelines.js +36 -36
- package/dashboard/js/render-plans.js +23 -23
- package/dashboard/js/render-prd.js +100 -100
- package/dashboard/js/render-prs.js +10 -10
- package/dashboard/js/render-schedules.js +23 -23
- package/dashboard/js/render-skills.js +7 -7
- package/dashboard/js/render-utils.js +22 -22
- package/dashboard/js/render-watches.js +37 -37
- package/dashboard/js/render-work-items.js +34 -34
- package/dashboard/js/utils.js +8 -8
- package/dashboard/layout.html +20 -20
- package/dashboard/pages/engine.html +4 -4
- package/dashboard/pages/home.html +4 -4
- package/dashboard/pages/inbox.html +3 -3
- package/dashboard/pages/meetings.html +2 -2
- package/dashboard/pages/pipelines.html +2 -2
- package/dashboard/pages/plans.html +3 -3
- package/dashboard/pages/prs.html +2 -2
- package/dashboard/pages/schedule.html +2 -2
- package/dashboard/pages/tools.html +1 -1
- package/dashboard/pages/watches.html +2 -2
- package/dashboard/pages/work.html +3 -3
- package/dashboard/styles.css +38 -38
- package/docs/onboarding.md +2 -2
- package/engine.js +52 -6
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@ function renderPinned(entries) {
|
|
|
17
17
|
';border-radius:4px;cursor:pointer" onclick="if(shouldIgnoreSelectionClick(event))return;openPinnedView(' + i + ')">' +
|
|
18
18
|
'<div style="display:flex;justify-content:space-between;align-items:center">' +
|
|
19
19
|
'<strong style="font-size:var(--text-md)">' + escHtml(e.title) + '</strong>' +
|
|
20
|
-
'<button class="pr-pager-btn" style="font-size:
|
|
20
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:1px 6px;color:var(--red);border-color:var(--red)" data-pin-title="' + escHtml(e.title) + '" onclick="event.stopPropagation();removePinnedNote(this.dataset.pinTitle)">Unpin</button>' +
|
|
21
21
|
'</div>' +
|
|
22
22
|
'<div style="font-size:var(--text-sm);color:var(--muted);margin-top:4px">' + renderMd(e.content.slice(0, 200)) + (e.content.length > 200 ? '...' : '') + '</div>' +
|
|
23
23
|
'</div>'
|
|
@@ -44,7 +44,7 @@ function _renderMonitoredResources(resources, options) {
|
|
|
44
44
|
var overflow = resources.length - maxShow;
|
|
45
45
|
|
|
46
46
|
var iconMap = { pr: '๐', workitem: 'โ', url: '๐', issue: '๐' };
|
|
47
|
-
var pillStyle = 'display:inline-flex;align-items:center;gap:2px;padding:1px 6px;border-radius:10px;font-size:
|
|
47
|
+
var pillStyle = 'display:inline-flex;align-items:center;gap:2px;padding:1px 6px;border-radius:10px;font-size:var(--text-sm);text-decoration:none;' +
|
|
48
48
|
'color:var(--text);background:color-mix(in srgb, var(--muted) 10%, transparent);border:1px solid color-mix(in srgb, var(--muted) 20%, transparent);max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';
|
|
49
49
|
|
|
50
50
|
var pills = shown.map(function(r) {
|
|
@@ -63,7 +63,7 @@ function _renderMonitoredResources(resources, options) {
|
|
|
63
63
|
pills.push('<span style="' + pillStyle + ';cursor:default;opacity:0.7" title="' + overflow + ' more resource' + (overflow !== 1 ? 's' : '') + '">+' + overflow + ' more</span>');
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
var heading = compact ? '' : '<span style="font-size:
|
|
66
|
+
var heading = compact ? '' : '<span style="font-size:var(--text-sm);color:var(--muted);margin-right:4px">Monitoring:</span>';
|
|
67
67
|
return '<div style="margin-top:4px;display:flex;flex-wrap:wrap;gap:3px;align-items:center">' + heading + pills.join('') + '</div>';
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -105,7 +105,7 @@ function _getPipelineDisplayTitle(pipeline) {
|
|
|
105
105
|
function _renderArtifactLinks(artifacts, pipelineId) {
|
|
106
106
|
if (!artifacts) return '';
|
|
107
107
|
var links = [];
|
|
108
|
-
var linkStyle = 'display:inline-flex;align-items:center;gap:2px;padding:1px 6px;border-radius:10px;font-size:
|
|
108
|
+
var linkStyle = 'display:inline-flex;align-items:center;gap:2px;padding:1px 6px;border-radius:10px;font-size:var(--text-sm);cursor:pointer;text-decoration:none;color:var(--blue);background:color-mix(in srgb, var(--blue) 10%, transparent);border:1px solid color-mix(in srgb, var(--blue) 20%, transparent)';
|
|
109
109
|
|
|
110
110
|
// Pushes current pipeline modal onto back stack so detail modals can navigate back
|
|
111
111
|
var backFn = pipelineId ? "pushModalBack(function(){openPipelineDetail('" + escHtml(pipelineId) + "')});" : '';
|
|
@@ -344,9 +344,9 @@ function renderPipelines(pipelines, opts) {
|
|
|
344
344
|
'</div>' +
|
|
345
345
|
'</div>' +
|
|
346
346
|
'<div class="pipeline-card-badges">' +
|
|
347
|
-
'<span style="color:' + statusColor + ';font-size:
|
|
348
|
-
(p.stopWhen ? '<span style="font-size:
|
|
349
|
-
(p.enabled === false ? '<span style="font-size:
|
|
347
|
+
'<span style="color:' + statusColor + ';font-size:var(--text-base);font-weight:600">' + escHtml(statusLabel) + '</span>' +
|
|
348
|
+
(p.stopWhen ? '<span style="font-size:var(--text-xs);color:var(--yellow)" title="Auto-stops when condition met: ' + escHtml(typeof p.stopWhen === 'string' ? p.stopWhen : (p.stopWhen.check || 'condition')) + '">STOP-WHEN</span>' : '') +
|
|
349
|
+
(p.enabled === false ? '<span style="font-size:var(--text-xs);color:var(--red)"' + (p._stopReason ? ' title="' + escHtml(p._stopReason) + '"' : '') + '>' + (p._stoppedBy ? 'AUTO-STOPPED' : 'DISABLED') + '</span>' : '') +
|
|
350
350
|
'</div>' +
|
|
351
351
|
'</div>' +
|
|
352
352
|
resourcesHtml +
|
|
@@ -396,15 +396,15 @@ function openPipelineDetail(id) {
|
|
|
396
396
|
// Status + actions
|
|
397
397
|
var activeRun = _getPipelineActiveRun(p);
|
|
398
398
|
html += '<div style="display:flex;justify-content:space-between;align-items:center">' +
|
|
399
|
-
'<span style="font-size:
|
|
399
|
+
'<span style="font-size:var(--text-sm);color:var(--muted)">' + _renderPipelineTriggerLabel(p.trigger?.cron) + ' ยท ' + escHtml(_getPipelineStageLabel(p)) + '</span>' +
|
|
400
400
|
'<div style="display:flex;gap:6px">' +
|
|
401
401
|
(activeRun
|
|
402
|
-
? '<button class="pr-pager-btn" style="font-size:
|
|
403
|
-
'<button class="pr-pager-btn" style="font-size:
|
|
404
|
-
: '<button class="pr-pager-btn" style="font-size:
|
|
405
|
-
'<button class="pr-pager-btn" style="font-size:
|
|
406
|
-
'<button class="pr-pager-btn' + (_pipelineToggleInFlight.has(id) ? ' disabled' : '') + '" style="font-size:
|
|
407
|
-
'<button class="pr-pager-btn" style="font-size:
|
|
402
|
+
? '<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--red);border-color:var(--red)" onclick="_abortPipeline(\'' + escHtml(id) + '\',this)">Abort</button>' +
|
|
403
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--yellow);border-color:var(--yellow)" onclick="_retriggerPipeline(\'' + escHtml(id) + '\',this)">Retrigger</button>'
|
|
404
|
+
: '<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--green);border-color:var(--green)" onclick="_triggerPipeline(\'' + escHtml(id) + '\',this)">Run Now</button>') +
|
|
405
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--blue);border-color:var(--blue)" onclick="openEditPipelineModal(\'' + escHtml(id) + '\')">Edit</button>' +
|
|
406
|
+
'<button class="pr-pager-btn' + (_pipelineToggleInFlight.has(id) ? ' disabled' : '') + '" style="font-size:var(--text-xs);padding:2px 8px" onclick="_togglePipelineEnabled(\'' + escHtml(id) + '\',' + !p.enabled + ',this)">' + (p.enabled !== false ? 'Disable' : 'Enable') + '</button>' +
|
|
407
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--red);border-color:var(--red)" onclick="_deletePipelineConfirm(\'' + escHtml(id) + '\')">Delete</button>' +
|
|
408
408
|
'</div>' +
|
|
409
409
|
'</div>';
|
|
410
410
|
|
|
@@ -417,7 +417,7 @@ function openPipelineDetail(id) {
|
|
|
417
417
|
var pipelineResources = _collectMonitoredResources(p);
|
|
418
418
|
if (pipelineResources.length > 0) {
|
|
419
419
|
html += '<div style="border:1px solid color-mix(in srgb, var(--blue) 20%, transparent);border-radius:6px;padding:6px 10px;background:color-mix(in srgb, var(--blue) 4%, transparent)">' +
|
|
420
|
-
'<span style="font-size:
|
|
420
|
+
'<span style="font-size:var(--text-sm);font-weight:600;color:var(--blue)">๐ก Monitored Resources</span>' +
|
|
421
421
|
_renderMonitoredResources(pipelineResources) +
|
|
422
422
|
'</div>';
|
|
423
423
|
}
|
|
@@ -425,18 +425,18 @@ function openPipelineDetail(id) {
|
|
|
425
425
|
// stopWhen info
|
|
426
426
|
if (p.stopWhen) {
|
|
427
427
|
var swLabel = typeof p.stopWhen === 'string' ? p.stopWhen : (p.stopWhen.check || JSON.stringify(p.stopWhen));
|
|
428
|
-
html += '<div style="border:1px solid color-mix(in srgb, var(--yellow) 30%, transparent);border-radius:6px;padding:4px 10px;background:color-mix(in srgb, var(--yellow) 6%, transparent);font-size:
|
|
428
|
+
html += '<div style="border:1px solid color-mix(in srgb, var(--yellow) 30%, transparent);border-radius:6px;padding:4px 10px;background:color-mix(in srgb, var(--yellow) 6%, transparent);font-size:var(--text-base)">' +
|
|
429
429
|
'<span style="color:var(--yellow);font-weight:600">Stop When:</span> <span style="color:var(--text)">' + escHtml(swLabel) + '</span>' +
|
|
430
|
-
(p._stoppedBy ? ' <span style="color:var(--green);font-size:
|
|
430
|
+
(p._stoppedBy ? ' <span style="color:var(--green);font-size:var(--text-sm)">\u2714 triggered' + (p._stoppedAt ? ' at ' + escHtml(formatLocalDateTime(p._stoppedAt)) : '') + '</span>' : '') +
|
|
431
431
|
'</div>';
|
|
432
432
|
}
|
|
433
433
|
if (p._stopReason && p.enabled === false) {
|
|
434
|
-
html += '<div style="border:1px solid color-mix(in srgb, var(--red) 30%, transparent);border-radius:6px;padding:4px 10px;background:color-mix(in srgb, var(--red) 6%, transparent);font-size:
|
|
434
|
+
html += '<div style="border:1px solid color-mix(in srgb, var(--red) 30%, transparent);border-radius:6px;padding:4px 10px;background:color-mix(in srgb, var(--red) 6%, transparent);font-size:var(--text-base)">' +
|
|
435
435
|
'<span style="color:var(--red);font-weight:600">Auto-stopped:</span> <span style="color:var(--text)">' + escHtml(p._stopReason) + '</span>' +
|
|
436
436
|
'</div>';
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
-
html += '<h4 style="font-size:
|
|
439
|
+
html += '<h4 style="font-size:var(--text-md);color:var(--blue);margin:0">Stages</h4>';
|
|
440
440
|
(p.stages || []).forEach(function(s, i) {
|
|
441
441
|
var stageRun = activeRun?.stages?.[s.id] || {};
|
|
442
442
|
var stageStatus = stageRun.status || 'pending';
|
|
@@ -445,30 +445,30 @@ function openPipelineDetail(id) {
|
|
|
445
445
|
|
|
446
446
|
html += '<div style="border:1px solid var(--border);border-radius:6px;padding:8px 12px;background:var(--surface2)">' +
|
|
447
447
|
'<div style="display:flex;justify-content:space-between;align-items:center">' +
|
|
448
|
-
'<span style="font-weight:600;font-size:
|
|
449
|
-
'<span style="color:' + statusColor + ';font-size:
|
|
448
|
+
'<span style="font-weight:600;font-size:var(--text-md)">' + (i + 1) + '. ' + escHtml(s.title || s.id) + '</span>' +
|
|
449
|
+
'<span style="color:' + statusColor + ';font-size:var(--text-sm);font-weight:600">' + stageStatus.toUpperCase() + '</span>' +
|
|
450
450
|
'</div>' +
|
|
451
|
-
'<div style="font-size:
|
|
451
|
+
'<div style="font-size:var(--text-sm);color:var(--muted);margin-top:4px">Type: ' + escHtml(s.type) + ' | Depends on: ' + escHtml(deps) + (s.agent ? ' | Agent: ' + escHtml(s.agent) : '') +
|
|
452
452
|
(s.type === 'condition' ? ' | Check: ' + escHtml(typeof (s.check || s.condition) === 'string' ? (s.check || s.condition) : ((s.check || s.condition || {}).check || '?')) + (s.onMet ? ' | onMet: ' + escHtml(s.onMet) : '') + (s.onUnmet ? ' | onUnmet: ' + escHtml(s.onUnmet) : '') : '') +
|
|
453
453
|
'</div>' +
|
|
454
454
|
_renderMonitoredResources(s.monitoredResources || []) +
|
|
455
455
|
_renderArtifactLinks(stageRun.artifacts, id) +
|
|
456
|
-
(stageRun.output ? '<div class="pipeline-stage-output" style="margin-top:6px;font-size:
|
|
457
|
-
(stageStatus === 'waiting-human' ? '<button class="pr-pager-btn" style="font-size:
|
|
456
|
+
(stageRun.output ? '<div class="pipeline-stage-output" style="margin-top:6px;font-size:var(--text-base);max-height:150px;overflow-y:auto">' + renderMd(stageRun.output.slice(0, 500)) + '</div>' : '') +
|
|
457
|
+
(stageStatus === 'waiting-human' ? '<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--green);border-color:var(--green);margin-top:6px" onclick="_continuePipeline(\'' + escHtml(id) + '\',\'' + escHtml(s.id) + '\',this)">Continue</button>' : '') +
|
|
458
458
|
'</div>';
|
|
459
459
|
});
|
|
460
460
|
|
|
461
461
|
// Run history
|
|
462
462
|
var runs = (p.runs || []).slice(-5).reverse();
|
|
463
463
|
if (runs.length > 0) {
|
|
464
|
-
html += '<h4 style="font-size:
|
|
464
|
+
html += '<h4 style="font-size:var(--text-md);color:var(--blue);margin:0">Recent Runs</h4>';
|
|
465
465
|
runs.forEach(function(r, ri) {
|
|
466
466
|
var color = r.status === 'completed' ? 'var(--green)' : r.status === 'failed' ? 'var(--red)' : r.status === 'running' ? 'var(--blue)' : r.status === 'stopped' ? 'var(--yellow)' : 'var(--muted)';
|
|
467
467
|
// Collect all artifacts across stages for this run
|
|
468
468
|
var runArtifacts = _collectRunArtifacts(r);
|
|
469
469
|
var artifactCount = runArtifacts.total;
|
|
470
470
|
var toggleId = 'run-artifacts-' + ri;
|
|
471
|
-
html += '<div style="font-size:
|
|
471
|
+
html += '<div style="font-size:var(--text-sm)">' +
|
|
472
472
|
'<div style="display:flex;gap:8px;align-items:center">' +
|
|
473
473
|
'<span style="color:' + color + ';font-weight:600">' + r.status + '</span>' +
|
|
474
474
|
'<span style="color:var(--muted)">' + (r.startedAt ? formatLocalDateTime(r.startedAt) : '') + '</span>' +
|
|
@@ -676,8 +676,8 @@ async function _deletePipelineConfirm(id) {
|
|
|
676
676
|
|
|
677
677
|
function openCreatePipelineModal() {
|
|
678
678
|
var inputStyle = 'display:block;width:100%;margin-top:4px;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);font-size:var(--text-md);font-family:inherit';
|
|
679
|
-
var pillStyle = 'display:inline-block;padding:4px 10px;margin:2px;border:1px solid var(--border);border-radius:12px;cursor:pointer;font-size:
|
|
680
|
-
var linkStyle = 'font-size:
|
|
679
|
+
var pillStyle = 'display:inline-block;padding:4px 10px;margin:2px;border:1px solid var(--border);border-radius:12px;cursor:pointer;font-size:var(--text-base);color:var(--text);background:var(--bg);user-select:none;transition:all 0.15s';
|
|
680
|
+
var linkStyle = 'font-size:var(--text-sm);color:var(--blue);cursor:pointer;text-decoration:underline;margin-right:8px';
|
|
681
681
|
var tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
682
682
|
|
|
683
683
|
// Hour options (0-23)
|
|
@@ -705,14 +705,14 @@ function openCreatePipelineModal() {
|
|
|
705
705
|
'<div>' +
|
|
706
706
|
'<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">' +
|
|
707
707
|
'<label style="color:var(--text);font-size:var(--text-md);margin:0">Schedule</label>' +
|
|
708
|
-
'<label style="font-size:
|
|
708
|
+
'<label style="font-size:var(--text-base);color:var(--muted);cursor:pointer"><input type="checkbox" id="pl-use-cron" checked onchange="_updatePlCronPreview()" style="accent-color:var(--blue)"> Enable automatic trigger</label>' +
|
|
709
709
|
'</div>' +
|
|
710
710
|
'<div id="pl-cron-picker">' +
|
|
711
711
|
'<div style="display:flex;gap:8px;align-items:center">' +
|
|
712
712
|
'<select id="pl-pick-hour" style="' + inputStyle + ';width:auto;display:inline-block" onchange="_updatePlCronPreview()">' + hourOpts + '</select>' +
|
|
713
713
|
'<span style="color:var(--muted)">:</span>' +
|
|
714
714
|
'<select id="pl-pick-minute" style="' + inputStyle + ';width:auto;display:inline-block" onchange="_updatePlCronPreview()">' + minOpts + '</select>' +
|
|
715
|
-
'<span style="font-size:
|
|
715
|
+
'<span style="font-size:var(--text-sm);color:var(--muted)">' + escHtml(tz) + '</span>' +
|
|
716
716
|
'</div>' +
|
|
717
717
|
'<div style="margin-top:8px">' + dayPills + '</div>' +
|
|
718
718
|
'<div style="margin-top:6px">' +
|
|
@@ -720,7 +720,7 @@ function openCreatePipelineModal() {
|
|
|
720
720
|
'<span style="' + linkStyle + '" onclick="_quickSelectDays(\'weekdays\');_updatePlCronPreview()">Weekdays</span>' +
|
|
721
721
|
'<span style="' + linkStyle + '" onclick="_quickSelectDays(\'weekends\');_updatePlCronPreview()">Weekends</span>' +
|
|
722
722
|
'</div>' +
|
|
723
|
-
'<div id="pl-cron-preview" style="margin-top:4px;font-size:
|
|
723
|
+
'<div id="pl-cron-preview" style="margin-top:4px;font-size:var(--text-base);color:var(--blue)"></div>' +
|
|
724
724
|
'</div>' +
|
|
725
725
|
'</div>' +
|
|
726
726
|
'<label style="color:var(--text);font-size:var(--text-md)">Stages (JSON array)<textarea id="pl-stages" rows="10" style="' + inputStyle + ';resize:vertical;font-family:Consolas,monospace" placeholder=\'[{"id":"audit","type":"task","title":"Audit codebase","taskType":"explore"},{"id":"discuss","type":"meeting","title":"Discuss findings","dependsOn":["audit"],"participants":["all"]}]\'></textarea></label>' +
|
|
@@ -776,8 +776,8 @@ function openEditPipelineModal(id) {
|
|
|
776
776
|
if (!p) return;
|
|
777
777
|
var displayTitle = _getPipelineDisplayTitle(p);
|
|
778
778
|
var inputStyle = 'display:block;width:100%;margin-top:4px;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);font-size:var(--text-md);font-family:inherit';
|
|
779
|
-
var pillStyle = 'display:inline-block;padding:4px 10px;margin:2px;border:1px solid var(--border);border-radius:12px;cursor:pointer;font-size:
|
|
780
|
-
var linkStyle = 'font-size:
|
|
779
|
+
var pillStyle = 'display:inline-block;padding:4px 10px;margin:2px;border:1px solid var(--border);border-radius:12px;cursor:pointer;font-size:var(--text-base);color:var(--text);background:var(--bg);user-select:none;transition:all 0.15s';
|
|
780
|
+
var linkStyle = 'font-size:var(--text-sm);color:var(--blue);cursor:pointer;text-decoration:underline;margin-right:8px';
|
|
781
781
|
var tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
782
782
|
var hasCron = !!p.trigger?.cron;
|
|
783
783
|
var picker = hasCron ? _parseCronToPicker(p.trigger.cron) : { hour: 9, minute: 0, days: [1,2,3,4,5] };
|
|
@@ -802,19 +802,19 @@ function openEditPipelineModal(id) {
|
|
|
802
802
|
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escHtml() (fields: pipeline id, title, stages JSON, timezone)
|
|
803
803
|
document.getElementById('modal-body').innerHTML =
|
|
804
804
|
'<div style="display:flex;flex-direction:column;gap:10px">' +
|
|
805
|
-
'<div style="color:var(--muted);font-size:
|
|
805
|
+
'<div style="color:var(--muted);font-size:var(--text-base)">ID: <strong style="color:var(--text)">' + escHtml(id) + '</strong></div>' +
|
|
806
806
|
'<label style="color:var(--text);font-size:var(--text-md)">Title<input id="pl-title" value="' + escHtml(p.title || '') + '" style="' + inputStyle + '"></label>' +
|
|
807
807
|
'<div>' +
|
|
808
808
|
'<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">' +
|
|
809
809
|
'<label style="color:var(--text);font-size:var(--text-md);margin:0">Schedule</label>' +
|
|
810
|
-
'<label style="font-size:
|
|
810
|
+
'<label style="font-size:var(--text-base);color:var(--muted);cursor:pointer"><input type="checkbox" id="pl-use-cron"' + (hasCron ? ' checked' : '') + ' onchange="_updatePlCronPreview()" style="accent-color:var(--blue)"> Enable automatic trigger</label>' +
|
|
811
811
|
'</div>' +
|
|
812
812
|
'<div id="pl-cron-picker">' +
|
|
813
813
|
'<div style="display:flex;gap:8px;align-items:center">' +
|
|
814
814
|
'<select id="pl-pick-hour" style="' + inputStyle + ';width:auto;display:inline-block" onchange="_updatePlCronPreview()">' + hourOpts + '</select>' +
|
|
815
815
|
'<span style="color:var(--muted)">:</span>' +
|
|
816
816
|
'<select id="pl-pick-minute" style="' + inputStyle + ';width:auto;display:inline-block" onchange="_updatePlCronPreview()">' + minOpts + '</select>' +
|
|
817
|
-
'<span style="font-size:
|
|
817
|
+
'<span style="font-size:var(--text-sm);color:var(--muted)">' + escHtml(tz) + '</span>' +
|
|
818
818
|
'</div>' +
|
|
819
819
|
'<div style="margin-top:8px">' + dayPills + '</div>' +
|
|
820
820
|
'<div style="margin-top:6px">' +
|
|
@@ -822,7 +822,7 @@ function openEditPipelineModal(id) {
|
|
|
822
822
|
'<span style="' + linkStyle + '" onclick="_quickSelectDays(\'weekdays\');_updatePlCronPreview()">Weekdays</span>' +
|
|
823
823
|
'<span style="' + linkStyle + '" onclick="_quickSelectDays(\'weekends\');_updatePlCronPreview()">Weekends</span>' +
|
|
824
824
|
'</div>' +
|
|
825
|
-
'<div id="pl-cron-preview" style="margin-top:4px;font-size:
|
|
825
|
+
'<div id="pl-cron-preview" style="margin-top:4px;font-size:var(--text-base);color:var(--blue)"></div>' +
|
|
826
826
|
'</div>' +
|
|
827
827
|
'</div>' +
|
|
828
828
|
'<label style="color:var(--text);font-size:var(--text-md)">Stages (JSON array)<textarea id="pl-stages" rows="10" style="' + inputStyle + ';resize:vertical;font-family:Consolas,monospace">' + escHtml(JSON.stringify(p.stages || [], null, 2)) + '</textarea></label>' +
|
|
@@ -17,9 +17,9 @@ function openCreatePlanModal() {
|
|
|
17
17
|
document.getElementById('modal-body').innerHTML =
|
|
18
18
|
'<div style="display:flex;flex-direction:column;gap:10px">' +
|
|
19
19
|
'<label style="color:var(--text);font-size:var(--text-md)">Title <input id="plan-new-title" style="' + inputStyle + '" placeholder="e.g. Add user authentication with JWT"></label>' +
|
|
20
|
-
'<label style="color:var(--text);font-size:var(--text-md)">Project <select id="plan-new-project" style="' + inputStyle + '"><option value="">Multiple / cross-repo (agent routes per item)</option>' + projOpts + '</select><span style="display:block;font-size:
|
|
21
|
-
'<label style="color:var(--text);font-size:var(--text-md)">Plan Content <textarea id="plan-new-content" rows="12" style="' + inputStyle + ';resize:vertical;font-family:monospace;font-size:
|
|
22
|
-
'<div style="font-size:
|
|
20
|
+
'<label style="color:var(--text);font-size:var(--text-md)">Project <select id="plan-new-project" style="' + inputStyle + '"><option value="">Multiple / cross-repo (agent routes per item)</option>' + projOpts + '</select><span style="display:block;font-size:var(--text-sm);color:var(--muted);margin-top:2px">Pick a project to scope every item to one repo. Leave on cross-repo for plans that span multiple projects.</span></label>' +
|
|
21
|
+
'<label style="color:var(--text);font-size:var(--text-md)">Plan Content <textarea id="plan-new-content" rows="12" style="' + inputStyle + ';resize:vertical;font-family:monospace;font-size:var(--text-md)" placeholder="Write your plan in markdown...\n\nDescribe what needs to be built, the approach, requirements, and any constraints.\n\nThe squad will convert this into a PRD with structured work items."></textarea></label>' +
|
|
22
|
+
'<div style="font-size:var(--text-base);color:var(--muted)">After creating, click Execute on the plan card to have an agent convert it into a PRD with work items.</div>' +
|
|
23
23
|
'<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:4px">' +
|
|
24
24
|
'<button onclick="closeModal()" class="pr-pager-btn">Cancel</button>' +
|
|
25
25
|
'<button onclick="_submitCreatePlan(event)" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Create Plan</button>' +
|
|
@@ -308,7 +308,7 @@ function renderPlans(plans) {
|
|
|
308
308
|
actions = '<div class="plan-card-meta" style="margin-top:6px;color:var(--purple,#a855f7)">Revision in progress: ' + escapeHtml((p.revisionFeedback || '').slice(0, 100)) + '</div>';
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
-
const executeBtn = isDraft && (effectiveStatus === 'active' || effectiveStatus === 'draft') && !isArchived && !prdFile ? '<button class="pr-pager-btn" style="font-size:
|
|
311
|
+
const executeBtn = isDraft && (effectiveStatus === 'active' || effectiveStatus === 'draft') && !isArchived && !prdFile ? '<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--green);font-weight:600" ' +
|
|
312
312
|
'onclick="event.stopPropagation();planExecute(\'' + escapeHtml(p.file) + '\',\'' + escapeHtml(p.project) + '\',this)">Execute</button>' : '';
|
|
313
313
|
const showPause = effectiveStatus === 'dispatched' && prdFile && !isArchived;
|
|
314
314
|
// Resume pill not needed โ paused state is handled by the actions block above
|
|
@@ -316,26 +316,26 @@ function renderPlans(plans) {
|
|
|
316
316
|
const verifyWi = allWi.find(w => w.itemType === 'verify' && w.sourcePlan === prdFile);
|
|
317
317
|
const hasVerifyWi = !!verifyWi;
|
|
318
318
|
const showVerify = effectiveStatus === 'completed' && prdFile && !isArchived && !hasVerifyWi;
|
|
319
|
-
const pauseBtn = showPause ? '<button class="pr-pager-btn" style="font-size:
|
|
319
|
+
const pauseBtn = showPause ? '<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--yellow)" ' +
|
|
320
320
|
'onclick="event.stopPropagation();planPause(\'' + escapeHtml(prdFile) + '\',this)">Pause</button>' : '';
|
|
321
321
|
const resumeBtn = showResume
|
|
322
|
-
? '<button class="pr-pager-btn" style="font-size:
|
|
322
|
+
? '<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--green)" ' +
|
|
323
323
|
'onclick="event.stopPropagation();planApprove(\'' + escapeHtml(prdFile) + '\',this)">Resume</button>'
|
|
324
324
|
: '';
|
|
325
|
-
const verifyBtn = showVerify ? '<button class="pr-pager-btn" style="font-size:
|
|
325
|
+
const verifyBtn = showVerify ? '<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--green)" ' +
|
|
326
326
|
'onclick="event.stopPropagation();triggerVerify(\'' + escapeHtml(prdFile) + '\',this)">Verify</button>' : '';
|
|
327
327
|
const showArchive = !isArchived;
|
|
328
328
|
const archiveFile = prdFile || p.file;
|
|
329
329
|
const archiveReady = p.archiveReady && !isArchived;
|
|
330
|
-
const archiveBtn = showArchive ? '<button class="pr-pager-btn" style="font-size:
|
|
330
|
+
const archiveBtn = showArchive ? '<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px' +
|
|
331
331
|
(archiveReady ? ';color:var(--green);font-weight:600;border:1px solid var(--green)' : '') + '" ' +
|
|
332
332
|
'onclick="event.stopPropagation();planArchive(\'' + escapeHtml(archiveFile) + '\',this)">' +
|
|
333
333
|
(archiveReady ? 'โ Archive' : 'Archive') + '</button>' : '';
|
|
334
|
-
const archiveReadyBadge = archiveReady ? '<span style="font-size:
|
|
335
|
-
const deleteBtn = !isArchived ? '<button class="pr-pager-btn" style="font-size:
|
|
334
|
+
const archiveReadyBadge = archiveReady ? '<span style="font-size:var(--text-xs);font-weight:600;padding:1px 6px;border-radius:3px;background:rgba(63,185,80,0.15);color:var(--green);vertical-align:middle" title="Verification passed โ ready to archive">Ready to archive</span>' : '';
|
|
335
|
+
const deleteBtn = !isArchived ? '<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--red)" ' +
|
|
336
336
|
'onclick="event.stopPropagation();planDelete(\'' + escapeHtml(p.file) + '\')">Delete</button>' : '';
|
|
337
337
|
|
|
338
|
-
const versionBadge = p.version ? ' <span style="font-size:
|
|
338
|
+
const versionBadge = p.version ? ' <span style="font-size:var(--text-xs);font-weight:700;padding:1px 5px;border-radius:3px;background:rgba(56,139,253,0.15);color:var(--blue);vertical-align:middle">v' + p.version + '</span>' : '';
|
|
339
339
|
const statusColors = { 'completed': 'var(--green)', 'dispatched': 'var(--blue)', 'converting': 'var(--yellow)', 'paused': 'var(--muted)', 'awaiting-approval': 'var(--yellow)', 'approved': 'var(--green)', 'rejected': 'var(--red)', 'has-failures': 'var(--red)', 'revision-requested': 'var(--purple,#a855f7)', 'active': 'var(--muted)' };
|
|
340
340
|
const cardClass = effectiveStatus === 'dispatched' || effectiveStatus === 'converting' ? 'working' : effectiveStatus === 'awaiting-approval' || effectiveStatus === 'paused' ? 'awaiting' : effectiveStatus;
|
|
341
341
|
return '<div class="plan-card ' + cardClass + '" data-file="plans/' + escapeHtml(p.file) + '" style="cursor:pointer' + (isArchived ? ';opacity:0.7' : '') + '" onclick="if(shouldIgnoreSelectionClick(event))return;planView(\'' + escapeHtml(p.file) + '\')">' +
|
|
@@ -378,7 +378,7 @@ function renderPlans(plans) {
|
|
|
378
378
|
window._archivedPlans = archivedPlans;
|
|
379
379
|
window._archivedPlanRenderer = renderPlanCard;
|
|
380
380
|
html += '<div style="margin-top:8px;text-align:right;position:relative" data-file="plan-archives">' +
|
|
381
|
-
'<button class="pr-pager-btn" style="font-size:
|
|
381
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:3px 10px;color:var(--muted)" onclick="openArchivedPlansModal()">' +
|
|
382
382
|
'View Archives (' + archivedPlans.length + ')' +
|
|
383
383
|
'</button>' +
|
|
384
384
|
'</div>';
|
|
@@ -401,7 +401,7 @@ function openArchivedPlansModal() {
|
|
|
401
401
|
const completed = p.completedAt ? p.completedAt.slice(0, 10) : '';
|
|
402
402
|
return '<div class="plan-card" data-file="plans/' + escapeHtml(p.file) + '" style="cursor:pointer;opacity:0.8" onclick="if(shouldIgnoreSelectionClick(event))return;planView(\'' + escapeHtml(p.file) + '\')">' +
|
|
403
403
|
'<div class="plan-card-header">' +
|
|
404
|
-
'<div><div class="plan-card-title" style="font-size:
|
|
404
|
+
'<div><div class="plan-card-title" style="font-size:var(--text-lg)">' + escapeHtml(p.summary || p.file) + '</div>' +
|
|
405
405
|
'<div class="plan-card-meta">' +
|
|
406
406
|
'<span style="color:var(--green);font-weight:600">Completed</span>' +
|
|
407
407
|
(p.project ? '<span>' + escapeHtml(p.project) + '</span>' : '') +
|
|
@@ -461,11 +461,11 @@ function planReexecuteModal(file, project) {
|
|
|
461
461
|
document.getElementById('modal-title').textContent = 'Re-execute Plan';
|
|
462
462
|
document.getElementById('modal-body').innerHTML =
|
|
463
463
|
'<div style="display:flex;flex-direction:column;gap:10px">' +
|
|
464
|
-
'<div style="font-size:
|
|
464
|
+
'<div style="font-size:var(--text-md);color:var(--muted)">' + escapeHtml(file) + '</div>' +
|
|
465
465
|
'<label style="color:var(--text);font-size:var(--text-md)">Steer the regeneration โ what should be fixed in the PRD?' +
|
|
466
466
|
'<textarea id="plan-reexec-feedback" rows="6" style="' + inputStyle + ';resize:vertical" placeholder="(Optional) Describe what was wrong with the previous PRD or what the agent should change..."></textarea>' +
|
|
467
467
|
'</label>' +
|
|
468
|
-
'<div style="font-size:
|
|
468
|
+
'<div style="font-size:var(--text-base);color:var(--muted)">Leave blank to re-run without steering. The agent will read the existing PRD and rewrite it.</div>' +
|
|
469
469
|
'<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:4px">' +
|
|
470
470
|
'<button onclick="closeModal()" class="pr-pager-btn">Cancel</button>' +
|
|
471
471
|
'<button onclick="planReexecuteSubmit(\'' + escapeHtml(file) + '\',\'' + escapeHtml(project || '') + '\')" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Re-execute</button>' +
|
|
@@ -548,7 +548,7 @@ function _renderPlanModal(normalizedFile, raw, lastMod) {
|
|
|
548
548
|
if (normalizedFile.endsWith('.json')) {
|
|
549
549
|
let plan;
|
|
550
550
|
try { plan = JSON.parse(raw); } catch (e) {
|
|
551
|
-
document.getElementById('modal-body').innerHTML = '<p style="color:var(--red)">Failed to parse plan JSON: ' + escapeHtml(e.message) + '</p><pre style="font-size:
|
|
551
|
+
document.getElementById('modal-body').innerHTML = '<p style="color:var(--red)">Failed to parse plan JSON: ' + escapeHtml(e.message) + '</p><pre style="font-size:var(--text-sm);max-height:200px;overflow:auto">' + escapeHtml((raw || '').slice(0, 500)) + '</pre>';
|
|
552
552
|
return;
|
|
553
553
|
}
|
|
554
554
|
title = plan.plan_summary || normalizedFile;
|
|
@@ -590,7 +590,7 @@ function _renderPlanModal(normalizedFile, raw, lastMod) {
|
|
|
590
590
|
const isDraft = isMdPlan && !prdFile && !isCompleted;
|
|
591
591
|
const isNeedsAction = (effectiveStatus === 'awaiting-approval' || effectiveStatus === 'paused') && !isArchived;
|
|
592
592
|
|
|
593
|
-
const bs = 'font-size:
|
|
593
|
+
const bs = 'font-size:var(--text-sm);padding:2px 10px'; // button style
|
|
594
594
|
let modalActions = '';
|
|
595
595
|
|
|
596
596
|
// Approve/Reject for awaiting-approval or paused
|
|
@@ -632,11 +632,11 @@ function _renderPlanModal(normalizedFile, raw, lastMod) {
|
|
|
632
632
|
modalActions += '<button class="pr-pager-btn" style="' + bs + ';color:var(--red)" onclick="planDelete(\'' + escapeHtml(normalizedFile) + '\')">Delete</button>';
|
|
633
633
|
}
|
|
634
634
|
|
|
635
|
-
const lastModLabel = lastMod ? '<div style="font-size:
|
|
635
|
+
const lastModLabel = lastMod ? '<div style="font-size:var(--text-sm);color:var(--muted);font-weight:400;margin-top:2px">Last updated: ' + formatLocalDateTime(lastMod) + '</div>' : '';
|
|
636
636
|
const actionBtns = '<div style="display:flex;gap:4px;flex-wrap:wrap;margin-top:4px">' + modalActions + '</div>';
|
|
637
637
|
|
|
638
638
|
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escapeHtml() (fields: plan title, version label, action target files)
|
|
639
|
-
document.getElementById('modal-title').innerHTML = escapeHtml(title) + (versionLabel ? ' <span style="font-size:
|
|
639
|
+
document.getElementById('modal-title').innerHTML = escapeHtml(title) + (versionLabel ? ' <span style="font-size:var(--text-base);font-weight:700;padding:1px 6px;border-radius:3px;background:rgba(56,139,253,0.15);color:var(--blue)">' + escapeHtml(versionLabel) + '</span>' : '') + lastModLabel + actionBtns;
|
|
640
640
|
const modalBody = document.getElementById('modal-body');
|
|
641
641
|
const scrollTop = modalBody.scrollTop;
|
|
642
642
|
if (normalizedFile.endsWith('.json')) {
|
|
@@ -928,13 +928,13 @@ function _renderVerifyBadge(verifyWi) {
|
|
|
928
928
|
const planFile = verifyWi.sourcePlan || '';
|
|
929
929
|
const planSlug = planFile.replace('.json', '');
|
|
930
930
|
const verifyPr = allPrs.find(pr => (pr.prdItems || []).includes(verifyWi.id) || (pr.branch && pr.branch.includes(planSlug) && (pr.title || '').includes('[E2E]')));
|
|
931
|
-
const prLink = verifyPr?.url ? ' <a href="' + escapeHtml(verifyPr.url) + '" target="_blank" onclick="event.stopPropagation()" style="color:var(--blue);text-decoration:underline;font-size:
|
|
931
|
+
const prLink = verifyPr?.url ? ' <a href="' + escapeHtml(verifyPr.url) + '" target="_blank" onclick="event.stopPropagation()" style="color:var(--blue);text-decoration:underline;font-size:var(--text-xs)">' + escapeHtml(verifyPr.id || 'E2E PR') + '</a>' : '';
|
|
932
932
|
// Testing guide. verifyGuides moved to /api/verify-guides
|
|
933
933
|
// (window._lastVerifyGuides set by refresh.js).
|
|
934
934
|
const guides = window._lastVerifyGuides || [];
|
|
935
935
|
const guide = guides.find(g => g.planFile === planFile);
|
|
936
|
-
const guideLink = guide ? ' <span onclick="event.stopPropagation();openVerifyGuide(\'' + escapeHtml(guide.file) + '\')" style="color:var(--green);cursor:pointer;text-decoration:underline;font-size:
|
|
937
|
-
return '<span style="font-size:
|
|
936
|
+
const guideLink = guide ? ' <span onclick="event.stopPropagation();openVerifyGuide(\'' + escapeHtml(guide.file) + '\')" style="color:var(--green);cursor:pointer;text-decoration:underline;font-size:var(--text-xs)">Testing Guide</span>' : '';
|
|
937
|
+
return '<span style="font-size:var(--text-xs);font-weight:600;color:' + color + ';padding:0 4px">' + label + '</span>' + prLink + guideLink;
|
|
938
938
|
}
|
|
939
939
|
|
|
940
940
|
async function openVerifyGuide(file) {
|
|
@@ -942,7 +942,7 @@ async function openVerifyGuide(file) {
|
|
|
942
942
|
const normalizedFile = normalizePlanFile(file);
|
|
943
943
|
const content = await fetch('/api/plans/' + encodeURIComponent(normalizedFile)).then(r => r.text());
|
|
944
944
|
document.getElementById('modal-title').innerHTML = 'Manual Testing Guide' +
|
|
945
|
-
' <button class="pr-pager-btn" style="font-size:
|
|
945
|
+
' <button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;margin-left:8px;vertical-align:middle" onclick="openArchivedPrdModal()">Back</button>';
|
|
946
946
|
// eslint-disable-next-line no-unsanitized/property -- reason: renderMd() escapes all user-controlled fields before assembling HTML (see dashboard/js/utils.js)
|
|
947
947
|
document.getElementById('modal-body').innerHTML = renderMd(content);
|
|
948
948
|
_modalDocContext = { title: 'Manual Testing Guide', content, selection: '' };
|