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:
parent
a80f09ec30
commit
7eebef126f
2 changed files with 115 additions and 3 deletions
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue