@@ -482,13 +482,36 @@ <h3>Synonyms</h3>
482482 </div>
483483 <div class="arrow" style="color: var(--warning-color, #856404);">↪</div>
484484 <div style="flex: 1;">
485- <div class="io-label">Target: "${ escapeHtml ( step . targetKeyword ) } " (${ step . targetTemplates . length } template(s))</div>
485+ <div class="io-label">Target Keyword: "${ escapeHtml ( step . targetKeyword ) } "</div>
486+ <div class="io-box" style="font-size: 0.85rem;">
487+ <div><strong>Pattern:</strong> "${ escapeHtml ( step . targetPatternString || '*' ) } "</div>
488+ <div style="margin-top: 4px;"><strong>Responses:</strong> ${ step . targetTemplates . length } available</div>
489+ </div>
490+ </div>
491+ </div>
492+ ` ;
493+ // Show the resolved template selection
494+ stepHTML += `
495+ <div class="step-io" style="margin-top: 12px;">
496+ <div style="flex: 1;">
497+ <div class="io-label">Select Response from "${ escapeHtml ( step . targetKeyword ) } "</div>
486498 <select class="template-dropdown" id="goto-template-selector" style="width: 100%; padding: 10px; border: 2px solid var(--border-color); border-radius: 6px; font-size: 0.9rem; background: var(--surface-color); color: var(--text-primary); cursor: pointer;">
487499 ${ step . targetTemplates . map ( ( t , i ) => `<option value="${ i } " ${ escapeHtml ( t ) === escapeHtml ( step . output . replace ( / ^ " | " $ / g, '' ) ) ? 'selected' : '' } >${ escapeHtml ( t ) } </option>` ) . join ( '' ) }
488500 </select>
489501 </div>
502+ <div class="arrow">→</div>
503+ <div style="flex: 1;">
504+ <div class="io-label">Resolved Template</div>
505+ <div class="io-box">${ escapeHtml ( step . output ) } </div>
506+ </div>
490507 </div>
491508 ` ;
509+ // Add note about captures being preserved
510+ if ( step . originalCapturesPreserved ) {
511+ stepHTML += `<div class="step-details" style="margin-top: 8px; font-style: italic;">
512+ Note: Original captures (${ step . capturesAvailable } ) are preserved and can be used in the resolved template.
513+ </div>` ;
514+ }
492515 stepHTML += `<div class="step-details">${ step . details } </div>` ;
493516 } else if ( step . input !== undefined && step . output !== undefined ) {
494517 // Add input/output visualization for other steps
@@ -642,11 +665,51 @@ <h3>Synonyms</h3>
642665 }
643666 }
644667
645- // Re-assemble the response with the new template
646668 const patternMatcher = eliza . patternMatcher ;
647669 const postSubs = eliza . postSubstitutions ;
670+ const rules = eliza . rules ;
671+
672+ // Check if selected template is a goto statement
673+ let finalTemplate = selectedTemplate ;
674+ let gotoResolutionData = null ;
675+
676+ if ( selectedTemplate . startsWith ( 'goto ' ) ) {
677+ // Resolve the goto
678+ const gotoChain = [ ] ;
679+ let currentTemplate = selectedTemplate ;
680+ let targetRule = null ;
681+ let targetPattern = null ;
682+
683+ while ( currentTemplate && currentTemplate . startsWith ( 'goto ' ) ) {
684+ const targetKeyword = currentTemplate . substring ( 5 ) . trim ( ) ;
685+ gotoChain . push ( targetKeyword ) ;
686+
687+ targetRule = rules . find ( r => r . keyword === targetKeyword ) ;
688+ if ( targetRule && targetRule . patterns && targetRule . patterns . length > 0 ) {
689+ targetPattern = targetRule . patterns [ 0 ] ;
690+ const targetTemplates = targetPattern . responses ;
691+ // Select first template by default (user can change via goto dropdown)
692+ currentTemplate = targetTemplates [ 0 ] ;
693+ } else {
694+ break ;
695+ }
648696
649- let newResponse = selectedTemplate ;
697+ if ( gotoChain . length >= 10 ) break ;
698+ }
699+
700+ finalTemplate = currentTemplate ;
701+ gotoResolutionData = {
702+ gotoChain,
703+ targetKeyword : gotoChain [ gotoChain . length - 1 ] ,
704+ targetRule,
705+ targetPattern,
706+ targetTemplates : targetPattern ? targetPattern . responses : [ ] ,
707+ resolvedTemplate : finalTemplate
708+ } ;
709+ }
710+
711+ // Re-assemble the response with the final template
712+ let newResponse = finalTemplate ;
650713 for ( let i = 0 ; i < captures . length ; i ++ ) {
651714 const placeholder = `(${ i + 1 } )` ;
652715 if ( newResponse . includes ( placeholder ) ) {
@@ -659,23 +722,217 @@ <h3>Synonyms</h3>
659722 // Format response (uppercase to match ELIZA style)
660723 newResponse = newResponse . toUpperCase ( ) ;
661724
662- // Update the final response display
663- const finalResponseContent = document . getElementById ( 'final-response-content' ) ;
664- if ( finalResponseContent ) {
665- finalResponseContent . textContent = newResponse ;
666- }
725+ // Update the breakdown steps dynamically
726+ updateBreakdownWithGoto ( selectedTemplate , gotoResolutionData , finalTemplate , newResponse , captures ) ;
727+ }
667728
668- // Also update the Post-substitutions & Assembly step if it exists
729+ // Update breakdown steps when a goto is selected or deselected
730+ function updateBreakdownWithGoto ( selectedTemplate , gotoData , finalTemplate , finalResponse , captures ) {
669731 const stepsDiv = document . getElementById ( 'breakdown-steps' ) ;
732+ const finalResponseStep = document . getElementById ( 'final-response-step' ) ;
733+
734+ // Find existing goto resolution step (by id or by title)
735+ let gotoStep = document . getElementById ( 'goto-resolution-step' ) ;
736+ if ( ! gotoStep ) {
737+ // Also check by title for steps created during initial render
738+ const stepElements = stepsDiv . querySelectorAll ( '.breakdown-step' ) ;
739+ stepElements . forEach ( stepEl => {
740+ const titleEl = stepEl . querySelector ( '.step-title' ) ;
741+ if ( titleEl && titleEl . textContent === 'Goto Resolution' ) {
742+ gotoStep = stepEl ;
743+ }
744+ } ) ;
745+ }
746+
747+ // Find the Post-substitutions step
748+ let postSubStep = null ;
670749 const stepElements = stepsDiv . querySelectorAll ( '.breakdown-step' ) ;
671750 stepElements . forEach ( stepEl => {
672751 const titleEl = stepEl . querySelector ( '.step-title' ) ;
673752 if ( titleEl && titleEl . textContent === 'Post-substitutions & Assembly' ) {
674- const outputBox = stepEl . querySelectorAll ( '.io-box' ) [ 1 ] ;
675- if ( outputBox ) {
676- outputBox . textContent = newResponse ;
753+ postSubStep = stepEl ;
754+ }
755+ } ) ;
756+
757+ if ( gotoData ) {
758+ // Need to show/update goto resolution step
759+ if ( ! gotoStep ) {
760+ // Create the goto resolution step
761+ gotoStep = document . createElement ( 'div' ) ;
762+ gotoStep . className = 'breakdown-step' ;
763+ gotoStep . id = 'goto-resolution-step' ;
764+ gotoStep . style . borderColor = 'var(--warning-color, #ffc107)' ;
765+
766+ // Insert before post-substitutions step
767+ if ( postSubStep ) {
768+ stepsDiv . insertBefore ( gotoStep , postSubStep ) ;
769+ // Update step numbers
770+ updateStepNumbers ( ) ;
677771 }
678772 }
773+
774+ // Update goto step content
775+ gotoStep . innerHTML = `
776+ <div class="step-header">
777+ <div class="step-number" style="background: var(--warning-color, #ffc107);">↪</div>
778+ <div class="step-title">Goto Resolution</div>
779+ </div>
780+ <div class="step-description">Following reference to another keyword's responses</div>
781+ <div class="step-io">
782+ <div style="flex: 1;">
783+ <div class="io-label">Goto Statement</div>
784+ <div class="io-box" style="background: var(--warning-bg, #fff3cd); border-color: var(--warning-border, #ffc107);">"${ escapeHtml ( selectedTemplate ) } "</div>
785+ </div>
786+ <div class="arrow" style="color: var(--warning-color, #856404);">↪</div>
787+ <div style="flex: 1;">
788+ <div class="io-label">Target Keyword: "${ escapeHtml ( gotoData . targetKeyword ) } "</div>
789+ <div class="io-box" style="font-size: 0.85rem;">
790+ <div><strong>Pattern:</strong> "${ escapeHtml ( gotoData . targetPattern ? gotoData . targetPattern . pattern : '*' ) } "</div>
791+ <div style="margin-top: 4px;"><strong>Responses:</strong> ${ gotoData . targetTemplates . length } available</div>
792+ </div>
793+ </div>
794+ </div>
795+ <div class="step-io" style="margin-top: 12px;">
796+ <div style="flex: 1;">
797+ <div class="io-label">Select Response from "${ escapeHtml ( gotoData . targetKeyword ) } "</div>
798+ <select class="template-dropdown" id="goto-template-selector" style="width: 100%; padding: 10px; border: 2px solid var(--border-color); border-radius: 6px; font-size: 0.9rem; background: var(--surface-color); color: var(--text-primary); cursor: pointer;">
799+ ${ gotoData . targetTemplates . map ( ( t , i ) => `<option value="${ i } " ${ i === 0 ? 'selected' : '' } >${ escapeHtml ( t ) } </option>` ) . join ( '' ) }
800+ </select>
801+ </div>
802+ <div class="arrow">→</div>
803+ <div style="flex: 1;">
804+ <div class="io-label">Resolved Template</div>
805+ <div class="io-box" id="goto-resolved-template">"${ escapeHtml ( finalTemplate ) } "</div>
806+ </div>
807+ </div>
808+ ${ captures . length > 0 ? `<div class="step-details" style="margin-top: 8px; font-style: italic;">
809+ Note: Original captures (${ captures . length } ) are preserved and can be used in the resolved template.
810+ </div>` : '' }
811+ <div class="step-details">Redirected to keyword "${ escapeHtml ( gotoData . targetKeyword ) } "</div>
812+ ` ;
813+
814+ // Add event listener for the goto template selector
815+ const gotoSelector = document . getElementById ( 'goto-template-selector' ) ;
816+ if ( gotoSelector ) {
817+ gotoSelector . addEventListener ( 'change' , handleGotoTemplateChange ) ;
818+ }
819+ } else {
820+ // Remove goto resolution step if it exists
821+ if ( gotoStep ) {
822+ gotoStep . remove ( ) ;
823+ updateStepNumbers ( ) ;
824+ }
825+ }
826+
827+ // Update Post-substitutions step
828+ if ( postSubStep ) {
829+ const inputBox = postSubStep . querySelector ( '.io-box' ) ;
830+ const outputBox = postSubStep . querySelectorAll ( '.io-box' ) [ 1 ] ;
831+ if ( inputBox ) inputBox . textContent = finalTemplate ;
832+ if ( outputBox ) outputBox . textContent = finalResponse ;
833+ }
834+
835+ // Update final response
836+ const finalResponseContent = document . getElementById ( 'final-response-content' ) ;
837+ if ( finalResponseContent ) {
838+ finalResponseContent . textContent = finalResponse ;
839+ }
840+ }
841+
842+ // Handle goto template dropdown change
843+ function handleGotoTemplateChange ( e ) {
844+ const selectedIndex = parseInt ( e . target . value ) ;
845+
846+ // We need to get the target templates from the current goto data
847+ // Re-resolve by looking at the current template selection
848+ const templateSelector = document . getElementById ( 'breakdown-template-selector' ) ;
849+ if ( ! templateSelector ) return ;
850+
851+ const templateStep = currentBreakdown . steps . find ( s => s . name === 'Template Selection' ) ;
852+ if ( ! templateStep ) return ;
853+
854+ const mainTemplate = templateStep . allTemplates [ parseInt ( templateSelector . value ) ] ;
855+ if ( ! mainTemplate . startsWith ( 'goto ' ) ) return ;
856+
857+ // Resolve the goto chain
858+ const rules = eliza . rules ;
859+ let targetKeyword = mainTemplate . substring ( 5 ) . trim ( ) ;
860+ let targetRule = rules . find ( r => r . keyword === targetKeyword ) ;
861+
862+ // Handle chain
863+ while ( targetRule && targetRule . patterns && targetRule . patterns . length > 0 ) {
864+ const targetPattern = targetRule . patterns [ 0 ] ;
865+ const templates = targetPattern . responses ;
866+ const selectedGotoTemplate = templates [ selectedIndex ] ;
867+
868+ if ( selectedGotoTemplate && selectedGotoTemplate . startsWith ( 'goto ' ) ) {
869+ targetKeyword = selectedGotoTemplate . substring ( 5 ) . trim ( ) ;
870+ targetRule = rules . find ( r => r . keyword === targetKeyword ) ;
871+ } else {
872+ // Found the final template
873+ const patternMatcher = eliza . patternMatcher ;
874+ const postSubs = eliza . postSubstitutions ;
875+
876+ // Get captures
877+ const patternStep = currentBreakdown . steps . find ( s => s . name === 'Pattern Matching' ) ;
878+ const captures = [ ] ;
879+ if ( patternStep && patternStep . patternTests ) {
880+ const matchedTest = patternStep . patternTests . find ( t => t . matched ) ;
881+ if ( matchedTest && matchedTest . captures ) {
882+ captures . push ( ...matchedTest . captures ) ;
883+ }
884+ }
885+
886+ // Assemble response
887+ let newResponse = selectedGotoTemplate ;
888+ for ( let i = 0 ; i < captures . length ; i ++ ) {
889+ const placeholder = `(${ i + 1 } )` ;
890+ if ( newResponse . includes ( placeholder ) ) {
891+ const captureText = Array . isArray ( captures [ i ] ) ? captures [ i ] . join ( ' ' ) : captures [ i ] ;
892+ const { result } = patternMatcher . applyPostSubstitutions ( captureText , postSubs ) ;
893+ newResponse = newResponse . replace ( placeholder , result ) ;
894+ }
895+ }
896+ newResponse = newResponse . toUpperCase ( ) ;
897+
898+ // Update displays
899+ const resolvedTemplateBox = document . getElementById ( 'goto-resolved-template' ) ;
900+ if ( resolvedTemplateBox ) {
901+ resolvedTemplateBox . textContent = `"${ selectedGotoTemplate } "` ;
902+ }
903+
904+ // Update post-sub step
905+ const stepsDiv = document . getElementById ( 'breakdown-steps' ) ;
906+ stepsDiv . querySelectorAll ( '.breakdown-step' ) . forEach ( stepEl => {
907+ const titleEl = stepEl . querySelector ( '.step-title' ) ;
908+ if ( titleEl && titleEl . textContent === 'Post-substitutions & Assembly' ) {
909+ const inputBox = stepEl . querySelector ( '.io-box' ) ;
910+ const outputBox = stepEl . querySelectorAll ( '.io-box' ) [ 1 ] ;
911+ if ( inputBox ) inputBox . textContent = selectedGotoTemplate ;
912+ if ( outputBox ) outputBox . textContent = newResponse ;
913+ }
914+ } ) ;
915+
916+ // Update final response
917+ const finalResponseContent = document . getElementById ( 'final-response-content' ) ;
918+ if ( finalResponseContent ) {
919+ finalResponseContent . textContent = newResponse ;
920+ }
921+
922+ break ;
923+ }
924+ }
925+ }
926+
927+ // Update step numbers after adding/removing steps
928+ function updateStepNumbers ( ) {
929+ const stepsDiv = document . getElementById ( 'breakdown-steps' ) ;
930+ const stepElements = stepsDiv . querySelectorAll ( '.breakdown-step:not(#final-response-step)' ) ;
931+ stepElements . forEach ( ( stepEl , index ) => {
932+ const stepNumber = stepEl . querySelector ( '.step-number' ) ;
933+ if ( stepNumber && ! stepNumber . textContent . includes ( '✓' ) && ! stepNumber . textContent . includes ( '↪' ) ) {
934+ stepNumber . textContent = `${ index + 1 } ` ;
935+ }
679936 } ) ;
680937 }
681938
0 commit comments