feat(flow): collapse back-to-back sent+delivered into one line
When a message directly wakes its recipient, the broker emits `sent` then `delivered` for the same row id ~instantly, rendering two near-identical lines on the FL0W timeline. Track each recent `sent` row and, when its `delivered` lands within COLLAPSE_SECS (3s), upgrade that row in place (arrow → green ✓, title "sent + delivered") instead of adding a second line. A delivery that arrives later (recipient was busy) still renders as its own row, so genuine delivery latency stays visible. `Sent` and `Delivered` share the same broker row id (dashboard_events.rs), so matching is exact. The recentSent map is bounded to 256 entries. Reply threading is unaffected (msgRowMap still resolves the surviving row). Closes #1472.
This commit is contained in:
parent
a5f1337876
commit
a2fe3f2e01
1 changed files with 41 additions and 2 deletions
|
|
@ -170,6 +170,41 @@ import {
|
|||
}
|
||||
// Register this row so future replies can reference it.
|
||||
if (ev.id != null && ev.id > 0) msgRowMap.set(ev.id, row);
|
||||
return row;
|
||||
}
|
||||
|
||||
// Sent→Delivered collapse (#1472). When a message directly wakes its
|
||||
// recipient the broker emits `sent` then `delivered` for the same row
|
||||
// id back-to-back, rendering two near-identical lines. We track each
|
||||
// recent `sent` row and, when its `delivered` lands within
|
||||
// COLLAPSE_SECS, upgrade that row in place (✓) instead of adding a
|
||||
// second line. A delivery that arrives *later* (recipient was busy)
|
||||
// stays a separate row so the latency remains visible.
|
||||
const COLLAPSE_SECS = 3;
|
||||
const recentSent = new Map(); // broker row id → { row, at }
|
||||
function rememberSent(ev, row) {
|
||||
if (ev.id == null || ev.id <= 0) return;
|
||||
recentSent.set(ev.id, { row, at: ev.at });
|
||||
// Bound the map — drop the oldest entries once it grows past a
|
||||
// page of un-collapsed sends (insertion order = oldest first).
|
||||
while (recentSent.size > 256) {
|
||||
recentSent.delete(recentSent.keys().next().value);
|
||||
}
|
||||
}
|
||||
// Returns true if the delivered event was folded into its sent row.
|
||||
function collapseDelivered(ev) {
|
||||
if (ev.id == null || ev.id <= 0) return false;
|
||||
const s = recentSent.get(ev.id);
|
||||
if (!s || ev.at - s.at > COLLAPSE_SECS) return false;
|
||||
const arrow = s.row.querySelector('.msg-arrow');
|
||||
if (arrow) arrow.textContent = '✓';
|
||||
// Re-style the row as delivered (green ✓) — the collapsed line now
|
||||
// represents the delivered state; it was sent + delivered as one.
|
||||
s.row.classList.remove('sent');
|
||||
s.row.classList.add('delivered');
|
||||
s.row.title = 'sent + delivered';
|
||||
recentSent.delete(ev.id);
|
||||
return true;
|
||||
}
|
||||
// Anchor the `↓ N new` pill in `.flow-main` rather than the
|
||||
// default `.terminal-wrap` parent — see docs/web-ui.md::Per-agent
|
||||
|
|
@ -195,8 +230,12 @@ import {
|
|||
// and won't accidentally share with tabs.js's wider subscribe.
|
||||
streamFactory: openStream,
|
||||
renderers: {
|
||||
sent: (ev, api) => renderMsg(ev, api, '→'),
|
||||
delivered: (ev, api) => renderMsg(ev, api, '✓'),
|
||||
sent: (ev, api) => rememberSent(ev, renderMsg(ev, api, '→')),
|
||||
delivered: (ev, api) => {
|
||||
// Fold into the matching sent row when it just happened;
|
||||
// otherwise render the delivery as its own line.
|
||||
if (!collapseDelivered(ev)) renderMsg(ev, api, '✓');
|
||||
},
|
||||
// Maintain the local containers cache from the same stream
|
||||
// (compose autocomplete reads from `flowContainers`). The
|
||||
// dashboard's tab renderers aren't on this page, so we don't
|
||||
|
|
|
|||
Loading…
Reference in a new issue