From 45df19b51b9accfe5921de3fd83f46e2dbc6f2b5 Mon Sep 17 00:00:00 2001 From: you Date: Sat, 21 Mar 2026 23:31:21 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20observation=20sort=20toggle=20=E2=80=94?= =?UTF-8?q?=20Observer=20(default)=20or=20Path=20length?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two sort modes for expanded packet groups: - Observer: group by observer, earliest first, ascending time within - Path length: shortest paths first, alphabetical observer within Sort bar appears above expanded children with bold active mode. --- public/index.html | 2 +- public/packets.js | 67 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/public/index.html b/public/index.html index 56f06adb..c954721e 100644 --- a/public/index.html +++ b/public/index.html @@ -84,7 +84,7 @@ - + diff --git a/public/packets.js b/public/packets.js index 23809051..dba51322 100644 --- a/public/packets.js +++ b/public/packets.js @@ -600,6 +600,13 @@ if (hash) selectPacket(null, hash); else selectPacket(Number(value)); } + else if (action === 'sort-group') { + e.preventDefault(); + const sortType = btn.dataset.sort; + groupSortModes[value] = sortType; + const group = packets.find(p => p.hash === value); + if (group) { sortGroupChildren(group); renderTableRows(); } + } else if (action === 'select-observation') { const parentHash = row.dataset.parentHash; const group = packets.find(p => p.hash === parentHash); @@ -692,6 +699,10 @@ `; // Child rows (loaded async when expanded) if (isExpanded && p._children) { + const sortMode = groupSortModes[p.hash] || SORT_OBSERVER; + const obsLabel = sortMode === SORT_OBSERVER ? 'Observer' : 'Observer'; + const pathLabel = sortMode === SORT_PATH_LENGTH ? 'Path length' : 'Path length'; + html += `Sort: ${obsLabel} · ${pathLabel}`; for (const c of p._children) { const typeName = payloadTypeName(c.payload_type); const typeClass = payloadTypeColor(c.payload_type); @@ -1237,6 +1248,45 @@ } catch {} })(); + // Observation sort modes + const SORT_OBSERVER = 'observer'; + const SORT_PATH_LENGTH = 'path'; + const groupSortModes = {}; // hash → sort mode + + function getPathHopCount(c) { + try { return JSON.parse(c.path_json || '[]').length; } catch { return 0; } + } + + function sortGroupChildren(group) { + if (!group || !group._children) return; + const mode = groupSortModes[group.hash] || SORT_OBSERVER; + + if (mode === SORT_PATH_LENGTH) { + group._children.sort((a, b) => { + const lenA = getPathHopCount(a), lenB = getPathHopCount(b); + if (lenA !== lenB) return lenA - lenB; + const oA = (a.observer_name || '').toLowerCase(), oB = (b.observer_name || '').toLowerCase(); + return oA < oB ? -1 : oA > oB ? 1 : 0; + }); + } else { + // Default: group by observer, earliest-observer first, then ascending time within each + const earliest = {}; + for (const c of group._children) { + const obs = c.observer_name || c.observer || ''; + const t = c.timestamp || c.rx_at || c.created_at || ''; + if (!earliest[obs] || t < earliest[obs]) earliest[obs] = t; + } + group._children.sort((a, b) => { + const oA = a.observer_name || a.observer || '', oB = b.observer_name || b.observer || ''; + const eA = earliest[oA] || '', eB = earliest[oB] || ''; + if (eA !== eB) return eA < eB ? -1 : 1; + if (oA !== oB) return oA < oB ? -1 : 1; + const tA = a.timestamp || a.rx_at || '', tB = b.timestamp || b.rx_at || ''; + return tA < tB ? -1 : tA > tB ? 1 : 0; + }); + } + } + // Global handlers async function pktToggleGroup(hash) { if (expandedHashes.has(hash)) { @@ -1253,21 +1303,8 @@ if (group && data.observations) { group._children = data.observations.map(o => ({...pkt, ...o, _isObservation: true})); group._fetchedData = data; - // Sort: group by observer, earliest-observer first, then by time within each observer - const earliest = {}; - for (const c of group._children) { - const obs = c.observer_name || c.observer || ''; - const t = c.timestamp || c.rx_at || c.created_at || ''; - if (!earliest[obs] || t < earliest[obs]) earliest[obs] = t; - } - group._children.sort((a, b) => { - const oA = a.observer_name || a.observer || '', oB = b.observer_name || b.observer || ''; - const eA = earliest[oA] || '', eB = earliest[oB] || ''; - if (eA !== eB) return eA < eB ? -1 : 1; - if (oA !== oB) return oA < oB ? -1 : 1; - const tA = a.timestamp || a.rx_at || '', tB = b.timestamp || b.rx_at || ''; - return tA < tB ? -1 : tA > tB ? 1 : 0; - }); + // Sort children based on current sort mode + sortGroupChildren(group); } // Resolve any new hops from children const childHops = new Set();