@@ -7045,7 +7045,6 @@ var AIGlossary = (function () {
70457045
70467046/* === src/modules/pipeline-builder.js === */
70477047
7048-
70497048/* ────────── Integration Pipeline Builder ────────── */
70507049var PipelineBuilder = ( function ( ) {
70517050 'use strict' ;
@@ -7086,6 +7085,25 @@ var PipelineBuilder = (function () {
70867085 'gmail+sheets' : { name : 'Email → Data' , flow : 'AgentBox extracts data from incoming emails and logs it into spreadsheets.' }
70877086 } ;
70887087
7088+ // Pre-built O(1) tool lookup by id — avoids linear scan in findTool()
7089+ var _toolById = { } ;
7090+ for ( var _i = 0 ; _i < INTEGRATIONS . length ; _i ++ ) {
7091+ _toolById [ INTEGRATIONS [ _i ] . id ] = INTEGRATIONS [ _i ] ;
7092+ }
7093+
7094+ // Pre-parsed pipeline entries with split keys — avoids re-splitting on
7095+ // every updatePipeline() call and enables fast Set-based membership checks
7096+ var _pipelineEntries = [ ] ;
7097+ ( function ( ) {
7098+ var keys = Object . keys ( PIPELINES ) ;
7099+ for ( var i = 0 ; i < keys . length ; i ++ ) {
7100+ _pipelineEntries . push ( {
7101+ parts : keys [ i ] . split ( '+' ) ,
7102+ pipeline : PIPELINES [ keys [ i ] ]
7103+ } ) ;
7104+ }
7105+ } ) ( ) ;
7106+
70897107 var selected = [ ] ;
70907108 var _section = null ;
70917109
@@ -7100,23 +7118,37 @@ var PipelineBuilder = (function () {
71007118 updatePipeline ( ) ;
71017119 }
71027120
7121+ /** Create a tool button element for the grid. */
7122+ function _createToolButton ( tool ) {
7123+ var btn = document . createElement ( 'button' ) ;
7124+ btn . className = 'pipeline-tool-btn' ;
7125+ btn . setAttribute ( 'data-tool' , tool . id ) ;
7126+ btn . setAttribute ( 'aria-pressed' , 'false' ) ;
7127+ btn . setAttribute ( 'role' , 'switch' ) ;
7128+ btn . setAttribute ( 'aria-label' , 'Add ' + tool . name + ' to pipeline' ) ;
7129+ btn . setAttribute ( 'title' , tool . desc ) ;
7130+
7131+ var iconSpan = document . createElement ( 'span' ) ;
7132+ iconSpan . className = 'pipeline-tool-icon' ;
7133+ iconSpan . textContent = tool . icon ;
7134+
7135+ var nameSpan = document . createElement ( 'span' ) ;
7136+ nameSpan . className = 'pipeline-tool-name' ;
7137+ nameSpan . textContent = tool . name ;
7138+
7139+ btn . appendChild ( iconSpan ) ;
7140+ btn . appendChild ( nameSpan ) ;
7141+ btn . addEventListener ( 'click' , function ( ) { toggleTool ( tool . id ) ; } ) ;
7142+ return btn ;
7143+ }
7144+
71037145 function renderToolGrid ( ) {
71047146 var grid = section ( ) . querySelector ( '.pipeline-tool-grid' ) ;
71057147 if ( ! grid ) return ;
7106- grid . innerHTML = '' ;
7107- INTEGRATIONS . forEach ( function ( tool ) {
7108- var btn = document . createElement ( 'button' ) ;
7109- btn . className = 'pipeline-tool-btn' ;
7110- btn . setAttribute ( 'data-tool' , tool . id ) ;
7111- btn . setAttribute ( 'aria-pressed' , 'false' ) ;
7112- btn . setAttribute ( 'role' , 'switch' ) ;
7113- btn . setAttribute ( 'aria-label' , 'Add ' + tool . name + ' to pipeline' ) ;
7114- btn . setAttribute ( 'title' , tool . desc ) ;
7115- btn . innerHTML = '<span class="pipeline-tool-icon">' + tool . icon +
7116- '</span><span class="pipeline-tool-name">' + tool . name + '</span>' ;
7117- btn . addEventListener ( 'click' , function ( ) { toggleTool ( tool . id ) ; } ) ;
7118- grid . appendChild ( btn ) ;
7119- } ) ;
7148+ while ( grid . firstChild ) grid . removeChild ( grid . firstChild ) ;
7149+ for ( var i = 0 ; i < INTEGRATIONS . length ; i ++ ) {
7150+ grid . appendChild ( _createToolButton ( INTEGRATIONS [ i ] ) ) ;
7151+ }
71207152 }
71217153
71227154 function toggleTool ( id ) {
@@ -7142,79 +7174,169 @@ var PipelineBuilder = (function () {
71427174 }
71437175 }
71447176
7145- function updatePipeline ( ) {
7146- var viz = section ( ) . querySelector ( '.pipeline-visualization' ) ;
7147- var desc = section ( ) . querySelector ( '.pipeline-description' ) ;
7148- var counter = section ( ) . querySelector ( '.pipeline-counter' ) ;
7149- if ( ! viz || ! desc ) return ;
7177+ // ── DOM builders for pipeline visualization ──────────────────────
71507178
7151- if ( counter ) counter . textContent = selected . length + ' / 5 tools selected' ;
7179+ /** Create a single pipeline node (icon + name). */
7180+ function _createPipelineNode ( tool ) {
7181+ var node = document . createElement ( 'div' ) ;
7182+ node . className = 'pipeline-node' ;
71527183
7153- if ( selected . length === 0 ) {
7154- viz . innerHTML = '<p class="pipeline-empty">Select tools above to build your agent pipeline</p>' ;
7155- desc . innerHTML = '' ;
7156- return ;
7157- }
7184+ var icon = document . createElement ( 'span' ) ;
7185+ icon . className = 'pipeline-node-icon' ;
7186+ icon . textContent = tool . icon ;
7187+
7188+ var name = document . createElement ( 'span' ) ;
7189+ name . className = 'pipeline-node-name' ;
7190+ name . textContent = tool . name ;
7191+
7192+ node . appendChild ( icon ) ;
7193+ node . appendChild ( name ) ;
7194+ return node ;
7195+ }
7196+
7197+ /** Create an arrow separator between pipeline nodes. */
7198+ function _createArrow ( ) {
7199+ var arrow = document . createElement ( 'div' ) ;
7200+ arrow . className = 'pipeline-arrow' ;
7201+ arrow . setAttribute ( 'aria-hidden' , 'true' ) ;
7202+ arrow . textContent = '→' ;
7203+ return arrow ;
7204+ }
7205+
7206+ /** Create the central AgentBox hub element. */
7207+ function _createHub ( ) {
7208+ var hub = document . createElement ( 'div' ) ;
7209+ hub . className = 'pipeline-hub' ;
7210+
7211+ var icon = document . createElement ( 'span' ) ;
7212+ icon . className = 'pipeline-hub-icon' ;
7213+ icon . textContent = '🤖' ;
7214+
7215+ var label = document . createElement ( 'span' ) ;
7216+ label . className = 'pipeline-hub-label' ;
7217+ label . textContent = 'AgentBox' ;
7218+
7219+ var sub = document . createElement ( 'span' ) ;
7220+ sub . className = 'pipeline-hub-sub' ;
7221+ sub . textContent = 'connects everything' ;
7222+
7223+ hub . appendChild ( icon ) ;
7224+ hub . appendChild ( label ) ;
7225+ hub . appendChild ( sub ) ;
7226+ return hub ;
7227+ }
7228+
7229+ /** Build the flow visualization (nodes + arrows + hub) for the current selection. */
7230+ function _buildVisualization ( ) {
7231+ var frag = document . createDocumentFragment ( ) ;
7232+ var flow = document . createElement ( 'div' ) ;
7233+ flow . className = 'pipeline-flow' ;
71587234
7159- // Build visual pipeline
7160- var html = '<div class="pipeline-flow">' ;
71617235 for ( var i = 0 ; i < selected . length ; i ++ ) {
7162- var tool = findTool ( selected [ i ] ) ;
7236+ var tool = _toolById [ selected [ i ] ] ;
71637237 if ( ! tool ) continue ;
7164- html += '<div class="pipeline-node">' ;
7165- html += '<span class="pipeline-node-icon">' + tool . icon + '</span>' ;
7166- html += '<span class="pipeline-node-name">' + tool . name + '</span>' ;
7167- html += '</div>' ;
7238+ flow . appendChild ( _createPipelineNode ( tool ) ) ;
71687239 if ( i < selected . length - 1 ) {
7169- html += '<div class="pipeline-arrow" aria-hidden="true">→</div>' ;
7240+ flow . appendChild ( _createArrow ( ) ) ;
71707241 }
71717242 }
7172- html += '</div>' ;
71737243
7174- // AgentBox hub in center
7175- html += '<div class="pipeline-hub">' ;
7176- html += '<span class="pipeline-hub-icon">🤖</span>' ;
7177- html += '<span class="pipeline-hub-label">AgentBox</span>' ;
7178- html += '<span class="pipeline-hub-sub">connects everything</span>' ;
7179- html += '</div>' ;
7244+ frag . appendChild ( flow ) ;
7245+ frag . appendChild ( _createHub ( ) ) ;
7246+ return frag ;
7247+ }
71807248
7181- viz . innerHTML = html ;
7249+ /** Create a single pipeline result card (name + description). */
7250+ function _createResultCard ( pipeline ) {
7251+ var card = document . createElement ( 'div' ) ;
7252+ card . className = 'pipeline-result-card' ;
7253+
7254+ var name = document . createElement ( 'strong' ) ;
7255+ name . className = 'pipeline-result-name' ;
7256+ name . textContent = pipeline . name ;
7257+
7258+ var flowP = document . createElement ( 'p' ) ;
7259+ flowP . className = 'pipeline-result-flow' ;
7260+ flowP . textContent = pipeline . flow ;
7261+
7262+ card . appendChild ( name ) ;
7263+ card . appendChild ( flowP ) ;
7264+ return card ;
7265+ }
7266+
7267+ /** Build the description section showing matched pipelines or a generic message. */
7268+ function _buildDescription ( matches ) {
7269+ var wrapper = document . createElement ( 'div' ) ;
71827270
7183- // Find matching pipelines
7184- var matches = findPipelines ( ) ;
71857271 if ( matches . length === 0 ) {
7186- desc . innerHTML = '<div class="pipeline-result"><p class="pipeline-generic">AgentBox can connect these tools and automate workflows between them. Add more tools to see specific pipeline recipes!</p></div>' ;
7272+ wrapper . className = 'pipeline-result' ;
7273+ var p = document . createElement ( 'p' ) ;
7274+ p . className = 'pipeline-generic' ;
7275+ p . textContent = 'AgentBox can connect these tools and automate workflows between them. Add more tools to see specific pipeline recipes!' ;
7276+ wrapper . appendChild ( p ) ;
71877277 } else {
7188- var descHtml = '<div class="pipeline-results-list">' ;
7189- descHtml += '<h4 class="pipeline-results-title">🔗 ' + matches . length + ' automation' + ( matches . length > 1 ? 's' : '' ) + ' available</h4>' ;
7190- for ( var m = 0 ; m < matches . length ; m ++ ) {
7191- descHtml += '<div class="pipeline-result-card">' ;
7192- descHtml += '<strong class="pipeline-result-name">' + matches [ m ] . name + '</strong>' ;
7193- descHtml += '<p class="pipeline-result-flow">' + matches [ m ] . flow + '</p>' ;
7194- descHtml += '</div>' ;
7278+ wrapper . className = 'pipeline-results-list' ;
7279+
7280+ var title = document . createElement ( 'h4' ) ;
7281+ title . className = 'pipeline-results-title' ;
7282+ title . textContent = '🔗 ' + matches . length + ' automation' + ( matches . length > 1 ? 's' : '' ) + ' available' ;
7283+ wrapper . appendChild ( title ) ;
7284+
7285+ for ( var i = 0 ; i < matches . length ; i ++ ) {
7286+ wrapper . appendChild ( _createResultCard ( matches [ i ] ) ) ;
71957287 }
7196- descHtml += '</div>' ;
7197- desc . innerHTML = descHtml ;
71987288 }
7289+
7290+ return wrapper ;
71997291 }
72007292
7201- function findTool ( id ) {
7202- for ( var i = 0 ; i < INTEGRATIONS . length ; i ++ ) {
7203- if ( INTEGRATIONS [ i ] . id === id ) return INTEGRATIONS [ i ] ;
7293+ // ── Core update logic ────────────────────────────────────────────
7294+
7295+ function updatePipeline ( ) {
7296+ var viz = section ( ) . querySelector ( '.pipeline-visualization' ) ;
7297+ var desc = section ( ) . querySelector ( '.pipeline-description' ) ;
7298+ var counter = section ( ) . querySelector ( '.pipeline-counter' ) ;
7299+ if ( ! viz || ! desc ) return ;
7300+
7301+ if ( counter ) counter . textContent = selected . length + ' / 5 tools selected' ;
7302+
7303+ // Clear previous content
7304+ while ( viz . firstChild ) viz . removeChild ( viz . firstChild ) ;
7305+ while ( desc . firstChild ) desc . removeChild ( desc . firstChild ) ;
7306+
7307+ if ( selected . length === 0 ) {
7308+ var empty = document . createElement ( 'p' ) ;
7309+ empty . className = 'pipeline-empty' ;
7310+ empty . textContent = 'Select tools above to build your agent pipeline' ;
7311+ viz . appendChild ( empty ) ;
7312+ return ;
72047313 }
7205- return null ;
7314+
7315+ viz . appendChild ( _buildVisualization ( ) ) ;
7316+
7317+ var matches = findPipelines ( ) ;
7318+ desc . appendChild ( _buildDescription ( matches ) ) ;
7319+ }
7320+
7321+ function findTool ( id ) {
7322+ return _toolById [ id ] || null ;
72067323 }
72077324
72087325 function findPipelines ( ) {
7326+ // Build a Set of selected ids for O(1) membership checks
7327+ var selectedSet = { } ;
7328+ for ( var s = 0 ; s < selected . length ; s ++ ) {
7329+ selectedSet [ selected [ s ] ] = true ;
7330+ }
7331+
72097332 var matches = [ ] ;
7210- var keys = Object . keys ( PIPELINES ) ;
7211- for ( var k = 0 ; k < keys . length ; k ++ ) {
7212- var parts = keys [ k ] . split ( '+' ) ;
7333+ for ( var k = 0 ; k < _pipelineEntries . length ; k ++ ) {
7334+ var parts = _pipelineEntries [ k ] . parts ;
72137335 var allPresent = true ;
72147336 for ( var p = 0 ; p < parts . length ; p ++ ) {
7215- if ( selected . indexOf ( parts [ p ] ) < 0 ) { allPresent = false ; break ; }
7337+ if ( ! selectedSet [ parts [ p ] ] ) { allPresent = false ; break ; }
72167338 }
7217- if ( allPresent ) matches . push ( PIPELINES [ keys [ k ] ] ) ;
7339+ if ( allPresent ) matches . push ( _pipelineEntries [ k ] . pipeline ) ;
72187340 }
72197341 return matches ;
72207342 }
0 commit comments