@yemi33/minions 0.1.2321 → 0.1.2323
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/docs/typography.md +21 -2
- package/dashboard/js/render-other.js +17 -1
- package/dashboard/js/render-prs.js +74 -18
- package/dashboard/slim/body.html +6 -1
- package/dashboard/slim/js/modals-tiles.js +23 -116
- package/dashboard/slim/styles.css +11 -28
- package/dashboard/styles.css +18 -8
- package/dashboard.js +58 -0
- package/docs/harness-transparency.md +17 -1
- package/docs/project-skills.md +61 -0
- package/engine/consolidation.js +69 -7
- package/engine/discover-project-skills.js +190 -15
- package/engine/lifecycle.js +15 -0
- package/engine/live-checkout.js +38 -0
- package/engine/playbook.js +31 -1
- package/engine/queries.js +15 -0
- package/engine/shared.js +83 -1
- package/engine.js +132 -16
- package/package.json +1 -1
- package/prompts/cc-system.md +15 -4
|
@@ -41,7 +41,7 @@ two stay in lockstep.
|
|
|
41
41
|
| Token | Value | Common use |
|
|
42
42
|
| ------------------ | ----- | --------------------------------------------------------- |
|
|
43
43
|
| `--text-xs` | 10px | Smallest legitimate text; sidebar badges, count pills |
|
|
44
|
-
| `--text-sm` |
|
|
44
|
+
| `--text-sm` | 12px | Status badges, chip / tag labels |
|
|
45
45
|
| `--text-base` | 12px | Captions, secondary metadata, dense table cells |
|
|
46
46
|
| `--text-md` | 13px | Default for slim chat / detail tables / button labels |
|
|
47
47
|
| `--text-lg` | 14px | Body text, modal textarea, card titles |
|
|
@@ -57,6 +57,25 @@ two stay in lockstep.
|
|
|
57
57
|
> token that breaks the "all px" rule and is explicitly allowlisted in
|
|
58
58
|
> the tripwire.
|
|
59
59
|
|
|
60
|
+
### 12px readability floor (W-mr3xgejt0008d3a5)
|
|
61
|
+
|
|
62
|
+
`--text-sm` was raised from an 11px base to **12px** so it clears the 12px
|
|
63
|
+
readability floor in both stylesheets: classic renders it at `12 × 1.05 =
|
|
64
|
+
12.6px`, slim at `12 × 1 = 12px`. `--text-xs` (10px base) stays below the
|
|
65
|
+
floor and is reserved for the very smallest chrome. To lift the
|
|
66
|
+
highest-signal small-text callsites off `--text-xs`, these selectors were
|
|
67
|
+
reassigned to `--text-sm`:
|
|
68
|
+
|
|
69
|
+
- `.sidebar-count` — primary nav count pill
|
|
70
|
+
- `.dispatch-stuck` — uppercase red "stuck" status badge
|
|
71
|
+
- `.pl-node-meta` — muted plan-node secondary text
|
|
72
|
+
- `.error-details-btn` — uppercase error-details button label
|
|
73
|
+
|
|
74
|
+
The `--text-sm` selectors already on the token (`.table th`, `.pr-table th`,
|
|
75
|
+
`.token-agent-table th`, `.status-badge`, `.sidebar-link`, slim
|
|
76
|
+
`.tile-item-subtle`) now clear the floor automatically via the token bump —
|
|
77
|
+
no per-callsite change needed.
|
|
78
|
+
|
|
60
79
|
## Role aliases (preferred for new code)
|
|
61
80
|
|
|
62
81
|
Use the role token when adding new UI. It survives a future size-scale
|
|
@@ -70,7 +89,7 @@ redesign without callsite edits.
|
|
|
70
89
|
| `--text-body` | `--text-lg` (14px) | Default body text, paragraphs |
|
|
71
90
|
| `--text-meta` | `--text-md` (13px) | Captions, timestamps, secondary metadata |
|
|
72
91
|
| `--text-caption` | `--text-base` (12px)| Small labels, table cells |
|
|
73
|
-
| `--text-micro` | `--text-sm` (
|
|
92
|
+
| `--text-micro` | `--text-sm` (12px) | Chip / tag pill labels |
|
|
74
93
|
| `--text-code` | (`0.9em`) | Inline code |
|
|
75
94
|
|
|
76
95
|
## Utility classes
|
|
@@ -69,8 +69,24 @@ function _renderProjectBranch(p) {
|
|
|
69
69
|
? ' <span class="muted">→ ' + mainBranch + '</span>'
|
|
70
70
|
: '';
|
|
71
71
|
const detachedSuffix = p.gitDetached ? ' <span class="muted">(detached)</span>' : '';
|
|
72
|
+
// opg-microsoft/minions#664 — the counts are always vs origin/<comparator>
|
|
73
|
+
// (configured mainBranch, falling back to remoteDefaultBranch; see
|
|
74
|
+
// engine/queries.js _probeProjectGitStatus), never vs the checked-out
|
|
75
|
+
// branch's own upstream. When branchDiffersFromMain the bare "↑N ↓M" chip
|
|
76
|
+
// reads as ambiguous (looks like it could be vs the current branch's
|
|
77
|
+
// tracking ref), so append the comparator name to the VISIBLE text too —
|
|
78
|
+
// not just the hover title — so the disambiguation doesn't require a
|
|
79
|
+
// hover. Skip the suffix when the checked-out branch IS the comparator
|
|
80
|
+
// since there's nothing to disambiguate.
|
|
81
|
+
const comparatorBranch = mainBranch || remoteDefault;
|
|
82
|
+
// Deliberately separate from branchDiffersFromMain (which only fires when a
|
|
83
|
+
// configured mainBranch is set, driving the "→ main" suffix): the ahead/
|
|
84
|
+
// behind comparator falls back to remoteDefaultBranch too, so the chip must
|
|
85
|
+
// disambiguate against whichever one actually produced the counts.
|
|
86
|
+
const branchDiffersFromComparator = !!(comparatorBranch && p.gitBranch !== (p.mainBranch || p.remoteDefaultBranch));
|
|
87
|
+
const comparatorSuffix = branchDiffersFromComparator ? ' vs ' + comparatorBranch : '';
|
|
72
88
|
const aheadBehind = (ahead !== null && behind !== null && (ahead > 0 || behind > 0))
|
|
73
|
-
? ' <span class="project-ab" title="' + escHtml('Ahead ' + ahead + ' / behind ' + behind + ' vs origin/' + (p.mainBranch || p.remoteDefaultBranch)) + '">↑' + ahead + ' ↓' + behind + '</span>'
|
|
89
|
+
? ' <span class="project-ab" title="' + escHtml('Ahead ' + ahead + ' / behind ' + behind + ' vs origin/' + (p.mainBranch || p.remoteDefaultBranch)) + '">↑' + ahead + ' ↓' + behind + comparatorSuffix + '</span>'
|
|
74
90
|
: '';
|
|
75
91
|
// Warning chip surfaces when the configured main disagrees with the remote
|
|
76
92
|
// default branch (the #2732 case: config=master, origin/HEAD=main) OR when
|
|
@@ -49,8 +49,14 @@ async function fetchPullRequestsFromDisk(projects) {
|
|
|
49
49
|
out.push(pr);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
// Same sort the engine applies:
|
|
52
|
+
// Same sort the engine applies (engine/queries.js#getPullRequests): group
|
|
53
|
+
// terminal (merged/abandoned/closed) PRs to the bottom, then most recent
|
|
54
|
+
// created first within each group, falling back to id.
|
|
55
|
+
const _isTerminalPr = (pr) => pr.status === 'merged' || pr.status === 'abandoned' || pr.status === 'closed';
|
|
53
56
|
out.sort((a, b) => {
|
|
57
|
+
const at = _isTerminalPr(a) ? 1 : 0;
|
|
58
|
+
const bt = _isTerminalPr(b) ? 1 : 0;
|
|
59
|
+
if (at !== bt) return at - bt;
|
|
54
60
|
const ac = a.created || '';
|
|
55
61
|
const bc = b.created || '';
|
|
56
62
|
if (ac && bc) return bc.localeCompare(ac);
|
|
@@ -196,8 +202,13 @@ function prRow(pr) {
|
|
|
196
202
|
// clearly-labeled indicator — a still-red build after an unchanged fix
|
|
197
203
|
// attempt usually means the failure isn't fixable by another agent pass
|
|
198
204
|
// (flaky infra, external outage, etc.), hence "infra issue suspected".
|
|
199
|
-
//
|
|
200
|
-
//
|
|
205
|
+
// Issue #671 — clicking resumes it via /api/pull-requests/clear-build-fix-ineffective
|
|
206
|
+
// (resumeBuildFixIneffective below), which clears _buildFixIneffective so
|
|
207
|
+
// BUILD_FAILURE auto-fix dispatch can resume. A new push already resets
|
|
208
|
+
// this pause automatically (see shared.isBuildFixIneffectivePaused); the
|
|
209
|
+
// click handler exists for the case where an operator has already
|
|
210
|
+
// confirmed (e.g. a fresh CI run queued against a rebased/retried head)
|
|
211
|
+
// and doesn't want to wait for the next poll to notice.
|
|
201
212
|
var buildFixIneffective = pr._buildFixIneffective;
|
|
202
213
|
if (buildFixIneffective && typeof buildFixIneffective === 'object' && buildFixIneffective.paused === true) {
|
|
203
214
|
var bfiTitle = 'Build-fix reported no branch change, and a live CI re-poll confirmed the build is still "'
|
|
@@ -205,11 +216,14 @@ function prRow(pr) {
|
|
|
205
216
|
+ (buildFixIneffective.headSha || 'unknown head') + ') after '
|
|
206
217
|
+ (buildFixIneffective.count || '?') + ' ineffective attempt(s). Reason: '
|
|
207
218
|
+ (buildFixIneffective.reason || 'unknown')
|
|
208
|
-
+ '. A new push to this branch will reset this pause.';
|
|
209
|
-
pausedChips += ' <
|
|
210
|
-
+ 'style="font-size:var(--text-xs)" '
|
|
211
|
-
+ 'title="' + escapeHtml(bfiTitle) + '"
|
|
212
|
-
+ '
|
|
219
|
+
+ '. A new push to this branch will reset this pause automatically, or click to resume now.';
|
|
220
|
+
pausedChips += ' <button class="pr-badge rejected pr-build-fix-ineffective-chip" '
|
|
221
|
+
+ 'style="font-size:var(--text-xs);cursor:pointer;border:none" '
|
|
222
|
+
+ 'title="' + escapeHtml(bfiTitle) + '" '
|
|
223
|
+
+ 'data-pr-id="' + escapeHtml(String(pr.id || '')) + '" '
|
|
224
|
+
+ 'data-pr-head-sha="' + escapeHtml(String(buildFixIneffective.headSha || '')) + '" '
|
|
225
|
+
+ 'onclick="event.stopPropagation();resumeBuildFixIneffective(this)">'
|
|
226
|
+
+ '⏸ infra issue suspected ▶</button>';
|
|
213
227
|
}
|
|
214
228
|
const titleText = pr.title || 'Untitled';
|
|
215
229
|
// Polish (W-mqv5ccn7): flatten Markdown in the body so the preview shows
|
|
@@ -264,15 +278,23 @@ function prRow(pr) {
|
|
|
264
278
|
// ellipsis-truncated content reveals the full text. Cell tags stay bare so
|
|
265
279
|
// the header-to-cell count assertion in test/unit.test.js continues to
|
|
266
280
|
// balance.
|
|
281
|
+
// First-column secondary project label (W-mr3mowg9) — mirrors the pr-desc
|
|
282
|
+
// secondary-text pattern used in the Title cell so reviewers can see at a
|
|
283
|
+
// glance which project a PR belongs to. `_project` is stamped by the engine
|
|
284
|
+
// enrichment / getAllPrs stamping (see top-of-file note + line ~33).
|
|
285
|
+
const projectName = pr._project ? String(pr._project) : '';
|
|
286
|
+
const projectLabelHtml = projectName
|
|
287
|
+
? '<div class="pr-desc pr-id-project" title="' + escapeHtml(projectName) + '">' + escapeHtml(projectName) + '</div>'
|
|
288
|
+
: '';
|
|
267
289
|
return '<tr>' +
|
|
268
|
-
'<td><span class="pr-id" title="' + escapeHtml(String(prId)) + '">' + escapeHtml(prShortId(String(prId))) + '</span
|
|
290
|
+
'<td><span class="pr-id" title="' + escapeHtml(String(prId)) + '">' + escapeHtml(prShortId(String(prId))) + '</span>' + projectLabelHtml + '</td>' +
|
|
269
291
|
'<td><a class="pr-title" title="' + escapeHtml(titleText) + '" href="' + escapeHtml(safeUrl(url)) + '" target="_blank" rel="noopener">' + escapeHtml(titleText) + '</a>' + followupChip + pausedChips + (descPreview ? '<div class="pr-desc" title="' + escapeHtml(descPreview) + '">' + escapeHtml(descPreview.length > 120 ? descPreview.slice(0, 120) + '...' : descPreview) + '</div>' : '') + '</td>' +
|
|
270
|
-
'<td><span class="pr-
|
|
271
|
-
'<td><span class="' + branchClass + '" title="' + escapeHtml(branchError || branchLabel) + '">' + escapeHtml(branchLabel) + '</span>' + pendingReasonHtml + '</td>' +
|
|
292
|
+
'<td><span class="pr-badge ' + statusClass + '" title="' + escapeHtml(statusLabel) + '">' + escapeHtml(statusLabel) + '</span></td>' +
|
|
272
293
|
'<td><span class="pr-badge ' + reviewClass + '" title="' + escapeHtml(reviewTitle || reviewLabel) + '">' + escapeHtml(reviewLabel) + '</span></td>' +
|
|
294
|
+
'<td><span class="' + branchClass + '" title="' + escapeHtml(branchError || branchLabel) + '">' + escapeHtml(branchLabel) + '</span>' + pendingReasonHtml + '</td>' +
|
|
295
|
+
'<td><span class="pr-agent" title="' + escapeHtml(agentText) + '">' + escapeHtml(agentText) + '</span></td>' +
|
|
273
296
|
'<td>' + reviewerCell + '</td>' +
|
|
274
297
|
'<td><span class="pr-badge ' + buildClass + '" title="' + escapeHtml(buildTitle || buildLabel) + '">' + escapeHtml(buildLabel) + '</span></td>' +
|
|
275
|
-
'<td><span class="pr-badge ' + statusClass + '" title="' + escapeHtml(statusLabel) + '">' + escapeHtml(statusLabel) + '</span></td>' +
|
|
276
298
|
'<td>' + (observeBtn || '<span style="color:var(--muted);font-size:var(--text-base)">—</span>') + '</td>' +
|
|
277
299
|
'<td><span class="pr-date" title="' + escapeHtml(createdLabel) + '">' + escapeHtml(createdLabel) + '</span></td>' +
|
|
278
300
|
'<td><button class="pr-info-pill" data-pr-id="' + escapeHtml(String(prId)) + '" onclick="event.stopPropagation();openPrDetail(this.dataset.prId)" title="Open PR detail (in-stack modal)">↗</button>' +
|
|
@@ -286,14 +308,14 @@ function prRow(pr) {
|
|
|
286
308
|
// the viewport (sticky scrollbar — see styles.css).
|
|
287
309
|
const PRS_COLGROUP =
|
|
288
310
|
'<colgroup>' +
|
|
289
|
-
'<col style="width:
|
|
311
|
+
'<col style="width:130px">' + // PR id (short label: `gh #481` / `ado #5383607`) + project label
|
|
290
312
|
'<col style="width:320px">' + // Title
|
|
291
|
-
'<col style="width:
|
|
292
|
-
'<col style="width:200px">' + // Branch
|
|
313
|
+
'<col style="width:110px">' + // Status
|
|
293
314
|
'<col style="width:130px">' + // Review
|
|
315
|
+
'<col style="width:200px">' + // Branch
|
|
316
|
+
'<col style="width:140px">' + // Agent
|
|
294
317
|
'<col style="width:140px">' + // Signed Off By
|
|
295
318
|
'<col style="width:130px">' + // Build
|
|
296
|
-
'<col style="width:110px">' + // Status
|
|
297
319
|
'<col style="width:100px">' + // Observe
|
|
298
320
|
'<col style="width:130px">' + // Created
|
|
299
321
|
'<col style="width:80px">' + // Actions (info-pill + remove)
|
|
@@ -303,7 +325,7 @@ function prTableHtml(rows) {
|
|
|
303
325
|
return '<div class="pr-table-wrap pr-table-wrap--prs"><table class="pr-table pr-table--prs">' +
|
|
304
326
|
PRS_COLGROUP +
|
|
305
327
|
'<thead><tr>' +
|
|
306
|
-
'<th>PR</th><th>Title</th><th>
|
|
328
|
+
'<th>PR</th><th>Title</th><th>Status</th><th>Review</th><th>Branch</th><th>Agent</th><th>Signed Off By</th><th>Build</th><th>Observe</th><th>Created</th><th></th>' +
|
|
307
329
|
'</tr></thead><tbody>' + rows + '</tbody></table></div>';
|
|
308
330
|
}
|
|
309
331
|
|
|
@@ -735,4 +757,38 @@ async function resumePausedCause(btn) {
|
|
|
735
757
|
}
|
|
736
758
|
}
|
|
737
759
|
|
|
738
|
-
|
|
760
|
+
// Issue #671 — resume a paused build-fix-ineffective ("infra issue
|
|
761
|
+
// suspected") pause. Mirrors resumePausedCause above but targets the
|
|
762
|
+
// headSha-keyed _buildFixIneffective record via its own endpoint. Sends the
|
|
763
|
+
// headSha the chip was rendered with so the server can reject the clear as
|
|
764
|
+
// stale if the pause has since moved to a different commit.
|
|
765
|
+
async function resumeBuildFixIneffective(btn) {
|
|
766
|
+
if (!btn || btn.disabled) return;
|
|
767
|
+
const prId = btn.dataset.prId;
|
|
768
|
+
const headSha = btn.dataset.prHeadSha || '';
|
|
769
|
+
if (!prId) return;
|
|
770
|
+
if (!confirm('Resume build-fix automation on ' + prId + '?\n\nThis clears the "infra issue suspected" pause so the engine can re-dispatch build fixes on the next discovery pass.')) return;
|
|
771
|
+
btn.disabled = true;
|
|
772
|
+
const prevDisplay = btn.style.display;
|
|
773
|
+
btn.style.display = 'none';
|
|
774
|
+
showToast('pr-toast', 'Resumed build-fix automation on ' + prId, true);
|
|
775
|
+
try {
|
|
776
|
+
const res = await fetch('/api/pull-requests/clear-build-fix-ineffective', {
|
|
777
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
778
|
+
body: JSON.stringify({ prId, headSha })
|
|
779
|
+
});
|
|
780
|
+
if (!res.ok) {
|
|
781
|
+
const d = await res.json().catch(() => ({}));
|
|
782
|
+
btn.style.display = prevDisplay;
|
|
783
|
+
btn.disabled = false;
|
|
784
|
+
showToast('pr-toast', 'Failed: ' + (d.error || 'unknown'), false);
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
} catch (e) {
|
|
788
|
+
btn.style.display = prevDisplay;
|
|
789
|
+
btn.disabled = false;
|
|
790
|
+
showToast('pr-toast', 'Error: ' + e.message, false);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
window.MinionsPrs = { prRow, prTableHtml, renderPrs, prPrev, prNext, openAllPrs, openModal, openAddPrModal, openPrDetail, unlinkPr, togglePrObserve, resumePausedCause, resumeBuildFixIneffective };
|
package/dashboard/slim/body.html
CHANGED
|
@@ -231,7 +231,12 @@
|
|
|
231
231
|
the standard TILE_VIEWS path in modals-tiles.js#renderPlansBody. There is no
|
|
232
232
|
slim-specific Plans/PRD flyout markup anymore (W-mr28ko750010199f / "reuse,
|
|
233
233
|
don't fork"): the classic /plans screen is the single source of truth for the
|
|
234
|
-
Plans + PRD tabs and every lifecycle action.
|
|
234
|
+
Plans + PRD tabs and every lifecycle action.
|
|
235
|
+
The "Active PRs" cockpit tile follows the same iframe-embed pattern
|
|
236
|
+
(W-mr3l7y0m0007102a): it opens the LITERAL classic /prs screen in a chrome-off
|
|
237
|
+
iframe (/prs?embed=1) via modals-tiles.js#renderPrsBody, so the full PR
|
|
238
|
+
filter/sort UI + row actions (comment/track/link/pr-action) are the classic
|
|
239
|
+
screen — no slim-specific PR list to maintain. -->
|
|
235
240
|
|
|
236
241
|
<!-- Pin editor — create (Pin Content action / "+ Pin") or edit an existing
|
|
237
242
|
note. Submits to POST /api/pinned (create) or /api/pinned/update (edit). -->
|
|
@@ -81,17 +81,6 @@
|
|
|
81
81
|
return card;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
// Parse the org/project slug from a canonical PR id, dropping the host prefix
|
|
85
|
-
// and the trailing "#<num>". E.g. "ado:office/iss/constellation#5315603" →
|
|
86
|
-
// "office/iss/constellation", "github:yemi33/minions#2911" → "yemi33/minions".
|
|
87
|
-
function parsePrSlug(id) {
|
|
88
|
-
if (!id || typeof id !== 'string') return '';
|
|
89
|
-
var hash = id.indexOf('#');
|
|
90
|
-
var head = hash >= 0 ? id.slice(0, hash) : id;
|
|
91
|
-
var colon = head.indexOf(':');
|
|
92
|
-
return colon >= 0 ? head.slice(colon + 1) : head;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
84
|
// Per-tile body renderers for the detail modal. Each reads the latest status
|
|
96
85
|
// snapshot and fills the modal body; openTileModal looks them up by key.
|
|
97
86
|
function renderEngineTileBody(body, data) {
|
|
@@ -160,107 +149,23 @@
|
|
|
160
149
|
body.appendChild(frame);
|
|
161
150
|
}
|
|
162
151
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
};
|
|
181
|
-
var REVIEW_STATUS = {
|
|
182
|
-
approved: { glyph: '✅', label: 'approved' },
|
|
183
|
-
'changes-requested': { glyph: '❌', label: 'changes requested' },
|
|
184
|
-
changes_requested: { glyph: '❌', label: 'changes requested' },
|
|
185
|
-
'requested-changes': { glyph: '❌', label: 'changes requested' },
|
|
186
|
-
rejected: { glyph: '❌', label: 'rejected' },
|
|
187
|
-
pending: { glyph: '💬', label: 'pending' },
|
|
188
|
-
commented: { glyph: '💬', label: 'commented' },
|
|
189
|
-
};
|
|
190
|
-
function statusSpan(subsystem, raw, table) {
|
|
191
|
-
var info = table[String(raw || '').toLowerCase()];
|
|
192
|
-
var glyph = info ? info.glyph : '➖';
|
|
193
|
-
var label = info ? info.label : (raw ? String(raw) : 'none');
|
|
194
|
-
var span = document.createElement('span');
|
|
195
|
-
span.className = 'pr-status';
|
|
196
|
-
span.textContent = glyph + ' ' + subsystem + ' ' + label;
|
|
197
|
-
span.title = subsystem + ': ' + (raw || 'unknown');
|
|
198
|
-
return span;
|
|
199
|
-
}
|
|
200
|
-
var prs = Array.isArray(data.pullRequests) ? data.pullRequests : [];
|
|
201
|
-
if (!prs.length) { tileEmpty(body, 'No tracked pull requests.'); return; }
|
|
202
|
-
function prCard(p) {
|
|
203
|
-
var num = p.prNumber || p.number || p.id;
|
|
204
|
-
var build = p.buildStatus || '';
|
|
205
|
-
var review = p.reviewStatus || '';
|
|
206
|
-
var cls = p.status === 'merged' ? 'green'
|
|
207
|
-
: (build === 'failing' ? 'red'
|
|
208
|
-
: ((p.status === 'active' || p.status === 'linked') ? 'blue' : ''));
|
|
209
|
-
var statusText = document.createElement('span');
|
|
210
|
-
statusText.textContent = p.status || 'unknown';
|
|
211
|
-
return tileCard({
|
|
212
|
-
title: (num ? '#' + num + ' ' : '') + (p.title || '(untitled PR)'),
|
|
213
|
-
// Project slug first (under title), then build/review row below — the
|
|
214
|
-
// project is the strongest disambiguator across PRs and benefits from
|
|
215
|
-
// being adjacent to the title.
|
|
216
|
-
subtle: parsePrSlug(p.id),
|
|
217
|
-
subtleBeforeMeta: true,
|
|
218
|
-
metaNodes: [statusText, statusSpan('build', build, BUILD_STATUS), statusSpan('review', review, REVIEW_STATUS)],
|
|
219
|
-
chip: { text: p.status || '—', cls: cls },
|
|
220
|
-
href: p.url || null,
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
// Partition once into three fixed buckets (status normalized to lowercase),
|
|
224
|
-
// preserving each PR's relative order within its bucket. Mirrors shared
|
|
225
|
-
// PR_STATUS (active/merged/abandoned/closed/linked); anything that does not
|
|
226
|
-
// match the merged/abandoned buckets defaults to Active so a PR with an
|
|
227
|
-
// unexpected status never silently disappears.
|
|
228
|
-
var buckets = { active: [], merged: [], abandoned: [] };
|
|
229
|
-
prs.forEach(function(p) {
|
|
230
|
-
var s = String(p.status || '').toLowerCase();
|
|
231
|
-
if (s === 'abandoned') buckets.abandoned.push(p);
|
|
232
|
-
else if (s === 'merged' || s === 'completed' || s === 'closed') buckets.merged.push(p);
|
|
233
|
-
else buckets.active.push(p);
|
|
234
|
-
});
|
|
235
|
-
// Fixed section order: Active (open) → Merged / Completed → Abandoned.
|
|
236
|
-
// Empty sections are omitted entirely.
|
|
237
|
-
var sections = [
|
|
238
|
-
{ label: 'Active', prs: buckets.active, open: true },
|
|
239
|
-
{ label: 'Merged / Completed', prs: buckets.merged, open: false },
|
|
240
|
-
{ label: 'Abandoned', prs: buckets.abandoned, open: false },
|
|
241
|
-
];
|
|
242
|
-
sections.forEach(function(section) {
|
|
243
|
-
if (!section.prs.length) return;
|
|
244
|
-
var details = document.createElement('details');
|
|
245
|
-
details.className = 'tile-section';
|
|
246
|
-
if (section.open) details.open = true;
|
|
247
|
-
var summary = document.createElement('summary');
|
|
248
|
-
summary.className = 'tile-section-summary';
|
|
249
|
-
var label = document.createElement('span');
|
|
250
|
-
label.className = 'tile-section-label';
|
|
251
|
-
label.textContent = section.label;
|
|
252
|
-
var count = document.createElement('span');
|
|
253
|
-
count.className = 'tile-section-count';
|
|
254
|
-
count.textContent = '(' + section.prs.length + ')';
|
|
255
|
-
summary.appendChild(label);
|
|
256
|
-
summary.appendChild(count);
|
|
257
|
-
details.appendChild(summary);
|
|
258
|
-
var sectionBody = document.createElement('div');
|
|
259
|
-
sectionBody.className = 'tile-section-body';
|
|
260
|
-
section.prs.forEach(function(p) { sectionBody.appendChild(prCard(p)); });
|
|
261
|
-
details.appendChild(sectionBody);
|
|
262
|
-
body.appendChild(details);
|
|
263
|
-
});
|
|
152
|
+
// Pull-requests tile body — reuses the LITERAL classic dashboard /prs screen
|
|
153
|
+
// instead of a slim-specific bucketed PR list, so there is one PRs UI, not two
|
|
154
|
+
// (W-mr3l7y0m0007102a / "reuse, don't fork" — mirrors renderQueuedWorkBody for
|
|
155
|
+
// the Work tile and renderPlansBody for the Plans tile). Slim and classic are
|
|
156
|
+
// two separate IIFE bundles with no shared scope, so we embed the real /prs
|
|
157
|
+
// screen in an iframe with the chrome-off ?embed=1 mode. The iframed page IS
|
|
158
|
+
// the classic screen — the full PR list with every filter/sort + row action
|
|
159
|
+
// (comment/track/link/pr-action), backed by the same /api/status pullRequests
|
|
160
|
+
// data + endpoints — with zero duplicated rendering logic. Classic is reachable
|
|
161
|
+
// at /prs even with slim-ux ON (only / is taken over). Built with createElement
|
|
162
|
+
// (no innerHTML) to satisfy the dashboard no-unsanitized lint gate.
|
|
163
|
+
function renderPrsBody(body) {
|
|
164
|
+
var frame = document.createElement('iframe');
|
|
165
|
+
frame.className = 'slim-prs-embed';
|
|
166
|
+
frame.src = '/prs?embed=1';
|
|
167
|
+
frame.title = 'Pull Requests';
|
|
168
|
+
body.appendChild(frame);
|
|
264
169
|
}
|
|
265
170
|
|
|
266
171
|
function renderWatchTileBody(body, data) {
|
|
@@ -283,7 +188,7 @@
|
|
|
283
188
|
dispatches: { title: 'Active dispatches', render: function(body, data) { renderDispatchTileBody(body, data, true); } },
|
|
284
189
|
queued: { title: 'Queued work', render: function(body) { renderQueuedWorkBody(body); } },
|
|
285
190
|
plans: { title: 'Plans', render: function(body) { renderPlansBody(body); } },
|
|
286
|
-
prs: { title: 'Pull requests', render:
|
|
191
|
+
prs: { title: 'Pull requests', render: function(body) { renderPrsBody(body); } },
|
|
287
192
|
watches: { title: 'Watches', render: renderWatchTileBody },
|
|
288
193
|
};
|
|
289
194
|
|
|
@@ -304,11 +209,13 @@
|
|
|
304
209
|
// The "+ Link PR" header chip only applies to the PR list.
|
|
305
210
|
var headerChip = document.getElementById('slim-tile-modal-linkpr');
|
|
306
211
|
if (headerChip) headerChip.style.display = (key === 'prs') ? '' : 'none';
|
|
307
|
-
// The queued +
|
|
308
|
-
// iframe — widen the modal + drop the body padding so the
|
|
309
|
-
// gets real estate.
|
|
212
|
+
// The queued, plans + prs tiles embed a full classic screen (/work, /plans,
|
|
213
|
+
// /prs) in an iframe — widen the modal + drop the body padding so the
|
|
214
|
+
// embedded screen gets real estate. All three reuse the shared wide-modal
|
|
215
|
+
// treatment.
|
|
310
216
|
modal.classList.toggle('tile-modal--work', key === 'queued');
|
|
311
217
|
modal.classList.toggle('tile-modal--plans', key === 'plans');
|
|
218
|
+
modal.classList.toggle('tile-modal--prs', key === 'prs');
|
|
312
219
|
view.render(body, lastStatusData || {});
|
|
313
220
|
modal.classList.add('open');
|
|
314
221
|
}
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
the slim UI at its current text size — the global bump is excluded
|
|
43
43
|
here by design. Do NOT raise this value to match main. */
|
|
44
44
|
--minions-text-scale: 1;
|
|
45
|
-
--text-xs: calc(10px * var(--minions-text-scale)); --text-sm: calc(
|
|
45
|
+
--text-xs: calc(10px * var(--minions-text-scale)); --text-sm: calc(12px * var(--minions-text-scale)); --text-base: calc(12px * var(--minions-text-scale));
|
|
46
46
|
--text-md: calc(13px * var(--minions-text-scale)); --text-lg: calc(14px * var(--minions-text-scale)); --text-xl: calc(16px * var(--minions-text-scale)); --text-2xl: calc(18px * var(--minions-text-scale));
|
|
47
47
|
--text-stat: calc(22px * var(--minions-text-scale)); --text-stat-lg: calc(28px * var(--minions-text-scale)); --text-display: calc(32px * var(--minions-text-scale));
|
|
48
48
|
--text-role-display: var(--text-display);
|
|
@@ -680,15 +680,19 @@
|
|
|
680
680
|
/* Cockpit-tile detail modal: list of dispatches / PRs / watches, or a
|
|
681
681
|
key/value engine readout (reuses .agent-detail-row). */
|
|
682
682
|
#slim-tile-modal .modal { width: 720px; max-width: calc(100vw - 32px); }
|
|
683
|
-
/* W-mqrdggys000f94a4 / W-mr28ko750010199f
|
|
684
|
-
embed a full classic screen (/work,
|
|
685
|
-
|
|
683
|
+
/* W-mqrdggys000f94a4 / W-mr28ko750010199f / W-mr3l7y0m0007102a — the
|
|
684
|
+
Queued-work + Plans + PRs tiles embed a full classic screen (/work,
|
|
685
|
+
/plans, /prs) in an iframe; widen the modal + remove body padding so the
|
|
686
|
+
embedded screen gets the full width/height. */
|
|
686
687
|
#slim-tile-modal.tile-modal--work .modal,
|
|
687
|
-
#slim-tile-modal.tile-modal--plans .modal
|
|
688
|
+
#slim-tile-modal.tile-modal--plans .modal,
|
|
689
|
+
#slim-tile-modal.tile-modal--prs .modal { width: 1180px; }
|
|
688
690
|
#slim-tile-modal.tile-modal--work .modal-body,
|
|
689
|
-
#slim-tile-modal.tile-modal--plans .modal-body
|
|
691
|
+
#slim-tile-modal.tile-modal--plans .modal-body,
|
|
692
|
+
#slim-tile-modal.tile-modal--prs .modal-body { padding: 0; }
|
|
690
693
|
.slim-work-embed,
|
|
691
|
-
.slim-plans-embed
|
|
694
|
+
.slim-plans-embed,
|
|
695
|
+
.slim-prs-embed {
|
|
692
696
|
display: block; width: 100%; height: calc(100vh - 160px); min-height: 360px;
|
|
693
697
|
border: 0; background: var(--bg);
|
|
694
698
|
}
|
|
@@ -711,7 +715,6 @@
|
|
|
711
715
|
}
|
|
712
716
|
.tile-item-meta { font-size: var(--text-base); color: var(--muted); margin-top: 3px; word-break: break-word; }
|
|
713
717
|
.tile-item-subtle { font-size: var(--text-sm); color: var(--muted); margin-top: 2px; word-break: break-word; }
|
|
714
|
-
.pr-status { display: inline-block; margin: 0 1px; text-decoration: none; }
|
|
715
718
|
.tile-chip {
|
|
716
719
|
flex: 0 0 auto;
|
|
717
720
|
font-size: var(--text-base); font-weight: 700; letter-spacing: 0.4px; text-transform: uppercase;
|
|
@@ -724,26 +727,6 @@
|
|
|
724
727
|
.tile-chip.blue { background: rgba(88, 166, 255, 0.12); color: var(--blue); border-color: var(--blue); }
|
|
725
728
|
.tile-empty { color: var(--muted); font-style: italic; font-size: var(--text-md); }
|
|
726
729
|
|
|
727
|
-
/* Collapsible PR sections (Active / Merged-Completed / Abandoned). Uses
|
|
728
|
-
native <details>/<summary> — no JS wiring needed for the toggle. */
|
|
729
|
-
.tile-section { margin-bottom: 10px; }
|
|
730
|
-
.tile-section:last-child { margin-bottom: 0; }
|
|
731
|
-
.tile-section-summary {
|
|
732
|
-
display: flex; align-items: center; gap: 6px;
|
|
733
|
-
cursor: pointer; user-select: none;
|
|
734
|
-
padding: 4px 2px; margin-bottom: 6px;
|
|
735
|
-
list-style: none;
|
|
736
|
-
font-size: var(--text-base); color: var(--muted);
|
|
737
|
-
}
|
|
738
|
-
.tile-section-summary::-webkit-details-marker { display: none; }
|
|
739
|
-
.tile-section-summary::before {
|
|
740
|
-
content: "▸"; color: var(--muted); font-size: var(--text-sm);
|
|
741
|
-
transition: transform 0.15s ease;
|
|
742
|
-
}
|
|
743
|
-
.tile-section[open] > .tile-section-summary::before { content: "▾"; }
|
|
744
|
-
.tile-section-label { font-weight: 700; letter-spacing: 0.3px; text-transform: uppercase; color: var(--text); }
|
|
745
|
-
.tile-section-count { color: var(--muted); }
|
|
746
|
-
|
|
747
730
|
/* Pinned-context list rows (Knowledge modal → Pinned Context tab). */
|
|
748
731
|
.pinned-row {
|
|
749
732
|
border: 1px solid var(--border);
|
package/dashboard/styles.css
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
its own (un-scaled) factor of 1, so the slim UI is excluded from the
|
|
29
29
|
bump by construction. */
|
|
30
30
|
--minions-text-scale: 1.05;
|
|
31
|
-
--text-xs: calc(10px * var(--minions-text-scale)); --text-sm: calc(
|
|
31
|
+
--text-xs: calc(10px * var(--minions-text-scale)); --text-sm: calc(12px * var(--minions-text-scale)); --text-base: calc(12px * var(--minions-text-scale));
|
|
32
32
|
--text-md: calc(13px * var(--minions-text-scale)); --text-lg: calc(14px * var(--minions-text-scale)); --text-xl: calc(16px * var(--minions-text-scale)); --text-2xl: calc(18px * var(--minions-text-scale));
|
|
33
33
|
--text-stat: calc(22px * var(--minions-text-scale)); --text-stat-lg: calc(28px * var(--minions-text-scale)); --text-display: calc(32px * var(--minions-text-scale));
|
|
34
34
|
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
--text-body: var(--text-lg); /* 14px — default body text */
|
|
46
46
|
--text-meta: var(--text-md); /* 13px — captions, timestamps, secondary metadata */
|
|
47
47
|
--text-caption: var(--text-base); /* 12px — small labels, table cells */
|
|
48
|
-
--text-micro: var(--text-sm); /*
|
|
48
|
+
--text-micro: var(--text-sm); /* 12px — chip / tag pill labels */
|
|
49
49
|
--text-code: 0.9em; /* inline code; relative so it scales with the surrounding text */
|
|
50
50
|
|
|
51
51
|
/* Border radius */
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
.sidebar-link .notif-badge.done { top: 50%; right: 6px; transform: translateY(-50%); width: 6px; height: 6px; }
|
|
108
108
|
.sidebar-link:hover { color: var(--text); background: var(--surface2); }
|
|
109
109
|
.sidebar-link.active { color: var(--blue); border-left-color: var(--blue); background: var(--surface2); font-weight: 600; }
|
|
110
|
-
.sidebar-count { font-size: var(--text-
|
|
110
|
+
.sidebar-count { font-size: var(--text-sm); color: var(--muted); background: var(--surface2); padding: 1px 5px; border-radius: 8px; min-width: 16px; text-align: center; }
|
|
111
111
|
.page-content { flex: 1; overflow-y: auto; min-width: 0; }
|
|
112
112
|
.page { display: none; }
|
|
113
113
|
.page.active { display: block; }
|
|
@@ -264,7 +264,7 @@
|
|
|
264
264
|
.pl-node-box.condition { border-style: dashed; }
|
|
265
265
|
.pl-node-box.pl-node-stop { border-style: dotted; border-color: var(--muted); opacity: 0.7; }
|
|
266
266
|
.pl-node-arrow { display: flex; align-items: center; padding: 0 2px; color: var(--muted); font-size: var(--text-md); flex-shrink: 0; margin-top: 8px; }
|
|
267
|
-
.pl-node-meta { font-size: var(--text-
|
|
267
|
+
.pl-node-meta { font-size: var(--text-sm); color: var(--muted); margin-top: 2px; text-align: center; max-width: 120px; overflow: hidden; text-overflow: ellipsis; }
|
|
268
268
|
.pl-node-loop { font-size: var(--text-sm); color: var(--muted); margin-top: 6px; display: flex; align-items: center; gap: 6px; }
|
|
269
269
|
.pipeline-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 12px 16px; margin-bottom: var(--space-4); cursor: pointer; transition: border-color var(--transition-fast), transform var(--transition-fast); }
|
|
270
270
|
.pipeline-card:hover { border-color: var(--blue); transform: translateY(-1px); }
|
|
@@ -358,6 +358,7 @@
|
|
|
358
358
|
.pr-title:hover { text-decoration: underline; }
|
|
359
359
|
.pr-desc { color: var(--muted); font-size: var(--text-sm); font-weight: 400; margin-top: 2px; line-height: 1.4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 400px; }
|
|
360
360
|
.pr-id { color: var(--muted); font-family: Consolas, monospace; font-size: var(--text-base); }
|
|
361
|
+
.pr-id-project { max-width: 120px; font-size: var(--text-xs); }
|
|
361
362
|
.pr-agent { font-size: var(--text-base); color: var(--text); }
|
|
362
363
|
.pr-branch { display: inline-block; max-width: 280px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: middle; font-family: Consolas, monospace; font-size: var(--text-sm); color: var(--muted); background: var(--bg); padding: var(--space-1) var(--space-3); border-radius: 3px; border: 1px solid var(--border); }
|
|
363
364
|
.pr-branch.branch-missing { color: var(--red); border-color: var(--red); background: rgba(248,81,73,0.12); }
|
|
@@ -377,7 +378,7 @@
|
|
|
377
378
|
.pr-badge.build-escalated { background: rgba(248,81,73,0.25); color: var(--red); border: 2px solid var(--red); font-weight: 600; }
|
|
378
379
|
/* W-mq08kuog001110a6 — Needs-Attention chip for stuck pending work items. */
|
|
379
380
|
.pr-badge.needs-attention { background: rgba(210,153,34,0.15); color: var(--yellow); border: 1px solid var(--yellow); font-weight: 600; cursor: help; }
|
|
380
|
-
.error-details-btn { font-size: var(--text-
|
|
381
|
+
.error-details-btn { font-size: var(--text-sm); padding: var(--space-1) var(--space-3); margin-left: var(--space-2); background: rgba(248,81,73,0.15); color: var(--red); border: 1px solid var(--red); border-radius: var(--radius-lg); cursor: pointer; font-weight: 600; text-transform: uppercase; letter-spacing: 0.3px; }
|
|
381
382
|
.error-details-btn:hover { background: rgba(248,81,73,0.3); }
|
|
382
383
|
.pr-empty { color: var(--muted); font-style: italic; font-size: var(--text-md); padding: var(--space-6) 0; }
|
|
383
384
|
.pr-pager { display: flex; align-items: center; justify-content: space-between; margin-top: 10px; }
|
|
@@ -670,7 +671,8 @@
|
|
|
670
671
|
in render-other.js, but with a dashed border + green text to read as
|
|
671
672
|
a placeholder/call-to-action. */
|
|
672
673
|
.empty-state-add {
|
|
673
|
-
display: inline-
|
|
674
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
675
|
+
min-height: 24px; min-width: 24px;
|
|
674
676
|
background: var(--surface2);
|
|
675
677
|
border: 1px dashed var(--border);
|
|
676
678
|
color: var(--green);
|
|
@@ -786,7 +788,15 @@
|
|
|
786
788
|
.cmd-chip.priority-medium { color: var(--yellow); border-color: rgba(210,153,34,0.3); background: rgba(210,153,34,0.08); }
|
|
787
789
|
.cmd-chip.priority-low { color: var(--muted); }
|
|
788
790
|
.cmd-chip.project-chip { color: var(--green); border-color: rgba(63,185,80,0.3); background: rgba(63,185,80,0.08); }
|
|
789
|
-
|
|
791
|
+
/* Close/remove affordance on command chips. Sized as a >=24x24px hit target
|
|
792
|
+
(W-mr3xgo9a a11y sweep) via inline-flex box + min dimensions rather than
|
|
793
|
+
relying on the bare glyph; the glyph stays centered. Negative vertical
|
|
794
|
+
margins keep the enlarged tap box from inflating the parent chip's height. */
|
|
795
|
+
.cmd-chip .chip-x {
|
|
796
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
797
|
+
min-width: 24px; min-height: 24px; margin: -4px -4px -4px 2px;
|
|
798
|
+
cursor: pointer; opacity: 0.5; font-size: var(--text-lg);
|
|
799
|
+
}
|
|
790
800
|
.cmd-chip .chip-x:hover { opacity: 1; }
|
|
791
801
|
.cmd-chip select {
|
|
792
802
|
background: transparent; border: none; color: inherit; font-size: var(--text-base);
|
|
@@ -978,7 +988,7 @@
|
|
|
978
988
|
.dispatch-agent { font-weight: 600; color: var(--text); }
|
|
979
989
|
.dispatch-task { flex: 1; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
980
990
|
.dispatch-time { font-size: var(--text-sm); color: var(--border); }
|
|
981
|
-
.dispatch-stuck { font-size: var(--text-
|
|
991
|
+
.dispatch-stuck { font-size: var(--text-sm); font-weight: 700; padding: var(--space-1) var(--space-3); border-radius: var(--radius-sm); text-transform: uppercase; letter-spacing: 0.3px; background: rgba(248,81,73,0.18); color: var(--red); border: 1px dashed var(--red); margin-left: var(--space-2); animation: pulse 1.5s infinite; cursor: help; }
|
|
982
992
|
|
|
983
993
|
/* Engine Log */
|
|
984
994
|
.log-list { max-height: 280px; overflow-y: auto; display: flex; flex-direction: column; gap: var(--space-1); }
|