@ra3orblade/swarm 0.8.0 → 0.9.0

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/web/viz.js CHANGED
@@ -131,7 +131,7 @@ function turnStripRender(turns, height) {
131
131
  * Session lanes over a time window. sessions: [{id,title,agent,projectId,startedAt,lastSeenAt,state,costUsd}]
132
132
  * One row per session, grouped by project; bar from start to last-seen, coloured by agent.
133
133
  */
134
- function timeline(sessions, { from, to, projName, now = Date.now() } = {}) {
134
+ function timeline(sessions, { from, to, projName, now = Date.now(), detail = null } = {}) {
135
135
  const span = Math.max(1, to - from);
136
136
  const x = (t) => Math.min(100, Math.max(0, (100 * (t - from)) / span));
137
137
  const byProj = new Map();
@@ -150,10 +150,27 @@ function timeline(sessions, { from, to, projName, now = Date.now() } = {}) {
150
150
  const b = Math.min(to, new Date(s.lastSeenAt).getTime());
151
151
  const live = s.state === "active" || s.state === "waiting";
152
152
  const tip = `<b>${attr(s.title ?? s.id.slice(0, 8))}</b><br><i style="background:${agentColor(s.agent)}"></i>${agentName(s.agent)} · ${s.state}<br>${hm(s.startedAt)} → ${hm(s.lastSeenAt)} · ${fmtUsd(s.costUsd)} · ${s.turns} turns`;
153
- return `<div class="tl-row" data-s="${s.id}"><span class="tl-name">${attr(s.title ?? s.id.slice(0, 8))}</span><span class="tl-track">${gridLines}<i data-tip="${attr(tip)}" class="${live ? "live" : ""}" style="left:${x(a)}%;width:${Math.max(0.4, x(b) - x(a))}%;background:${agentColor(s.agent)};color:${agentColor(s.agent)}"></i></span></div>`;
153
+ // M5.7: with per-turn timestamps the bar is a faint base + a tick per turn the blank
154
+ // stretches between ticks are the idle gaps, visible instead of painted over.
155
+ const ts = (detail?.turns?.[s.id] ?? []).filter((t) => t >= a && t <= b);
156
+ const ticks = ts.map((t) => `<u style="left:${x(t)}%;background:${agentColor(s.agent)}"></u>`).join("");
157
+ const barCls = `${live ? "live" : ""}${ts.length ? " thin" : ""}`;
158
+ return `<div class="tl-row" data-s="${s.id}"><span class="tl-name">${attr(s.title ?? s.id.slice(0, 8))}</span><span class="tl-track">${gridLines}<i data-tip="${attr(tip)}" class="${barCls}" style="left:${x(a)}%;width:${Math.max(0.4, x(b) - x(a))}%;background:${agentColor(s.agent)};color:${agentColor(s.agent)}"></i>${ticks}</span></div>`;
154
159
  })
155
160
  .join("");
156
- return `<div class="tl-group"><div class="tl-proj">${attr(projName(pid))}<small>${list.length}</small></div>${rows}</div>`;
161
+ // M5.7: claim & lease overlay — one thin lane per project when any claim overlaps the window
162
+ const spans = (detail?.claims ?? []).filter((c) => c.projectId === pid && new Date(c.expiresAt).getTime() >= from && new Date(c.acquiredAt).getTime() <= to);
163
+ const lane = spans.length
164
+ ? `<div class="tl-row claimlane"><span class="tl-name dim">claims</span><span class="tl-track">${gridLines}${spans
165
+ .map((c) => {
166
+ const a = Math.max(from, new Date(c.acquiredAt).getTime());
167
+ const b = Math.min(to, new Date(c.expiresAt).getTime());
168
+ const tip = `<b>${attr(c.task)}</b><br>${c.state} · ${attr(c.owner || "?")}<br>lease ${hm(c.acquiredAt)} → ${hm(c.expiresAt)}`;
169
+ return `<i data-tip="${attr(tip)}" class="claim ${c.state}" style="left:${x(a)}%;width:${Math.max(0.4, x(b) - x(a))}%"></i>`;
170
+ })
171
+ .join("")}</span></div>`
172
+ : "";
173
+ return `<div class="tl-group"><div class="tl-proj">${attr(projName(pid))}<small>${list.length}</small></div>${lane}${rows}</div>`;
157
174
  });
158
175
  return `<div class="tl"><div class="tl-row head"><span class="tl-name"></span><span class="tl-track">${axis}${nowLine}</span></div>${groups.join("")}</div>`;
159
176
  }