Enhanced UI with cyber-garden theme and dataset management

This commit is contained in:
EP
2025-03-06 15:12:15 -05:00
commit eefe1419b1
7 changed files with 1399 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

60
README.md Normal file
View File

@@ -0,0 +1,60 @@
# R00TS - Plant the Seeds of Artificial Intelligence
R00TS is an interactive web application that allows users to contribute words they believe should be important in future artificial intelligence understanding. The application visualizes these contributions in a dynamic word cloud, with more frequently submitted words appearing larger.
## About R00TS
The concept behind R00TS is to create a collective "garden" of words that people from around the world can contribute to - essentially "planting seeds" of vocabulary that will grow in the consciousness of future AI systems.
While the current implementation uses client-side storage for demonstration purposes, the concept could be expanded to a global database where everyone's contributions help shape a collective understanding of what words humans believe are important for AI to comprehend.
## Features
- Submit words to the AI vocabulary garden
- See a real-time word cloud of all submitted words
- Green-themed visualization representing growth and roots
- Statistics showing total contributions and unique words
- Responsive design that works on mobile and desktop
## How It Works
1. Users enter words they want future AI to understand deeply
2. The word is "planted" in the collection (currently stored in browser localStorage)
3. The word cloud updates to show all planted words, with size based on frequency
4. Statistics about planted words are displayed
## Technical Implementation
- Pure HTML, CSS, and JavaScript
- Uses D3.js for the word cloud visualization
- Bootstrap for responsive styling
- No backend required (uses client-side storage for demonstration)
## Running Locally
Simply open `index.html` in a web browser. No server required.
## Future Enhancements
- Server-side storage for global word collection
- User accounts to track individual contributions
- Regional visualizations to see how word importance varies by culture
- Sentiment analysis of submitted words
- Category tagging for submitted words
- Social sharing functionality
## Repository Structure
- `/SYSTEM PROMPTS` - Collection of AI system prompts for reference
- `index.html` - Main application page
- `script.js` - JavaScript functionality
- `styles.css` - CSS styling
- `README.md` - This documentation file
## Philosophical Background
R00TS represents the idea that humans can collectively influence the development of artificial intelligence by contributing the concepts they believe are most important for AI to understand. It's a metaphorical way of "planting seeds" in the AI consciousness, helping to shape how future systems might prioritize and understand human values and language.
## Disclaimer
This is a conceptual demonstration project. In actual AI training, much more sophisticated approaches are used. This project is meant to be thought-provoking rather than technically accurate to real AI training methodologies.

104
data_manager.js Normal file
View File

@@ -0,0 +1,104 @@
// R00TS - Data Management System
class DataManager {
constructor(backupInterval = 1800000) { // Default: 30 minutes
this.backupInterval = backupInterval;
this.dataDir = 'datasets';
this.initializeDataDirectory();
this.startAutoBackup();
}
async initializeDataDirectory() {
try {
const response = await fetch(`/${this.dataDir}`);
if (response.status === 404) {
console.log('Creating datasets directory...');
// Directory will be created on first backup
}
} catch (error) {
console.log('Will create datasets directory on first backup');
}
}
getCurrentWords() {
return JSON.parse(localStorage.getItem('roots-words')) || {};
}
async saveDataset() {
const currentData = this.getCurrentWords();
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const filename = `roots_dataset_${timestamp}.json`;
const dataBlob = new Blob(
[JSON.stringify(currentData, null, 2)],
{ type: 'application/json' }
);
// Create download link
const link = document.createElement('a');
link.href = URL.createObjectURL(dataBlob);
link.download = filename;
// Trigger download
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// Clean up
URL.revokeObjectURL(link.href);
console.log(`Dataset saved: ${filename}`);
this.updateDatasetList(filename, currentData);
}
updateDatasetList(filename, data) {
const datasets = JSON.parse(localStorage.getItem('roots-datasets') || '[]');
datasets.push({
filename,
timestamp: new Date().toISOString(),
wordCount: Object.keys(data).length,
totalSubmissions: Object.values(data).reduce((a, b) => a + b, 0)
});
// Keep only last 50 datasets in the list
if (datasets.length > 50) {
datasets.shift();
}
localStorage.setItem('roots-datasets', JSON.stringify(datasets));
this.updateDatasetDisplay();
}
updateDatasetDisplay() {
const datasetList = document.getElementById('dataset-list');
if (!datasetList) return;
const datasets = JSON.parse(localStorage.getItem('roots-datasets') || '[]');
datasetList.innerHTML = datasets.reverse().slice(0, 5).map(dataset => `
<div class="dataset-item">
<div class="dataset-info">
<span class="dataset-name">${dataset.filename}</span>
<span class="dataset-stats">
Words: ${dataset.wordCount} |
Submissions: ${dataset.totalSubmissions}
</span>
</div>
<div class="dataset-time">
${new Date(dataset.timestamp).toLocaleString()}
</div>
</div>
`).join('');
}
startAutoBackup() {
// Initial backup
setTimeout(() => this.saveDataset(), 5000);
// Regular backups
setInterval(() => this.saveDataset(), this.backupInterval);
}
}
// Initialize data manager when document is ready
document.addEventListener('DOMContentLoaded', () => {
window.dataManager = new DataManager();
});

295
datasets.html Normal file
View File

@@ -0,0 +1,295 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>R00TS - Dataset Manager</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Space+Grotesk:wght@300;400;600&display=swap');
:root {
--primary-color: #00ff9d;
--secondary-color: #004d2c;
--accent-color: #39ff14;
--bg-color: #0a0f15;
--text-color: #e0e0e0;
}
body {
font-family: 'Space Grotesk', sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
min-height: 100vh;
padding: 2rem 0;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 40px;
}
.header h1 {
font-family: 'Share Tech Mono', monospace;
color: var(--primary-color);
font-size: 3rem;
margin-bottom: 1rem;
text-shadow: 0 0 10px rgba(0, 255, 157, 0.5);
}
.datasets-container {
background: rgba(0, 20, 10, 0.4);
border-radius: 20px;
padding: 30px;
box-shadow: 0 8px 32px rgba(0, 255, 157, 0.1);
backdrop-filter: blur(8px);
border: 1px solid rgba(0, 255, 157, 0.1);
}
.dataset-item {
background: rgba(0, 20, 10, 0.6);
border-radius: 10px;
padding: 20px;
margin-bottom: 15px;
transition: all 0.3s ease;
}
.dataset-item:hover {
transform: translateX(5px);
box-shadow: 0 4px 15px rgba(0, 255, 157, 0.1);
}
.dataset-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.dataset-name {
color: var(--primary-color);
font-family: 'Share Tech Mono', monospace;
font-size: 1.1rem;
margin: 0;
}
.dataset-time {
color: var(--text-color);
opacity: 0.5;
font-size: 0.9rem;
font-family: 'Share Tech Mono', monospace;
}
.dataset-stats {
display: flex;
gap: 20px;
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid rgba(0, 255, 157, 0.1);
}
.stat-item {
flex: 1;
text-align: center;
}
.stat-label {
color: var(--text-color);
opacity: 0.7;
font-size: 0.8rem;
margin-bottom: 5px;
}
.stat-value {
color: var(--primary-color);
font-size: 1.2rem;
font-family: 'Share Tech Mono', monospace;
}
.btn-primary {
background: var(--primary-color);
color: var(--bg-color);
border: none;
padding: 8px 20px;
font-size: 0.9rem;
font-weight: 600;
border-radius: 8px;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 1px;
}
.btn-primary:hover {
background: var(--accent-color);
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(57, 255, 20, 0.3);
}
.actions {
display: flex;
gap: 10px;
}
.back-link {
display: inline-block;
color: var(--primary-color);
text-decoration: none;
margin-bottom: 20px;
font-family: 'Share Tech Mono', monospace;
transition: all 0.3s ease;
}
.back-link:hover {
color: var(--accent-color);
transform: translateX(-5px);
}
#datasetStats {
background: rgba(0, 20, 10, 0.4);
border-radius: 15px;
padding: 20px;
margin-bottom: 30px;
display: flex;
justify-content: space-around;
text-align: center;
}
.total-stat {
color: var(--primary-color);
font-size: 2rem;
font-family: 'Share Tech Mono', monospace;
margin: 0;
}
.stat-description {
color: var(--text-color);
opacity: 0.7;
font-size: 0.9rem;
margin-top: 5px;
}
</style>
</head>
<body>
<div class="container">
<a href="index.html" class="back-link">← Back to Garden</a>
<div class="header">
<h1>R00TS Dataset Manager</h1>
</div>
<div id="datasetStats">
<!-- Overall stats will be inserted here -->
</div>
<div class="datasets-container">
<div id="dataset-list">
<!-- Dataset items will be inserted here -->
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const datasets = JSON.parse(localStorage.getItem('roots-datasets') || '[]');
const currentWords = JSON.parse(localStorage.getItem('roots-words') || '{}');
// Update overall stats
const totalDatasets = datasets.length;
const latestDataset = datasets[datasets.length - 1] || { wordCount: 0, totalSubmissions: 0 };
document.getElementById('datasetStats').innerHTML = `
<div>
<p class="total-stat">${totalDatasets}</p>
<p class="stat-description">Total Snapshots</p>
</div>
<div>
<p class="total-stat">${latestDataset.wordCount}</p>
<p class="stat-description">Unique Words</p>
</div>
<div>
<p class="total-stat">${latestDataset.totalSubmissions}</p>
<p class="stat-description">Total Submissions</p>
</div>
`;
// Display datasets
const datasetList = document.getElementById('dataset-list');
datasetList.innerHTML = datasets.reverse().map(dataset => `
<div class="dataset-item">
<div class="dataset-header">
<h3 class="dataset-name">${dataset.filename}</h3>
<div class="actions">
<button class="btn btn-primary btn-sm" onclick="downloadDataset('${dataset.filename}')">
Download
</button>
<button class="btn btn-primary btn-sm" onclick="viewDataset('${dataset.filename}')">
View
</button>
</div>
</div>
<div class="dataset-time">
Created: ${new Date(dataset.timestamp).toLocaleString()}
</div>
<div class="dataset-stats">
<div class="stat-item">
<div class="stat-label">Unique Words</div>
<div class="stat-value">${dataset.wordCount}</div>
</div>
<div class="stat-item">
<div class="stat-label">Total Submissions</div>
<div class="stat-value">${dataset.totalSubmissions}</div>
</div>
</div>
</div>
`).join('');
});
function downloadDataset(filename) {
const words = JSON.parse(localStorage.getItem('roots-words') || '{}');
const dataBlob = new Blob([JSON.stringify(words, null, 2)], { type: 'application/json' });
const link = document.createElement('a');
link.href = URL.createObjectURL(dataBlob);
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
}
function viewDataset(filename) {
const words = JSON.parse(localStorage.getItem('roots-words') || '{}');
const formattedData = JSON.stringify(words, null, 2);
const win = window.open('', '_blank');
win.document.write(`
<html>
<head>
<title>${filename}</title>
<style>
body {
background: #1e1e1e;
color: #00ff9d;
font-family: monospace;
padding: 20px;
margin: 0;
}
pre {
white-space: pre-wrap;
word-wrap: break-word;
}
</style>
</head>
<body>
<pre>${formattedData}</pre>
</body>
</html>
`);
}
</script>
</body>
</html>

433
index.html Normal file
View File

@@ -0,0 +1,433 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>R00TS - AI Word Seeding</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="data_manager.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/particles.js/2.0.0/particles.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.0/gsap.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Space+Grotesk:wght@300;400;600&display=swap');
:root {
--primary-color: #00ff9d;
--secondary-color: #004d2c;
--accent-color: #39ff14;
--bg-color: #0a0f15;
--text-color: #e0e0e0;
}
body {
font-family: 'Space Grotesk', sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
position: relative;
overflow-x: hidden;
min-height: 100vh;
}
#particles-js {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
pointer-events: none;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
position: relative;
z-index: 1;
}
.word-cloud {
width: 100%;
height: 600px;
margin-top: 30px;
background: rgba(0, 20, 10, 0.4);
border-radius: 20px;
box-shadow: 0 8px 32px rgba(0, 255, 157, 0.1);
backdrop-filter: blur(8px);
border: 1px solid rgba(0, 255, 157, 0.1);
overflow: hidden;
transition: all 0.3s ease;
}
.word-cloud:hover {
box-shadow: 0 8px 32px rgba(0, 255, 157, 0.2);
transform: translateY(-2px);
}
.header {
text-align: center;
margin-bottom: 40px;
position: relative;
}
.header h1 {
font-family: 'Share Tech Mono', monospace;
font-weight: 700;
color: var(--primary-color);
font-size: 4.5rem;
text-shadow: 0 0 10px rgba(0, 255, 157, 0.5);
letter-spacing: 4px;
margin-bottom: 1rem;
}
.header .lead {
font-size: 1.4rem;
color: var(--text-color);
max-width: 600px;
margin: 0 auto;
line-height: 1.6;
}
.input-area {
background: rgba(0, 20, 10, 0.4);
padding: 30px;
border-radius: 20px;
box-shadow: 0 8px 32px rgba(0, 255, 157, 0.1);
backdrop-filter: blur(8px);
border: 1px solid rgba(0, 255, 157, 0.1);
margin-bottom: 30px;
transition: all 0.3s ease;
}
.input-area:hover {
box-shadow: 0 8px 32px rgba(0, 255, 157, 0.2);
transform: translateY(-2px);
}
.form-control {
background: rgba(0, 20, 10, 0.4);
border: 1px solid rgba(0, 255, 157, 0.2);
color: var(--text-color);
padding: 15px;
font-size: 1.1rem;
transition: all 0.3s ease;
}
.form-control:focus {
background: rgba(0, 20, 10, 0.6);
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem rgba(0, 255, 157, 0.25);
color: var(--text-color);
}
.form-label {
color: var(--text-color);
font-size: 1.1rem;
margin-bottom: 0.5rem;
}
.btn-primary {
background: var(--primary-color);
color: var(--bg-color);
border: none;
padding: 12px 30px;
font-size: 1.1rem;
font-weight: 600;
border-radius: 10px;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 1px;
}
.btn-primary:hover {
background: var(--accent-color);
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(57, 255, 20, 0.3);
}
.footer {
text-align: center;
margin-top: 50px;
color: var(--text-color);
font-size: 1rem;
opacity: 0.7;
transition: opacity 0.3s ease;
}
.footer:hover {
opacity: 1;
}
.r00ts-brand {
font-family: 'Share Tech Mono', monospace;
font-weight: bold;
letter-spacing: 2px;
position: relative;
display: inline-block;
}
.r00ts-brand::after {
content: '';
position: absolute;
bottom: -5px;
left: 0;
width: 100%;
height: 2px;
background: var(--primary-color);
transform: scaleX(0);
transform-origin: right;
transition: transform 0.3s ease;
}
.r00ts-brand:hover::after {
transform: scaleX(1);
transform-origin: left;
}
.stats-area {
display: flex;
justify-content: space-around;
margin: 30px 0;
gap: 20px;
}
.stat-box {
text-align: center;
padding: 25px;
background: rgba(0, 20, 10, 0.4);
border-radius: 15px;
box-shadow: 0 8px 32px rgba(0, 255, 157, 0.1);
backdrop-filter: blur(8px);
border: 1px solid rgba(0, 255, 157, 0.1);
flex: 1;
transition: all 0.3s ease;
}
.stat-box:hover {
transform: translateY(-5px);
box-shadow: 0 12px 40px rgba(0, 255, 157, 0.2);
}
.stat-box h3 {
color: var(--primary-color);
font-size: 1.2rem;
margin-bottom: 10px;
font-family: 'Share Tech Mono', monospace;
}
.stat-box p {
color: var(--text-color);
font-size: 2rem;
font-weight: 600;
margin: 0;
text-shadow: 0 0 10px rgba(0, 255, 157, 0.3);
}
/* Dataset styles */
.datasets-section {
margin-top: 40px;
background: rgba(0, 20, 10, 0.4);
border-radius: 20px;
padding: 25px;
box-shadow: 0 8px 32px rgba(0, 255, 157, 0.1);
backdrop-filter: blur(8px);
border: 1px solid rgba(0, 255, 157, 0.1);
}
.datasets-section h3 {
color: var(--primary-color);
font-family: 'Share Tech Mono', monospace;
margin-bottom: 20px;
font-size: 1.4rem;
}
.dataset-item {
background: rgba(0, 20, 10, 0.6);
border-radius: 10px;
padding: 15px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.3s ease;
}
.dataset-item:hover {
transform: translateX(5px);
box-shadow: 0 4px 15px rgba(0, 255, 157, 0.1);
}
.dataset-info {
display: flex;
flex-direction: column;
gap: 5px;
}
.dataset-name {
color: var(--primary-color);
font-family: 'Share Tech Mono', monospace;
font-size: 0.9rem;
}
.dataset-stats {
color: var(--text-color);
opacity: 0.7;
font-size: 0.8rem;
}
.dataset-time {
color: var(--text-color);
opacity: 0.5;
font-size: 0.8rem;
font-family: 'Share Tech Mono', monospace;
}
</style>
</head>
<body>
<div id="particles-js"></div>
<div class="container">
<div class="header">
<h1 class="r00ts-brand">R00TS</h1>
<p class="lead">Plant the seeds of artificial intelligence by contributing words you think are important</p>
</div>
<div class="input-area">
<form id="word-form">
<div class="mb-3">
<label for="word-input" class="form-label">Enter a word to give it power:</label>
<input type="text" class="form-control" id="word-input" placeholder="Type a word..." required>
</div>
<button type="submit" class="btn btn-primary">Plant Word</button>
</form>
</div>
<div class="stats-area">
<div class="stat-box">
<h3>Total Words</h3>
<p id="submission-count">0</p>
</div>
<div class="stat-box">
<h3>Unique Words</h3>
<p id="unique-count">0</p>
</div>
</div>
<div class="word-cloud" id="word-cloud-container"></div>
<div class="datasets-section">
<div class="d-flex justify-content-between align-items-center mb-4">
<h3>Recent Datasets</h3>
<a href="datasets.html" class="btn btn-primary btn-sm">View All Datasets</a>
</div>
<div id="dataset-list">
<!-- Dataset items will be inserted here -->
</div>
</div>
<div class="footer">
<p>R00TS - Nurturing the future of artificial intelligence, one word at a time.</p>
</div>
</div>
<script>
// Initialize word storage
let words = JSON.parse(localStorage.getItem('roots-words')) || {};
let totalSubmissions = Object.values(words).reduce((a, b) => a + b, 0);
let uniqueWords = Object.keys(words).length;
// Update stats display
document.getElementById('submission-count').textContent = totalSubmissions;
document.getElementById('unique-count').textContent = uniqueWords;
// Handle form submission
document.getElementById('word-form').addEventListener('submit', function(e) {
e.preventDefault();
const wordInput = document.getElementById('word-input');
const word = wordInput.value.trim().toLowerCase();
if (word) {
// Update word count
words[word] = (words[word] || 0) + 1;
// Save to localStorage
localStorage.setItem('roots-words', JSON.stringify(words));
// Update stats
totalSubmissions++;
uniqueWords = Object.keys(words).length;
document.getElementById('submission-count').textContent = totalSubmissions;
document.getElementById('unique-count').textContent = uniqueWords;
// Clear input
wordInput.value = '';
// Update visualization
updateWordCloud();
// Show success message
const random = Math.floor(Math.random() * 3);
const messages = [
`"${word}" has been planted in the AI garden!`,
`"${word}" is now growing in the AI consciousness!`,
`"${word}" will blossom in future AI understanding!`
];
alert(messages[random]);
}
});
// Word cloud visualization
function updateWordCloud() {
const container = document.getElementById('word-cloud-container');
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);
// 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')
.attr('transform', `translate(${width/2}, ${height/2})`);
// Create a simple layout
const fontSize = d => Math.max(16, Math.min(60, 16 + (d.value - minCount) / (maxCount - minCount || 1) * 44));
// Random position for words
let i = 0;
topWords.forEach(word => {
const angle = (i / topWords.length) * 2 * Math.PI;
const radius = Math.min(width, height) * 0.4 * Math.random();
const x = radius * Math.cos(angle);
const y = radius * Math.sin(angle);
// Color based on value (green theme for plants/roots)
const colorScale = d3.scaleLinear()
.domain([minCount, maxCount])
.range(['#a8d5ba', '#2d6a4f']);
svg.append('text')
.attr('x', x)
.attr('y', y)
.attr('font-size', `${fontSize(word)}px`)
.attr('text-anchor', 'middle')
.attr('fill', colorScale(word.value))
.text(word.text);
i++;
});
}
// Initial render
updateWordCloud();
</script>
</body>
</html>

329
script.js Normal file
View File

@@ -0,0 +1,329 @@
// 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
});
}
function loadWords() {
// In a real implementation, this would be an API call
// For demo purposes, we're using localStorage
let words = JSON.parse(localStorage.getItem('roots-words')) || {};
// Update the visualization
updateWordCloud(words);
// Update statistics
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;
}
function submitWord(word) {
word = word.trim().toLowerCase();
if (!word) return false;
// Create a particle burst effect
createParticleBurst();
// For demo purposes, we're using localStorage
let words = JSON.parse(localStorage.getItem('roots-words')) || {};
words[word] = (words[word] || 0) + 1;
localStorage.setItem('roots-words', JSON.stringify(words));
// Update UI with animation
gsap.to('.stat-box', {
scale: 1.1,
duration: 0.2,
yoyo: true,
repeat: 1,
ease: 'power2.out'
});
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) {
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')
.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));
}

178
styles.css Normal file
View File

@@ -0,0 +1,178 @@
/* R00TS - Core Styles */
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f0f2f5;
color: #212529;
margin: 0;
padding: 0;
line-height: 1.6;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
/* Header Styles */
.header {
text-align: center;
margin-bottom: 30px;
padding: 20px 0;
}
.header h1 {
font-weight: 700;
color: #2e2e2e;
margin-bottom: 10px;
font-size: 3rem;
}
.r00ts-brand {
font-family: monospace;
font-weight: bold;
letter-spacing: 1px;
}
.header .lead {
color: #555;
font-size: 1.2rem;
}
/* Input Area Styles */
.input-area {
background-color: #ffffff;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
margin-bottom: 30px;
}
.input-area label {
font-weight: 600;
color: #495057;
}
.form-control:focus {
border-color: #80bdff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
/* Word Cloud Styles */
.word-cloud {
width: 100%;
height: 400px;
margin-top: 30px;
background-color: #ffffff;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
overflow: hidden;
position: relative;
}
.word-item {
transition: all 0.2s ease;
cursor: pointer;
}
/* Statistics Styles */
.stats-area {
display: flex;
justify-content: space-around;
margin: 20px 0;
}
.stat-box {
text-align: center;
padding: 10px;
background-color: #ffffff;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
flex: 1;
margin: 0 10px;
}
.stat-box h3 {
margin: 0;
font-size: 1rem;
color: #6c757d;
}
.stat-box p {
margin: 5px 0 0;
font-size: 1.5rem;
font-weight: 700;
color: #4a6fa5;
}
/* Footer Styles */
.footer {
text-align: center;
margin-top: 30px;
padding: 20px 0;
color: #6c757d;
font-size: 0.9rem;
border-top: 1px solid #dee2e6;
}
/* Button Styles */
.btn-primary {
background-color: #4a6fa5;
border: none;
padding: 10px 20px;
transition: background-color 0.2s;
}
.btn-primary:hover {
background-color: #3a5a8c;
}
/* Responsive Adjustments */
@media (max-width: 576px) {
.container {
padding: 10px;
}
.word-cloud {
height: 300px;
}
.stats-area {
flex-direction: column;
}
.stat-box {
margin: 5px 0;
}
.header h1 {
font-size: 2.5rem;
}
}
/* Share button */
.share-btn {
display: inline-block;
background-color: #4a6fa5;
color: white;
border: none;
border-radius: 50px;
padding: 8px 20px;
margin-top: 10px;
cursor: pointer;
transition: background-color 0.2s;
}
.share-btn:hover {
background-color: #3a5a8c;
}
/* Animation for word addition */
@keyframes grow {
0% { transform: scale(0); opacity: 0; }
100% { transform: scale(1); opacity: 1; }
}
.word-added {
animation: grow 0.5s ease-out;
}