import * as THREE from 'three'; import { THEME } from './core/theme.js'; import { S } from './state.js'; import { Broadcaster } from './state.js'; export class SovOS { constructor(scene) { this.scene = scene; this.apps = new Map(); this.init(); } init() { this.container = new THREE.Group(); this.container.position.set(0, 3, -7.5); this.scene.add(this.container); } registerApp(id, config) { const app = this.createWindow(id, config); this.apps.set(id, app); this.container.add(app.group); } createWindow(id, config) { const { x, y, rot, title, color } = config; const w = 2.8, h = 3.8; const group = new THREE.Group(); group.position.set(x, y || 0, 0); group.rotation.y = rot || 0; // Glassmorphism Frame const glassMat = new THREE.MeshPhysicalMaterial({ color: THEME.glass.color, transparent: true, opacity: THEME.glass.opacity, roughness: THEME.glass.roughness, metalness: THEME.glass.metalness, transmission: THEME.glass.transmission, thickness: THEME.glass.thickness, ior: THEME.glass.ior, side: THREE.DoubleSide }); const frame = new THREE.Mesh(new THREE.PlaneGeometry(w, h), glassMat); group.add(frame); // Canvas UI const canvas = document.createElement('canvas'); canvas.width = 512; canvas.height = 700; const ctx = canvas.getContext('2d'); const texture = new THREE.CanvasTexture(canvas); const mat = new THREE.MeshBasicMaterial({ map: texture, transparent: true, side: THREE.DoubleSide }); const screen = new THREE.Mesh(new THREE.PlaneGeometry(w * 0.92, h * 0.92), mat); screen.position.z = 0.05; group.add(screen); const renderUI = (state) => { ctx.clearRect(0, 0, 512, 700); // Header ctx.fillStyle = 'rgba(0, 0, 0, 0.4)'; ctx.fillRect(0, 0, 512, 80); ctx.fillStyle = '#' + new THREE.Color(color).getHexString(); ctx.font = 'bold 32px "Orbitron"'; ctx.fillText(title, 30, 50); // Body ctx.font = '20px "JetBrains Mono"'; ctx.fillStyle = '#ffffff'; config.renderBody(ctx, state); texture.needsUpdate = true; }; Broadcaster.subscribe(renderUI); return { group, renderUI }; } }