11import { DiagramModel , NodeModel } from '@projectstorm/react-diagrams' ;
22
33export interface SearchResult {
4- /** total number of matches */
4+ /** total number of matches */
55 count : number ;
6- /** zero-based indices of matching nodes in model.getNodes() */
6+ /** zero-based indices of matching nodes in model.getNodes() */
77 indices : number [ ] ;
8+ /** target ports to highlight in UI when a Literal is attached */
9+ portHits : { nodeId : string ; portName : string } [ ] ;
810}
911
12+ const getOptions = ( n : any ) => ( n ?. getOptions ?.( ) ?? { } ) as any ;
13+ const getNodeID = ( n : any ) => n ?. getID ?.( ) ?? n ?. options ?. id ?? getOptions ( n ) ?. id ;
14+ const getPort = ( n : any ) => ( Object . values ( n ?. getPorts ?.( ) ?? { } ) as any [ ] ) [ 0 ] ;
15+
1016/**
1117 * Collect searchable texts only from ports (for literals).
1218 */
1319function collectLiteralValues ( node : NodeModel ) : string [ ] {
14- const texts : string [ ] = [ ] ;
15- const opts = node . getOptions ( ) as any ;
16-
17- const nodeName = ( opts . name || "" ) . toLowerCase ( ) ;
18- const isLiteral = nodeName . startsWith ( "literal" ) ;
19-
20- if ( isLiteral ) {
21- Object . values ( node . getPorts ( ) ) . forEach ( ( p : any ) => {
22- const pOpt = p . getOptions ?.( ) ?? { } ;
23- if ( pOpt . label ) {
24- texts . push ( pOpt . label ) ;
25- }
26- } ) ;
20+ const rawName = String ( getOptions ( node ) . name || '' ) ;
21+ if ( ! rawName . startsWith ( 'Literal ' ) ) return [ ] ;
22+
23+ const port = getPort ( node ) ;
24+ if ( ! port ) return [ ] ;
25+
26+ const label = port . getOptions ?.( ) ?. label ;
27+ return label != null ? [ String ( label ) . toLowerCase ( ) ] : [ ] ;
28+ }
29+
30+ /**
31+ * Return (nodeId, portName) of the opposite end connected to a Literal node.
32+ */
33+ function getAttachedTargetByPortName ( node : NodeModel ) : { nodeId : string ; portName : string } [ ] {
34+ const results : { nodeId : string ; portName : string } [ ] = [ ] ;
35+
36+ const port = getPort ( node ) ;
37+ if ( ! port ) return results ;
38+
39+ const links = Object . values ( port . getLinks ?.( ) ?? { } ) as any [ ] ;
40+ for ( const link of links ) {
41+ const src = link . getSourcePort ?.( ) ;
42+ const trg = link . getTargetPort ?.( ) ;
43+
44+ // Identify the opposite port on the link
45+ const otherPort = src ?. getNode ?.( ) === node ? trg : src ;
46+ if ( ! otherPort ) continue ;
47+
48+ const otherNodeId = getNodeID ( otherPort . getNode ?.( ) ) ;
49+ const otherPortName =
50+ otherPort . getName ?.( ) ??
51+ otherPort . options ?. name ??
52+ otherPort . getOptions ?.( ) ?. name ;
53+
54+ if ( otherNodeId && otherPortName ) {
55+ results . push ( { nodeId : String ( otherNodeId ) , portName : String ( otherPortName ) } ) ;
56+ }
2757 }
2858
29- return texts . filter ( Boolean ) . map ( ( s ) => String ( s ) . toLowerCase ( ) ) ;
59+ return results ;
3060}
3161
3262/**
@@ -37,22 +67,43 @@ export function searchModel(model: DiagramModel, text: string): SearchResult {
3767 const nodes = model . getNodes ( ) ;
3868 const query = text . trim ( ) . toLowerCase ( ) ;
3969 if ( ! query ) {
40- return { count : 0 , indices : [ ] } ;
70+ return { count : 0 , indices : [ ] , portHits : [ ] } ;
4171 }
4272
43- const indices : number [ ] = [ ] ;
73+ const idToIdx = new Map < string , number > ( ) ;
74+ nodes . forEach ( ( node : NodeModel , i ) => {
75+ const id = getNodeID ( node ) ;
76+ if ( id ) idToIdx . set ( String ( id ) , i ) ;
77+ } ) ;
78+
79+ const indices = new Set < number > ( ) ;
80+ const portHits : { nodeId : string ; portName : string } [ ] = [ ] ;
81+
82+ nodes . forEach ( ( node , idx ) => {
83+ const rawName = String ( getOptions ( node ) . name || '' ) ;
84+ const nameLower = rawName . toLowerCase ( ) ;
85+ const texts = [ nameLower , ...collectLiteralValues ( node ) ] ;
86+ if ( ! texts . some ( ( t ) => t . includes ( query ) ) ) return ;
87+
88+ const isLiteral = rawName . startsWith ( 'Literal ' ) ;
89+ const isAttached = Boolean ( getOptions ( node ) . extras ?. attached ) ;
4490
45- nodes . forEach ( ( node : NodeModel , idx : number ) => {
46- const opts = node . getOptions ( ) as any ;
47- const name : string = ( opts . name || '' ) . toString ( ) . toLowerCase ( ) ;
48- const texts = [ name , ...collectLiteralValues ( node ) ] ;
49- if ( texts . some ( ( t ) => t . includes ( query ) ) ) {
50- indices . push ( idx ) ;
91+ if ( isLiteral && isAttached ) {
92+ const targets = getAttachedTargetByPortName ( node ) ;
93+ portHits . push ( ...targets ) ;
94+ targets . forEach ( ( { nodeId } ) => {
95+ const i = idToIdx . get ( nodeId ) ;
96+ if ( typeof i === 'number' ) indices . add ( i ) ;
97+ } ) ;
98+ } else {
99+ indices . add ( idx ) ;
51100 }
52101 } ) ;
53102
103+ const idxArr = Array . from ( indices ) ;
54104 return {
55- count : indices . length ,
56- indices
105+ count : idxArr . length ,
106+ indices : idxArr ,
107+ portHits
57108 } ;
58109}
0 commit comments