@@ -118,6 +118,27 @@ export class CVVariable extends vscode.TreeItem {
118118 }
119119}
120120
121+ export class CVCategoryGroup extends vscode . TreeItem {
122+ constructor (
123+ public readonly label : string ,
124+ public readonly collapsibleState : vscode . TreeItemCollapsibleState ,
125+ public readonly kind : 'mat' | 'plot' | 'pointcloud' ,
126+ public readonly children : ( CVGroup | CVVariable ) [ ]
127+ ) {
128+ super ( label , collapsibleState ) ;
129+ this . contextValue = `cvCategory:${ kind } ` ;
130+
131+ // Set icon based on category type
132+ if ( kind === 'mat' ) {
133+ this . iconPath = new vscode . ThemeIcon ( 'file-media' ) ;
134+ } else if ( kind === 'plot' ) {
135+ this . iconPath = new vscode . ThemeIcon ( 'graph' ) ;
136+ } else if ( kind === 'pointcloud' ) {
137+ this . iconPath = new vscode . ThemeIcon ( 'layers' ) ;
138+ }
139+ }
140+ }
141+
121142export class CVGroup extends vscode . TreeItem {
122143 constructor (
123144 public readonly label : string ,
@@ -126,7 +147,8 @@ export class CVGroup extends vscode.TreeItem {
126147 public readonly groupIndex ?: number
127148 ) {
128149 super ( label , collapsibleState ) ;
129- this . contextValue = 'cvGroup' ;
150+ // Only paired groups (with groupIndex) can have add button
151+ this . contextValue = groupIndex !== undefined ? 'cvGroup:paired' : 'cvGroup' ;
130152
131153 if ( groupIndex !== undefined ) {
132154 const color = COLORS [ groupIndex % COLORS . length ] ;
@@ -137,9 +159,9 @@ export class CVGroup extends vscode.TreeItem {
137159 }
138160}
139161
140- export class CVVariablesProvider implements vscode . TreeDataProvider < CVVariable | CVGroup > {
141- private _onDidChangeTreeData : vscode . EventEmitter < CVVariable | CVGroup | undefined | void > = new vscode . EventEmitter < CVVariable | CVGroup | undefined | void > ( ) ;
142- readonly onDidChangeTreeData : vscode . Event < CVVariable | CVGroup | undefined | void > = this . _onDidChangeTreeData . event ;
162+ export class CVVariablesProvider implements vscode . TreeDataProvider < CVVariable | CVGroup | CVCategoryGroup > {
163+ private _onDidChangeTreeData : vscode . EventEmitter < CVVariable | CVGroup | CVCategoryGroup | undefined | void > = new vscode . EventEmitter < CVVariable | CVGroup | CVCategoryGroup | undefined | void > ( ) ;
164+ readonly onDidChangeTreeData : vscode . Event < CVVariable | CVGroup | CVCategoryGroup | undefined | void > = this . _onDidChangeTreeData . event ;
143165
144166 private variables : CVVariable [ ] = [ ] ;
145167 private groups : CVGroup [ ] = [ ] ;
@@ -166,19 +188,38 @@ export class CVVariablesProvider implements vscode.TreeDataProvider<CVVariable |
166188 return this . variables ;
167189 }
168190
169- getTreeItem ( element : CVVariable | CVGroup ) : vscode . TreeItem {
191+ getTreeItem ( element : CVVariable | CVGroup | CVCategoryGroup ) : vscode . TreeItem {
170192 return element ;
171193 }
172194
173- async getChildren ( element ?: CVVariable | CVGroup ) : Promise < ( CVVariable | CVGroup ) [ ] > {
195+ async getChildren ( element ?: CVVariable | CVGroup | CVCategoryGroup ) : Promise < ( CVVariable | CVGroup | CVCategoryGroup ) [ ] > {
196+ // Level 3: Variable children (no children)
197+ if ( element instanceof CVVariable ) {
198+ return [ ] ;
199+ }
200+
201+ // Level 2: Group children (return variables in the group)
174202 if ( element instanceof CVGroup ) {
175203 return element . variables ;
176204 }
177205
178- if ( element instanceof CVVariable ) {
179- return [ ] ;
206+ // Level 1: Category children (return groups + unpaired variables)
207+ if ( element instanceof CVCategoryGroup ) {
208+ const result : ( CVGroup | CVVariable ) [ ] = [ ] ;
209+
210+ // Add paired groups
211+ const pairedGroups = element . children . filter ( c => c instanceof CVGroup && ( c as CVGroup ) . groupIndex !== undefined ) as CVGroup [ ] ;
212+ pairedGroups . sort ( ( a , b ) => ( a . groupIndex || 0 ) - ( b . groupIndex || 0 ) ) ;
213+ result . push ( ...pairedGroups ) ;
214+
215+ // Add unpaired variables
216+ const unpairedVars = element . children . filter ( c => c instanceof CVVariable && ! ( c as CVVariable ) . isPaired ) as CVVariable [ ] ;
217+ result . push ( ...unpairedVars ) ;
218+
219+ return result ;
180220 }
181-
221+
222+ // Level 0: Root (return category groups)
182223 const debugSession = vscode . debug . activeDebugSession ;
183224 if ( ! debugSession ) {
184225 this . variables = [ ] ;
@@ -611,39 +652,73 @@ export class CVVariablesProvider implements vscode.TreeDataProvider<CVVariable |
611652
612653 this . variables = visualizableVariables ;
613654
614- const groupMap = new Map < number | undefined , CVVariable [ ] > ( ) ;
615- for ( const v of visualizableVariables ) {
616- const idx = v . groupIndex ;
617- if ( ! groupMap . has ( idx ) ) {
618- groupMap . set ( idx , [ ] ) ;
655+ // Separate variables by type
656+ const matVars = visualizableVariables . filter ( v => v . kind === 'mat' ) ;
657+ const plotVars = visualizableVariables . filter ( v => v . kind === 'plot' ) ;
658+ const pointcloudVars = visualizableVariables . filter ( v => v . kind === 'pointcloud' ) ;
659+
660+ const resultCategories : CVCategoryGroup [ ] = [ ] ;
661+
662+ // 1. Images (2D) category
663+ if ( matVars . length > 0 ) {
664+ const matChildren : ( CVGroup | CVVariable ) [ ] = [ ] ;
665+
666+ // Group paired mat variables
667+ const groupMap = new Map < number , CVVariable [ ] > ( ) ;
668+ for ( const v of matVars ) {
669+ if ( v . isPaired && v . groupIndex !== undefined ) {
670+ if ( ! groupMap . has ( v . groupIndex ) ) {
671+ groupMap . set ( v . groupIndex , [ ] ) ;
672+ }
673+ groupMap . get ( v . groupIndex ) ! . push ( v ) ;
674+ }
619675 }
620- groupMap . get ( idx ) ! . push ( v ) ;
676+
677+ // Create paired groups
678+ const sortedIndices = Array . from ( groupMap . keys ( ) ) . sort ( ( a , b ) => a - b ) ;
679+ for ( const idx of sortedIndices ) {
680+ const vars = groupMap . get ( idx ) ! ;
681+ matChildren . push ( new CVGroup (
682+ `Group ${ idx + 1 } ` ,
683+ vscode . TreeItemCollapsibleState . Expanded ,
684+ vars ,
685+ idx
686+ ) ) ;
687+ }
688+
689+ // Add unpaired mat variables
690+ const unpairedMatVars = matVars . filter ( v => ! v . isPaired ) ;
691+ matChildren . push ( ...unpairedMatVars ) ;
692+
693+ resultCategories . push ( new CVCategoryGroup (
694+ 'Images (2D)' ,
695+ vscode . TreeItemCollapsibleState . Expanded ,
696+ 'mat' ,
697+ matChildren
698+ ) ) ;
621699 }
622-
623- const resultGroups : CVGroup [ ] = [ ] ;
624- const sortedIndices = Array . from ( groupMap . keys ( ) )
625- . filter ( ( idx ) : idx is number => idx !== undefined )
626- . sort ( ( a , b ) => a - b ) ;
627700
628- for ( const idx of sortedIndices ) {
629- resultGroups . push ( new CVGroup (
630- `Group ${ idx + 1 } ` ,
701+ // 2. Plot (1D) category
702+ if ( plotVars . length > 0 ) {
703+ resultCategories . push ( new CVCategoryGroup (
704+ 'Plot (1D)' ,
631705 vscode . TreeItemCollapsibleState . Expanded ,
632- groupMap . get ( idx ) ! ,
633- idx
706+ 'plot' ,
707+ plotVars
634708 ) ) ;
635709 }
636-
637- const ungrouped = groupMap . get ( undefined ) ;
638- if ( ungrouped && ungrouped . length > 0 ) {
639- resultGroups . push ( new CVGroup (
640- '(ungrouped )' ,
710+
711+ // 3. Points (3D) category
712+ if ( pointcloudVars . length > 0 ) {
713+ resultCategories . push ( new CVCategoryGroup (
714+ 'Points (3D )' ,
641715 vscode . TreeItemCollapsibleState . Expanded ,
642- ungrouped
716+ 'pointcloud' ,
717+ pointcloudVars
643718 ) ) ;
644719 }
645720
646- return resultGroups ;
721+ return resultCategories ;
647722 } catch ( error ) {
648723 console . error ( 'Error fetching variables:' , error ) ;
649724 this . variables = [ ] ;
0 commit comments