diff --git a/.claude/metrics/hooks-metrics.json b/.claude/metrics/hooks-metrics.json
index 1e0379ce..680e21e4 100644
--- a/.claude/metrics/hooks-metrics.json
+++ b/.claude/metrics/hooks-metrics.json
@@ -4626,6 +4626,55 @@
"file": "/home/theflow/projects/tractatus/public/index.html",
"result": "passed",
"reason": null
+ },
+ {
+ "hook": "validate-file-edit",
+ "timestamp": "2025-10-19T08:53:43.833Z",
+ "file": "/home/theflow/projects/tractatus/public/demos/27027-demo.html",
+ "result": "passed",
+ "reason": null
+ },
+ {
+ "hook": "validate-file-edit",
+ "timestamp": "2025-10-19T08:53:52.793Z",
+ "file": "/home/theflow/projects/tractatus/public/js/demos/27027-demo.js",
+ "result": "passed",
+ "reason": null
+ },
+ {
+ "hook": "validate-file-edit",
+ "timestamp": "2025-10-19T08:54:06.854Z",
+ "file": "/home/theflow/projects/tractatus/public/js/demos/27027-demo.js",
+ "result": "passed",
+ "reason": null
+ },
+ {
+ "hook": "validate-file-edit",
+ "timestamp": "2025-10-19T08:54:17.313Z",
+ "file": "/home/theflow/projects/tractatus/public/js/demos/27027-demo.js",
+ "result": "passed",
+ "reason": null
+ },
+ {
+ "hook": "validate-file-edit",
+ "timestamp": "2025-10-19T08:54:36.048Z",
+ "file": "/home/theflow/projects/tractatus/public/js/demos/27027-demo.js",
+ "result": "passed",
+ "reason": null
+ },
+ {
+ "hook": "validate-file-edit",
+ "timestamp": "2025-10-19T08:54:47.491Z",
+ "file": "/home/theflow/projects/tractatus/public/js/demos/27027-demo.js",
+ "result": "passed",
+ "reason": null
+ },
+ {
+ "hook": "validate-file-edit",
+ "timestamp": "2025-10-19T08:54:56.274Z",
+ "file": "/home/theflow/projects/tractatus/public/js/demos/27027-demo.js",
+ "result": "passed",
+ "reason": null
}
],
"blocks": [
@@ -4889,9 +4938,9 @@
}
],
"session_stats": {
- "total_edit_hooks": 473,
+ "total_edit_hooks": 480,
"total_edit_blocks": 36,
- "last_updated": "2025-10-19T08:46:16.191Z",
+ "last_updated": "2025-10-19T08:54:56.274Z",
"total_write_hooks": 188,
"total_write_blocks": 7
}
diff --git a/public/demos/27027-demo.html b/public/demos/27027-demo.html
index fa0e4635..2d91b63f 100644
--- a/public/demos/27027-demo.html
+++ b/public/demos/27027-demo.html
@@ -69,6 +69,37 @@
+
+
+
Playback Speed:
+
+
+
+
+
+
+
+
+
+
Active Services:
+
+
+
+
InstructionPersistence
+
+
+
+
CrossReferenceValidator
+
+
+
+
diff --git a/public/js/demos/27027-demo.js b/public/js/demos/27027-demo.js
index 1cfa9257..f873e35e 100644
--- a/public/js/demos/27027-demo.js
+++ b/public/js/demos/27027-demo.js
@@ -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) => `
-
+
@@ -147,6 +153,18 @@ function initTimeline() {
`).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();