From 2fb66c5baecdd8b49be904f6244f66ba04f68061 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Mon, 14 Sep 2026 11:17:42 +0200 Subject: feat(ui): an indexing dock above the music bar, on every page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding a large directory left the operator nothing to look at once they left the Settings panel that started it, and nothing at all when it was added from another machine. A band now sits above the music bar on every page: one row per group with indexing under way, naming the root being walked, percent, bytes and files, and the roots waiting their turn; "indexing finished" for a few seconds at the end. A click opens the group's Settings, and × hides the row until that group is idle. Two sources feed it. On the node's own machine the desktop client polls the loopback `GET /api/index-status` for every group, whatever the route. An operator's group page forwards MNP `index_progress` pushes, resolving the root from the roots table it opened; an ordinary member keeps the sidebar dot only, and a page clears its row when it lets go of the group. Where both describe a group, loopback wins. Reconcile passes and watchdog bursts show only past 1 GB or 5 s, so a single dropped file does not flash a bar. The logic lives in index-dock-model.js, which has no imports and is tested under node. The dock publishes `--index-dock-h` and the sidebar stops above it and the music bar. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01T6jPTeocXA1BePekdsgPya --- .../src/meshbay_hub/static/group-page.js | 26 +++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'packages/meshbay-hub/src/meshbay_hub/static/group-page.js') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js index 18d3a8f..0f28adf 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js @@ -15,6 +15,7 @@ import { useStickyBand } from './sticky.js'; import { FilePreview } from './files-app.js'; import { VideoPlayer } from './video-player.js'; import { GroupSettingsPanel } from './group-settings.js'; +import { reportIndexPush } from './index-dock.js'; /** * The group shell: everything a group's "applications" (Chat, Files, and @@ -96,6 +97,9 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, // unplugged keeps its files listed — they are frozen, not deleted — so this is // the only thing that lets the UI say which of the two it is. const [nodeRoots, setNodeRoots] = useState([]); + // Read by the index_progress handler, which connect() installs once. + const nodeRootsRef = useRef(nodeRoots); + nodeRootsRef.current = nodeRoots; const [isNodeAdmin, setIsNodeAdmin] = useState(false); // Which applications this group has enabled, from the node. Falls back to @@ -430,11 +434,18 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, // False transition (daemon.py _progress_pusher), so this always // settles back to 'online' rather than getting stuck. transport.onIndexProgress = (status) => { - if (cancelled || !onPresence) return; - const pct = status.total_bytes - ? Math.min(100, Math.round(100 * status.scanned_bytes / status.total_bytes)) - : 0; - onPresence(groupId, status.scanning ? 'indexing' : 'online', pct); + if (cancelled) return; + if (onPresence) { + const pct = status.total_bytes + ? Math.min(100, Math.round(100 * status.scanned_bytes / status.total_bytes)) + : 0; + onPresence(groupId, status.scanning ? 'indexing' : 'online', pct); + } + // The operator's indexing dock. The push names no root, so the + // page that opened the roots table names it. + if (transport.memberRole === 'operator') { + reportIndexPush(groupId, status, nodeRootsRef.current); + } }; setOperatorPaired(transport.memberRole === 'operator'); @@ -503,6 +514,9 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, onPresence(groupId, 'online'); } } + if (transport.memberRole === 'operator' && ack.indexing) { + reportIndexPush(groupId, ack.indexing, indexMsg.roots || nodeRootsRef.current); + } } catch (err) { if (cancelled) return; @@ -554,6 +568,8 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, return () => { cancelled = true; + // Nothing will update this group's dock row once the page lets go of it. + reportIndexPush(groupId, null); if (transportRef.current) { // Handed over rather than closed: a download running when you leave the // group keeps its connection, and the last transfer using it closes it. -- cgit v1.2.3