7290c1fd2b
Two visualization gaps in the cross-repo graph view: 1. Fixed LAYOUT_GALAXY_SPACING=600 buried satellite galaxies inside the primary cluster on any non-trivial project — a 1000-node primary cluster has bounding radius ~1500. Added layout_radius() (max distance from origin across all node positions) and compute the per-satellite distance as primary_radius + sat_radius + LAYOUT_GALAXY_PAD, with the old constant retained as a lower bound for tiny projects. 2. cross_edges was hard-coded to an empty array (TODO placeholder), so inter-galaxy CROSS_* edges never rendered even when the matcher had produced them. Populate from a join on the source store's CROSS_* edges → Route node qualified_name, then resolve the canonical QN in the linked store to get its node id. The Route QN is the cross-repo matching contract; properties.target_function isn't unique. Required keeping both `store` and `lp_store` open through the linked- projects loop instead of closing them right after layout compute. Added cbm_store_close to all early-error paths to balance the lifetime. Frontend side: - EdgeLines accepts an optional targetNodes prop so a single component can render edges with source in one node array and target in another (offset-adjusted satellite nodes). - GraphScene renders an EdgeLines layer per linked project for its cross_edges, using primary nodes as source and offsetNodes as target. - GraphTab folds linked-project labels and edge types into the filter init / enableAll / filteredData paths so cross_edges respect the current filter state. - Edge color palette extended with GRPC/GRAPHQL/TRPC/CROSS_* tones. Co-authored-by: sponger94 <45746997+sponger94@users.noreply.github.com>