Skip to content

Commit 011f2b5

Browse files
jeremymanningclaude
andcommitted
feat(demo-01): Add expandable breakdowns and fix GUI loading
Rule Breakdown improvements: - Make "Found X keyword(s)" clickable to expand and show all keywords with their ranks and pattern counts - Make "Tested X pattern(s)" clickable to expand and show all patterns tested with match results and captures - Add styling for expandable headers with hover effects Live Rule Editor fix: - Fix GUI not loading rules by parsing directly from textarea instead of using getContent() which caused circular dependency 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 730bafe commit 011f2b5

File tree

4 files changed

+79
-26
lines changed

4 files changed

+79
-26
lines changed

demos/01-eliza/css/eliza.css

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,30 @@
400400
font-style: italic;
401401
}
402402

403+
/* Pattern tests header - clickable to expand */
404+
.pattern-tests-header {
405+
display: flex;
406+
align-items: center;
407+
gap: 8px;
408+
padding: 8px 12px;
409+
background: var(--surface-color);
410+
border-radius: 6px;
411+
border: 1px solid var(--border-color);
412+
font-size: 0.9rem;
413+
transition: var(--eliza-transition);
414+
}
415+
416+
.pattern-tests-header:hover {
417+
background: var(--surface-hover);
418+
border-color: var(--primary-color);
419+
}
420+
421+
.pattern-toggle-icon {
422+
font-size: 0.75rem;
423+
color: var(--text-secondary);
424+
transition: transform 0.2s ease;
425+
}
426+
403427
.pattern-test {
404428
background: var(--surface-color);
405429
padding: 10px;

demos/01-eliza/index.html

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -490,31 +490,44 @@ <h3>Synonyms</h3>
490490
}
491491

492492
// Add details (but not for template selection since we add custom details)
493+
// For Keyword Detection, make it expandable
493494
if (step.details && step.name !== 'Template Selection') {
494-
stepHTML += `<div class="step-details">${step.details}</div>`;
495+
if (step.name === 'Keyword Detection' && step.keywordsFound && step.keywordsFound.length > 0) {
496+
const keywordStepId = `keywords-${index}`;
497+
stepHTML += `<div class="pattern-tests-header" onclick="togglePatternTests('${keywordStepId}')" style="cursor: pointer; color: var(--primary-color); margin-bottom: 8px;">
498+
<span class="pattern-toggle-icon" id="${keywordStepId}-icon">▶</span>
499+
Found ${step.keywordsFound.length} keyword(s) - <em>click to show details</em>
500+
</div>`;
501+
stepHTML += `<div class="step-content" id="${keywordStepId}" style="display: none;">`;
502+
step.keywordsFound.forEach(kw => {
503+
stepHTML += `
504+
<div class="pattern-test" style="opacity: 1;">
505+
<strong>${escapeHtml(kw.keyword)}</strong> (rank: ${kw.rank}) - ${kw.patternCount} pattern(s)
506+
</div>
507+
`;
508+
});
509+
stepHTML += '</div>';
510+
} else {
511+
stepHTML += `<div class="step-details">${step.details}</div>`;
512+
}
495513
}
496514

497515
// Add pattern tests if available
498516
if (step.patternTests && step.patternTests.length > 0) {
499-
stepHTML += '<div class="step-content">';
517+
const stepId = `pattern-tests-${index}`;
518+
const totalPatterns = step.patternTests.length;
500519

501-
// Find the index of the matched pattern (if any)
502-
const matchedIndex = step.patternTests.findIndex(t => t.matched);
520+
// Add clickable header showing total patterns tested
521+
stepHTML += `<div class="pattern-tests-header" onclick="togglePatternTests('${stepId}')" style="cursor: pointer; color: var(--primary-color); margin-bottom: 8px;">
522+
<span class="pattern-toggle-icon" id="${stepId}-icon">▶</span>
523+
Tested ${totalPatterns} pattern(s) - <em>click to ${totalPatterns > 5 ? 'show all' : 'toggle'}</em>
524+
</div>`;
503525

504-
// Display logic: show up to 5 patterns, but always include the matched one
505-
const maxToShow = 5;
506-
let displayedCount = 0;
507-
let stoppedEarly = false;
526+
stepHTML += `<div class="step-content" id="${stepId}" style="display: none;">`;
508527

528+
// Show ALL patterns when expanded
509529
for (let i = 0; i < step.patternTests.length; i++) {
510530
const test = step.patternTests[i];
511-
512-
// Stop after maxToShow patterns if we've already shown the match
513-
if (displayedCount >= maxToShow && matchedIndex !== -1 && i > matchedIndex) {
514-
stoppedEarly = true;
515-
break;
516-
}
517-
518531
const testClass = test.matched ? 'matched' : 'not-matched';
519532
const capturesHtml = test.captures && test.captures.length > 0
520533
? `<br>Captures: ${test.captures.map((c, j) => {
@@ -530,13 +543,6 @@ <h3>Synonyms</h3>
530543
${capturesHtml}
531544
</div>
532545
`;
533-
displayedCount++;
534-
}
535-
536-
// Show remaining count only if we actually stopped early
537-
const remaining = step.patternTests.length - displayedCount;
538-
if (stoppedEarly && remaining > 0) {
539-
stepHTML += `<div class="step-details">... and ${remaining} more pattern(s) tested</div>`;
540546
}
541547

542548
stepHTML += '</div>';
@@ -661,6 +667,19 @@ <h3>Synonyms</h3>
661667
return div.innerHTML;
662668
}
663669

670+
// Toggle pattern tests visibility (exposed globally for onclick handler)
671+
window.togglePatternTests = function(stepId) {
672+
const content = document.getElementById(stepId);
673+
const icon = document.getElementById(stepId + '-icon');
674+
if (content.style.display === 'none') {
675+
content.style.display = 'block';
676+
icon.textContent = '▼';
677+
} else {
678+
content.style.display = 'none';
679+
icon.textContent = '▶';
680+
}
681+
};
682+
664683
// Editor Functions
665684
function updateEditorStats() {
666685
const stats = ruleEditor.getStatistics();

demos/01-eliza/js/pattern-matcher.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,12 @@ export class PatternMatcher {
443443
description: 'Finding relevant keywords in input',
444444
input: processedInput,
445445
output: keywordsFound.map(k => `"${k.keyword}" (rank: ${k.rank})`).join(', ') || 'No keywords found',
446-
details: `Found ${keywordsFound.length} keyword(s), testing in priority order`
446+
details: `Found ${keywordsFound.length} keyword(s), testing in priority order`,
447+
keywordsFound: keywordsFound.map(k => ({
448+
keyword: k.keyword,
449+
rank: k.rank,
450+
patternCount: k.rule.patterns.length
451+
}))
447452
});
448453

449454
// Step 3: Pattern matching (specific patterns first, then catch-all)

demos/01-eliza/js/rule-editor.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,14 @@ class RuleEditor {
164164
* Populate GUI with current rules data
165165
*/
166166
populateGUI() {
167-
const validation = this.validate();
168-
if (!validation.valid) return;
169-
const data = validation.data;
167+
// Always parse from the JSON textarea, not from getContent() which may recurse
168+
let data;
169+
try {
170+
data = JSON.parse(this.editor.value);
171+
} catch (e) {
172+
console.error('Failed to parse rules JSON for GUI:', e);
173+
return;
174+
}
170175
this.renderSubstitutions('pre-sub', data.preSubstitutions || {});
171176
this.renderSubstitutions('post-sub', data.postSubstitutions || {});
172177
this.renderSynonyms(data.synonyms || {});

0 commit comments

Comments
 (0)