eaglelandsonce's picture
Update index.html
c7c9136 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CI/CD Trigger Simulation</title>
<style>
body { margin: 0; overflow: hidden; font-family: 'Segoe UI', sans-serif; background-color: #1a1a2e; color: white; }
#controls {
position: absolute;
top: 20px;
left: 20px;
background: rgba(255, 255, 255, 0.1);
padding: 20px;
border-radius: 8px;
backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
h1 { margin-top: 0; font-size: 1.2rem; color: #e94560; }
p { font-size: 0.9rem; color: #ccc; margin-bottom: 15px; }
button {
display: block;
width: 100%;
padding: 10px;
margin-bottom: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
transition: transform 0.1s;
}
button:active { transform: scale(0.98); }
.btn-commit { background-color: #4cc9f0; color: #000; }
.btn-schedule { background-color: #f72585; color: white; }
.btn-manual { background-color: #ffd60a; color: #000; }
/* Labels in 3D space */
.label {
position: absolute;
color: white;
font-size: 12px;
background: rgba(0,0,0,0.6);
padding: 4px 8px;
border-radius: 4px;
pointer-events: none;
}
</style>
</head>
<body>
<div id="controls">
<h1>CI/CD Trigger Control</h1>
<p>Activate a trigger to start the pipeline:</p>
<button class="btn-commit" onclick="triggerPipeline('commit')">simulate Code Commit &lt;/&gt;</button>
<button class="btn-schedule" onclick="triggerPipeline('schedule')">simulate Schedule (Timer)</button>
<button class="btn-manual" onclick="triggerPipeline('manual')">simulate Manual Approval</button>
</div>
<div id="canvas-container"></div>
<script type="module">
import * as THREE from 'https://unpkg.com/three@0.160.0/build/three.module.js';
// --- SCENE SETUP ---
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e); // Dark Navy
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 10, 20);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('canvas-container').appendChild(renderer.domElement);
// Lights
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 10, 7);
scene.add(dirLight);
// --- MATERIALS & GEOMETRY ---
const matTrigger = new THREE.MeshStandardMaterial({ color: 0x4cc9f0 }); // Blue
const matSchedule = new THREE.MeshStandardMaterial({ color: 0xf72585 }); // Pink
const matManual = new THREE.MeshStandardMaterial({ color: 0xffd60a }); // Yellow
const matPipeline = new THREE.MeshStandardMaterial({ color: 0xb5179e }); // Purple/Red
const matOutput = new THREE.MeshStandardMaterial({ color: 0x2ec4b6 }); // Teal
// --- OBJECTS ---
// 1. Inputs (Left Side)
const commitNode = createBox(-6, 0, -3, matTrigger, "Commit");
const scheduleNode = createBox(-6, 0, 0, matSchedule, "Schedule");
const manualNode = createBox(-6, 0, 3, matManual, "Manual");
// 2. Central Pipeline (Center)
const pipelineGeo = new THREE.CylinderGeometry(1.5, 1.5, 6, 32);
pipelineGeo.rotateZ(Math.PI / 2); // Lay flat
const pipeline = new THREE.Mesh(pipelineGeo, matPipeline);
scene.add(pipeline);
// 3. Outputs (Right Side)
const buildNode = createBox(6, 0, -2, matOutput, "Build & Test");
const deployNode = createBox(6, 0, 2, matOutput, "Deploy");
// 4. Connectors (Lines)
drawPath(commitNode.position, new THREE.Vector3(-3, 0, 0));
drawPath(scheduleNode.position, new THREE.Vector3(-3, 0, 0));
drawPath(manualNode.position, new THREE.Vector3(-3, 0, 0));
drawPath(new THREE.Vector3(3, 0, 0), buildNode.position);
drawPath(new THREE.Vector3(3, 0, 0), deployNode.position);
// --- DATA PARTICLES ---
const particles = [];
function createBox(x, y, z, material, name) {
const geo = new THREE.BoxGeometry(1.5, 1.5, 1.5);
const mesh = new THREE.Mesh(geo, material);
mesh.position.set(x, y, z);
scene.add(mesh);
// Add simple floating text label (using HTML overlay technique logic for simplicity in positioning)
addLabel(name, x, y + 1.2, z);
return mesh;
}
function drawPath(start, end) {
const points = [start, end];
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const material = new THREE.LineBasicMaterial({ color: 0xffffff, opacity: 0.3, transparent: true });
const line = new THREE.Line(geometry, material);
scene.add(line);
}
function addLabel(text, x, y, z) {
const div = document.createElement('div');
div.className = 'label';
div.textContent = text;
document.body.appendChild(div);
// We attach position data to the div to update it in the render loop
div.dataset.x = x;
div.dataset.y = y;
div.dataset.z = z;
}
// --- ANIMATION LOGIC ---
window.triggerPipeline = (type) => {
let startPos, color, destination;
if (type === 'commit') {
startPos = commitNode.position.clone();
color = 0x4cc9f0;
destination = buildNode.position.clone();
} else if (type === 'schedule') {
startPos = scheduleNode.position.clone();
color = 0xf72585;
destination = buildNode.position.clone();
} else if (type === 'manual') {
startPos = manualNode.position.clone();
color = 0xffd60a;
destination = deployNode.position.clone();
}
// Create a packet
const geo = new THREE.SphereGeometry(0.4, 16, 16);
const mat = new THREE.MeshBasicMaterial({ color: color });
const packet = new THREE.Mesh(geo, mat);
packet.position.copy(startPos);
scene.add(packet);
// Add to animation array
particles.push({
mesh: packet,
progress: 0,
path: [
startPos,
new THREE.Vector3(-3, 0, 0), // Entry to pipe
new THREE.Vector3(3, 0, 0), // Exit pipe
destination
]
});
};
function updateParticles() {
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.progress += 0.015; // Speed
if (p.progress >= 3) {
// Reached end
scene.remove(p.mesh);
particles.splice(i, 1);
// Add a visual "pulse" to the destination (simple scale effect)
// (Omitted for brevity, but this is where you'd trigger a success effect)
continue;
}
// Determine which segment of the path we are on
let segmentIndex = Math.floor(p.progress);
let segmentProgress = p.progress - segmentIndex;
if (segmentIndex < 3) {
const start = p.path[segmentIndex];
const end = p.path[segmentIndex + 1];
p.mesh.position.lerpVectors(start, end, segmentProgress);
}
}
}
function updateLabels() {
const labels = document.querySelectorAll('.label');
labels.forEach(label => {
const pos = new THREE.Vector3(
parseFloat(label.dataset.x),
parseFloat(label.dataset.y),
parseFloat(label.dataset.z)
);
pos.project(camera);
const x = (pos.x * .5 + .5) * window.innerWidth;
const y = (pos.y * -.5 + .5) * window.innerHeight;
label.style.transform = `translate(-50%, -50%) translate(${x}px, ${y}px)`;
});
}
function animate() {
requestAnimationFrame(animate);
// Rotate pipeline slowly for visual effect
pipeline.rotation.x += 0.01;
updateParticles();
updateLabels();
renderer.render(scene, camera);
}
// Handle Resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
animate();
</script>
</body>
</html>