Frontend uudelleenrakennettu: Astro-komponentit, Wasm pääsäikeessä, ei Workeria
Vanha frontend siirretty temp/. Uusi rakenne: - StatusBar.astro, Terminal.astro, Editor.astro, Guide.astro - global.css erillinen - Wasm pääsäikeessä (ei Worker — yksinkertainen, debugattava) - Tab-completion, dropdown, projektikortti, Monaco, GUIDE.md - Ei tokenisointia eikä koodilaboratoriota Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
---
|
||||
// AgentChat Placeholder
|
||||
---
|
||||
<div id="astro-agent-chat">
|
||||
<!-- Chat component will go here -->
|
||||
</div>
|
||||
15
network-poc/frontend/src/components/Editor.astro
Normal file
15
network-poc/frontend/src/components/Editor.astro
Normal file
@@ -0,0 +1,15 @@
|
||||
<!-- Monaco Editor paneeli -->
|
||||
<div id="panel-editor" class="panel">
|
||||
<div style="display:flex;height:calc(100vh - 200px);gap:0;border:1px solid var(--border);border-radius:6px;overflow:hidden">
|
||||
<div id="editor-filetree" style="width:200px;min-width:150px;background:var(--bg);border-right:1px solid var(--border);overflow-y:auto;font-family:'Courier New',monospace;font-size:13px">
|
||||
<div style="padding:10px 12px;color:#8b949e;font-size:11px;text-transform:uppercase;letter-spacing:0.5px;border-bottom:1px solid var(--border)">Tiedostot</div>
|
||||
<div id="editor-file-list" style="padding:4px 0">
|
||||
<div style="padding:8px 16px;color:#8b949e;font-size:12px">Generoi projekti:<br><code style="color:var(--accent)">kpn project "..."</code></div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="flex:1;display:flex;flex-direction:column">
|
||||
<div id="editor-tabs" style="display:flex;background:var(--bg);border-bottom:1px solid var(--border);min-height:35px;align-items:flex-end;padding:0 8px;gap:2px;overflow-x:auto"></div>
|
||||
<div id="monaco-container" style="flex:1"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
6
network-poc/frontend/src/components/Guide.astro
Normal file
6
network-poc/frontend/src/components/Guide.astro
Normal file
@@ -0,0 +1,6 @@
|
||||
<!-- Opas-paneeli: ladataan GUIDE.md fetchillä -->
|
||||
<div id="panel-guide" class="panel">
|
||||
<div id="guide-content" style="max-width:800px;margin:0 auto;padding:20px;line-height:1.7;font-size:15px">
|
||||
<p style="color:#8b949e">Ladataan opasta...</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,215 +0,0 @@
|
||||
---
|
||||
// Network3D.astro - Visualizes the Agentic Network connections
|
||||
---
|
||||
<div id="network-canvas-container"></div>
|
||||
|
||||
<style>
|
||||
#network-canvas-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
opacity: 0.6;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import * as THREE from 'three';
|
||||
|
||||
const container = document.getElementById('network-canvas-container');
|
||||
if (container) {
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||
const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
|
||||
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
container.appendChild(renderer.domElement);
|
||||
|
||||
// Kipinän brändivärit
|
||||
const palette = [
|
||||
new THREE.Color(0x58a6ff), // Sininen
|
||||
new THREE.Color(0xff6b00), // Oranssi
|
||||
new THREE.Color(0x2ea043), // Vihreä
|
||||
new THREE.Color(0xd29922), // Keltainen
|
||||
new THREE.Color(0x8b949e) // Harmaa
|
||||
];
|
||||
|
||||
// 1. Luodaan perusnoodit (partikkelit)
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
const particlesCount = 120;
|
||||
const posArray = new Float32Array(particlesCount * 3);
|
||||
const colorArray = new Float32Array(particlesCount * 3);
|
||||
|
||||
for (let i = 0; i < particlesCount; i++) {
|
||||
// Levitä pallomaisesti
|
||||
posArray[i*3] = (Math.random() - 0.5) * 12;
|
||||
posArray[i*3+1] = (Math.random() - 0.5) * 12;
|
||||
posArray[i*3+2] = (Math.random() - 0.5) * 12;
|
||||
|
||||
// Valitse satunnainen väri paletista
|
||||
const color = palette[Math.floor(Math.random() * palette.length)];
|
||||
colorArray[i*3] = color.r;
|
||||
colorArray[i*3+1] = color.g;
|
||||
colorArray[i*3+2] = color.b;
|
||||
}
|
||||
|
||||
geometry.setAttribute('position', new THREE.BufferAttribute(posArray, 3));
|
||||
geometry.setAttribute('color', new THREE.BufferAttribute(colorArray, 3));
|
||||
|
||||
const material = new THREE.PointsMaterial({
|
||||
size: 0.08,
|
||||
vertexColors: true,
|
||||
transparent: true,
|
||||
opacity: 0.8
|
||||
});
|
||||
|
||||
const particlesMesh = new THREE.Points(geometry, material);
|
||||
scene.add(particlesMesh);
|
||||
|
||||
// 2. Yhdistävät viivat (vain visualisointiin, himmeät)
|
||||
const lineMaterial = new THREE.LineBasicMaterial({
|
||||
vertexColors: true,
|
||||
transparent: true,
|
||||
opacity: 0.15,
|
||||
blending: THREE.AdditiveBlending
|
||||
});
|
||||
const lineGeometry = new THREE.BufferGeometry();
|
||||
lineGeometry.setAttribute('position', new THREE.BufferAttribute(posArray, 3));
|
||||
lineGeometry.setAttribute('color', new THREE.BufferAttribute(colorArray, 3));
|
||||
const linesMesh = new THREE.LineSegments(lineGeometry, lineMaterial);
|
||||
particlesMesh.add(linesMesh);
|
||||
|
||||
// 3. Räjähdykset ja kipinät (Sparks)
|
||||
const maxSparks = 1000;
|
||||
const sparkGeometry = new THREE.BufferGeometry();
|
||||
const sparkPositions = new Float32Array(maxSparks * 3);
|
||||
const sparkColors = new Float32Array(maxSparks * 3);
|
||||
const sparkVelocities = new Float32Array(maxSparks * 3);
|
||||
const sparkLifetimes = new Float32Array(maxSparks);
|
||||
|
||||
// Piilota aluksi kaikki kipinät kauas
|
||||
for(let i=0; i<maxSparks*3; i++) sparkPositions[i] = 1000;
|
||||
|
||||
sparkGeometry.setAttribute('position', new THREE.BufferAttribute(sparkPositions, 3));
|
||||
sparkGeometry.setAttribute('color', new THREE.BufferAttribute(sparkColors, 3));
|
||||
|
||||
const sparkMaterial = new THREE.PointsMaterial({
|
||||
size: 0.04,
|
||||
vertexColors: true,
|
||||
transparent: true,
|
||||
opacity: 1.0,
|
||||
blending: THREE.AdditiveBlending
|
||||
});
|
||||
const sparksMesh = new THREE.Points(sparkGeometry, sparkMaterial);
|
||||
particlesMesh.add(sparksMesh); // Lisätään pääverkon sisään jotta pyörii sen mukana
|
||||
|
||||
camera.position.z = 6;
|
||||
|
||||
function triggerExplosion() {
|
||||
// Valitse satunnainen noodisolmu lähtöpisteeksi
|
||||
const nodeIdx = Math.floor(Math.random() * particlesCount);
|
||||
const nx = posArray[nodeIdx * 3];
|
||||
const ny = posArray[nodeIdx * 3 + 1];
|
||||
const nz = posArray[nodeIdx * 3 + 2];
|
||||
|
||||
const nr = colorArray[nodeIdx * 3];
|
||||
const ng = colorArray[nodeIdx * 3 + 1];
|
||||
const nb = colorArray[nodeIdx * 3 + 2];
|
||||
|
||||
const sparkCount = 15 + Math.random() * 25; // 15-40 kipinää
|
||||
let spawned = 0;
|
||||
|
||||
for(let i=0; i<maxSparks; i++) {
|
||||
if(sparkLifetimes[i] <= 0) {
|
||||
sparkLifetimes[i] = 0.5 + Math.random() * 1.5; // Elinikä 0.5-2sek
|
||||
|
||||
// Alkusijainti on noodi itse
|
||||
sparkPositions[i*3] = nx;
|
||||
sparkPositions[i*3+1] = ny;
|
||||
sparkPositions[i*3+2] = nz;
|
||||
|
||||
// Satunnainen lentosuunta ja voima (pallomaisesti)
|
||||
const u = Math.random();
|
||||
const v = Math.random();
|
||||
const theta = u * 2.0 * Math.PI;
|
||||
const phi = Math.acos(2.0 * v - 1.0);
|
||||
const speed = 2.0 + Math.random() * 3.0; // Nopeus
|
||||
|
||||
sparkVelocities[i*3] = Math.sin(phi) * Math.cos(theta) * speed;
|
||||
sparkVelocities[i*3+1] = Math.sin(phi) * Math.sin(theta) * speed;
|
||||
sparkVelocities[i*3+2] = Math.cos(phi) * speed;
|
||||
|
||||
// Kipinän väri (noodin väri + satunnaista vaaleutta)
|
||||
sparkColors[i*3] = Math.min(1.0, nr + Math.random() * 0.5);
|
||||
sparkColors[i*3+1] = Math.min(1.0, ng + Math.random() * 0.5);
|
||||
sparkColors[i*3+2] = Math.min(1.0, nb + Math.random() * 0.5);
|
||||
|
||||
spawned++;
|
||||
if(spawned >= sparkCount) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Animaatiolooppi
|
||||
let lastTime = performance.now();
|
||||
|
||||
function animate(time: number) {
|
||||
requestAnimationFrame(animate);
|
||||
const dt = (time - lastTime) / 1000; // sekunteja
|
||||
lastTime = time;
|
||||
if (dt > 0.1) return; // vältä lagipiikkien aiheuttamat hyperhypyt
|
||||
|
||||
const elapsedTime = time / 1000;
|
||||
particlesMesh.rotation.y = elapsedTime * 0.05;
|
||||
particlesMesh.rotation.x = elapsedTime * 0.02;
|
||||
|
||||
// Arvo satunnaisia räjähdyksiä (n. 1 per sekunti)
|
||||
if (Math.random() < 1.0 * dt) {
|
||||
triggerExplosion();
|
||||
}
|
||||
|
||||
// Päivitä kipinät
|
||||
let sparksUpdated = false;
|
||||
for(let i=0; i<maxSparks; i++) {
|
||||
if(sparkLifetimes[i] > 0) {
|
||||
sparkLifetimes[i] -= dt;
|
||||
if(sparkLifetimes[i] <= 0) {
|
||||
sparkPositions[i*3] = 1000; // Piilota kuollessaan
|
||||
} else {
|
||||
// Liikuta nopeuden mukaan
|
||||
sparkPositions[i*3] += sparkVelocities[i*3] * dt;
|
||||
sparkPositions[i*3+1] += sparkVelocities[i*3+1] * dt;
|
||||
sparkPositions[i*3+2] += sparkVelocities[i*3+2] * dt;
|
||||
|
||||
// Painovoimaefekti / ilmanvastus
|
||||
sparkVelocities[i*3+1] -= 2.0 * dt; // Painovoima
|
||||
sparkVelocities[i*3] *= 0.98; // Ilmanvastus
|
||||
sparkVelocities[i*3+1] *= 0.98;
|
||||
sparkVelocities[i*3+2] *= 0.98;
|
||||
}
|
||||
sparksUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (sparksUpdated) {
|
||||
sparkGeometry.attributes.position.needsUpdate = true;
|
||||
sparkGeometry.attributes.color.needsUpdate = true;
|
||||
}
|
||||
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
requestAnimationFrame((time) => { lastTime = time; requestAnimationFrame(animate); });
|
||||
|
||||
// Pakotettu uudelleenkokoyritys
|
||||
window.addEventListener('resize', () => {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
15
network-poc/frontend/src/components/StatusBar.astro
Normal file
15
network-poc/frontend/src/components/StatusBar.astro
Normal file
@@ -0,0 +1,15 @@
|
||||
<!-- Hub-yhteys + laskentasolmun tila -->
|
||||
<div class="status-bar">
|
||||
<span class="status-group" title="Hub-yhteyden tila">
|
||||
<span id="hub-dot" class="status-dot" style="background:#d29922"></span>
|
||||
<span style="color:#8b949e">Hub:</span>
|
||||
<span id="hub-label" style="color:#d29922">Yhdistetään...</span>
|
||||
</span>
|
||||
<span class="status-separator">│</span>
|
||||
<span class="status-group">
|
||||
<span id="compute-dot" class="status-dot" style="background:#30363d"></span>
|
||||
<span style="color:#8b949e">Laskenta:</span>
|
||||
<span id="compute-label" style="color:#8b949e">—</span>
|
||||
<button id="compute-btn" class="btn btn-accent" title="Käynnistä kielimalli">Alusta</button>
|
||||
</span>
|
||||
</div>
|
||||
10
network-poc/frontend/src/components/Terminal.astro
Normal file
10
network-poc/frontend/src/components/Terminal.astro
Normal file
@@ -0,0 +1,10 @@
|
||||
<!-- Pipeline-palkki + Terminaali + Input -->
|
||||
<div id="pipeline-bar" class="pipeline-bar"></div>
|
||||
<div id="terminal" class="terminal"></div>
|
||||
<div class="terminal-input-row">
|
||||
<span class="terminal-prompt">$</span>
|
||||
<input id="term-input" class="terminal-input" type="text"
|
||||
placeholder='kpn run coder "hello world in python"'
|
||||
spellcheck="false" autocomplete="off">
|
||||
<div id="term-dropdown" class="terminal-dropdown"></div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user