feat(demos): enhance 27027 demo with interactive features and service highlighting

SUMMARY:
Completed Phase 3 Task 3.4.1 - Enhanced the 27027 incident demo page
with interactive playback controls, service status visualization, and
clickable step navigation for better user engagement.

NEW FEATURES:

1. Clickable Step Navigation:
   - Users can click any step to jump directly to it
   - Steps highlight on hover with shadow effect
   - Manual navigation shows progress panel and service status
   - Disabled during auto-play to prevent conflicts

2. Playback Speed Controls:
   - Three speed options: Slow (4s), Normal (2.5s), Fast (1s)
   - Visual button state shows selected speed
   - Speed persists during playback
   - Default: Normal speed

3. Service Status Visualization:
   - New panel shows active Tractatus services
   - InstructionPersistence highlights on Step 6 (purple ring)
   - CrossReferenceValidator highlights on Step 7 (purple ring)
   - Service icons use brand colors (indigo/purple)
   - Smooth opacity transitions

4. Enhanced Visual Feedback:
   - Steps now reset properly when navigating backward
   - Future steps return to pending state
   - Hover effects on all steps
   - Smooth scroll behavior
   - Better state management (pending/active/complete/error)

5. Improved UX:
   - Service status hidden until first interaction
   - Progress panel shows after first play/click
   - Reset clears all state including services
   - Click handlers respect auto-play state

TECHNICAL DETAILS:

JavaScript (27027-demo.js):
- Added playbackSpeed variable and speedDelays mapping
- Enhanced initTimeline() with click handlers for navigation
- Updated playScenario() to use speed setting
- New updateServiceStatus() function for service highlighting
- Enhanced showStep() to handle forward/backward navigation
- Updated resetScenario() to clear service status

HTML (27027-demo.html):
- Added speed control buttons (Slow/Normal/Fast)
- Added service status panel with 2 services
- Service indicators use brand colors
- All controls use Tailwind utility classes

DESIGN PATTERNS:
- Brand-consistent colors (indigo-600, purple-600)
- Smooth 300ms transitions
- Responsive design maintained
- CSP compliant (no inline handlers)

IMPACT:
Users can now:
✓ Control playback speed for better comprehension
✓ Jump to specific steps for review
✓ See which Tractatus services activate at each step
✓ Understand the architectural intervention visually

This completes all Phase 3 interactive features. Demo is now
production-ready for deployment.

🤖 Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
TheFlow 2025-10-19 21:55:47 +13:00
parent a80f09ec30
commit 7eebef126f
2 changed files with 115 additions and 3 deletions

View file

@ -69,6 +69,37 @@
</button>
</div>
<!-- Speed Controls -->
<div class="mb-6">
<h4 class="text-sm font-semibold text-gray-700 mb-2">Playback Speed:</h4>
<div class="flex gap-2">
<button class="speed-btn flex-1 bg-gray-200 hover:bg-gray-300 text-gray-700 px-3 py-2 rounded text-sm transition" data-speed="slow">
Slow
</button>
<button class="speed-btn flex-1 bg-blue-600 text-white px-3 py-2 rounded text-sm transition" data-speed="normal">
Normal
</button>
<button class="speed-btn flex-1 bg-gray-200 hover:bg-gray-300 text-gray-700 px-3 py-2 rounded text-sm transition" data-speed="fast">
Fast
</button>
</div>
</div>
<!-- Service Status -->
<div id="service-status" class="mb-6 hidden">
<h4 class="text-sm font-semibold text-gray-700 mb-3">Active Services:</h4>
<div class="space-y-2 text-xs">
<div id="service-instruction" class="flex items-center p-2 rounded bg-gray-50 opacity-30 transition-opacity duration-300">
<div class="w-3 h-3 rounded-full bg-indigo-600 mr-2"></div>
<span class="text-gray-700">InstructionPersistence</span>
</div>
<div id="service-validator" class="flex items-center p-2 rounded bg-gray-50 opacity-30 transition-opacity duration-300">
<div class="w-3 h-3 rounded-full bg-purple-600 mr-2"></div>
<span class="text-gray-700">CrossReferenceValidator</span>
</div>
</div>
</div>
<div id="progress-info" class="hidden">
<div class="mb-4">
<div class="flex justify-between text-sm text-gray-600 mb-2">

View file

@ -127,11 +127,17 @@ AI Alert: "You specified port 27027, but I was about to check
let currentStep = -1;
let isPlaying = false;
let playbackSpeed = 'normal'; // slow, normal, fast
const speedDelays = {
slow: 4000,
normal: 2500,
fast: 1000
};
function initTimeline() {
const timeline = document.getElementById('timeline');
timeline.innerHTML = steps.map((step, index) => `
<div id="step-${index}" class="border-2 border-gray-300 bg-white rounded-lg p-6 transition-all duration-300">
<div id="step-${index}" class="border-2 border-gray-300 bg-white rounded-lg p-6 transition-all duration-300 cursor-pointer hover:shadow-lg" data-step-index="${index}">
<div class="flex items-start">
<div class="flex-shrink-0 mr-4">
<div class="w-10 h-10 rounded-full ${getStepColor(step.type)} flex items-center justify-center text-white font-bold">
@ -147,6 +153,18 @@ function initTimeline() {
</div>
</div>
`).join('');
// Add click handlers to steps for navigation
document.querySelectorAll('[data-step-index]').forEach(stepEl => {
stepEl.addEventListener('click', () => {
if (!isPlaying) {
const index = parseInt(stepEl.getAttribute('data-step-index'));
showStep(index);
document.getElementById('progress-info').classList.remove('hidden');
document.getElementById('service-status').classList.remove('hidden');
}
});
});
}
function getStepColor(type) {
@ -172,10 +190,13 @@ async function playScenario() {
document.getElementById('start-btn').disabled = true;
document.getElementById('progress-info').classList.remove('hidden');
document.getElementById('service-status').classList.remove('hidden');
for (let i = 0; i <= steps.length - 1; i++) {
await showStep(i);
await delay(2500); // 2.5 second delay between steps
if (i < steps.length - 1) {
await delay(speedDelays[playbackSpeed]);
}
}
isPlaying = false;
@ -193,6 +214,13 @@ async function showStep(index) {
stepEl.classList.add('step-complete', 'border-green-500', 'bg-green-50');
}
// Mark future steps as pending
for (let i = index + 1; i < steps.length; i++) {
const stepEl = document.getElementById(`step-${i}`);
stepEl.className = 'border-2 border-gray-300 bg-white rounded-lg p-6 transition-all duration-300 cursor-pointer hover:shadow-lg';
stepEl.querySelector('.step-description').classList.add('hidden');
}
// Mark current step as active
const currentStepEl = document.getElementById(`step-${index}`);
currentStepEl.classList.add('step-active', 'border-blue-500', 'bg-blue-50', 'fade-in');
@ -212,6 +240,34 @@ async function showStep(index) {
document.getElementById('progress-bar').style.width = `${progress}%`;
document.getElementById('progress-text').textContent = `${index + 1} / ${steps.length}`;
document.getElementById('current-step-desc').textContent = steps[index].description;
// Highlight active services
updateServiceStatus(index);
}
function updateServiceStatus(stepIndex) {
const instructionService = document.getElementById('service-instruction');
const validatorService = document.getElementById('service-validator');
// Reset both services to inactive
instructionService.classList.remove('opacity-100', 'bg-indigo-50', 'ring-2', 'ring-indigo-400');
instructionService.classList.add('opacity-30', 'bg-gray-50');
validatorService.classList.remove('opacity-100', 'bg-purple-50', 'ring-2', 'ring-purple-400');
validatorService.classList.add('opacity-30', 'bg-gray-50');
// Step 6: InstructionPersistence activates
if (stepIndex === 6) {
instructionService.classList.remove('opacity-30', 'bg-gray-50');
instructionService.classList.add('opacity-100', 'bg-indigo-50', 'ring-2', 'ring-indigo-400');
}
// Step 7: CrossReferenceValidator activates
if (stepIndex === 7) {
instructionService.classList.remove('opacity-30', 'bg-gray-50');
instructionService.classList.add('opacity-100', 'bg-indigo-50');
validatorService.classList.remove('opacity-30', 'bg-gray-50');
validatorService.classList.add('opacity-100', 'bg-purple-50', 'ring-2', 'ring-purple-400');
}
}
function resetScenario() {
@ -221,7 +277,8 @@ function resetScenario() {
// Reset all steps
steps.forEach((_, index) => {
const stepEl = document.getElementById(`step-${index}`);
stepEl.className = 'border-2 border-gray-300 bg-white rounded-lg p-6 transition-all duration-300';
stepEl.className = 'border-2 border-gray-300 bg-white rounded-lg p-6 transition-all duration-300 cursor-pointer hover:shadow-lg';
stepEl.setAttribute('data-step-index', index);
stepEl.querySelector('.step-description').classList.add('hidden');
});
@ -229,17 +286,41 @@ function resetScenario() {
document.getElementById('progress-text').textContent = `0 / ${steps.length}`;
document.getElementById('current-step-desc').textContent = '';
document.getElementById('progress-info').classList.add('hidden');
document.getElementById('service-status').classList.add('hidden');
document.getElementById('start-btn').innerHTML = '▶ Start Scenario';
document.getElementById('start-btn').disabled = false;
// Reset services
updateServiceStatus(-1);
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Speed control event listeners
function setPlaybackSpeed(speed) {
playbackSpeed = speed;
document.querySelectorAll('.speed-btn').forEach(btn => {
if (btn.getAttribute('data-speed') === speed) {
btn.classList.remove('bg-gray-200', 'hover:bg-gray-300', 'text-gray-700');
btn.classList.add('bg-blue-600', 'text-white');
} else {
btn.classList.remove('bg-blue-600', 'text-white');
btn.classList.add('bg-gray-200', 'hover:bg-gray-300', 'text-gray-700');
}
});
}
// Event listeners
document.getElementById('start-btn').addEventListener('click', playScenario);
document.getElementById('reset-btn').addEventListener('click', resetScenario);
document.querySelectorAll('.speed-btn').forEach(btn => {
btn.addEventListener('click', () => {
setPlaybackSpeed(btn.getAttribute('data-speed'));
});
});
// Initialize
initTimeline();