vibe-squire 2.3.0 → 2.3.1
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.
|
@@ -130,13 +130,16 @@
|
|
|
130
130
|
return '';
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
function renderItemsTable(items) {
|
|
133
|
+
function renderItemsTable(items, opts) {
|
|
134
134
|
if (!items || !items.length) return '';
|
|
135
|
+
var highlight = opts && opts.highlight;
|
|
135
136
|
applyOptimistic(items);
|
|
136
137
|
var sorted = sortItemsForTriage(items);
|
|
137
|
-
var hasTriageable =
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
var hasTriageable =
|
|
139
|
+
highlight &&
|
|
140
|
+
sorted.some(function (it) {
|
|
141
|
+
return isTriageable(effectiveOf(it));
|
|
142
|
+
});
|
|
140
143
|
var rows = sorted
|
|
141
144
|
.map(function (it) {
|
|
142
145
|
var detailParts = [];
|
|
@@ -212,7 +215,8 @@
|
|
|
212
215
|
);
|
|
213
216
|
}
|
|
214
217
|
|
|
215
|
-
function renderRun(r) {
|
|
218
|
+
function renderRun(r, opts) {
|
|
219
|
+
var highlight = opts && opts.highlight;
|
|
216
220
|
var meta =
|
|
217
221
|
'<div class="activity-run-meta">' +
|
|
218
222
|
'<span class="activity-run-time mono">' +
|
|
@@ -274,7 +278,9 @@
|
|
|
274
278
|
'</p>';
|
|
275
279
|
}
|
|
276
280
|
|
|
277
|
-
var details = r.itemCount
|
|
281
|
+
var details = r.itemCount
|
|
282
|
+
? renderItemsTable(r.items, { highlight: highlight })
|
|
283
|
+
: '';
|
|
278
284
|
|
|
279
285
|
return (
|
|
280
286
|
'<div class="card activity-run">' +
|
|
@@ -287,25 +293,32 @@
|
|
|
287
293
|
);
|
|
288
294
|
}
|
|
289
295
|
|
|
290
|
-
function
|
|
296
|
+
function findLatestCompletedRun(runs) {
|
|
297
|
+
for (var i = 0; i < runs.length; i++) {
|
|
298
|
+
if (runs[i].phase === 'completed') return runs[i];
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function countTriageableInRun(run) {
|
|
304
|
+
if (!run) return 0;
|
|
305
|
+
var items = run.items || [];
|
|
291
306
|
var seen = {};
|
|
292
307
|
var count = 0;
|
|
293
|
-
for (var
|
|
294
|
-
var
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
seen[items[i].prUrl] = true;
|
|
299
|
-
count++;
|
|
300
|
-
}
|
|
308
|
+
for (var i = 0; i < items.length; i++) {
|
|
309
|
+
var eff = items[i].effectiveDecision || items[i].decision;
|
|
310
|
+
if (isTriageable(eff) && !seen[items[i].prUrl]) {
|
|
311
|
+
seen[items[i].prUrl] = true;
|
|
312
|
+
count++;
|
|
301
313
|
}
|
|
302
314
|
}
|
|
303
315
|
return count;
|
|
304
316
|
}
|
|
305
317
|
|
|
306
|
-
function renderTriageBanner(
|
|
307
|
-
|
|
308
|
-
|
|
318
|
+
function renderTriageBanner(latestRun) {
|
|
319
|
+
if (!latestRun) return '';
|
|
320
|
+
applyOptimistic(latestRun.items || []);
|
|
321
|
+
var count = countTriageableInRun(latestRun);
|
|
309
322
|
if (count === 0) return '';
|
|
310
323
|
return (
|
|
311
324
|
'<div class="banner banner-triage-attention">' +
|
|
@@ -326,7 +339,16 @@
|
|
|
326
339
|
'</div>'
|
|
327
340
|
);
|
|
328
341
|
}
|
|
329
|
-
|
|
342
|
+
var latestCompleted = findLatestCompletedRun(runs);
|
|
343
|
+
var latestId = latestCompleted && latestCompleted.id;
|
|
344
|
+
return (
|
|
345
|
+
renderTriageBanner(latestCompleted) +
|
|
346
|
+
runs
|
|
347
|
+
.map(function (r) {
|
|
348
|
+
return renderRun(r, { highlight: r.id === latestId });
|
|
349
|
+
})
|
|
350
|
+
.join('')
|
|
351
|
+
);
|
|
330
352
|
}
|
|
331
353
|
|
|
332
354
|
var pollTimer = null;
|
|
@@ -52,6 +52,45 @@
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/* ── Favicon notification dot ── */
|
|
56
|
+
|
|
57
|
+
var faviconLink = document.querySelector('link[rel="icon"]');
|
|
58
|
+
var baseFaviconImg = null;
|
|
59
|
+
var faviconCanvas = null;
|
|
60
|
+
var faviconReady = false;
|
|
61
|
+
var lastFaviconHasDot = false;
|
|
62
|
+
|
|
63
|
+
(function initFavicon() {
|
|
64
|
+
if (!faviconLink) return;
|
|
65
|
+
var img = new Image();
|
|
66
|
+
img.onload = function () {
|
|
67
|
+
baseFaviconImg = img;
|
|
68
|
+
faviconCanvas = document.createElement('canvas');
|
|
69
|
+
faviconCanvas.width = 32;
|
|
70
|
+
faviconCanvas.height = 32;
|
|
71
|
+
faviconReady = true;
|
|
72
|
+
};
|
|
73
|
+
img.src = '/ui/assets/favicon.svg';
|
|
74
|
+
})();
|
|
75
|
+
|
|
76
|
+
function setFaviconDot(show) {
|
|
77
|
+
if (!faviconReady || show === lastFaviconHasDot) return;
|
|
78
|
+
lastFaviconHasDot = show;
|
|
79
|
+
var ctx = faviconCanvas.getContext('2d');
|
|
80
|
+
ctx.clearRect(0, 0, 32, 32);
|
|
81
|
+
ctx.drawImage(baseFaviconImg, 0, 0, 32, 32);
|
|
82
|
+
if (show) {
|
|
83
|
+
ctx.beginPath();
|
|
84
|
+
ctx.arc(25, 7, 6, 0, 2 * Math.PI);
|
|
85
|
+
ctx.fillStyle = '#c9a227';
|
|
86
|
+
ctx.fill();
|
|
87
|
+
ctx.lineWidth = 1.5;
|
|
88
|
+
ctx.strokeStyle = '#121815';
|
|
89
|
+
ctx.stroke();
|
|
90
|
+
}
|
|
91
|
+
faviconLink.href = faviconCanvas.toDataURL('image/png');
|
|
92
|
+
}
|
|
93
|
+
|
|
55
94
|
/* ── Snapshot dispatch ── */
|
|
56
95
|
|
|
57
96
|
function publishSnapshot(snap) {
|
|
@@ -67,6 +106,7 @@
|
|
|
67
106
|
: 0;
|
|
68
107
|
updateTriageBadge(triageCount);
|
|
69
108
|
maybeNotifyTriage(triageCount);
|
|
109
|
+
setFaviconDot(triageCount > 0);
|
|
70
110
|
g.dispatchEvent(new CustomEvent(SSE_EVENT, { detail: snap }));
|
|
71
111
|
}
|
|
72
112
|
|