mirror of
https://github.com/elder-plinius/R00TS.git
synced 2026-02-12 09:12:51 +00:00
354 lines
11 KiB
JavaScript
354 lines
11 KiB
JavaScript
// R00TS - Plant the seeds of artificial intelligence
|
|
// Main application functionality
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Initialize particles
|
|
initParticles();
|
|
|
|
// Load words from storage
|
|
loadWords();
|
|
|
|
// Set up automatic updates
|
|
setInterval(loadWords, 5000);
|
|
});
|
|
|
|
function initParticles() {
|
|
particlesJS('particles-js', {
|
|
particles: {
|
|
number: {
|
|
value: 80,
|
|
density: {
|
|
enable: true,
|
|
value_area: 800
|
|
}
|
|
},
|
|
color: {
|
|
value: '#00ff9d'
|
|
},
|
|
shape: {
|
|
type: 'circle'
|
|
},
|
|
opacity: {
|
|
value: 0.5,
|
|
random: true,
|
|
animation: {
|
|
enable: true,
|
|
speed: 1,
|
|
opacity_min: 0.1,
|
|
sync: false
|
|
}
|
|
},
|
|
size: {
|
|
value: 3,
|
|
random: true,
|
|
animation: {
|
|
enable: true,
|
|
speed: 2,
|
|
size_min: 0.1,
|
|
sync: false
|
|
}
|
|
},
|
|
line_linked: {
|
|
enable: true,
|
|
distance: 150,
|
|
color: '#00ff9d',
|
|
opacity: 0.2,
|
|
width: 1
|
|
},
|
|
move: {
|
|
enable: true,
|
|
speed: 1,
|
|
direction: 'none',
|
|
random: true,
|
|
straight: false,
|
|
out_mode: 'out',
|
|
bounce: false
|
|
}
|
|
},
|
|
interactivity: {
|
|
detect_on: 'canvas',
|
|
events: {
|
|
onhover: {
|
|
enable: true,
|
|
mode: 'grab'
|
|
},
|
|
resize: true
|
|
},
|
|
modes: {
|
|
grab: {
|
|
distance: 140,
|
|
line_linked: {
|
|
opacity: 0.5
|
|
}
|
|
}
|
|
}
|
|
},
|
|
retina_detect: true
|
|
});
|
|
}
|
|
|
|
async function loadWords() {
|
|
try {
|
|
// Use the data manager to get words from API
|
|
let words = await window.dataManager.getCurrentWords();
|
|
|
|
// Update the visualization
|
|
updateWordCloud(words);
|
|
|
|
// Update statistics
|
|
updateStats(words);
|
|
} catch (error) {
|
|
console.error('Error loading words:', error);
|
|
// Fallback to localStorage if API fails
|
|
let words = JSON.parse(localStorage.getItem('roots-words')) || {};
|
|
updateWordCloud(words);
|
|
updateStats(words);
|
|
}
|
|
}
|
|
|
|
function updateStats(words) {
|
|
const totalSubmissions = Object.values(words).reduce((a, b) => a + b, 0);
|
|
const uniqueWords = Object.keys(words).length;
|
|
|
|
document.getElementById('submission-count').textContent = totalSubmissions;
|
|
document.getElementById('unique-count').textContent = uniqueWords;
|
|
}
|
|
|
|
async function submitWord(word) {
|
|
word = word.trim().toLowerCase();
|
|
|
|
if (!word) return false;
|
|
|
|
// Create a particle burst effect
|
|
if (typeof createParticleBurst === 'function') {
|
|
createParticleBurst();
|
|
}
|
|
|
|
try {
|
|
// Use the data manager to add word via API
|
|
await window.dataManager.addWord(word);
|
|
|
|
// Update UI with animation if GSAP is available
|
|
if (typeof gsap !== 'undefined') {
|
|
gsap.to('.stat-box', {
|
|
scale: 1.1,
|
|
duration: 0.2,
|
|
yoyo: true,
|
|
repeat: 1,
|
|
ease: 'power2.out'
|
|
});
|
|
}
|
|
|
|
loadWords();
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Error submitting word:', error);
|
|
// Fallback to localStorage if API fails
|
|
let words = JSON.parse(localStorage.getItem('roots-words')) || {};
|
|
words[word] = (words[word] || 0) + 1;
|
|
localStorage.setItem('roots-words', JSON.stringify(words));
|
|
|
|
loadWords();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function createParticleBurst() {
|
|
const container = document.querySelector('.input-area');
|
|
const rect = container.getBoundingClientRect();
|
|
const centerX = rect.left + rect.width / 2;
|
|
const centerY = rect.top + rect.height / 2;
|
|
|
|
for (let i = 0; i < 20; i++) {
|
|
const particle = document.createElement('div');
|
|
particle.style.cssText = `
|
|
position: fixed;
|
|
width: 8px;
|
|
height: 8px;
|
|
background: #00ff9d;
|
|
border-radius: 50%;
|
|
pointer-events: none;
|
|
z-index: 1000;
|
|
`;
|
|
document.body.appendChild(particle);
|
|
|
|
const angle = (i / 20) * Math.PI * 2;
|
|
const velocity = 2 + Math.random() * 2;
|
|
const dx = Math.cos(angle) * velocity;
|
|
const dy = Math.sin(angle) * velocity;
|
|
|
|
gsap.fromTo(particle,
|
|
{
|
|
x: centerX,
|
|
y: centerY,
|
|
scale: 1,
|
|
opacity: 1
|
|
},
|
|
{
|
|
duration: 1 + Math.random(),
|
|
x: centerX + dx * 50,
|
|
y: centerY + dy * 50,
|
|
scale: 0,
|
|
opacity: 0,
|
|
ease: 'power2.out',
|
|
onComplete: () => particle.remove()
|
|
}
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
function updateWordCloud(words) {
|
|
const container = document.getElementById('word-cloud-container');
|
|
if (!container) return;
|
|
|
|
container.innerHTML = '';
|
|
|
|
const width = container.offsetWidth;
|
|
const height = container.offsetHeight;
|
|
|
|
const wordData = Object.entries(words).map(([text, value]) => ({ text, value }));
|
|
|
|
// Sort by frequency
|
|
wordData.sort((a, b) => b.value - a.value);
|
|
|
|
// Take top 100 words
|
|
const topWords = wordData.slice(0, 100);
|
|
|
|
if (topWords.length === 0) {
|
|
<<<<<<< HEAD
|
|
=======
|
|
// Show placeholder if no words
|
|
>>>>>>> 6fcedc69cfea5193a6809e1f2fb705b42479c5bd
|
|
container.innerHTML = '<div class="d-flex justify-content-center align-items-center h-100"><p class="text-muted">Plant some words to see them grow here!</p></div>';
|
|
return;
|
|
}
|
|
|
|
// Calculate min/max for scaling
|
|
const minCount = Math.min(...topWords.map(d => d.value)) || 1;
|
|
const maxCount = Math.max(...topWords.map(d => d.value)) || 1;
|
|
|
|
// Create SVG
|
|
const svg = d3.select('#word-cloud-container')
|
|
.append('svg')
|
|
.attr('width', width)
|
|
.attr('height', height)
|
|
.append('g')
|
|
<<<<<<< HEAD
|
|
.attr('transform', `translate(${width/2}, ${height * 0.8})`);
|
|
|
|
// Create tree trunk
|
|
const trunk = svg.append('path')
|
|
.attr('d', `M0,0 L0,-${height * 0.6}`)
|
|
.attr('stroke', '#00ff9d')
|
|
.attr('stroke-width', 8)
|
|
.attr('fill', 'none')
|
|
.style('opacity', 0)
|
|
.transition()
|
|
.duration(1000)
|
|
.style('opacity', 1);
|
|
|
|
// Create branches function
|
|
function createBranch(startX, startY, length, angle, depth) {
|
|
if (depth <= 0) return;
|
|
|
|
const endX = startX + length * Math.sin(angle);
|
|
const endY = startY - length * Math.cos(angle);
|
|
|
|
svg.append('path')
|
|
.attr('d', `M${startX},${startY} L${endX},${endY}`)
|
|
.attr('stroke', '#00ff9d')
|
|
.attr('stroke-width', Math.max(1, depth))
|
|
.attr('fill', 'none')
|
|
.style('opacity', 0)
|
|
.transition()
|
|
.delay(1000 + (5 - depth) * 300)
|
|
.duration(500)
|
|
.style('opacity', 0.5);
|
|
|
|
createBranch(endX, endY, length * 0.7, angle + 0.5, depth - 1);
|
|
createBranch(endX, endY, length * 0.7, angle - 0.5, depth - 1);
|
|
}
|
|
|
|
// Create initial branches
|
|
createBranch(0, -height * 0.6, height * 0.2, -Math.PI/4, 5);
|
|
createBranch(0, -height * 0.6, height * 0.2, Math.PI/4, 5);
|
|
|
|
// Create a force simulation for the words
|
|
const simulation = d3.forceSimulation(topWords)
|
|
.force('charge', d3.forceManyBody().strength(5))
|
|
.force('collide', d3.forceCollide(d => fontSize(d) / 2 + 2))
|
|
.force('x', d3.forceX(d => {
|
|
const angle = (d.index / topWords.length) * Math.PI - Math.PI/2;
|
|
return Math.cos(angle) * (height * 0.3);
|
|
}))
|
|
.force('y', d3.forceY(d => {
|
|
const angle = (d.index / topWords.length) * Math.PI - Math.PI/2;
|
|
return Math.sin(angle) * (height * 0.3) - height * 0.4;
|
|
}));
|
|
|
|
// Create word elements
|
|
const words = svg.selectAll('.word')
|
|
.data(topWords)
|
|
.enter()
|
|
.append('text')
|
|
.attr('class', 'word')
|
|
.style('fill', d => d3.interpolateGreenYellow(d.value / maxCount))
|
|
.style('font-size', d => `${fontSize(d)}px`)
|
|
.style('font-family', '"Space Grotesk", sans-serif')
|
|
.style('cursor', 'pointer')
|
|
.text(d => d.text)
|
|
.style('opacity', 0)
|
|
.on('mouseover', function(event, d) {
|
|
d3.select(this)
|
|
.transition()
|
|
.duration(200)
|
|
.style('fill', '#39ff14')
|
|
.style('font-size', d => `${fontSize(d) * 1.2}px`)
|
|
.style('text-shadow', '0 0 10px rgba(57, 255, 20, 0.5)');
|
|
})
|
|
.on('mouseout', function(event, d) {
|
|
d3.select(this)
|
|
.transition()
|
|
.duration(200)
|
|
.style('fill', d3.interpolateGreenYellow(d.value / maxCount))
|
|
.style('font-size', d => `${fontSize(d)}px`)
|
|
.style('text-shadow', 'none');
|
|
});
|
|
|
|
// Add transition for words appearing
|
|
words.transition()
|
|
.delay((d, i) => 2000 + i * 50)
|
|
.duration(500)
|
|
.style('opacity', 1);
|
|
|
|
// Update word positions on each tick
|
|
simulation.on('tick', () => {
|
|
words
|
|
.attr('x', d => d.x)
|
|
.attr('y', d => d.y)
|
|
.attr('text-anchor', 'middle')
|
|
.attr('alignment-baseline', 'middle');
|
|
});
|
|
}
|
|
|
|
// Function to share words
|
|
function shareResults() {
|
|
const words = JSON.parse(localStorage.getItem('roots-words')) || {};
|
|
const totalWords = Object.values(words).reduce((a, b) => a + b, 0);
|
|
const uniqueWords = Object.keys(words).length;
|
|
|
|
const topWords = Object.entries(words)
|
|
.sort((a, b) => b[1] - a[1])
|
|
.slice(0, 5)
|
|
.map(([word, count]) => word)
|
|
.join(', ');
|
|
|
|
const shareText = `I've planted ${totalWords} words (${uniqueWords} unique) to help grow the future of AI with R00TS! Top contributions: ${topWords}`;
|
|
|
|
// In a real implementation, this would integrate with social sharing APIs
|
|
// For demo purposes, we'll just copy to clipboard
|
|
navigator.clipboard.writeText(shareText)
|
|
.then(() => alert('Share text copied to clipboard!'))
|
|
.catch(err => console.error('Failed to copy: ', err));
|
|
} |