@@ -75,20 +75,40 @@ function normalizeLayer(layer) {
7575 return VALID_LAYERS . has ( lower ) ? lower : null ;
7676}
7777
78- function upsertCustomEntry ( { id, label, layer, icon = 'custom' } ) {
78+ function ensureCategoryContainers ( category ) {
79+ if ( ! category ) return ;
80+ if ( ! customEntries [ category ] ) customEntries [ category ] = [ ] ;
81+ if ( ! addedItems [ category ] ) addedItems [ category ] = new Set ( ) ;
82+ }
83+
84+ function isItemAlreadyAdded ( itemId ) {
85+ return Object . values ( addedItems ) . some ( set => set . has ( itemId ) ) ;
86+ }
87+
88+ function guessLayerFromKind ( kind ) {
89+ const lower = ( kind || '' ) . toString ( ) . trim ( ) . toLowerCase ( ) ;
90+ if ( ! lower ) return null ;
91+ if ( lower === 'activation' ) return 'activation' ;
92+ if ( lower === 'warehouse' || lower === 'analysis' ) return 'analysis' ;
93+ if ( lower === 'amplitude' ) return 'analysis' ;
94+ if ( lower === 'datasource' || lower === 'source' ) return 'sources' ;
95+ return null ;
96+ }
97+
98+ function upsertCustomEntry ( { id, label, layer, kind, icon = 'custom' } ) {
7999 const safeId = id || slugify ( label ) ;
80100 const name = label || safeId ;
81101
82102 const existingCategory = itemCategoryIndex [ safeId ] ;
83103 const normalizedLayer = normalizeLayer ( layer ) ;
104+ const kindLayer = guessLayerFromKind ( kind ) ;
84105 const defaultLayer = AI_NODE_DEFAULTS [ safeId ] ?. layer ;
85- const category = normalizedLayer || existingCategory || defaultLayer ;
106+ // Prefer the catalog category if it exists; otherwise use AI layer, kind hint, then defaults.
107+ const category = existingCategory || normalizedLayer || kindLayer || defaultLayer ;
86108 const resolvedIcon = AI_NODE_DEFAULTS [ safeId ] ?. icon || icon ;
87109
88- if ( existingCategory && category && existingCategory !== category ) {
89- return null ;
90- }
91110 if ( ! category ) return null ;
111+ ensureCategoryContainers ( category ) ;
92112
93113 // If this is a known, pre-defined item (e.g., amplitude-sdk, s3), just add it.
94114 if ( existingCategory ) {
@@ -126,13 +146,38 @@ function applyDiagramFromAi(result) {
126146 id : node ?. id ,
127147 label : node ?. label || node ?. name ,
128148 layer : node ?. layer ,
149+ kind : node ?. kind ,
129150 icon : 'custom'
130151 } ) ;
131152 if ( id ) {
132153 createdIds . add ( id ) ;
133154 }
134155 } ) ;
135156
157+ // Safety net: if any node failed to add (missing category resolution, etc.), try once more with a fallback.
158+ nodes . forEach ( node => {
159+ const nodeId = node ?. id ;
160+ if ( ! nodeId || isItemAlreadyAdded ( nodeId ) ) return ;
161+ const normalizedLayer = normalizeLayer ( node ?. layer ) ;
162+ const kindLayer = guessLayerFromKind ( node ?. kind ) ;
163+ const defaultLayer = AI_NODE_DEFAULTS [ nodeId ] ?. layer || AI_NODE_DEFAULTS [ slugify ( node ?. label ) ] ?. layer ;
164+ const category = itemCategoryIndex [ nodeId ] || normalizedLayer || kindLayer || defaultLayer || 'analysis' ;
165+ ensureCategoryContainers ( category ) ;
166+ const existsInCustom = customEntries [ category ] . some ( entry => entry . id === nodeId ) ;
167+ if ( ! existsInCustom ) {
168+ customEntries [ category ] . push ( {
169+ id : nodeId ,
170+ name : node ?. label || nodeId ,
171+ icon : AI_NODE_DEFAULTS [ nodeId ] ?. icon || 'custom' ,
172+ isCustom : true
173+ } ) ;
174+ }
175+ itemCategoryIndex [ nodeId ] = category ;
176+ if ( ! addedItems [ category ] . has ( nodeId ) ) {
177+ addItemToLayer ( nodeId , node ?. label || nodeId , AI_NODE_DEFAULTS [ nodeId ] ?. icon || 'custom' , category ) ;
178+ }
179+ } ) ;
180+
136181 edges . forEach ( edge => {
137182 const sourceId = edge ?. sourceId ;
138183 const targetId = edge ?. targetId ;
0 commit comments