diff --git a/interface/app/$libraryId/peer/$id.tsx b/interface/app/$libraryId/peer/$id.tsx index b950f8224..62c76486e 100644 --- a/interface/app/$libraryId/peer/$id.tsx +++ b/interface/app/$libraryId/peer/$id.tsx @@ -3,18 +3,14 @@ import { NodeIdParamsSchema } from '~/app/route-schemas'; import { Icon } from '~/components'; import { useRouteTitle, useZodRouteParams } from '~/hooks'; import { hardwareModelToIcon } from '~/util/hardware'; - import { TopBarPortal } from '../TopBar/Portal'; +import StarfieldEffect from './StarfieldEffect'; // Import the StarfieldEffect component export const Component = () => { const { id: _nodeId } = useZodRouteParams(NodeIdParamsSchema); - // we encode/decode because nodeId has special characters and I'm not willing to change that rn const nodeId = decodeURIComponent(_nodeId); - const peers = usePeers(); - const peer = peers.get(nodeId); - const title = useRouteTitle(peer?.metadata?.name || 'Peer'); return ( @@ -39,8 +35,12 @@ export const Component = () => { {peer?.metadata.operating_system?.toString()}

{nodeId}

-
- Drop files here to send with Spacedrop + +
+ +
+ Drop files here to send with Spacedrop +
)} diff --git a/interface/app/$libraryId/peer/StarfieldEffect.tsx b/interface/app/$libraryId/peer/StarfieldEffect.tsx new file mode 100644 index 000000000..515ee2a21 --- /dev/null +++ b/interface/app/$libraryId/peer/StarfieldEffect.tsx @@ -0,0 +1,254 @@ +import React, { useEffect, useRef } from 'react'; + +const StarfieldEffect: React.FC = () => { + const canvasRef = useRef(null); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + const resizeCanvas = () => { + const scale = window.devicePixelRatio || 1; + const width = canvas.parentElement?.clientWidth || 800; + const height = canvas.parentElement?.clientHeight || 300; + canvas.width = width * scale; + canvas.height = height * scale; + canvas.style.width = `${width}px`; + canvas.style.height = `${height}px`; + ctx.scale(scale, scale); + }; + + resizeCanvas(); + window.addEventListener('resize', resizeCanvas); + + canvas.style.position = 'absolute'; + canvas.oncontextmenu = e => e.preventDefault(); + + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + const pix = imageData.data; + + const center = { x: canvas.width / 2, y: canvas.height / 2 }; + + let mouseActive = false; + let mouseDown = false; + let mousePos = { x: center.x, y: center.y }; + + let starSpeed = 20; + const starSpeedMin = starSpeed; + const starSpeedMax = 100; + const starDistance = 5000; + + let fov = 320; + const fovMin = 210; + const fovMax = fov; + + const starHolderCount = 8000; // Increased star count + const starHolder: any[] = []; + const starBgHolder: any[] = []; + + const backgroundColor = { r: 28, g: 29, b: 37, a: 255 }; + + const clearImageData = () => { + for (let i = 0, l = pix.length; i < l; i += 4) { + pix[i] = backgroundColor.r; + pix[i + 1] = backgroundColor.g; + pix[i + 2] = backgroundColor.b; + pix[i + 3] = backgroundColor.a; + } + }; + + const setPixel = (x: number, y: number, r: number, g: number, b: number, a: number) => { + const i = (x + y * canvas.width) * 4; + pix[i] = r; + pix[i + 1] = g; + pix[i + 2] = b; + pix[i + 3] = a; + }; + + const setPixelAdditive = (x: number, y: number, r: number, g: number, b: number, a: number) => { + const i = (x + y * canvas.width) * 4; + pix[i] += r; + pix[i + 1] += g; + pix[i + 2] += b; + pix[i + 3] = a; + }; + + const drawLine = (x1: number, y1: number, x2: number, y2: number, r: number, g: number, b: number, a: number) => { + const dx = Math.abs(x2 - x1); + const dy = Math.abs(y2 - y1); + const sx = x1 < x2 ? 1 : -1; + const sy = y1 < y2 ? 1 : -1; + let err = dx - dy; + let lx = x1; + let ly = y1; + + const continueLoop = true + while (continueLoop) { + if (lx > 0 && lx < canvas.width && ly > 0 && ly < canvas.height) { + setPixel(lx, ly, r, g, b, a); + } + if (lx === x2 && ly === y2) break; + const e2 = 2 * err; + if (e2 > -dx) { + err -= dy; + lx += sx; + } + if (e2 < dy) { + err += dx; + ly += sy; + } + } + }; + + const addParticle = (x: number, y: number, z: number, ox: number, oy: number, oz: number) => { + const particle = { x, y, z, ox, oy, x2d: 0, y2d: 0, color: { r: 0, g: 0, b: 0, a: 0 }, oColor: { r: 0, g: 0, b: 0, a: 0 }, w: 0, distance: 0, distanceTotal: 0 }; + return particle; + }; + + const addParticles = () => { + let x, y, z, colorValue, particle; + for (let i = 0; i < starHolderCount / 3; i++) { + x = Math.random() * 24000 - 12000; + y = Math.random() * 4500 - 2250; + z = Math.round(Math.random() * starDistance); + colorValue = 185; // Adjusted color + particle = addParticle(x, y, z, x, y, z); + particle.color = { r: 171, g: 172, b: 185, a: 255 }; + starBgHolder.push(particle); + } + for (let i = 0; i < starHolderCount; i++) { + x = Math.random() * 10000 - 5000; + y = Math.random() * 10000 - 5000; + z = Math.round(Math.random() * starDistance); + colorValue = 185; // Adjusted color + particle = addParticle(x, y, z, x, y, z); + particle.color = { r: 171, g: 172, b: 185, a: 255 }; + particle.oColor = { r: 171, g: 172, b: 185, a: 255 }; + particle.w = 1; + particle.distance = starDistance - z; + particle.distanceTotal = Math.round(starDistance + fov - particle.w); + starHolder.push(particle); + } + }; + + const animloop = () => { + requestAnimationFrame(animloop); + render(); + }; + + const render = () => { + clearImageData(); + let star, scale; + + if (mouseActive) { + starSpeed += 2; + if (starSpeed > starSpeedMax) starSpeed = starSpeedMax; + } else { + starSpeed -= 1; + if (starSpeed < starSpeedMin) starSpeed = starSpeedMin; + } + + fov += mouseActive ? -1 : 0.5; + fov = Math.max(fovMin, Math.min(fovMax, fov)); + + const warpSpeedValue = starSpeed * (starSpeed / (starSpeedMax / 2)); + + for (const bgStar of starBgHolder) { + star = bgStar; + scale = fov / (fov + star.z); + star.x2d = star.x * scale + center.x; + star.y2d = star.y * scale + center.y; + if (star.x2d > 0 && star.x2d < canvas.width && star.y2d > 0 && star.y2d < canvas.height) { + setPixel(star.x2d | 0, star.y2d | 0, star.color.r, star.color.g, star.color.b, 255); + } + } + + for (const mainStar of starHolder) { + star = mainStar; + star.z -= starSpeed; + star.distance += starSpeed; + if (star.z < -fov + star.w) { + star.z = starDistance; + star.distance = 0; + } + + const distancePercent = star.distance / star.distanceTotal; + star.color.r = Math.floor(star.oColor.r * distancePercent); + star.color.g = Math.floor(star.oColor.g * distancePercent); + star.color.b = Math.floor(star.oColor.b * distancePercent); + + scale = fov / (fov + star.z); + star.x2d = star.x * scale + center.x; + star.y2d = star.y * scale + center.y; + + if (star.x2d > 0 && star.x2d < canvas.width && star.y2d > 0 && star.y2d < canvas.height) { + setPixelAdditive(star.x2d | 0, star.y2d | 0, star.color.r, star.color.g, star.color.b, 255); + } + + if (starSpeed !== starSpeedMin) { + const nz = star.z + warpSpeedValue; + scale = fov / (fov + nz); + const x2d = star.x * scale + center.x; + const y2d = star.y * scale + center.y; + if (x2d > 0 && x2d < canvas.width && y2d > 0 && y2d < canvas.height) { + drawLine(star.x2d | 0, star.y2d | 0, x2d | 0, y2d | 0, star.color.r, star.color.g, star.color.b, 255); + } + } + } + + ctx.putImageData(imageData, 0, 0); + + center.x += (mousePos.x - center.x) * 0.015; + if (!mouseActive) { + center.x += (canvas.width / 2 - center.x) * 0.015; + } + }; + + const getMousePos = (event: MouseEvent) => { + const rect = canvas.getBoundingClientRect(); + return { x: event.clientX - rect.left, y: event.clientY - rect.top }; + }; + + const mouseMoveHandler = (event: MouseEvent) => { + mousePos = getMousePos(event); + }; + + const mouseEnterHandler = () => { + mouseActive = true; + }; + + const mouseLeaveHandler = () => { + mouseActive = false; + mouseDown = false; + }; + + canvas.addEventListener('mousemove', mouseMoveHandler); + canvas.addEventListener('mousedown', () => { mouseDown = true; }); + canvas.addEventListener('mouseup', () => { mouseDown = false; }); + canvas.addEventListener('mouseenter', mouseEnterHandler); + canvas.addEventListener('mouseleave', mouseLeaveHandler); + + addParticles(); + animloop(); + + return () => { + canvas.removeEventListener('mousemove', mouseMoveHandler); + canvas.removeEventListener('mousedown', () => { mouseDown = true; }); + canvas.removeEventListener('mouseup', () => { mouseDown = false; }); + canvas.removeEventListener('mouseenter', mouseEnterHandler); + canvas.removeEventListener('mouseleave', mouseLeaveHandler); + window.removeEventListener('resize', resizeCanvas); + }; + }, []); + + return ( + + Drop files here to send with Spacedrop + + ); +}; + +export default StarfieldEffect;