vibe-squire 2.3.0 → 2.3.2

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 = sorted.some(function (it) {
138
- return isTriageable(effectiveOf(it));
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 ? renderItemsTable(r.items) : '';
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 countUniqueTriageable(runs) {
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 r = 0; r < runs.length; r++) {
294
- var items = runs[r].items || [];
295
- for (var i = 0; i < items.length; i++) {
296
- var eff = items[i].effectiveDecision || items[i].decision;
297
- if (isTriageable(eff) && !seen[items[i].prUrl]) {
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(runs) {
307
- applyOptimistic((runs[0] && runs[0].items) || []);
308
- var count = countUniqueTriageable(runs);
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
- return renderTriageBanner(runs) + runs.map(renderRun).join('');
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-squire",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "description": "Local orchestrator: GitHub PR review queue → Vibe Kanban via MCP",
5
5
  "author": "alexpialetski",
6
6
  "license": "MIT",