[object Object]

← back to AbramsEgo

fix: Kamatera fleet list — sort+density+infinite scroll, no silent 40-row cap

d83163b34f6ae1600824feeceb87586056cb07e3 · 2026-07-01 13:50:56 -0700 · Steve

Files touched

Diff

commit d83163b34f6ae1600824feeceb87586056cb07e3
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Jul 1 13:50:56 2026 -0700

    fix: Kamatera fleet list — sort+density+infinite scroll, no silent 40-row cap
---
 public/index.html | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/public/index.html b/public/index.html
index 678c07b..e8d2703 100644
--- a/public/index.html
+++ b/public/index.html
@@ -139,6 +139,7 @@
   <section class="card span4">
     <h2>🛰 Fleet · Kamatera</h2>
     <div class="big" id="kam-up">—</div>
+    <div id="kam-ctl"></div>
     <div class="list" id="kam-list" style="margin-top:8px"></div>
   </section>
   <section class="card span4">
@@ -337,7 +338,7 @@ async function load(){
   // ---- fleet ----
   const k=d.kamatera||{}, l=d.localFleet||{};
   $('kam-up').innerHTML=`<span class="${cls(100-(k.up/(k.total||1)*100),20,50)}">${k.up??'—'}</span><span class="dim" style="font-size:16px">/${k.total??'—'} up</span>`;
-  $('kam-list').innerHTML=(k.procs||[]).slice(0,40).map(pr=>lrow(`${statusDot(pr.status)}${esc(pr.name)}`,`${pr.restarts??''}↺`,whenChip(pr.startedAt))).join('')|| (k.note?`<span class="muted">${esc(k.note)}</span>`:'');
+  renderKamFleet(k);
   $('loc-up').innerHTML=`<span class="good">${l.up??'—'}</span><span class="dim" style="font-size:16px">/${l.total??'—'} up</span>`;
   $('loc-list').innerHTML=(l.procs||[]).map(pr=>lrow(`${statusDot(pr.status)}${esc(pr.name)}`,`${pr.memMB!=null?pr.memMB+'M':''}`,whenChip(pr.startedAt))).join('')||'<span class="muted">no local pm2 procs</span>';
 
@@ -558,6 +559,26 @@ function renderCanaries(cans){
   });
   feedGrid('canaries', cans);
 }
+// Kamatera fleet — 226 live procs; grid (sort + density + infinite scroll)
+// replaces the old silent slice(0,40) cap. Same lrow render, 🕓 chip kept.
+function renderKamFleet(k){
+  const srank=(pr)=> pr.status==='online'?1:0; // offline first
+  initGrid('kamfleet',{
+    controlsHost:$('kam-ctl'), host:$('kam-list'), batch:25, minCols:1, maxCols:3, defaultCols:1,
+    empty:(k.note||'kamatera unreachable'),
+    sortModes:[
+      {k:'new',     label:'Newest',     cmp:(a,b)=>tsOf(b.startedAt)-tsOf(a.startedAt)},
+      {k:'old',     label:'Oldest',     cmp:(a,b)=>tsOf(a.startedAt)-tsOf(b.startedAt)},
+      {k:'name',    label:'Name A→Z',   cmp:(a,b)=>(a.name||'').localeCompare(b.name||'')},
+      {k:'restarts',label:'Restarts ↓', cmp:(a,b)=>(b.restarts||0)-(a.restarts||0)},
+      {k:'status',  label:'Status',     cmp:(a,b)=>srank(a)-srank(b)||(a.name||'').localeCompare(b.name||'')},
+    ],
+    render:(pr)=>lrow(`${statusDot(pr.status)}${esc(pr.name)}`,`${pr.restarts??''}↺`,whenChip(pr.startedAt)),
+  });
+  // empty text can change per snapshot (unreachable note vs default)
+  GRIDS['kamfleet'].cfg.empty=(k.note||'kamatera unreachable');
+  feedGrid('kamfleet', k.procs||[]);
+}
 function statusDot(st){const c=st==='online'?'var(--good)':st?'var(--bad)':'var(--dim)';return `<span class="dot" style="background:${c}"></span>`;}
 function fmtDur(s){if(s==null)return null;if(s<60)return s+'s';if(s<3600)return Math.floor(s/60)+'m';return Math.floor(s/3600)+'h';}
 function esc(x){return (x==null?'':String(x)).replace(/[<>&]/g,m=>({'<':'&lt;','>':'&gt;','&':'&amp;'}[m]));}

← 060582a chore: replenish audit (fable) — 1 real gap: kam fleet list  ·  back to AbramsEgo  ·  chore: mark task 15 done (Kamatera fleet grid controls lande d3f2f9c →