Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm
Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)
Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability
Migration completed: $(date)
🤖 Clean migration with full functionality preserved
202 lines
4.8 KiB
JavaScript
202 lines
4.8 KiB
JavaScript
// The Conscious Being - Main Controller
|
|
import { Renderer } from './Renderer.js';
|
|
import { ParticleSystem } from './ParticleSystem.js';
|
|
import { FlockingBehavior, BreathingBehavior, FollowBehavior } from './Behaviors.js';
|
|
|
|
export class Being {
|
|
constructor(canvas) {
|
|
this.canvas = canvas;
|
|
this.renderer = null;
|
|
this.particleSystem = null;
|
|
this.behaviors = [];
|
|
|
|
this.mousePosition = { x: 0.5, y: 0.5 }; // Normalized 0-1
|
|
this.target = null;
|
|
this.mode = 'idle'; // idle, aware, engaged, background
|
|
this.isRunning = false;
|
|
this.lastTime = 0;
|
|
this.initialized = false;
|
|
|
|
// Store bound handlers for cleanup
|
|
this._resizeHandler = this.handleResize.bind(this);
|
|
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
try {
|
|
// Initialize renderer with WebGL error handling
|
|
this.renderer = new Renderer(this.canvas);
|
|
|
|
// Initialize particle system
|
|
this.particleSystem = new ParticleSystem(800);
|
|
|
|
// Initialize behaviors
|
|
this.behaviors = [
|
|
new BreathingBehavior(),
|
|
new FlockingBehavior(),
|
|
new FollowBehavior()
|
|
];
|
|
|
|
// Add particle system to scene
|
|
this.renderer.add(this.particleSystem.mesh);
|
|
|
|
// Handle resize
|
|
window.addEventListener('resize', this._resizeHandler);
|
|
|
|
this.initialized = true;
|
|
} catch (error) {
|
|
console.error('[Being] WebGL initialization failed:', error);
|
|
this.showFallback();
|
|
}
|
|
}
|
|
|
|
showFallback() {
|
|
// Show CSS-based fallback animation when WebGL is unavailable
|
|
if (this.canvas) {
|
|
this.canvas.style.display = 'none';
|
|
const fallback = document.createElement('div');
|
|
fallback.className = 'conscious-fallback';
|
|
fallback.innerHTML = `
|
|
<div class="fallback-orb"></div>
|
|
<div class="fallback-ring"></div>
|
|
<div class="fallback-ring fallback-ring--delayed"></div>
|
|
`;
|
|
this.canvas.parentNode?.insertBefore(fallback, this.canvas);
|
|
}
|
|
}
|
|
|
|
start() {
|
|
if (this.isRunning || !this.initialized) return;
|
|
this.isRunning = true;
|
|
this.lastTime = performance.now();
|
|
this.animate();
|
|
console.log('[Being] Started');
|
|
}
|
|
|
|
stop() {
|
|
this.isRunning = false;
|
|
console.log('[Being] Stopped');
|
|
}
|
|
|
|
animate() {
|
|
if (!this.isRunning) return;
|
|
|
|
const currentTime = performance.now();
|
|
const deltaTime = (currentTime - this.lastTime) / 1000; // Convert to seconds
|
|
this.lastTime = currentTime;
|
|
|
|
this.update(deltaTime, currentTime);
|
|
this.renderer.render();
|
|
|
|
requestAnimationFrame(() => this.animate());
|
|
}
|
|
|
|
update(deltaTime, time) {
|
|
// Apply behaviors based on mode
|
|
const params = {
|
|
time,
|
|
deltaTime,
|
|
mousePosition: this.mousePosition,
|
|
target: this.target,
|
|
mode: this.mode
|
|
};
|
|
|
|
// Apply each behavior
|
|
this.behaviors.forEach(behavior => {
|
|
behavior.apply(this.particleSystem, params);
|
|
});
|
|
|
|
// Update particle system
|
|
this.particleSystem.update(deltaTime);
|
|
|
|
// Update uniforms
|
|
this.particleSystem.material.uniforms.uTime.value = time * 0.001;
|
|
this.particleSystem.material.uniforms.uMouse.value.set(
|
|
this.mousePosition.x,
|
|
this.mousePosition.y
|
|
);
|
|
}
|
|
|
|
updateMousePosition(x, y) {
|
|
// Normalize to 0-1
|
|
this.mousePosition = {
|
|
x: x / window.innerWidth,
|
|
y: 1 - (y / window.innerHeight) // Flip Y for WebGL
|
|
};
|
|
|
|
// Update mode based on mouse activity
|
|
if (this.mode === 'idle') {
|
|
this.setMode('aware');
|
|
}
|
|
}
|
|
|
|
setTarget(element) {
|
|
if (!element) {
|
|
this.target = null;
|
|
return;
|
|
}
|
|
|
|
const rect = element.getBoundingClientRect();
|
|
this.target = {
|
|
x: (rect.left + rect.width / 2) / window.innerWidth,
|
|
y: 1 - ((rect.top + rect.height / 2) / window.innerHeight),
|
|
width: rect.width / window.innerWidth,
|
|
height: rect.height / window.innerHeight
|
|
};
|
|
|
|
this.setMode('engaged');
|
|
}
|
|
|
|
clearTarget() {
|
|
this.target = null;
|
|
if (this.mode === 'engaged') {
|
|
this.setMode('aware');
|
|
}
|
|
}
|
|
|
|
setMode(mode) {
|
|
if (this.mode === mode) return;
|
|
|
|
const oldMode = this.mode;
|
|
this.mode = mode;
|
|
|
|
console.log(`[Being] Mode: ${oldMode} -> ${mode}`);
|
|
|
|
// Adjust behaviors based on mode
|
|
this.behaviors.forEach(behavior => {
|
|
behavior.onModeChange(mode, oldMode);
|
|
});
|
|
|
|
// Adjust particle appearance
|
|
if (mode === 'background') {
|
|
this.particleSystem.setOpacity(0.3);
|
|
} else {
|
|
this.particleSystem.setOpacity(1.0);
|
|
}
|
|
}
|
|
|
|
handleResize() {
|
|
this.renderer.handleResize();
|
|
}
|
|
|
|
dispose() {
|
|
this.stop();
|
|
|
|
// Remove event listeners
|
|
window.removeEventListener('resize', this._resizeHandler);
|
|
|
|
// Dispose of WebGL resources
|
|
if (this.renderer) {
|
|
this.renderer.dispose();
|
|
}
|
|
if (this.particleSystem) {
|
|
this.particleSystem.dispose();
|
|
}
|
|
|
|
this.initialized = false;
|
|
}
|
|
}
|
|
|
|
export default Being;
|