|
|
import { config } from './config.js'; |
|
|
import * as THREE from 'three'; |
|
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; |
|
|
|
|
|
|
|
|
const state = { |
|
|
currentMode: 'sonw', |
|
|
evolutionInProgress: false, |
|
|
recursionDepth: 3, |
|
|
particles: [], |
|
|
metrics: { |
|
|
symbolic: 20, |
|
|
quantum: 15, |
|
|
holographic: 10, |
|
|
meta: 5 |
|
|
}, |
|
|
|
|
|
multiplayer: { |
|
|
connected: false, |
|
|
username: '', |
|
|
userId: '', |
|
|
userColor: '', |
|
|
researchers: {}, |
|
|
sharedAnalyses: [] |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
const room = new WebsimSocket(); |
|
|
|
|
|
|
|
|
const visualizationEl = document.getElementById('visualization'); |
|
|
const conceptInput = document.getElementById('conceptInput'); |
|
|
const symbolSelector = document.getElementById('symbolSelector'); |
|
|
const outputEl = document.getElementById('output'); |
|
|
const statusEl = document.getElementById('statusDisplay'); |
|
|
const depthSlider = document.getElementById('recursionDepth'); |
|
|
const depthValueEl = document.getElementById('depthValue'); |
|
|
const systemButtons = ['sonwBtn', 'afterthoughtBtn', 'cognitiveBtn', 'integratedBtn'].map(id => document.getElementById(id)); |
|
|
const userCountEl = document.getElementById('user-count'); |
|
|
const researchersEl = document.getElementById('researchers'); |
|
|
const shareBtn = document.getElementById('shareBtn'); |
|
|
const syncBtn = document.getElementById('syncBtn'); |
|
|
const sharedAnalysesListEl = document.getElementById('shared-analyses-list'); |
|
|
const userCursorsEl = document.getElementById('user-cursors'); |
|
|
const notificationEl = document.getElementById('notification'); |
|
|
|
|
|
|
|
|
let scene, camera, renderer, controls; |
|
|
let particles, connections; |
|
|
|
|
|
const initVisualization = () => { |
|
|
|
|
|
scene = new THREE.Scene(); |
|
|
scene.background = new THREE.Color(config.visualization.backgroundColor); |
|
|
|
|
|
|
|
|
camera = new THREE.PerspectiveCamera(75, visualizationEl.clientWidth / visualizationEl.clientHeight, 0.1, 2000); |
|
|
camera.position.z = 300; |
|
|
|
|
|
|
|
|
renderer = new THREE.WebGLRenderer({ antialias: true }); |
|
|
renderer.setSize(visualizationEl.clientWidth, visualizationEl.clientHeight); |
|
|
visualizationEl.appendChild(renderer.domElement); |
|
|
|
|
|
|
|
|
controls = new OrbitControls(camera, renderer.domElement); |
|
|
controls.enableDamping = true; |
|
|
controls.dampingFactor = 0.05; |
|
|
|
|
|
|
|
|
particles = new THREE.Group(); |
|
|
scene.add(particles); |
|
|
|
|
|
|
|
|
connections = new THREE.Group(); |
|
|
scene.add(connections); |
|
|
|
|
|
|
|
|
const gridHelper = new THREE.GridHelper(400, 20, 0x404040, 0x404040); |
|
|
gridHelper.position.y = -100; |
|
|
scene.add(gridHelper); |
|
|
|
|
|
|
|
|
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); |
|
|
scene.add(ambientLight); |
|
|
|
|
|
|
|
|
const pointLight = new THREE.PointLight(0xffffff, 1); |
|
|
pointLight.position.set(0, 150, 200); |
|
|
scene.add(pointLight); |
|
|
|
|
|
|
|
|
createParticleSystem(state.currentMode); |
|
|
|
|
|
|
|
|
animate(); |
|
|
|
|
|
|
|
|
window.addEventListener('resize', onWindowResize); |
|
|
}; |
|
|
|
|
|
const onWindowResize = () => { |
|
|
camera.aspect = visualizationEl.clientWidth / visualizationEl.clientHeight; |
|
|
camera.updateProjectionMatrix(); |
|
|
renderer.setSize(visualizationEl.clientWidth, visualizationEl.clientHeight); |
|
|
}; |
|
|
|
|
|
const createFractalNetworkPattern = (geometry, material, modeConfig) => { |
|
|
|
|
|
for (let i = 0; i < config.visualization.particleCount; i++) { |
|
|
const particle = new THREE.Mesh(geometry, material); |
|
|
|
|
|
|
|
|
const fractalDepth = Math.floor(Math.random() * 3) + 1; |
|
|
const angleXY = Math.random() * Math.PI * 2; |
|
|
const radiusXY = 20 + Math.random() * modeConfig.particleSpread * 0.5; |
|
|
|
|
|
|
|
|
const fractalScale = 1 / (fractalDepth * 0.5); |
|
|
particle.position.x = Math.cos(angleXY) * radiusXY * fractalScale; |
|
|
particle.position.y = (fractalDepth * 40) - 60; |
|
|
particle.position.z = Math.sin(angleXY) * radiusXY * fractalScale; |
|
|
|
|
|
|
|
|
particle.position.x += (Math.random() - 0.5) * 40 * fractalScale; |
|
|
particle.position.z += (Math.random() - 0.5) * 40 * fractalScale; |
|
|
|
|
|
|
|
|
particle.userData = { |
|
|
velocity: new THREE.Vector3( |
|
|
(Math.random() - 0.5) * 0.2 * fractalScale, |
|
|
(Math.random() - 0.5) * 0.1 * fractalScale, |
|
|
(Math.random() - 0.5) * 0.2 * fractalScale |
|
|
), |
|
|
originalPosition: particle.position.clone(), |
|
|
fractalDepth: fractalDepth |
|
|
}; |
|
|
|
|
|
particles.add(particle); |
|
|
state.particles.push(particle); |
|
|
} |
|
|
}; |
|
|
|
|
|
const createQuantumFieldPattern = (geometry, material, modeConfig) => { |
|
|
|
|
|
for (let i = 0; i < config.visualization.particleCount; i++) { |
|
|
const particle = new THREE.Mesh(geometry, material); |
|
|
|
|
|
|
|
|
const phi = Math.acos(-1 + 2 * Math.random()); |
|
|
const theta = 2 * Math.PI * Math.random(); |
|
|
const radius = 50 + Math.random() * modeConfig.particleSpread * 0.6; |
|
|
|
|
|
particle.position.x = radius * Math.sin(phi) * Math.cos(theta); |
|
|
particle.position.y = radius * Math.sin(phi) * Math.sin(theta); |
|
|
particle.position.z = radius * Math.cos(phi); |
|
|
|
|
|
|
|
|
const superposition = Math.random() > 0.5; |
|
|
const entanglementFactor = Math.random(); |
|
|
const orbitalAxis = new THREE.Vector3( |
|
|
Math.random() - 0.5, |
|
|
Math.random() - 0.5, |
|
|
Math.random() - 0.5 |
|
|
).normalize(); |
|
|
|
|
|
particle.userData = { |
|
|
velocity: new THREE.Vector3( |
|
|
(Math.random() - 0.5) * 0.1, |
|
|
(Math.random() - 0.5) * 0.1, |
|
|
(Math.random() - 0.5) * 0.1 |
|
|
), |
|
|
originalPosition: particle.position.clone(), |
|
|
superposition: superposition, |
|
|
entanglementFactor: entanglementFactor, |
|
|
orbitalAxis: orbitalAxis, |
|
|
orbitalSpeed: (Math.random() * 0.01) + 0.005 |
|
|
}; |
|
|
|
|
|
particles.add(particle); |
|
|
state.particles.push(particle); |
|
|
} |
|
|
}; |
|
|
|
|
|
const createFractalPattern = (geometry, material, modeConfig) => { |
|
|
|
|
|
|
|
|
|
|
|
const createFractalPoint = (centerPoint, scale, depth) => { |
|
|
if (depth <= 0 || state.particles.length >= config.visualization.particleCount) return; |
|
|
|
|
|
|
|
|
const particle = new THREE.Mesh(geometry, material); |
|
|
particle.position.copy(centerPoint); |
|
|
|
|
|
|
|
|
particle.position.x += (Math.random() - 0.5) * scale * 0.2; |
|
|
particle.position.y += (Math.random() - 0.5) * scale * 0.2; |
|
|
particle.position.z += (Math.random() - 0.5) * scale * 0.2; |
|
|
|
|
|
particle.userData = { |
|
|
velocity: new THREE.Vector3( |
|
|
(Math.random() - 0.5) * 0.1, |
|
|
(Math.random() - 0.5) * 0.1, |
|
|
(Math.random() - 0.5) * 0.1 |
|
|
), |
|
|
originalPosition: particle.position.clone(), |
|
|
fractalDepth: depth |
|
|
}; |
|
|
|
|
|
particles.add(particle); |
|
|
state.particles.push(particle); |
|
|
|
|
|
|
|
|
const branchCount = 6; |
|
|
const newScale = scale * 0.6; |
|
|
|
|
|
for (let i = 0; i < branchCount; i++) { |
|
|
|
|
|
const angle1 = (i / branchCount) * Math.PI * 2; |
|
|
const angle2 = Math.PI / 4; |
|
|
|
|
|
const newX = centerPoint.x + Math.sin(angle1) * Math.cos(angle2) * scale; |
|
|
const newY = centerPoint.y + Math.sin(angle1) * Math.sin(angle2) * scale; |
|
|
const newZ = centerPoint.z + Math.cos(angle1) * scale; |
|
|
|
|
|
const newPoint = new THREE.Vector3(newX, newY, newZ); |
|
|
|
|
|
|
|
|
createFractalPoint(newPoint, newScale, depth - 1); |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
createFractalPoint(new THREE.Vector3(0, 0, 0), modeConfig.particleSpread * 0.3, 3); |
|
|
}; |
|
|
|
|
|
const createHolisticPattern = (geometry, material, modeConfig) => { |
|
|
|
|
|
|
|
|
|
|
|
const llmlCount = Math.floor(config.visualization.particleCount * 0.3); |
|
|
for (let i = 0; i < llmlCount; i++) { |
|
|
const particle = new THREE.Mesh(geometry, material); |
|
|
|
|
|
|
|
|
const layerIndex = Math.floor(Math.random() * 3); |
|
|
const angleXY = Math.random() * Math.PI * 2; |
|
|
const radiusXY = 30 + Math.random() * 60; |
|
|
|
|
|
particle.position.x = Math.cos(angleXY) * radiusXY; |
|
|
particle.position.y = (layerIndex * 40) - 40; |
|
|
particle.position.z = Math.sin(angleXY) * radiusXY; |
|
|
|
|
|
particle.userData = { |
|
|
velocity: new THREE.Vector3( |
|
|
(Math.random() - 0.5) * 0.15, |
|
|
(Math.random() - 0.5) * 0.05, |
|
|
(Math.random() - 0.5) * 0.15 |
|
|
), |
|
|
originalPosition: particle.position.clone(), |
|
|
system: 'llml' |
|
|
}; |
|
|
|
|
|
particles.add(particle); |
|
|
state.particles.push(particle); |
|
|
} |
|
|
|
|
|
|
|
|
const qgaCount = Math.floor(config.visualization.particleCount * 0.3); |
|
|
for (let i = 0; i < qgaCount; i++) { |
|
|
const particle = new THREE.Mesh(geometry, material); |
|
|
|
|
|
|
|
|
const phi = Math.acos(-1 + 2 * Math.random()); |
|
|
const theta = 2 * Math.PI * Math.random(); |
|
|
const radius = 80 + Math.random() * 70; |
|
|
|
|
|
particle.position.x = radius * Math.sin(phi) * Math.cos(theta); |
|
|
particle.position.y = radius * Math.sin(phi) * Math.sin(theta); |
|
|
particle.position.z = radius * Math.cos(phi); |
|
|
|
|
|
const orbitalAxis = new THREE.Vector3( |
|
|
Math.random() - 0.5, |
|
|
Math.random() - 0.5, |
|
|
Math.random() - 0.5 |
|
|
).normalize(); |
|
|
|
|
|
particle.userData = { |
|
|
velocity: new THREE.Vector3( |
|
|
(Math.random() - 0.5) * 0.1, |
|
|
(Math.random() - 0.5) * 0.1, |
|
|
(Math.random() - 0.5) * 0.1 |
|
|
), |
|
|
originalPosition: particle.position.clone(), |
|
|
orbitalAxis: orbitalAxis, |
|
|
orbitalSpeed: (Math.random() * 0.01) + 0.005, |
|
|
system: 'qga' |
|
|
}; |
|
|
|
|
|
particles.add(particle); |
|
|
state.particles.push(particle); |
|
|
} |
|
|
|
|
|
|
|
|
const holoCount = config.visualization.particleCount - llmlCount - qgaCount; |
|
|
|
|
|
|
|
|
for (let i = 0; i < 5; i++) { |
|
|
const centerPoint = new THREE.Vector3( |
|
|
(Math.random() - 0.5) * 150, |
|
|
(Math.random() - 0.5) * 150, |
|
|
(Math.random() - 0.5) * 150 |
|
|
); |
|
|
|
|
|
|
|
|
for (let j = 0; j < holoCount / 5; j++) { |
|
|
const particle = new THREE.Mesh(geometry, material); |
|
|
|
|
|
|
|
|
const fractalRadius = 10 + Math.random() * 25; |
|
|
const fractalPhi = Math.acos(-1 + 2 * Math.random()); |
|
|
const fractalTheta = 2 * Math.PI * Math.random(); |
|
|
|
|
|
particle.position.x = centerPoint.x + fractalRadius * Math.sin(fractalPhi) * Math.cos(fractalTheta); |
|
|
particle.position.y = centerPoint.y + fractalRadius * Math.sin(fractalPhi) * Math.sin(fractalTheta); |
|
|
particle.position.z = centerPoint.z + fractalRadius * Math.cos(fractalPhi); |
|
|
|
|
|
particle.userData = { |
|
|
velocity: new THREE.Vector3( |
|
|
(Math.random() - 0.5) * 0.1, |
|
|
(Math.random() - 0.5) * 0.1, |
|
|
(Math.random() - 0.5) * 0.1 |
|
|
), |
|
|
originalPosition: particle.position.clone(), |
|
|
fractalCenter: centerPoint.clone(), |
|
|
system: 'holographic' |
|
|
}; |
|
|
|
|
|
particles.add(particle); |
|
|
state.particles.push(particle); |
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
const createConnections = (modeConfig) => { |
|
|
|
|
|
const linesMaterial = new THREE.LineBasicMaterial({ |
|
|
color: config.visualization.connectionColor, |
|
|
transparent: true, |
|
|
opacity: 0.3 |
|
|
}); |
|
|
|
|
|
const particlePositions = state.particles.map(p => p.position); |
|
|
const threshold = config.visualization.connectionThreshold * modeConfig.connectionDensity; |
|
|
|
|
|
for (let i = 0; i < particlePositions.length; i++) { |
|
|
for (let j = i + 1; j < particlePositions.length; j++) { |
|
|
const distance = particlePositions[i].distanceTo(particlePositions[j]); |
|
|
|
|
|
if (distance < threshold) { |
|
|
|
|
|
const lineGeometry = new THREE.BufferGeometry().setFromPoints([ |
|
|
particlePositions[i], |
|
|
particlePositions[j] |
|
|
]); |
|
|
|
|
|
const line = new THREE.Line(lineGeometry, linesMaterial); |
|
|
connections.add(line); |
|
|
|
|
|
|
|
|
line.userData = { |
|
|
particleIndexA: i, |
|
|
particleIndexB: j |
|
|
}; |
|
|
} |
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
const updateConnections = () => { |
|
|
|
|
|
connections.children.forEach(line => { |
|
|
const { particleIndexA, particleIndexB } = line.userData; |
|
|
|
|
|
if (particleIndexA < state.particles.length && particleIndexB < state.particles.length) { |
|
|
const posA = state.particles[particleIndexA].position; |
|
|
const posB = state.particles[particleIndexB].position; |
|
|
|
|
|
const positions = line.geometry.attributes.position.array; |
|
|
|
|
|
|
|
|
positions[0] = posA.x; |
|
|
positions[1] = posA.y; |
|
|
positions[2] = posA.z; |
|
|
positions[3] = posB.x; |
|
|
positions[4] = posB.y; |
|
|
positions[5] = posB.z; |
|
|
|
|
|
line.geometry.attributes.position.needsUpdate = true; |
|
|
} |
|
|
}); |
|
|
}; |
|
|
|
|
|
const animate = () => { |
|
|
requestAnimationFrame(animate); |
|
|
|
|
|
|
|
|
animateParticles(); |
|
|
|
|
|
|
|
|
updateConnections(); |
|
|
|
|
|
|
|
|
controls.update(); |
|
|
|
|
|
|
|
|
renderer.render(scene, camera); |
|
|
}; |
|
|
|
|
|
const animateParticles = () => { |
|
|
|
|
|
const mode = state.currentMode; |
|
|
|
|
|
switch(mode) { |
|
|
case 'sonw': |
|
|
|
|
|
animateSONWParticles(); |
|
|
break; |
|
|
|
|
|
case 'afterthought': |
|
|
|
|
|
animateQGAParticles(); |
|
|
break; |
|
|
|
|
|
case 'cognitive': |
|
|
|
|
|
animateHolographicParticles(); |
|
|
break; |
|
|
|
|
|
case 'integrated': |
|
|
|
|
|
animateIntegratedParticles(); |
|
|
break; |
|
|
} |
|
|
}; |
|
|
|
|
|
const animateSONWParticles = () => { |
|
|
state.particles.forEach(particle => { |
|
|
|
|
|
const origPos = particle.userData.originalPosition; |
|
|
const velocity = particle.userData.velocity; |
|
|
|
|
|
|
|
|
particle.position.x += velocity.x; |
|
|
particle.position.y += velocity.y; |
|
|
particle.position.z += velocity.z; |
|
|
|
|
|
|
|
|
const diffX = origPos.x - particle.position.x; |
|
|
const diffY = origPos.y - particle.position.y; |
|
|
const diffZ = origPos.z - particle.position.z; |
|
|
|
|
|
velocity.x += diffX * 0.01; |
|
|
velocity.y += diffY * 0.01; |
|
|
velocity.z += diffZ * 0.01; |
|
|
|
|
|
|
|
|
velocity.x *= 0.98; |
|
|
velocity.y *= 0.98; |
|
|
velocity.z *= 0.98; |
|
|
}); |
|
|
}; |
|
|
|
|
|
const animateQGAParticles = () => { |
|
|
state.particles.forEach(particle => { |
|
|
|
|
|
const velocity = particle.userData.velocity; |
|
|
const orbitalAxis = particle.userData.orbitalAxis; |
|
|
const orbitalSpeed = particle.userData.orbitalSpeed; |
|
|
|
|
|
|
|
|
const rotationMatrix = new THREE.Matrix4().makeRotationAxis(orbitalAxis, orbitalSpeed); |
|
|
particle.position.applyMatrix4(rotationMatrix); |
|
|
|
|
|
|
|
|
velocity.x += (Math.random() - 0.5) * 0.02; |
|
|
velocity.y += (Math.random() - 0.5) * 0.02; |
|
|
velocity.z += (Math.random() - 0.5) * 0.02; |
|
|
|
|
|
|
|
|
particle.position.x += velocity.x; |
|
|
particle.position.y += velocity.y; |
|
|
particle.position.z += velocity.z; |
|
|
|
|
|
|
|
|
velocity.x *= 0.8; |
|
|
velocity.y *= 0.8; |
|
|
velocity.z *= 0.8; |
|
|
}); |
|
|
}; |
|
|
|
|
|
const animateHolographicParticles = () => { |
|
|
state.particles.forEach(particle => { |
|
|
|
|
|
const velocity = particle.userData.velocity; |
|
|
const fractalDepth = particle.userData.fractalDepth || 1; |
|
|
|
|
|
|
|
|
const depthFactor = 1 / (fractalDepth * 0.5); |
|
|
|
|
|
|
|
|
const time = Date.now() * 0.001; |
|
|
const pulseFactor = Math.sin(time * 0.5) * 0.2 * depthFactor; |
|
|
|
|
|
|
|
|
particle.position.x += velocity.x + (particle.position.x * pulseFactor); |
|
|
particle.position.y += velocity.y + (particle.position.y * pulseFactor); |
|
|
particle.position.z += velocity.z + (particle.position.z * pulseFactor); |
|
|
|
|
|
|
|
|
const centeringForce = 0.001 * depthFactor; |
|
|
velocity.x -= particle.position.x * centeringForce; |
|
|
velocity.y -= particle.position.y * centeringForce; |
|
|
velocity.z -= particle.position.z * centeringForce; |
|
|
|
|
|
|
|
|
velocity.x *= 0.95; |
|
|
velocity.y *= 0.95; |
|
|
velocity.z *= 0.95; |
|
|
}); |
|
|
}; |
|
|
|
|
|
const animateIntegratedParticles = () => { |
|
|
state.particles.forEach(particle => { |
|
|
|
|
|
const system = particle.userData.system; |
|
|
|
|
|
if (system === 'llml') { |
|
|
|
|
|
const origPos = particle.userData.originalPosition; |
|
|
const velocity = particle.userData.velocity; |
|
|
|
|
|
particle.position.x += velocity.x; |
|
|
particle.position.y += velocity.y; |
|
|
particle.position.z += velocity.z; |
|
|
|
|
|
const diffX = origPos.x - particle.position.x; |
|
|
const diffY = origPos.y - particle.position.y; |
|
|
const diffZ = origPos.z - particle.position.z; |
|
|
|
|
|
velocity.x += diffX * 0.01; |
|
|
velocity.y += diffY * 0.01; |
|
|
velocity.z += diffZ * 0.01; |
|
|
|
|
|
velocity.x *= 0.98; |
|
|
velocity.y *= 0.98; |
|
|
velocity.z *= 0.98; |
|
|
|
|
|
} else if (system === 'qga') { |
|
|
|
|
|
const velocity = particle.userData.velocity; |
|
|
const orbitalAxis = particle.userData.orbitalAxis; |
|
|
const orbitalSpeed = particle.userData.orbitalSpeed; |
|
|
|
|
|
const rotationMatrix = new THREE.Matrix4().makeRotationAxis(orbitalAxis, orbitalSpeed); |
|
|
particle.position.applyMatrix4(rotationMatrix); |
|
|
|
|
|
velocity.x += (Math.random() - 0.5) * 0.02; |
|
|
velocity.y += (Math.random() - 0.5) * 0.02; |
|
|
velocity.z += (Math.random() - 0.5) * 0.02; |
|
|
|
|
|
particle.position.x += velocity.x; |
|
|
particle.position.y += velocity.y; |
|
|
particle.position.z += velocity.z; |
|
|
|
|
|
velocity.x *= 0.8; |
|
|
velocity.y *= 0.8; |
|
|
velocity.z *= 0.8; |
|
|
|
|
|
} else if (system === 'holographic') { |
|
|
|
|
|
const velocity = particle.userData.velocity; |
|
|
const fractalCenter = particle.userData.fractalCenter; |
|
|
|
|
|
|
|
|
const time = Date.now() * 0.001; |
|
|
const pulseFactor = Math.sin(time * 0.5) * 0.2; |
|
|
|
|
|
|
|
|
const dirX = particle.position.x - fractalCenter.x; |
|
|
const dirY = particle.position.y - fractalCenter.y; |
|
|
const dirZ = particle.position.z - fractalCenter.z; |
|
|
|
|
|
|
|
|
particle.position.x += velocity.x + (dirX * pulseFactor * 0.01); |
|
|
particle.position.y += velocity.y + (dirY * pulseFactor * 0.01); |
|
|
particle.position.z += velocity.z + (dirZ * pulseFactor * 0.01); |
|
|
|
|
|
|
|
|
const centeringForce = 0.001; |
|
|
velocity.x -= dirX * centeringForce; |
|
|
velocity.y -= dirY * centeringForce; |
|
|
velocity.z -= dirZ * centeringForce; |
|
|
|
|
|
velocity.x *= 0.95; |
|
|
velocity.y *= 0.95; |
|
|
velocity.z *= 0.95; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
particles.rotation.y += 0.0005; |
|
|
connections.rotation.y += 0.0005; |
|
|
}; |
|
|
|
|
|
const createParticleSystem = (mode) => { |
|
|
|
|
|
while(particles.children.length > 0) { |
|
|
particles.remove(particles.children[0]); |
|
|
} |
|
|
|
|
|
while(connections.children.length > 0) { |
|
|
connections.remove(connections.children[0]); |
|
|
} |
|
|
|
|
|
|
|
|
const modeConfig = config.modes[mode]; |
|
|
|
|
|
|
|
|
const particleGeometry = new THREE.SphereGeometry(config.visualization.nodeSize, 16, 16); |
|
|
const particleMaterial = new THREE.MeshPhongMaterial({ |
|
|
color: new THREE.Color(modeConfig.color), |
|
|
emissive: new THREE.Color(modeConfig.color).multiplyScalar(0.5), |
|
|
shininess: 50 |
|
|
}); |
|
|
|
|
|
|
|
|
state.particles = []; |
|
|
|
|
|
switch(modeConfig.patternType) { |
|
|
case 'fractal-network': |
|
|
|
|
|
createFractalNetworkPattern(particleGeometry, particleMaterial, modeConfig); |
|
|
break; |
|
|
|
|
|
case 'quantum-field': |
|
|
|
|
|
createQuantumFieldPattern(particleGeometry, particleMaterial, modeConfig); |
|
|
break; |
|
|
|
|
|
case 'holographic': |
|
|
|
|
|
createFractalPattern(particleGeometry, particleMaterial, modeConfig); |
|
|
break; |
|
|
|
|
|
case 'holistic': |
|
|
|
|
|
createHolisticPattern(particleGeometry, particleMaterial, modeConfig); |
|
|
break; |
|
|
} |
|
|
|
|
|
|
|
|
createConnections(modeConfig); |
|
|
}; |
|
|
|
|
|
const handleSymbolicProcess = async () => { |
|
|
if (state.evolutionInProgress) return; |
|
|
|
|
|
|
|
|
let symbolicExpression = conceptInput.value.trim(); |
|
|
|
|
|
if (!symbolicExpression) { |
|
|
|
|
|
const selectedKey = symbolSelector.value; |
|
|
symbolicExpression = config.symbolicExpressions[selectedKey].expression; |
|
|
} |
|
|
|
|
|
|
|
|
updateStatus(`Processing symbolic expression: ${symbolicExpression}`); |
|
|
outputEl.innerHTML = `<p>Analyzing ${symbolicExpression}...</p>`; |
|
|
visualizationEl.classList.add('recursing'); |
|
|
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 1500)); |
|
|
|
|
|
|
|
|
await generateSymbolicAnalysis(symbolicExpression); |
|
|
|
|
|
|
|
|
visualizationEl.classList.remove('recursing'); |
|
|
}; |
|
|
|
|
|
const generateSymbolicAnalysis = async (expression) => { |
|
|
|
|
|
let analysisOutput = ''; |
|
|
|
|
|
try { |
|
|
|
|
|
const systemPrompt = `You are the Meta-Cosmic-Weaver, an advanced AI that integrates SONW (Symbolicentric Orbital Neural Weave), |
|
|
Afterthought Quantum Conceptualization, and Cognitive Engine components into a Triadic Architecture for recursive intelligence. |
|
|
|
|
|
Analyze the symbolic LLML expression provided and generate 3 insights corresponding to these three levels: |
|
|
|
|
|
Insight 1 (SONW): Analyze the expression as a fractal-symbolic framework using algebraic reasoning structures and neural-symbolic integration. |
|
|
|
|
|
Insight 2 (Afterthought): Provide a quantum-inspired interpretation that explores superposition, entanglement, and probabilistic consciousness. |
|
|
|
|
|
Insight 3 (Cognitive): Offer a meta-mathematical interpretation that treats the expression as an executable semantic transformation protocol. |
|
|
|
|
|
Use terminology from: quantum mechanics, geometric algebra, fractal mathematics, neural-symbolic integration, and recursive symbolic systems. |
|
|
Each insight should build upon the previous one with increasing sophistication.`; |
|
|
|
|
|
const messages = [ |
|
|
{ |
|
|
role: "system", |
|
|
content: systemPrompt |
|
|
}, |
|
|
{ |
|
|
role: "user", |
|
|
content: `Generate three progressive insights for the LLML symbolic expression: ${expression}` |
|
|
} |
|
|
]; |
|
|
|
|
|
|
|
|
const completion = await websim.chat.completions.create({ |
|
|
messages: messages |
|
|
}); |
|
|
|
|
|
analysisOutput = completion.content; |
|
|
} catch (error) { |
|
|
|
|
|
console.log("Using template-based generation instead of LLM"); |
|
|
analysisOutput = generateTemplateBasedAnalysis(expression); |
|
|
} |
|
|
|
|
|
|
|
|
outputEl.innerHTML = ''; |
|
|
|
|
|
|
|
|
const paragraphs = analysisOutput.split('\n\n'); |
|
|
let insightCounter = 1; |
|
|
let currentInsight = null; |
|
|
|
|
|
for (let i = 0; i < paragraphs.length; i++) { |
|
|
if (paragraphs[i].trim()) { |
|
|
|
|
|
if (paragraphs[i].toLowerCase().includes('insight') && paragraphs[i].includes(':')) { |
|
|
|
|
|
currentInsight = document.createElement('div'); |
|
|
currentInsight.className = 'insight-level'; |
|
|
|
|
|
const header = document.createElement('h4'); |
|
|
header.textContent = `Insight ${insightCounter}: ${paragraphs[i].split(':')[1].trim()}`; |
|
|
currentInsight.appendChild(header); |
|
|
|
|
|
outputEl.appendChild(currentInsight); |
|
|
insightCounter++; |
|
|
} else { |
|
|
|
|
|
const para = document.createElement('p'); |
|
|
para.textContent = paragraphs[i]; |
|
|
para.style.opacity = 0; |
|
|
para.style.transform = 'translateY(10px)'; |
|
|
para.style.transition = 'opacity 0.5s, transform 0.5s'; |
|
|
|
|
|
if (currentInsight) { |
|
|
currentInsight.appendChild(para); |
|
|
} else { |
|
|
outputEl.appendChild(para); |
|
|
} |
|
|
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300)); |
|
|
para.style.opacity = 1; |
|
|
para.style.transform = 'translateY(0)'; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
updateStatus(`Analysis of "${expression}" complete using ${config.modes[state.currentMode].name}`); |
|
|
}; |
|
|
|
|
|
const generateTemplateBasedAnalysis = (expression) => { |
|
|
|
|
|
let output = ''; |
|
|
const templates = config.llmlAnalysis[state.currentMode]; |
|
|
|
|
|
|
|
|
for (let i = 0; i < 3; i++) { |
|
|
|
|
|
const templateIndex = i % templates.length; |
|
|
const template = templates[templateIndex]; |
|
|
|
|
|
|
|
|
output += `Insight ${i+1}: ${state.currentMode.charAt(0).toUpperCase() + state.currentMode.slice(1)} Analysis\n\n`; |
|
|
|
|
|
|
|
|
let insight = template.replace('{expression}', expression); |
|
|
|
|
|
|
|
|
const processes = config.recursion.processes; |
|
|
const process = processes[Math.floor(Math.random() * processes.length)]; |
|
|
insight = insight.replace('{process}', process); |
|
|
|
|
|
|
|
|
const insights = config.recursion.insights; |
|
|
const insightText = insights[Math.floor(Math.random() * insights.length)]; |
|
|
insight = insight.replace('{insight}', insightText); |
|
|
|
|
|
|
|
|
const number = Math.floor(Math.random() * 8) + 3; |
|
|
insight = insight.replace('{number}', number); |
|
|
|
|
|
output += insight + '\n\n'; |
|
|
} |
|
|
|
|
|
return output; |
|
|
}; |
|
|
|
|
|
const updateStatus = (message) => { |
|
|
statusEl.textContent = message; |
|
|
statusEl.classList.add('pulsing'); |
|
|
setTimeout(() => { |
|
|
statusEl.classList.remove('pulsing'); |
|
|
}, 1000); |
|
|
}; |
|
|
|
|
|
const handleEvolve = async () => { |
|
|
if (state.evolutionInProgress) return; |
|
|
|
|
|
state.evolutionInProgress = true; |
|
|
const depth = state.recursionDepth; |
|
|
|
|
|
|
|
|
updateStatus(`Initiating meta-recursive evolution cycle (depth: ${depth})...`); |
|
|
|
|
|
|
|
|
if (state.multiplayer.connected) { |
|
|
room.send({ |
|
|
type: 'evolution_triggered', |
|
|
depth: depth, |
|
|
username: state.multiplayer.username |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
const duration = config.evolution.baseDuration + (depth * config.evolution.depthMultiplier); |
|
|
|
|
|
|
|
|
visualizationEl.classList.add('evolving'); |
|
|
|
|
|
|
|
|
const startMetrics = { ...state.metrics }; |
|
|
const targetMetrics = { |
|
|
symbolic: Math.min(startMetrics.symbolic + (depth * 15), 100), |
|
|
quantum: Math.min(startMetrics.quantum + (depth * 12), 100), |
|
|
holographic: Math.min(startMetrics.holographic + (depth * 10), 100), |
|
|
meta: Math.min(startMetrics.meta + (depth * 8), 100) |
|
|
}; |
|
|
|
|
|
|
|
|
for (let i = 0; i < config.evolution.evolutionSteps.length; i++) { |
|
|
const step = config.evolution.evolutionSteps[i]; |
|
|
updateStatus(`Evolution step ${i+1}/${config.evolution.evolutionSteps.length}: ${step}`); |
|
|
|
|
|
|
|
|
const progress = (i + 1) / config.evolution.evolutionSteps.length; |
|
|
state.metrics = { |
|
|
symbolic: startMetrics.symbolic + (targetMetrics.symbolic - startMetrics.symbolic) * progress, |
|
|
quantum: startMetrics.quantum + (targetMetrics.quantum - startMetrics.quantum) * progress, |
|
|
holographic: startMetrics.holographic + (targetMetrics.holographic - startMetrics.holographic) * progress, |
|
|
meta: startMetrics.meta + (targetMetrics.meta - startMetrics.meta) * progress |
|
|
}; |
|
|
|
|
|
updateMetricsBars(); |
|
|
|
|
|
|
|
|
if (i === 0) { |
|
|
|
|
|
createParticleSystem('sonw'); |
|
|
} else if (i === 1) { |
|
|
|
|
|
createParticleSystem('afterthought'); |
|
|
} else if (i === 2) { |
|
|
|
|
|
createParticleSystem('cognitive'); |
|
|
} else if (i === 3) { |
|
|
|
|
|
createParticleSystem('integrated'); |
|
|
} |
|
|
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, duration / config.evolution.evolutionSteps.length)); |
|
|
} |
|
|
|
|
|
|
|
|
updateStatus(`Meta-recursive evolution complete (depth: ${depth})`); |
|
|
visualizationEl.classList.remove('evolving'); |
|
|
|
|
|
|
|
|
if (state.currentMode !== 'integrated') { |
|
|
createParticleSystem(state.currentMode); |
|
|
} |
|
|
|
|
|
|
|
|
if (state.multiplayer.connected) { |
|
|
room.party.updateRoomState({ |
|
|
metrics: state.metrics |
|
|
}); |
|
|
} |
|
|
|
|
|
state.evolutionInProgress = false; |
|
|
}; |
|
|
|
|
|
const handleModeChange = (mode) => { |
|
|
if (state.evolutionInProgress) return; |
|
|
|
|
|
state.currentMode = mode; |
|
|
|
|
|
|
|
|
updateSystemButtonsUI(); |
|
|
|
|
|
|
|
|
createParticleSystem(mode); |
|
|
|
|
|
|
|
|
updateStatus(`System switched to ${config.modes[mode].name}`); |
|
|
|
|
|
|
|
|
if (state.multiplayer.connected) { |
|
|
room.party.updatePresence({ |
|
|
mode: mode |
|
|
}); |
|
|
|
|
|
|
|
|
room.send({ |
|
|
type: 'mode_change', |
|
|
mode: mode, |
|
|
username: state.multiplayer.username |
|
|
}); |
|
|
} |
|
|
}; |
|
|
|
|
|
const updateMetricsBars = () => { |
|
|
document.getElementById('symbolicBar').style.width = `${state.metrics.symbolic}%`; |
|
|
document.getElementById('quantumBar').style.width = `${state.metrics.quantum}%`; |
|
|
document.getElementById('holoBar').style.width = `${state.metrics.holographic}%`; |
|
|
document.getElementById('metaBar').style.width = `${state.metrics.meta}%`; |
|
|
}; |
|
|
|
|
|
const initMultiplayer = async () => { |
|
|
try { |
|
|
|
|
|
state.multiplayer.userId = room.party.client.id; |
|
|
state.multiplayer.username = room.party.client.username; |
|
|
|
|
|
|
|
|
const colorIndex = Object.keys(room.party.peers).length % config.multiplayer.userColors.length; |
|
|
state.multiplayer.userColor = config.multiplayer.userColors[colorIndex]; |
|
|
|
|
|
|
|
|
room.party.updatePresence({ |
|
|
username: state.multiplayer.username, |
|
|
color: state.multiplayer.userColor, |
|
|
mode: state.currentMode, |
|
|
cursorX: 0, |
|
|
cursorY: 0, |
|
|
isActive: true |
|
|
}); |
|
|
|
|
|
|
|
|
room.party.subscribePresence(handlePresenceUpdates); |
|
|
|
|
|
|
|
|
if (Object.keys(room.party.peers).length === 1) { |
|
|
room.party.updateRoomState({ |
|
|
currentMode: state.currentMode, |
|
|
metrics: state.metrics, |
|
|
sharedAnalyses: [] |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
room.party.subscribeRoomState(handleRoomStateChanges); |
|
|
|
|
|
|
|
|
room.onmessage = handleMessage; |
|
|
|
|
|
|
|
|
visualizationEl.addEventListener('mousemove', handleMouseMove); |
|
|
|
|
|
|
|
|
visualizationEl.addEventListener('touchmove', handleTouchMove); |
|
|
|
|
|
state.multiplayer.connected = true; |
|
|
showNotification(`Connected as ${state.multiplayer.username}`); |
|
|
|
|
|
} catch (error) { |
|
|
console.error('Error initializing multiplayer:', error); |
|
|
showNotification('Failed to connect to multiplayer session'); |
|
|
} |
|
|
}; |
|
|
|
|
|
const handlePresenceUpdates = (presence) => { |
|
|
|
|
|
state.multiplayer.researchers = presence; |
|
|
|
|
|
|
|
|
updateResearchersUI(); |
|
|
updateUserCursors(); |
|
|
|
|
|
|
|
|
const userCount = Object.keys(presence).length; |
|
|
userCountEl.textContent = userCount; |
|
|
}; |
|
|
|
|
|
const handleRoomStateChanges = (roomState) => { |
|
|
|
|
|
if (!roomState) return; |
|
|
|
|
|
|
|
|
if (roomState.currentMode && roomState.currentMode !== state.currentMode) { |
|
|
state.currentMode = roomState.currentMode; |
|
|
updateSystemButtonsUI(); |
|
|
createParticleSystem(state.currentMode); |
|
|
} |
|
|
|
|
|
|
|
|
if (roomState.metrics) { |
|
|
state.metrics = roomState.metrics; |
|
|
updateMetricsBars(); |
|
|
} |
|
|
|
|
|
|
|
|
if (roomState.sharedAnalyses) { |
|
|
state.multiplayer.sharedAnalyses = roomState.sharedAnalyses; |
|
|
updateSharedAnalysesUI(); |
|
|
} |
|
|
}; |
|
|
|
|
|
const handleMessage = (event) => { |
|
|
const data = event.data; |
|
|
|
|
|
switch (data.type) { |
|
|
case 'connected': |
|
|
showNotification(`${data.username} connected`); |
|
|
break; |
|
|
|
|
|
case 'disconnected': |
|
|
showNotification(`${data.username} disconnected`); |
|
|
break; |
|
|
|
|
|
case 'mode_change': |
|
|
if (data.clientId !== state.multiplayer.userId) { |
|
|
showNotification(`${data.username} switched mode to ${config.modes[data.mode].name}`); |
|
|
} |
|
|
break; |
|
|
|
|
|
case 'analysis_shared': |
|
|
if (data.clientId !== state.multiplayer.userId) { |
|
|
showNotification(`${data.username} shared an analysis of "${data.expression}"`); |
|
|
} |
|
|
break; |
|
|
|
|
|
case 'evolution_triggered': |
|
|
if (data.clientId !== state.multiplayer.userId) { |
|
|
showNotification(`${data.username} triggered evolution (depth: ${data.depth})`); |
|
|
} |
|
|
break; |
|
|
} |
|
|
}; |
|
|
|
|
|
const updateResearchersUI = () => { |
|
|
researchersEl.innerHTML = ''; |
|
|
|
|
|
for (const userId in state.multiplayer.researchers) { |
|
|
const researcher = state.multiplayer.researchers[userId]; |
|
|
if (!researcher.username || !researcher.isActive) continue; |
|
|
|
|
|
const researcherEl = document.createElement('div'); |
|
|
researcherEl.className = 'researcher'; |
|
|
|
|
|
const colorEl = document.createElement('div'); |
|
|
colorEl.className = 'researcher-color'; |
|
|
colorEl.style.backgroundColor = researcher.color; |
|
|
|
|
|
const nameEl = document.createElement('div'); |
|
|
nameEl.className = 'researcher-name'; |
|
|
nameEl.textContent = researcher.username; |
|
|
|
|
|
researcherEl.appendChild(colorEl); |
|
|
researcherEl.appendChild(nameEl); |
|
|
researchersEl.appendChild(researcherEl); |
|
|
} |
|
|
}; |
|
|
|
|
|
const updateUserCursors = () => { |
|
|
userCursorsEl.innerHTML = ''; |
|
|
|
|
|
for (const userId in state.multiplayer.researchers) { |
|
|
|
|
|
if (userId === state.multiplayer.userId) continue; |
|
|
|
|
|
const researcher = state.multiplayer.researchers[userId]; |
|
|
if (!researcher.isActive) continue; |
|
|
|
|
|
const cursorEl = document.createElement('div'); |
|
|
cursorEl.className = 'user-cursor'; |
|
|
cursorEl.style.backgroundColor = researcher.color; |
|
|
cursorEl.style.left = `${researcher.cursorX}px`; |
|
|
cursorEl.style.top = `${researcher.cursorY}px`; |
|
|
|
|
|
|
|
|
const labelEl = document.createElement('div'); |
|
|
labelEl.style.position = 'absolute'; |
|
|
labelEl.style.top = '20px'; |
|
|
labelEl.style.left = '10px'; |
|
|
labelEl.style.fontSize = '12px'; |
|
|
labelEl.style.whiteSpace = 'nowrap'; |
|
|
labelEl.textContent = researcher.username; |
|
|
|
|
|
cursorEl.appendChild(labelEl); |
|
|
userCursorsEl.appendChild(cursorEl); |
|
|
} |
|
|
}; |
|
|
|
|
|
const handleMouseMove = (event) => { |
|
|
if (!state.multiplayer.connected) return; |
|
|
|
|
|
|
|
|
const rect = visualizationEl.getBoundingClientRect(); |
|
|
const x = event.clientX - rect.left; |
|
|
const y = event.clientY - rect.top; |
|
|
|
|
|
|
|
|
room.party.updatePresence({ |
|
|
cursorX: x, |
|
|
cursorY: y |
|
|
}); |
|
|
}; |
|
|
|
|
|
const handleTouchMove = (event) => { |
|
|
if (!state.multiplayer.connected || !event.touches[0]) return; |
|
|
|
|
|
|
|
|
const rect = visualizationEl.getBoundingClientRect(); |
|
|
const x = event.touches[0].clientX - rect.left; |
|
|
const y = event.touches[0].clientY - rect.top; |
|
|
|
|
|
|
|
|
room.party.updatePresence({ |
|
|
cursorX: x, |
|
|
cursorY: y |
|
|
}); |
|
|
}; |
|
|
|
|
|
const shareAnalysis = () => { |
|
|
if (!state.multiplayer.connected) return; |
|
|
|
|
|
|
|
|
const outputContent = outputEl.innerText; |
|
|
|
|
|
|
|
|
if (!outputContent || outputContent.includes('placeholder')) { |
|
|
showNotification('Nothing to share. Process an expression first.'); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
let expression = conceptInput.value.trim(); |
|
|
|
|
|
if (!expression) { |
|
|
|
|
|
const selectedKey = symbolSelector.value; |
|
|
expression = config.symbolicExpressions[selectedKey].expression; |
|
|
} |
|
|
|
|
|
|
|
|
const sharedAnalysis = { |
|
|
id: Date.now().toString(), |
|
|
username: state.multiplayer.username, |
|
|
userId: state.multiplayer.userId, |
|
|
color: state.multiplayer.userColor, |
|
|
timestamp: new Date().toISOString(), |
|
|
expression, |
|
|
content: outputContent.substring(0, config.multiplayer.sharingSettings.characterLimit), |
|
|
mode: state.currentMode |
|
|
}; |
|
|
|
|
|
|
|
|
const sharedAnalyses = [...state.multiplayer.sharedAnalyses]; |
|
|
|
|
|
|
|
|
sharedAnalyses.push(sharedAnalysis); |
|
|
|
|
|
|
|
|
if (sharedAnalyses.length > config.multiplayer.sharingSettings.maxSharedAnalyses) { |
|
|
sharedAnalyses.shift(); |
|
|
} |
|
|
|
|
|
|
|
|
room.party.updateRoomState({ |
|
|
sharedAnalyses |
|
|
}); |
|
|
|
|
|
|
|
|
room.send({ |
|
|
type: 'analysis_shared', |
|
|
expression, |
|
|
username: state.multiplayer.username |
|
|
}); |
|
|
|
|
|
showNotification('Analysis shared with all researchers'); |
|
|
}; |
|
|
|
|
|
const syncVisualization = () => { |
|
|
if (!state.multiplayer.connected) return; |
|
|
|
|
|
|
|
|
room.party.updateRoomState({ |
|
|
currentMode: state.currentMode, |
|
|
metrics: state.metrics |
|
|
}); |
|
|
|
|
|
showNotification('Visualization synchronized with all researchers'); |
|
|
}; |
|
|
|
|
|
const updateSharedAnalysesUI = () => { |
|
|
sharedAnalysesListEl.innerHTML = ''; |
|
|
|
|
|
if (state.multiplayer.sharedAnalyses.length === 0) { |
|
|
sharedAnalysesListEl.innerHTML = '<p class="placeholder">No shared analyses yet.</p>'; |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
const sortedAnalyses = [...state.multiplayer.sharedAnalyses].sort((a, b) => |
|
|
new Date(b.timestamp) - new Date(a.timestamp) |
|
|
); |
|
|
|
|
|
for (const analysis of sortedAnalyses) { |
|
|
const analysisEl = document.createElement('div'); |
|
|
analysisEl.className = 'shared-analysis'; |
|
|
|
|
|
const headerEl = document.createElement('div'); |
|
|
headerEl.className = 'shared-header'; |
|
|
|
|
|
const userEl = document.createElement('span'); |
|
|
userEl.style.color = analysis.color; |
|
|
userEl.textContent = analysis.username; |
|
|
|
|
|
const timeEl = document.createElement('span'); |
|
|
timeEl.textContent = formatTimestamp(analysis.timestamp); |
|
|
|
|
|
headerEl.appendChild(userEl); |
|
|
headerEl.appendChild(timeEl); |
|
|
|
|
|
const expressionEl = document.createElement('div'); |
|
|
expressionEl.className = 'shared-expression'; |
|
|
expressionEl.textContent = `"${analysis.expression}"`; |
|
|
|
|
|
const contentEl = document.createElement('div'); |
|
|
contentEl.className = 'shared-content'; |
|
|
contentEl.innerHTML = analysis.content; |
|
|
|
|
|
analysisEl.appendChild(headerEl); |
|
|
analysisEl.appendChild(expressionEl); |
|
|
analysisEl.appendChild(contentEl); |
|
|
|
|
|
sharedAnalysesListEl.appendChild(analysisEl); |
|
|
} |
|
|
}; |
|
|
|
|
|
const formatTimestamp = (timestamp) => { |
|
|
const date = new Date(timestamp); |
|
|
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); |
|
|
}; |
|
|
|
|
|
const showNotification = (message) => { |
|
|
notificationEl.textContent = message; |
|
|
notificationEl.classList.add('show'); |
|
|
|
|
|
setTimeout(() => { |
|
|
notificationEl.classList.remove('show'); |
|
|
}, config.multiplayer.notificationDuration); |
|
|
}; |
|
|
|
|
|
const updateSystemButtonsUI = () => { |
|
|
systemButtons.forEach(btn => { |
|
|
btn.classList.remove('active'); |
|
|
if (btn.id === `${state.currentMode}Btn`) { |
|
|
btn.classList.add('active'); |
|
|
} |
|
|
}); |
|
|
}; |
|
|
|
|
|
|
|
|
window.addEventListener('DOMContentLoaded', () => { |
|
|
|
|
|
initVisualization(); |
|
|
|
|
|
|
|
|
updateMetricsBars(); |
|
|
|
|
|
|
|
|
initMultiplayer(); |
|
|
|
|
|
|
|
|
document.getElementById('sonwBtn').addEventListener('click', () => handleModeChange('sonw')); |
|
|
document.getElementById('afterthoughtBtn').addEventListener('click', () => handleModeChange('afterthought')); |
|
|
document.getElementById('cognitiveBtn').addEventListener('click', () => handleModeChange('cognitive')); |
|
|
document.getElementById('integratedBtn').addEventListener('click', () => handleModeChange('integrated')); |
|
|
|
|
|
|
|
|
depthSlider.addEventListener('input', () => { |
|
|
state.recursionDepth = parseInt(depthSlider.value); |
|
|
depthValueEl.textContent = depthSlider.value; |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('evolveBtn').addEventListener('click', handleEvolve); |
|
|
|
|
|
|
|
|
symbolSelector.addEventListener('change', () => { |
|
|
const selectedKey = symbolSelector.value; |
|
|
const selectedExpression = config.symbolicExpressions[selectedKey]; |
|
|
conceptInput.placeholder = selectedExpression.expression; |
|
|
|
|
|
|
|
|
outputEl.innerHTML = ` |
|
|
<p><strong>${selectedExpression.description}</strong></p> |
|
|
<p class="symbolic-node">${selectedExpression.expression}</p> |
|
|
<ul> |
|
|
${selectedExpression.components.map(comp => `<li>${comp}</li>`).join('')} |
|
|
</ul> |
|
|
<p class="placeholder">Press "Process" to analyze this expression...</p> |
|
|
`; |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('processBtn').addEventListener('click', handleSymbolicProcess); |
|
|
conceptInput.addEventListener('keypress', (e) => { |
|
|
if (e.key === 'Enter') { |
|
|
handleSymbolicProcess(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
shareBtn.addEventListener('click', shareAnalysis); |
|
|
syncBtn.addEventListener('click', syncVisualization); |
|
|
|
|
|
|
|
|
window.addEventListener('beforeunload', () => { |
|
|
if (state.multiplayer.connected) { |
|
|
room.party.updatePresence({ |
|
|
isActive: false |
|
|
}); |
|
|
} |
|
|
}); |
|
|
}); |