@@ -17,6 +17,7 @@ interface GraphNode {
1717 id : string
1818 name : string
1919 label : string
20+ properties ?: Record < string , string >
2021 tableId ?: number
2122 rowid ?: number
2223 community ?: number
@@ -144,6 +145,7 @@ interface SigmaGraphViewProps {
144145 labelNodeIds : Set < string >
145146 newlyExpandedNodeIds : Set < string >
146147 darkMode : boolean
148+ getNodeDisplayName : ( node : GraphNode ) => string
147149 getNodeColor : ( node : GraphNode ) => string
148150 getNodeSize : ( node : GraphNode ) => number
149151 getEdgeColor : ( label : string ) => string
@@ -193,9 +195,23 @@ function buildLlmClusterConfig(config: LlmClusterNamingConfig): LlmClusterNaming
193195 }
194196}
195197
196- function getNodeClusterLabel ( node : GraphNode ) {
198+ const AUTO_DISPLAY_COLUMN = '__auto__'
199+
200+ function getNodeDisplayName ( node : GraphNode , displayColumns : Record < string , string > ) {
201+ if ( isExpanderNode ( node ) || isClusterNode ( node ) ) return node . name || node . id
202+
203+ const selectedColumn = displayColumns [ node . label ]
204+ if ( selectedColumn && selectedColumn !== AUTO_DISPLAY_COLUMN ) {
205+ const selectedValue = node . properties ?. [ selectedColumn ]
206+ if ( selectedValue ?. trim ( ) ) return selectedValue
207+ }
208+
209+ return node . name || node . id
210+ }
211+
212+ function getNodeClusterLabel ( node : GraphNode , displayColumns : Record < string , string > ) {
197213 const label = node . label . trim ( )
198- const name = node . name . trim ( )
214+ const name = getNodeDisplayName ( node , displayColumns ) . trim ( )
199215 return ! name || name === label ? label : `${ label } : ${ name } `
200216}
201217
@@ -699,7 +715,7 @@ function createInitialLayout(graphData: NormalizedGraphData) {
699715 return { degrees, positions }
700716}
701717
702- function SigmaGraphView ( { graphData, labelNodeIds, newlyExpandedNodeIds, darkMode, getNodeColor, getNodeSize, getEdgeColor, onNodeClick } : SigmaGraphViewProps ) {
718+ function SigmaGraphView ( { graphData, labelNodeIds, newlyExpandedNodeIds, darkMode, getNodeDisplayName , getNodeColor, getNodeSize, getEdgeColor, onNodeClick } : SigmaGraphViewProps ) {
703719 const containerRef = useRef < HTMLDivElement | null > ( null )
704720 const rendererRef = useRef < Sigma | null > ( null )
705721 const hoveredEdgeRef = useRef < string | null > ( null )
@@ -708,15 +724,16 @@ function SigmaGraphView({ graphData, labelNodeIds, newlyExpandedNodeIds, darkMod
708724 const { positions } = createInitialLayout ( graphData )
709725 const nodes = graphData . nodes . map ( node => {
710726 const position = positions [ node . id ] || { x : 0 , y : 0 }
727+ const displayName = getNodeDisplayName ( node )
711728 return {
712729 key : node . id ,
713730 attributes : {
714731 x : position . x ,
715732 y : position . y ,
716733 size : getNodeSize ( node ) ,
717734 color : isExpanderNode ( node ) ? '#f59e0b' : getNodeColor ( node ) ,
718- label : isExpanderNode ( node ) || labelNodeIds . has ( node . id ) ? node . name || node . id : '' ,
719- hoverLabel : node . name || node . id ,
735+ label : isExpanderNode ( node ) || labelNodeIds . has ( node . id ) ? displayName : '' ,
736+ hoverLabel : displayName ,
720737 isNewlyExpanded : newlyExpandedNodeIds . has ( node . id ) ,
721738 nodeType : node . label ,
722739 } ,
@@ -752,7 +769,7 @@ function SigmaGraphView({ graphData, labelNodeIds, newlyExpandedNodeIds, darkMod
752769 edgeAttributes,
753770 edgeKeys,
754771 } )
755- } , [ graphData , labelNodeIds , newlyExpandedNodeIds , getNodeColor , getNodeSize , getEdgeColor ] )
772+ } , [ graphData , labelNodeIds , newlyExpandedNodeIds , getNodeDisplayName , getNodeColor , getNodeSize , getEdgeColor ] )
756773
757774 useEffect ( ( ) => {
758775 const container = containerRef . current
@@ -857,6 +874,7 @@ function App() {
857874 const [ clusterViewEnabled , setClusterViewEnabled ] = useState ( true )
858875 const [ nodeSearch , setNodeSearch ] = useState ( '' )
859876 const [ searchResults , setSearchResults ] = useState < GraphNode [ ] > ( [ ] )
877+ const [ displayColumnsByLabel , setDisplayColumnsByLabel ] = useState < Record < string , string > > ( { } )
860878 const [ searching , setSearching ] = useState ( false )
861879 const [ searchError , setSearchError ] = useState < string | null > ( null )
862880 const [ focusedNodeId , setFocusedNodeId ] = useState < string | null > ( null )
@@ -1159,6 +1177,27 @@ function App() {
11591177 } , [ graphData , selectedId , currentLlmClusterConfig , resetClusterNameRequests ] )
11601178
11611179 const normalizedGraphData = useMemo ( ( ) => normalizeGraphData ( graphData ) , [ graphData ] )
1180+ const displayColumnOptions = useMemo ( ( ) => {
1181+ const columnsByLabel = new Map < string , Set < string > > ( )
1182+ graphData . nodes . forEach ( node => {
1183+ if ( isExpanderNode ( node ) || isClusterNode ( node ) ) return
1184+ const propertyNames = Object . keys ( node . properties || { } )
1185+ if ( propertyNames . length === 0 ) return
1186+ const columns = columnsByLabel . get ( node . label ) || new Set < string > ( )
1187+ propertyNames . forEach ( name => columns . add ( name ) )
1188+ columnsByLabel . set ( node . label , columns )
1189+ } )
1190+
1191+ return [ ...columnsByLabel . entries ( ) ]
1192+ . map ( ( [ label , columns ] ) => ( {
1193+ label,
1194+ columns : [ ...columns ] . sort ( ( a , b ) => a . localeCompare ( b ) ) ,
1195+ } ) )
1196+ . sort ( ( a , b ) => a . label . localeCompare ( b . label ) )
1197+ } , [ graphData . nodes ] )
1198+ const getDisplayName = useCallback ( ( node : GraphNode ) => (
1199+ getNodeDisplayName ( node , displayColumnsByLabel )
1200+ ) , [ displayColumnsByLabel ] )
11621201 const clusterLevels = useMemo ( ( ) => buildCommunityClusterLevels ( normalizedGraphData ) , [ normalizedGraphData ] )
11631202 const coarsestClusterLevel = useMemo ( ( ) => getCoarsestClusterLevel ( clusterLevels ) , [ clusterLevels ] )
11641203 const currentClusterLevel = useMemo ( ( ) => {
@@ -1184,6 +1223,10 @@ function App() {
11841223 : normalizedGraphData
11851224 ) , [ clusterViewEnabled , clusterLevels , normalizedGraphData , visibleClusterPath ] )
11861225
1226+ useEffect ( ( ) => {
1227+ resetClusterNameRequests ( )
1228+ } , [ displayColumnsByLabel , resetClusterNameRequests ] )
1229+
11871230 useEffect ( ( ) => {
11881231 const llmConfig = currentLlmClusterConfig ( )
11891232 if ( ! llmConfig || ! clusterViewEnabled || ! currentClusterLevel ) return
@@ -1207,7 +1250,7 @@ function App() {
12071250 && currentClusterLevel . membership [ index ] === clusterId
12081251 && nodeMatchesClusterPath ( clusterLevels , index , visibleClusterPath )
12091252 ) )
1210- . map ( getNodeClusterLabel )
1253+ . map ( node => getNodeClusterLabel ( node , displayColumnsByLabel ) )
12111254 const sampledLabels = sampleLabels ( labels , llmConfig . sampleSize )
12121255 if ( sampledLabels . length === 0 ) return
12131256
@@ -1273,6 +1316,7 @@ function App() {
12731316 clusterViewEnabled ,
12741317 currentClusterLevel ,
12751318 currentLlmClusterConfig ,
1319+ displayColumnsByLabel ,
12761320 normalizedGraphData ,
12771321 visibleClusterPath ,
12781322 visibleGraphData . nodes ,
@@ -1382,15 +1426,16 @@ function App() {
13821426 ctx . lineWidth = highlighted ? 3 : 1
13831427 ctx . stroke ( )
13841428
1385- if ( ( isExpanderNode ( node ) || topLabelNodeIds . has ( node . id ) ) && node . name ) {
1429+ const displayName = getDisplayName ( node )
1430+ if ( ( isExpanderNode ( node ) || topLabelNodeIds . has ( node . id ) ) && displayName ) {
13861431 const fontSize = 3
13871432 ctx . font = `${ fontSize } px Sans-Serif`
13881433 ctx . textAlign = 'center'
13891434 ctx . textBaseline = 'middle'
13901435 ctx . fillStyle = highlighted ? '#f59e0b' : '#fff'
13911436
13921437 const maxWidth = size * 1.6
1393- let label = node . name
1438+ let label = displayName
13941439 const measured = ctx . measureText ( label )
13951440 if ( measured . width > maxWidth ) {
13961441 while ( label . length > 1 && ctx . measureText ( label + '\u2026' ) . width > maxWidth ) {
@@ -1400,7 +1445,7 @@ function App() {
14001445 }
14011446 ctx . fillText ( label , node . x , node . y )
14021447 }
1403- } , [ getNodeSize , getNodeColor , darkMode , topLabelNodeIds , lastExpandedNodeIds ] )
1448+ } , [ getNodeSize , getNodeColor , getDisplayName , darkMode , topLabelNodeIds , lastExpandedNodeIds ] )
14041449
14051450 return (
14061451 < div className = "app-container" >
@@ -1439,6 +1484,40 @@ function App() {
14391484 ) ) }
14401485 </ ul >
14411486 ) }
1487+ { displayColumnOptions . length > 0 && (
1488+ < div className = "display-column-settings" >
1489+ < div className = "panel-title" > Node Labels</ div >
1490+ < div className = "display-column-list" >
1491+ { displayColumnOptions . map ( ( { label, columns } ) => {
1492+ const selectedColumn = displayColumnsByLabel [ label ]
1493+ const selectedValue = selectedColumn && columns . includes ( selectedColumn )
1494+ ? selectedColumn
1495+ : AUTO_DISPLAY_COLUMN
1496+
1497+ return (
1498+ < label className = "display-column-row" key = { label } >
1499+ < span title = { label } > { label } </ span >
1500+ < select
1501+ value = { selectedValue }
1502+ onChange = { event => {
1503+ const value = event . target . value
1504+ setDisplayColumnsByLabel ( current => ( {
1505+ ...current ,
1506+ [ label ] : value ,
1507+ } ) )
1508+ } }
1509+ >
1510+ < option value = { AUTO_DISPLAY_COLUMN } > Auto</ option >
1511+ { columns . map ( column => (
1512+ < option key = { column } value = { column } > { column } </ option >
1513+ ) ) }
1514+ </ select >
1515+ </ label >
1516+ )
1517+ } ) }
1518+ </ div >
1519+ </ div >
1520+ ) }
14421521 < div className = "node-search" >
14431522 < div className = "panel-title" > Find Node</ div >
14441523 < div className = "node-search-row" >
@@ -1464,9 +1543,9 @@ function App() {
14641543 key = { result . id }
14651544 className = "search-result"
14661545 onClick = { ( ) => exploreSearchResult ( result ) }
1467- title = { `${ result . label } : ${ result . name } ` }
1546+ title = { `${ result . label } : ${ getDisplayName ( result ) } ` }
14681547 >
1469- < span > { result . name } </ span >
1548+ < span > { getDisplayName ( result ) } </ span >
14701549 < small > { result . label } · { result . id } </ small >
14711550 </ button >
14721551 ) ) }
@@ -1605,6 +1684,7 @@ function App() {
16051684 labelNodeIds = { topLabelNodeIds }
16061685 newlyExpandedNodeIds = { lastExpandedNodeIds }
16071686 darkMode = { darkMode }
1687+ getNodeDisplayName = { getDisplayName }
16081688 getNodeColor = { getNodeColor }
16091689 getNodeSize = { getNodeSize }
16101690 getEdgeColor = { getEdgeColor }
@@ -1622,7 +1702,7 @@ function App() {
16221702 onNodeClick = { ( node ) => handleVisibleNodeClick ( String ( node . id ) ) }
16231703 nodeVal = { ( node ) => { const s = getNodeSize ( node ) ; return s * s ; } }
16241704 nodeRelSize = { 1 }
1625- nodeLabel = { ( node ) => `${ node . label } : ${ node . name } ` }
1705+ nodeLabel = { ( node ) => `${ node . label } : ${ getDisplayName ( node ) } ` }
16261706 linkLabel = { ( link ) => link . label }
16271707 linkColor = { ( link ) => getEdgeColor ( link . label ) }
16281708 linkWidth = { 2.5 }
0 commit comments