Skip to content

feat: multi-edge graph with date filtering and layout modes#67

Open
cdxker wants to merge 3 commits intomainfrom
adding-multi-edges
Open

feat: multi-edge graph with date filtering and layout modes#67
cdxker wants to merge 3 commits intomainfrom
adding-multi-edges

Conversation

@cdxker
Copy link
Owner

@cdxker cdxker commented Mar 4, 2026

Summary

  • Adds multi-edge support to the listening graph, allowing multiple edges per node pair
  • Introduces three layout modes (PageRank, MDS, Weighted MDS) with toggle controls
  • Adds date range filtering with quick-select buttons to explore listening history over time
  • Implements node click-to-focus, dynamic node sizing by layout metric, and dark/light mode toggle
  • Removes Spotify ingestion code (auth + client) and simplifies the pipeline to use a local reindex script

Test plan

  • Verify graph renders with multi-edges between nodes
  • Test toggling between PageRank, MDS, and Weighted MDS layouts
  • Test date range picker and quick filter buttons
  • Verify clicking a node focuses on its neighborhood
  • Test dark/light mode toggle
  • Run existing test suite (pnpm test)

🤖 Generated with Claude Code


Note

Medium Risk
Changes the core graph data model and SQLite schema (edges become per-transition timestamped events) and updates both API + frontend filtering/rendering to match, which could break existing data or assumptions if any consumers/migrations are missed.

Overview
Switches the listening graph from aggregated weighted edges to per-transition, timestamped edge events end-to-end. The pipeline now builds ListeningGraph.edges[] as individual scrobble-to-scrobble transitions (with a 1-hour cutoff) and derives node.next/previous aggregates from those events.

Reworks persistence and API shape accordingly. The SQLite edges table becomes append-only event rows with timestamp (new id PK), node/source-related Spotify fields are removed, saveGraph writes fresh after clearing, and GET /graph always returns the edges array.

Updates the frontend to aggregate and visualize multi-edges and support time-based exploration. graphContext collapses parallel edges into a single graphology edge carrying timestamps[], the main route filters visibility/edge strength by date range using raw edge timestamps, enables edge hover events with a tooltip listing transition times, and keeps layout mode switching intact.

Removes Spotify ingestion/config/tests and adds a reindex.sh helper to wipe local DB/cache and run the Last.fm pipeline; stats/tests are updated to drop source breakdown and include the new edges field.

Written by Cursor Bugbot for commit a6fa7eb. This will update automatically on new commits. Configure here.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

return { ...data, hidden: true }
}
// Use filtered weight for visual sizing
return { ...data, color: edgeBase(Math.min(0.6, 0.15 + filteredWeight * 0.05)) }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edge reducer skips neighbor filter when date filter active

Medium Severity

When dateFilter is active and an edge passes the date range check (has a filteredWeight), the edgeReducer returns early with styling, completely skipping the selectedNeighbors check. This means that when a user has both a date filter and a clicked/focused node active simultaneously, nodes are correctly filtered by both criteria (the nodeReducer checks both), but edges only respect the date filter and ignore the node neighborhood selection. The old code's filteredPlayCounts block only returned early to hide edges, otherwise falling through to the selectedNeighbors check — so this is a regression.

Fix in Cursor Fix in Web

}
return c.json({
nodes: paginatedNodes,
edges: graph.edges,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Paginated endpoint returns all edges regardless of node subset

Low Severity

When the /graph endpoint is called with pagination (limit > 0), graph.edges returns the entire edges array even though only a subset of nodes is included. Previously, edges were embedded in node data (next/previous maps), so pagination naturally scoped edges. Now that edges are a top-level array, a paginated response includes many edges referencing nodes absent from the response, producing an inconsistent payload and potentially transferring a lot of unnecessary data.

Fix in Cursor Fix in Web

@cdxker cdxker force-pushed the adding-multi-edges branch from 8664cb4 to 80dcf05 Compare March 4, 2026 05:05
@cdxker cdxker force-pushed the adding-multi-edges branch from 80dcf05 to a6fa7eb Compare March 4, 2026 05:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant