[eric] dashboard minimap and thumbnail no longer lie when an underlying output gets deleted from the Views page — orphan view cards now get

pruned from layout state on the next render (gated on outputs loaded so the fix can't wipe valid cards during the load race), and clearing
  all cards from a dashboard now actually clears its thumbnail on exit instead of leaving the last 'alive' image stuck forever.
This commit is contained in:
ciregenz
2026-05-04 00:12:12 -07:00
parent 616e2e0be9
commit b0515ac3c5
2 changed files with 23 additions and 3 deletions
+22 -2
View File
@@ -118,6 +118,7 @@ const DashboardInner: React.FC<DashboardProps> = ({ dashboardId, isActive = true
const expandNewChats = useAppSelector((state) => state.settings.data.expand_new_chats_in_dashboard);
const autoRevealSubAgents = useAppSelector((state) => state.settings.data.auto_reveal_sub_agents);
const outputs = useAppSelector((state) => state.outputs.items);
const outputsLoaded = useAppSelector((state) => state.outputs.loaded);
const glowingAgentCards = useAppSelector((state) => state.dashboardLayout.glowingAgentCards);
const glowingBrowserCards = useAppSelector((state) => state.dashboardLayout.glowingBrowserCards);
// sessions is the top-level dict; useMemo on its identity so sessionList
@@ -553,7 +554,13 @@ const DashboardInner: React.FC<DashboardProps> = ({ dashboardId, isActive = true
const hasCards = Object.keys(allCards.cards).length > 0
|| Object.keys(allCards.viewCards).length > 0
|| Object.keys(allCards.browserCards).length > 0;
if (!hasCards) return;
if (!hasCards) {
// Empty dashboard — queue a thumbnail clear (sent on exit alongside
// the existing capture-update path). Backend treats '' as "set to
// empty"; null in PUT body means "don't update".
pendingThumbnailRef.current = '';
return;
}
captureDashboardThumbnail(viewportEl, contentEl, allCards)
.then((thumbnail) => { if (thumbnail) pendingThumbnailRef.current = thumbnail; })
.catch(() => {});
@@ -573,7 +580,8 @@ const DashboardInner: React.FC<DashboardProps> = ({ dashboardId, isActive = true
const exitingId = dashboardId;
return () => {
const thumbnail = pendingThumbnailRef.current;
if (thumbnail) {
// null = no pending change; '' = pending clear; other = pending update.
if (thumbnail !== null) {
store.dispatch(updateDashboardThumbnail({ id: exitingId, thumbnail }));
pendingThumbnailRef.current = null;
}
@@ -655,6 +663,18 @@ const DashboardInner: React.FC<DashboardProps> = ({ dashboardId, isActive = true
dispatch(reconcileSessions({ sessionIds: dashboardSessionIds, expandedSessionIds }));
}, [sessions, layoutInitialized, dispatch, dashboardId, expandedSessionIds]);
// Prune orphan view cards whose underlying output was deleted (e.g. via
// the Views page). Without this, the layout entry persists in the
// minimap and contentBounds even though DashboardViewCard renders
// nothing. Gated on outputsLoaded so we don't wipe valid cards during
// the brief window between fetchLayout returning and outputs finishing.
useEffect(() => {
if (!layoutInitialized || !outputsLoaded) return;
for (const outputId of Object.keys(viewCards)) {
if (!outputs[outputId]) dispatch(removeViewCard(outputId));
}
}, [layoutInitialized, outputsLoaded, viewCards, outputs, dispatch]);
// ---- Auto-reveal / collapse / unreveal sub-agent cards ----
const autoRevealedRef = useRef(new Set<string>());
const prevSubStatusRef = useRef<Record<string, string>>({});
File diff suppressed because one or more lines are too long