Port BR-95 desktop to Next.js components

This commit is contained in:
Alexa Amundson
2025-11-20 19:11:04 -06:00
parent c96df78772
commit 160a4954ec
25 changed files with 2131 additions and 0 deletions

749
br95/app/globals.css Normal file
View File

@@ -0,0 +1,749 @@
:root {
/* Brand system */
--br-black: #02030a;
--br-bg-elevated: #050816;
--br-bg-alt: #090c1f;
--br-white: #ffffff;
--br-muted: #a7b0c7;
--br-border-subtle: rgba(255,255,255,0.08);
--br-accent-warm: #ff9a3c; /* top of logo gradient */
--br-accent-mid: #ff4fa3; /* magenta */
--br-accent-cool: #327cff; /* electric blue */
--br-accent-neo: #69f7ff; /* cyan */
--br-radius-lg: 24px;
--br-radius-md: 16px;
--br-radius-sm: 10px;
--br-shadow-soft: 0 18px 45px rgba(0,0,0,0.45);
--br-font-sans: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif;
--br-font-mono: "JetBrains Mono", ui-monospace, Menlo, Monaco, Consolas, monospace;
/* BR95 chrome (retro-style, brand-colored) */
--br95-gray: #181c2a;
--br95-gray-light: #232842;
--br95-gray-lighter: #2f3554;
--br95-border-light: #4b5378;
--br95-border-dark: #050816;
--br95-border-darkest: #000000;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--br-font-sans);
background: radial-gradient(circle at top, #121735 0, var(--br-black) 60%);
height: 100vh;
overflow: hidden;
color: var(--br-white);
-webkit-font-smoothing: antialiased;
}
/* CRT scanline overlay */
.scanline {
position: fixed;
inset: 0;
background: linear-gradient(to bottom, transparent 50%, rgba(255,255,255,0.06) 50%);
background-size: 100% 3px;
mix-blend-mode: soft-light;
opacity: 0.4;
pointer-events: none;
z-index: 9999;
}
/* Boot screen (from Desktop OS vibe) */
.boot-screen {
position: fixed;
inset: 0;
background: var(--br-black);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 9000;
animation: bootFadeOut 0.6s ease 2.6s forwards;
}
@keyframes bootFadeOut {
to {
opacity: 0;
visibility: hidden;
}
}
.boot-logo-container {
text-align: center;
animation: bootIn 0.7s ease-out;
}
@keyframes bootIn {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
.boot-logo {
width: 180px;
height: 180px;
margin: 0 auto 28px;
background: conic-gradient(from 180deg,
var(--br-accent-warm),
var(--br-accent-mid),
var(--br-accent-cool),
var(--br-accent-warm));
border-radius: 38px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
box-shadow:
0 0 60px rgba(255,154,60,0.35),
0 0 100px rgba(255,79,163,0.28),
0 20px 40px rgba(0,0,0,0.8);
animation: logoGlow 2s ease-in-out infinite;
}
@keyframes logoGlow {
0%,100% {
box-shadow:
0 0 60px rgba(255,154,60,0.35),
0 0 100px rgba(255,79,163,0.28),
0 20px 40px rgba(0,0,0,0.8);
}
50% {
box-shadow:
0 0 80px rgba(255,154,60,0.5),
0 0 120px rgba(255,79,163,0.4),
0 20px 40px rgba(0,0,0,0.9);
}
}
.boot-logo::before {
content: 'R';
font-size: 130px;
font-weight: 900;
color: var(--br-black);
font-family: var(--br-font-sans);
position: relative;
line-height: 1;
}
.boot-road {
position: absolute;
width: 34px;
height: 110px;
background: linear-gradient(to bottom,
transparent 0%,
rgba(255,255,255,0.98) 30%,
rgba(255,255,255,0.98) 70%,
transparent 100%);
border-radius: 17px;
left: 50%;
top: 50%;
transform: translate(-50%, -50%) rotate(-18deg);
opacity: 0.96;
}
.boot-title {
background: linear-gradient(120deg,
var(--br-accent-warm),
var(--br-accent-mid),
var(--br-accent-cool));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
font-size: 28px;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
margin-bottom: 10px;
}
.boot-sub {
font-size: 12px;
color: var(--br-muted);
letter-spacing: 0.08em;
text-transform: uppercase;
}
.boot-loading {
margin-top: 18px;
font-size: 11px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--br-muted);
}
.boot-loading::after {
content: '...';
animation: dots 1.2s infinite;
}
@keyframes dots {
0%,20% { content: '.'; }
40% { content: '..'; }
60%,100% { content: '...'; }
}
/* Desktop shell */
.shell {
position: relative;
display: flex;
flex-direction: column;
height: 100vh;
opacity: 0;
pointer-events: none;
transition: opacity 0.4s ease;
}
.shell.ready {
opacity: 1;
pointer-events: auto;
}
/* Top menubar (from Desktop OS) */
.menubar {
background: linear-gradient(180deg, rgba(5,8,22,0.98) 0%, rgba(2,3,10,0.98) 100%);
backdrop-filter: blur(18px);
padding: 8px 16px;
display: flex;
align-items: center;
border-bottom: 1px solid var(--br-border-subtle);
box-shadow: 0 4px 24px rgba(0,0,0,0.5);
z-index: 50;
}
.menu-logo {
width: 28px;
height: 28px;
border-radius: 8px;
background: conic-gradient(from 180deg,
var(--br-accent-warm),
var(--br-accent-mid),
var(--br-accent-cool),
var(--br-accent-warm));
margin-right: 14px;
box-shadow: 0 2px 10px rgba(0,0,0,0.7);
}
.menu-items {
display: flex;
gap: 6px;
flex: 1;
}
.menu-item {
padding: 4px 10px;
border-radius: 8px;
font-size: 12px;
color: var(--br-white);
cursor: pointer;
transition: background 0.14s ease, transform 0.14s ease;
}
.menu-item:hover {
background: rgba(255,255,255,0.05);
transform: translateY(-1px);
}
.system-info {
display: flex;
gap: 10px;
font-size: 11px;
color: var(--br-muted);
font-family: var(--br-font-mono);
}
.system-info span {
padding: 3px 8px;
border-radius: 6px;
border: 1px solid rgba(255,255,255,0.1);
background: rgba(255,255,255,0.03);
}
/* Desktop icons grid (retro layout) */
.desktop-area {
flex: 1;
padding: 20px;
display: grid;
grid-template-columns: repeat(auto-fill, 90px);
grid-auto-rows: 95px;
gap: 22px;
align-content: start;
background-image:
linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
background-size: 24px 24px;
position: relative;
overflow: auto;
}
.icon {
width: 86px;
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
user-select: none;
transition: transform 0.15s ease;
}
.icon:hover {
transform: translateY(-3px);
}
.icon-image {
width: 64px;
height: 64px;
border-radius: var(--br-radius-md);
border: 1px solid var(--br-border-subtle);
background: linear-gradient(135deg, rgba(255,255,255,0.06), rgba(255,255,255,0.02));
display: flex;
align-items: center;
justify-content: center;
font-size: 34px;
margin-bottom: 8px;
box-shadow: 0 4px 18px rgba(0,0,0,0.55);
position: relative;
backdrop-filter: blur(10px);
}
.icon-image::after {
content: '';
position: absolute;
inset: 0;
border-radius: var(--br-radius-md);
background: radial-gradient(circle at 18% 18%, rgba(255,255,255,0.18), transparent 60%);
pointer-events: none;
}
.icon-label {
text-align: center;
font-size: 12px;
font-weight: 500;
text-shadow: 0 2px 8px rgba(0,0,0,0.8);
}
/* BR95 window chrome (retro + brand) */
.window {
position: absolute;
background: var(--br95-gray);
border-radius: 10px;
border-top: 2px solid var(--br95-border-light);
border-left: 2px solid var(--br95-border-light);
border-right: 2px solid var(--br95-border-darkest);
border-bottom: 2px solid var(--br95-border-darkest);
box-shadow:
inset 1px 1px 0 var(--br95-gray-lighter),
0 14px 40px rgba(0,0,0,0.7);
min-width: 420px;
min-height: 260px;
display: none;
z-index: 10;
overflow: hidden;
}
.window.active {
display: flex;
flex-direction: column;
}
.window.maximized {
left: 0 !important;
top: 36px !important;
width: 100% !important;
height: calc(100vh - 36px - 40px) !important;
border-radius: 0;
z-index: 100;
}
.title-bar {
height: 26px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 3px 4px 3px 6px;
background: linear-gradient(90deg,
var(--br-accent-warm),
var(--br-accent-mid),
var(--br-accent-cool));
color: var(--br-white);
cursor: move;
user-select: none;
font-size: 12px;
font-weight: 600;
}
.title-text {
display: flex;
align-items: center;
gap: 6px;
text-shadow: 0 1px 2px rgba(0,0,0,0.6);
}
.title-buttons {
display: flex;
gap: 2px;
}
.title-button {
width: 18px;
height: 18px;
background: var(--br95-gray-light);
border-top: 1px solid var(--br95-border-light);
border-left: 1px solid var(--br95-border-light);
border-right: 1px solid var(--br95-border-darkest);
border-bottom: 1px solid var(--br95-border-darkest);
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 700;
color: var(--br-black);
cursor: pointer;
box-shadow: inset 1px 1px 0 rgba(255,255,255,0.4);
}
.title-button:active {
border-top: 1px solid var(--br95-border-darkest);
border-left: 1px solid var(--br95-border-darkest);
border-right: 1px solid var(--br95-border-light);
border-bottom: 1px solid var(--br95-border-light);
box-shadow: inset 1px 1px 2px rgba(0,0,0,0.4);
}
.window-inner {
flex: 1;
display: flex;
flex-direction: column;
background: var(--br-bg-elevated);
padding: 10px;
color: var(--br-white);
font-size: 13px;
}
.content-header {
padding: 10px 12px;
border-radius: 8px;
background: linear-gradient(135deg,
rgba(255,154,60,0.08),
rgba(255,79,163,0.08),
rgba(50,124,255,0.08));
border: 1px solid rgba(255,255,255,0.06);
margin-bottom: 10px;
}
.content-header h2 {
font-size: 15px;
margin-bottom: 4px;
}
.content-header p {
font-size: 12px;
color: var(--br-muted);
}
.content-body {
flex: 1;
overflow: auto;
padding: 4px 2px 2px;
}
.grid {
display: grid;
gap: 10px;
}
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.card {
background: rgba(4,10,32,0.98);
border-radius: 14px;
border: 1px solid var(--br-border-subtle);
padding: 10px 12px;
box-shadow: 0 8px 20px rgba(0,0,0,0.55);
}
.stat-row {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
padding: 4px 0;
border-bottom: 1px solid rgba(255,255,255,0.04);
}
.stat-row:last-child { border-bottom: none; }
.stat-label { color: var(--br-muted); }
.stat-value { font-family: var(--br-font-mono); }
.badge {
display: inline-flex;
align-items: center;
padding: 2px 8px;
border-radius: 999px;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.badge-success {
background: rgba(34,197,94,0.16);
border: 1px solid rgba(34,197,94,0.5);
color: #22c55e;
}
.badge-info {
background: rgba(105,247,255,0.16);
border: 1px solid rgba(105,247,255,0.45);
color: var(--br-accent-neo);
}
.badge-warning {
background: rgba(251,191,36,0.16);
border: 1px solid rgba(251,191,36,0.45);
color: #fbbf24;
}
.btn-primary {
padding: 7px 16px;
border-radius: 999px;
border: none;
background: linear-gradient(135deg, var(--br-accent-warm), var(--br-accent-mid));
color: var(--br-black);
font-size: 12px;
font-weight: 600;
cursor: pointer;
box-shadow: 0 6px 16px rgba(0,0,0,0.7);
transition: transform 0.14s ease, box-shadow 0.14s ease, filter 0.14s ease;
}
.btn-primary:hover {
transform: translateY(-1px);
filter: brightness(1.05);
box-shadow: 0 10px 22px rgba(0,0,0,0.9);
}
.btn-primary:active {
transform: translateY(1px) scale(0.99);
box-shadow: 0 3px 10px rgba(0,0,0,0.8);
}
/* Terminal styling */
.terminal-screen {
background: var(--br-black);
color: var(--br-accent-neo);
font-family: var(--br-font-mono);
padding: 12px;
font-size: 12px;
height: 100%;
border-radius: 10px;
overflow-y: auto;
}
.terminal-line { margin-bottom: 4px; }
.terminal-prompt { color: var(--br-accent-mid); }
.terminal-cursor {
display: inline-block;
width: 8px;
height: 14px;
background: var(--br-accent-neo);
margin-left: 2px;
animation: blink 1s steps(1) infinite;
}
@keyframes blink {
0%,49% { opacity: 1; }
50%,100% { opacity: 0; }
}
/* Taskbar BR95 style */
.taskbar {
height: 40px;
background: var(--br95-gray-light);
border-top: 2px solid var(--br95-border-light);
display: flex;
align-items: center;
padding: 4px;
gap: 4px;
box-shadow: 0 -6px 18px rgba(0,0,0,0.7);
z-index: 60;
}
.road-button {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
background: var(--br95-gray);
border-top: 2px solid var(--br95-border-light);
border-left: 2px solid var(--br95-border-light);
border-right: 2px solid var(--br95-border-darkest);
border-bottom: 2px solid var(--br95-border-darkest);
cursor: pointer;
font-size: 13px;
font-weight: 700;
color: var(--br-white);
box-shadow: inset 1px 1px 0 rgba(255,255,255,0.4);
}
.road-logo {
width: 22px;
height: 22px;
border-radius: 4px;
background: conic-gradient(from 180deg,
var(--br-accent-warm),
var(--br-accent-mid),
var(--br-accent-cool),
var(--br-accent-warm));
display: inline-block;
}
.taskbar-apps {
flex: 1;
display: flex;
gap: 4px;
padding: 0 6px;
overflow-x: auto;
}
.taskbar-app {
min-width: 120px;
max-width: 160px;
height: 30px;
background: var(--br95-gray);
border-top: 2px solid var(--br95-border-light);
border-left: 2px solid var(--br95-border-light);
border-right: 2px solid var(--br95-border-darkest);
border-bottom: 2px solid var(--br95-border-darkest);
padding: 0 8px;
display: flex;
align-items: center;
font-size: 12px;
color: var(--br-white);
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
box-shadow: inset 1px 1px 0 rgba(255,255,255,0.25);
}
.taskbar-app.active-app {
border-top: 2px solid var(--br95-border-darkest);
border-left: 2px solid var(--br95-border-darkest);
border-right: 2px solid var(--br95-border-light);
border-bottom: 2px solid var(--br95-border-light);
box-shadow: inset 1px 1px 2px rgba(0,0,0,0.5);
background: var(--br95-gray-lighter);
}
.system-tray {
display: flex;
align-items: center;
gap: 6px;
padding: 0 6px;
color: var(--br-white);
font-size: 14px;
}
.clock {
min-width: 80px;
padding: 3px 8px;
border-top: 2px solid var(--br95-border-darkest);
border-left: 2px solid var(--br95-border-darkest);
border-right: 2px solid var(--br95-border-light);
border-bottom: 2px solid var(--br95-border-light);
font-size: 12px;
text-align: center;
background: var(--br95-gray);
font-family: var(--br-font-mono);
}
/* Road menu (Start menu but BlackRoad) */
.road-menu {
position: fixed;
left: 4px;
bottom: 44px;
width: 280px;
background: rgba(4,10,32,0.98);
border-radius: 12px;
border: 1px solid rgba(255,255,255,0.18);
box-shadow: 0 18px 50px rgba(0,0,0,0.85);
display: none;
flex-direction: column;
overflow: hidden;
z-index: 80;
}
.road-menu.active { display: flex; }
.road-menu-header {
padding: 14px;
background: linear-gradient(135deg,
rgba(255,154,60,0.2),
rgba(255,79,163,0.18),
rgba(50,124,255,0.18));
border-bottom: 1px solid rgba(255,255,255,0.18);
}
.road-menu-header h3 {
font-size: 15px;
margin-bottom: 4px;
}
.road-menu-header p {
font-size: 11px;
color: var(--br-muted);
}
.road-menu-content {
padding: 6px;
max-height: 60vh;
overflow-y: auto;
background: rgba(2,5,18,0.96);
}
.road-menu-item {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 8px;
border-radius: 8px;
font-size: 12px;
cursor: pointer;
color: var(--br-muted);
transition: background 0.12s ease, color 0.12s ease;
}
.road-menu-item span.emoji { font-size: 18px; }
.road-menu-item:hover {
background: rgba(255,255,255,0.07);
color: var(--br-white);
}
.road-menu-separator {
height: 1px;
margin: 4px 4px;
background: rgba(255,255,255,0.12);
}
/* Scrollbars */
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track {
background: var(--br95-gray);
border-left: 1px solid var(--br95-border-darkest);
}
::-webkit-scrollbar-thumb {
background: var(--br95-gray-light);
border-top: 2px solid var(--br95-border-light);
border-left: 2px solid var(--br95-border-light);
border-right: 2px solid var(--br95-border-darkest);
border-bottom: 2px solid var(--br95-border-darkest);
}

15
br95/app/layout.tsx Normal file
View File

@@ -0,0 +1,15 @@
import type { Metadata } from 'next';
import './globals.css';
export const metadata: Metadata = {
title: 'BlackRoad OS BR95 Edition',
description: 'BlackRoad OS retro desktop rebuilt as modern Next.js components.',
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}

7
br95/app/page.tsx Normal file
View File

@@ -0,0 +1,7 @@
'use client';
import DesktopLayout from '../components/DesktopLayout';
export default function HomePage() {
return <DesktopLayout />;
}

View File

@@ -0,0 +1,200 @@
import React from 'react';
import AgentsWindow from './windows/AgentsWindow';
import BlackstreamWindow from './windows/BlackstreamWindow';
import LucidiaWindow from './windows/LucidiaWindow';
import MinerWindow from './windows/MinerWindow';
import PiWindow from './windows/PiWindow';
import RoadchainWindow from './windows/RoadchainWindow';
import RoadcraftWindow from './windows/RoadcraftWindow';
import RoadmailWindow from './windows/RoadmailWindow';
import RoadviewWindow from './windows/RoadviewWindow';
import SocialWindow from './windows/SocialWindow';
import TerminalWindow from './windows/TerminalWindow';
import WalletWindow from './windows/WalletWindow';
import BootScreen from './shared/BootScreen';
import Taskbar from './shared/Taskbar';
import RoadMenu from './shared/RoadMenu';
import { useWindowManager, WindowId } from '../hooks/useWindowManager';
const desktopIcons: { id: WindowId; icon: string; label: string }[] = [
{ id: 'lucidia', icon: '🧠', label: 'Lucidia Core' },
{ id: 'agents', icon: '🤖', label: 'AI Agents' },
{ id: 'roadchain', icon: '⛓️', label: 'RoadChain' },
{ id: 'wallet', icon: '💰', label: 'Wallet' },
{ id: 'terminal', icon: '💻', label: 'Terminal' },
{ id: 'roadmail', icon: '📧', label: 'RoadMail' },
{ id: 'social', icon: '👥', label: 'Social' },
{ id: 'blackstream', icon: '📺', label: 'BlackStream' },
{ id: 'roadview', icon: '🌍', label: 'RoadView' },
{ id: 'pi', icon: '🥧', label: 'Pi Network' },
{ id: 'miner', icon: '⛏️', label: 'Miner' },
{ id: 'roadcraft', icon: '⛏️', label: 'RoadCraft' },
];
export default function DesktopLayout() {
const {
windowStates,
openWindows,
activeWindow,
roadMenuOpen,
shellReady,
clockText,
lucidiaStats,
roadchainStats,
walletStats,
minerStats,
menuRef,
menuButtonRef,
openWindow,
closeWindow,
minimizeWindow,
maximizeWindow,
startDrag,
focusWindow,
toggleRoadMenu,
taskbarToggle,
} = useWindowManager();
return (
<>
<div className="scanline"></div>
<BootScreen bootActive={!shellReady} />
<div className={`shell ${shellReady ? 'ready' : ''}`} id="shell">
<div className="menubar">
<div className="menu-logo"></div>
<div className="menu-items">
<div className="menu-item">File</div>
<div className="menu-item">View</div>
<div className="menu-item">Agents</div>
<div className="menu-item">RoadChain</div>
</div>
<div className="system-info">
<span>BlackRoad OS v1.0</span>
<span>Agents: 1000</span>
</div>
</div>
<div className="desktop-area" id="desktop">
{desktopIcons.map((item) => (
<div key={item.id} className="icon" onDoubleClick={() => openWindow(item.id)}>
<div className="icon-image">{item.icon}</div>
<div className="icon-label">{item.label}</div>
</div>
))}
</div>
<LucidiaWindow
state={windowStates.lucidia}
stats={lucidiaStats}
onClose={closeWindow}
onMinimize={minimizeWindow}
onMaximize={maximizeWindow}
onDragStart={startDrag}
onFocus={focusWindow}
/>
<AgentsWindow
state={windowStates.agents}
onClose={closeWindow}
onMinimize={minimizeWindow}
onMaximize={maximizeWindow}
onDragStart={startDrag}
onFocus={focusWindow}
/>
<RoadchainWindow
state={windowStates.roadchain}
stats={roadchainStats}
onClose={closeWindow}
onMinimize={minimizeWindow}
onMaximize={maximizeWindow}
onDragStart={startDrag}
onFocus={focusWindow}
/>
<WalletWindow
state={windowStates.wallet}
stats={walletStats}
onClose={closeWindow}
onMinimize={minimizeWindow}
onMaximize={maximizeWindow}
onDragStart={startDrag}
onFocus={focusWindow}
/>
<TerminalWindow
state={windowStates.terminal}
onClose={closeWindow}
onMinimize={minimizeWindow}
onMaximize={maximizeWindow}
onDragStart={startDrag}
onFocus={focusWindow}
/>
<RoadmailWindow
state={windowStates.roadmail}
onClose={closeWindow}
onMinimize={minimizeWindow}
onMaximize={maximizeWindow}
onDragStart={startDrag}
onFocus={focusWindow}
/>
<SocialWindow
state={windowStates.social}
onClose={closeWindow}
onMinimize={minimizeWindow}
onMaximize={maximizeWindow}
onDragStart={startDrag}
onFocus={focusWindow}
/>
<BlackstreamWindow
state={windowStates.blackstream}
onClose={closeWindow}
onMinimize={minimizeWindow}
onMaximize={maximizeWindow}
onDragStart={startDrag}
onFocus={focusWindow}
/>
<RoadviewWindow
state={windowStates.roadview}
onClose={closeWindow}
onMinimize={minimizeWindow}
onMaximize={maximizeWindow}
onDragStart={startDrag}
onFocus={focusWindow}
/>
<PiWindow
state={windowStates.pi}
onClose={closeWindow}
onMinimize={minimizeWindow}
onMaximize={maximizeWindow}
onDragStart={startDrag}
onFocus={focusWindow}
/>
<MinerWindow
state={windowStates.miner}
stats={minerStats}
onClose={closeWindow}
onMinimize={minimizeWindow}
onMaximize={maximizeWindow}
onDragStart={startDrag}
onFocus={focusWindow}
/>
<RoadcraftWindow
state={windowStates.roadcraft}
onClose={closeWindow}
onMinimize={minimizeWindow}
onMaximize={maximizeWindow}
onDragStart={startDrag}
onFocus={focusWindow}
/>
<Taskbar
openWindows={openWindows}
activeWindow={activeWindow}
clockText={clockText}
onTaskbarClick={(id) => taskbarToggle(id)}
onToggleMenu={toggleRoadMenu}
menuButtonRef={menuButtonRef}
/>
<RoadMenu isOpen={roadMenuOpen} onOpenWindow={(id) => openWindow(id)} menuRef={menuRef} />
</div>
</>
);
}

View File

@@ -0,0 +1,20 @@
import React from 'react';
type Props = {
bootActive: boolean;
};
export default function BootScreen({ bootActive }: Props) {
return (
<div className="boot-screen" id="boot" style={{ pointerEvents: bootActive ? 'auto' : 'none' }}>
<div className="boot-logo-container">
<div className="boot-logo">
<div className="boot-road"></div>
</div>
<div className="boot-title">BlackRoad OS</div>
<div className="boot-sub">BR95 Edition · Safe Passage Through The Chaos</div>
<div className="boot-loading">System Loading</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,42 @@
import React from 'react';
import { WindowId } from '../../hooks/useWindowManager';
type Props = {
isOpen: boolean;
onOpenWindow: (id: WindowId) => void;
menuRef: React.RefObject<HTMLDivElement>;
};
const items: { id: WindowId; icon: string; label: string }[] = [
{ id: 'lucidia', icon: '🧠', label: 'Lucidia Core' },
{ id: 'agents', icon: '🤖', label: 'AI Agents' },
{ id: 'roadchain', icon: '⛓️', label: 'RoadChain Explorer' },
{ id: 'wallet', icon: '💰', label: 'RoadCoin Wallet' },
{ id: 'roadmail', icon: '📧', label: 'RoadMail' },
{ id: 'terminal', icon: '💻', label: 'Terminal' },
{ id: 'pi', icon: '🥧', label: 'Pi Network' },
{ id: 'miner', icon: '⛏️', label: 'RoadCoin Miner' },
{ id: 'roadcraft', icon: '⛏️', label: 'RoadCraft' },
];
export default function RoadMenu({ isOpen, onOpenWindow, menuRef }: Props) {
return (
<div className={`road-menu ${isOpen ? 'active' : ''}`} id="road-menu" ref={menuRef}>
<div className="road-menu-header">
<h3>BlackRoad OS</h3>
<p>BR95 Desktop · Agent Orchestration</p>
</div>
<div className="road-menu-content">
{items.map((item, index) => (
<React.Fragment key={item.id}>
<div className="road-menu-item" onClick={() => onOpenWindow(item.id)}>
<span className="emoji">{item.icon}</span>
<span>{item.label}</span>
</div>
{index === 2 || index === 5 ? <div className="road-menu-separator"></div> : null}
</React.Fragment>
))}
</div>
</div>
);
}

View File

@@ -0,0 +1,63 @@
import React from 'react';
import { WindowId } from '../../hooks/useWindowManager';
type Props = {
openWindows: WindowId[];
activeWindow: WindowId | null;
clockText: string;
onTaskbarClick: (id: WindowId) => void;
onToggleMenu: () => void;
menuButtonRef: React.RefObject<HTMLDivElement>;
};
const titles: Record<WindowId, string> = {
lucidia: '🧠 Lucidia',
agents: '🤖 Agents',
roadchain: '⛓️ Chain',
wallet: '💰 Wallet',
roadmail: '📧 Mail',
social: '👥 Social',
blackstream: '📺 Stream',
roadview: '🌍 RoadView',
terminal: '💻 Terminal',
pi: '🥧 Pi',
miner: '⛏️ Miner',
roadcraft: '⛏️ RoadCraft',
};
export default function Taskbar({
openWindows,
activeWindow,
clockText,
onTaskbarClick,
onToggleMenu,
menuButtonRef,
}: Props) {
return (
<div className="taskbar">
<div className="road-button" id="road-button" onClick={onToggleMenu} ref={menuButtonRef}>
<span className="road-logo"></span>
<span>Road</span>
</div>
<div className="taskbar-apps" id="taskbar-apps">
{openWindows.map((id) => (
<div
key={id}
className={`taskbar-app ${activeWindow === id ? 'active-app' : ''}`}
onClick={() => onTaskbarClick(id)}
>
{titles[id] ?? id}
</div>
))}
</div>
<div className="system-tray">
<span>🌐</span>
<span>🔊</span>
<span></span>
</div>
<div className="clock" id="clock">
{clockText}
</div>
</div>
);
}

View File

@@ -0,0 +1,67 @@
import React from 'react';
import { WindowState } from '../../hooks/useWindowManager';
type Props = {
id: string;
title: string;
icon: string;
state: WindowState;
children: React.ReactNode;
onClose: (id: string) => void;
onMinimize: (id: string) => void;
onMaximize: (id: string) => void;
onDragStart: (id: string, event: React.MouseEvent) => void;
onFocus: (id: string) => void;
};
export default function WindowFrame({
id,
title,
icon,
state,
children,
onClose,
onMinimize,
onMaximize,
onDragStart,
onFocus,
}: Props) {
if (!state) return null;
const style = state.isMaximized
? { zIndex: state.zIndex }
: {
left: state.position.x,
top: state.position.y,
width: state.size.width,
height: state.size.height,
zIndex: state.zIndex,
};
return (
<div
className={`window ${state.isOpen ? 'active' : ''} ${state.isMaximized ? 'maximized' : ''}`}
style={style}
onMouseDown={() => onFocus(id)}
>
<div className="title-bar" onMouseDown={(e) => onDragStart(id, e)}>
<div className="title-text">
<span>{icon}</span>
<span>{title}</span>
</div>
<div className="title-buttons">
<div className="title-button" onClick={() => onMinimize(id)}>
_
</div>
<div className="title-button" onClick={() => onMaximize(id)}>
</div>
<div className="title-button" onClick={() => onClose(id)}>
×
</div>
</div>
</div>
{children}
</div>
);
}

View File

@@ -0,0 +1,46 @@
import React from 'react';
import WindowFrame from '../shared/WindowFrame';
import { WindowState } from '../../hooks/useWindowManager';
type Props = {
state: WindowState;
onClose: (id: string) => void;
onMinimize: (id: string) => void;
onMaximize: (id: string) => void;
onDragStart: (id: string, event: React.MouseEvent) => void;
onFocus: (id: string) => void;
};
export default function AgentsWindow({ state, ...handlers }: Props) {
return (
<WindowFrame id="agents" title="AI Agent Orchestration" icon="🤖" state={state} {...handlers}>
<div className="window-inner">
<div className="content-header">
<h2>1,000 Unique Agents</h2>
<p>Each with names, memories, families &amp; Unity homes</p>
</div>
<div className="content-body">
<div className="grid grid-2">
<div className="card">
<div style={{ textAlign: 'center', fontSize: '28px', marginBottom: '6px' }}>🤖</div>
<div className="stat-row"><span className="stat-label">ID</span><span className="stat-value">Agent042</span></div>
<div className="stat-row"><span className="stat-label">Persona</span><span className="stat-value">Alice</span></div>
<div className="stat-row"><span className="stat-label">Status</span><span className="badge badge-success">Active</span></div>
<div className="stat-row"><span className="stat-label">Tasks Today</span><span className="stat-value">247</span></div>
</div>
<div className="card">
<div style={{ textAlign: 'center', fontSize: '28px', marginBottom: '6px' }}>🤖</div>
<div className="stat-row"><span className="stat-label">ID</span><span className="stat-value">Agent189</span></div>
<div className="stat-row"><span className="stat-label">Persona</span><span className="stat-value">Marcus</span></div>
<div className="stat-row"><span className="stat-label">Status</span><span className="badge badge-success">Active</span></div>
<div className="stat-row"><span className="stat-label">Tasks Today</span><span className="stat-value">189</span></div>
</div>
</div>
<div style={{ marginTop: 12, textAlign: 'center' }}>
<button className="btn-primary">View All 1000 Agents</button>
</div>
</div>
</div>
</WindowFrame>
);
}

View File

@@ -0,0 +1,33 @@
import React from 'react';
import WindowFrame from '../shared/WindowFrame';
import { WindowState } from '../../hooks/useWindowManager';
type Props = {
state: WindowState;
onClose: (id: string) => void;
onMinimize: (id: string) => void;
onMaximize: (id: string) => void;
onDragStart: (id: string, event: React.MouseEvent) => void;
onFocus: (id: string) => void;
};
export default function BlackstreamWindow({ state, ...handlers }: Props) {
return (
<WindowFrame id="blackstream" title="BlackStream" icon="📺" state={state} {...handlers}>
<div className="window-inner">
<div className="content-header">
<h2>Trending Streams</h2>
<p>OS demos, agent orchestration runs, and live RoadChain dashboards</p>
</div>
<div className="content-body">
<div
className="card"
style={{ height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 13, color: 'var(--br-muted)' }}
>
Embedded stream player coming soon.
</div>
</div>
</div>
</WindowFrame>
);
}

View File

@@ -0,0 +1,79 @@
import React from 'react';
import WindowFrame from '../shared/WindowFrame';
import { LucidiaStats, WindowState } from '../../hooks/useWindowManager';
type Props = {
state: WindowState;
stats: LucidiaStats;
onClose: (id: string) => void;
onMinimize: (id: string) => void;
onMaximize: (id: string) => void;
onDragStart: (id: string, event: React.MouseEvent) => void;
onFocus: (id: string) => void;
};
export default function LucidiaWindow({ state, stats, ...handlers }: Props) {
return (
<WindowFrame id="lucidia" title="Lucidia Core System" icon="🧠" state={state} {...handlers}>
<div className="window-inner">
<div className="content-header">
<h2>Recursive AI Engine</h2>
<p>Trinary logic · Paraconsistent reasoning · PSSHA memory</p>
</div>
<div className="content-body">
<div className="grid grid-2">
<div className="card">
<div className="stat-row">
<span className="stat-label">Active Agents</span>
<span className="stat-value">{stats.activeAgents} / {stats.totalAgents}</span>
</div>
<div className="stat-row">
<span className="stat-label">Memory Journals</span>
<span className="stat-value">{stats.memoryJournals} streams</span>
</div>
<div className="stat-row">
<span className="stat-label">Event Bus</span>
<span className="stat-value">{stats.eventBusRate} events/sec</span>
</div>
<div className="stat-row">
<span className="stat-label">Uptime</span>
<span className="stat-value">{stats.uptime.toFixed(2)}%</span>
</div>
</div>
<div className="card">
<div className="stat-row">
<span className="stat-label">Mode</span>
<span className="stat-value">Trinary (1/0/1)</span>
</div>
<div className="stat-row">
<span className="stat-label">Contradictions</span>
<span className="stat-value">Paraconsistent</span>
</div>
<div className="stat-row">
<span className="stat-label">Memory Hash</span>
<span className="stat-value">PSSHA</span>
</div>
<div className="stat-row">
<span className="stat-label">Coordination</span>
<span className="stat-value">Hybrid P2P</span>
</div>
</div>
</div>
<div className="card" style={{ marginTop: 10 }}>
<div style={{ fontSize: '12px' }}>
<div style={{ marginBottom: '6px' }}>
<span className="badge badge-success">Agent042</span> proposed new routing rule 2m ago
</div>
<div style={{ marginBottom: '6px' }}>
<span className="badge badge-info">Agent189</span> memory journal sync complete 5m ago
</div>
<div>
<span className="badge badge-warning">Agent734</span> contradiction quarantined 12m ago
</div>
</div>
</div>
</div>
</div>
</WindowFrame>
);
}

View File

@@ -0,0 +1,33 @@
import React from 'react';
import WindowFrame from '../shared/WindowFrame';
import { MinerStats, WindowState } from '../../hooks/useWindowManager';
type Props = {
state: WindowState;
stats: MinerStats;
onClose: (id: string) => void;
onMinimize: (id: string) => void;
onMaximize: (id: string) => void;
onDragStart: (id: string, event: React.MouseEvent) => void;
onFocus: (id: string) => void;
};
export default function MinerWindow({ state, stats, ...handlers }: Props) {
return (
<WindowFrame id="miner" title="RoadCoin Miner" icon="⛏️" state={state} {...handlers}>
<div className="window-inner">
<div className="content-header">
<h2>Mining Active</h2>
<p>Contributing work to RoadChain network</p>
</div>
<div className="content-body">
<div className="card">
<div className="stat-row"><span className="stat-label">Hashrate</span><span className="stat-value">{stats.hashRate}</span></div>
<div className="stat-row"><span className="stat-label">Accepted Shares</span><span className="stat-value">{stats.sharesAccepted.toLocaleString()}</span></div>
<div className="stat-row"><span className="stat-label">Pool</span><span className="stat-value">{stats.poolName}</span></div>
</div>
</div>
</div>
</WindowFrame>
);
}

View File

@@ -0,0 +1,31 @@
import React from 'react';
import WindowFrame from '../shared/WindowFrame';
import { WindowState } from '../../hooks/useWindowManager';
type Props = {
state: WindowState;
onClose: (id: string) => void;
onMinimize: (id: string) => void;
onMaximize: (id: string) => void;
onDragStart: (id: string, event: React.MouseEvent) => void;
onFocus: (id: string) => void;
};
export default function PiWindow({ state, ...handlers }: Props) {
return (
<WindowFrame id="pi" title="Pi Network Control Panel" icon="🥧" state={state} {...handlers}>
<div className="window-inner">
<div className="content-header">
<h2>Connected Devices</h2>
<p>4 devices online · 1 Jetson Orin Nano</p>
</div>
<div className="content-body">
<div className="card">
<div className="stat-row"><span className="stat-label">Jetson Orin Nano</span><span className="badge badge-success">Online</span></div>
<div className="stat-row"><span className="stat-label">LucidiaPi01</span><span className="badge badge-success">Online</span></div>
</div>
</div>
</div>
</WindowFrame>
);
}

View File

@@ -0,0 +1,56 @@
import React from 'react';
import WindowFrame from '../shared/WindowFrame';
import { RoadChainStats, WindowState } from '../../hooks/useWindowManager';
type Props = {
state: WindowState;
stats: RoadChainStats;
onClose: (id: string) => void;
onMinimize: (id: string) => void;
onMaximize: (id: string) => void;
onDragStart: (id: string, event: React.MouseEvent) => void;
onFocus: (id: string) => void;
};
export default function RoadchainWindow({ state, stats, ...handlers }: Props) {
return (
<WindowFrame id="roadchain" title="RoadChain Explorer" icon="⛓️" state={state} {...handlers}>
<div className="window-inner">
<div className="content-header">
<h2>RoadChain Network</h2>
<p>Transparent AI governance on a distributed ledger</p>
</div>
<div className="content-body">
<div className="grid grid-2" style={{ marginBottom: 10 }}>
<div className="card">
<div className="stat-row"><span className="stat-label">Block Height</span><span className="stat-value">{stats.currentBlock.toLocaleString()}</span></div>
<div className="stat-row"><span className="stat-label">Network Hash</span><span className="stat-value">{stats.networkHashrate}</span></div>
<div className="stat-row"><span className="stat-label">Active Nodes</span><span className="stat-value">{stats.activeNodes.toLocaleString()}</span></div>
</div>
<div className="card">
<div className="stat-row"><span className="stat-label">Your Hashrate</span><span className="stat-value">{stats.yourHashrate}</span></div>
<div className="stat-row"><span className="stat-label">Shares</span><span className="stat-value">{stats.shares}</span></div>
<div className="stat-row"><span className="stat-label">Daily Earnings</span><span className="stat-value">{stats.dailyEarnings}</span></div>
</div>
</div>
<div className="card">
<div style={{ fontFamily: 'var(--br-font-mono)', fontSize: '12px' }}>
<div style={{ marginBottom: '8px' }}>
<strong>Block #1,247,891</strong> 23s ago<br />
<span style={{ color: 'var(--br-muted)' }}>Hash: 0x8f4a2...c7b9d 247 tx</span>
</div>
<div style={{ marginBottom: '8px' }}>
<strong>Block #1,247,890</strong> 2m ago<br />
<span style={{ color: 'var(--br-muted)' }}>Hash: 0x3c9e1...f2a4b 189 tx</span>
</div>
<div>
<strong>Block #1,247,889</strong> 4m ago<br />
<span style={{ color: 'var(--br-muted)' }}>Hash: 0x7d2b3...e8c1f 312 tx</span>
</div>
</div>
</div>
</div>
</div>
</WindowFrame>
);
}

View File

@@ -0,0 +1,28 @@
import React from 'react';
import WindowFrame from '../shared/WindowFrame';
import { WindowState } from '../../hooks/useWindowManager';
type Props = {
state: WindowState;
onClose: (id: string) => void;
onMinimize: (id: string) => void;
onMaximize: (id: string) => void;
onDragStart: (id: string, event: React.MouseEvent) => void;
onFocus: (id: string) => void;
};
export default function RoadcraftWindow({ state, ...handlers }: Props) {
return (
<WindowFrame id="roadcraft" title="RoadCraft" icon="⛏️" state={state} {...handlers}>
<div className="window-inner">
<div className="content-header">
<h2>Voxel World Builder</h2>
<p>Design agent habitats &amp; quantum sandboxes</p>
</div>
<div className="content-body" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<button className="btn-primary">New World</button>
</div>
</div>
</WindowFrame>
);
}

View File

@@ -0,0 +1,31 @@
import React from 'react';
import WindowFrame from '../shared/WindowFrame';
import { WindowState } from '../../hooks/useWindowManager';
type Props = {
state: WindowState;
onClose: (id: string) => void;
onMinimize: (id: string) => void;
onMaximize: (id: string) => void;
onDragStart: (id: string, event: React.MouseEvent) => void;
onFocus: (id: string) => void;
};
export default function RoadmailWindow({ state, ...handlers }: Props) {
return (
<WindowFrame id="roadmail" title="RoadMail" icon="📧" state={state} {...handlers}>
<div className="window-inner">
<div className="content-header">
<h2>Inbox</h2>
<p>Secure AIaware communication</p>
</div>
<div className="content-body">
<div className="card">
<div style={{ fontSize: 13, fontWeight: 600, marginBottom: 4 }}>Welcome to BlackRoad OS</div>
<div style={{ fontSize: 11, color: 'var(--br-muted)' }}>From: BlackRoad Team Today</div>
</div>
</div>
</div>
</WindowFrame>
);
}

View File

@@ -0,0 +1,34 @@
import React from 'react';
import WindowFrame from '../shared/WindowFrame';
import { WindowState } from '../../hooks/useWindowManager';
type Props = {
state: WindowState;
onClose: (id: string) => void;
onMinimize: (id: string) => void;
onMaximize: (id: string) => void;
onDragStart: (id: string, event: React.MouseEvent) => void;
onFocus: (id: string) => void;
};
export default function RoadviewWindow({ state, ...handlers }: Props) {
return (
<WindowFrame id="roadview" title="RoadView Browser" icon="🌍" state={state} {...handlers}>
<div className="window-inner">
<div className="content-header">
<h2>Welcome to BlackRoad</h2>
<p>The complete AI orchestration ecosystem, in one desktop.</p>
</div>
<div className="content-body">
<div className="card">
<p style={{ fontSize: 13, color: 'var(--br-muted)' }}>
This browser surface will eventually connect to your real BlackRoad services:
<code>core.blackroad.systems</code>, <code>api.blackroad.systems</code>,
<code>agents.blackroad.systems</code>, and more.
</p>
</div>
</div>
</div>
</WindowFrame>
);
}

View File

@@ -0,0 +1,33 @@
import React from 'react';
import WindowFrame from '../shared/WindowFrame';
import { WindowState } from '../../hooks/useWindowManager';
type Props = {
state: WindowState;
onClose: (id: string) => void;
onMinimize: (id: string) => void;
onMaximize: (id: string) => void;
onDragStart: (id: string, event: React.MouseEvent) => void;
onFocus: (id: string) => void;
};
export default function SocialWindow({ state, ...handlers }: Props) {
return (
<WindowFrame id="social" title="BlackRoad Social" icon="👥" state={state} {...handlers}>
<div className="window-inner">
<div className="content-header">
<h2>Your Feed</h2>
<p>Operators, agents, and ledgers talking in real time</p>
</div>
<div className="content-body">
<div className="card">
<div style={{ fontSize: 12 }}>
<strong>Prism Console ·</strong> New deployment shipped to RoadChain.<br />
<span style={{ color: 'var(--br-muted)', fontSize: 11 }}>2 minutes ago</span>
</div>
</div>
</div>
</div>
</WindowFrame>
);
}

View File

@@ -0,0 +1,44 @@
import React from 'react';
import WindowFrame from '../shared/WindowFrame';
import { WindowState } from '../../hooks/useWindowManager';
type Props = {
state: WindowState;
onClose: (id: string) => void;
onMinimize: (id: string) => void;
onMaximize: (id: string) => void;
onDragStart: (id: string, event: React.MouseEvent) => void;
onFocus: (id: string) => void;
};
export default function TerminalWindow({ state, ...handlers }: Props) {
return (
<WindowFrame id="terminal" title="C:\\BLACKROAD\\TERMINAL.EXE" icon="💻" state={state} {...handlers}>
<div className="window-inner" style={{ padding: 8 }}>
<div className="terminal-screen">
<div className="terminal-line">BlackRoad OS Terminal v2.4.1</div>
<div className="terminal-line">Copyright (c) 2024 BlackRoad Inc.</div>
<div className="terminal-line" style={{ marginTop: 10 }}></div>
<div className="terminal-line" style={{ marginTop: 10 }}>
<span className="terminal-prompt">blackroad@cecilia:~$</span> lucidia status
</div>
<div className="terminal-line" style={{ marginLeft: 18 }}> Lucidia Core: OPERATIONAL</div>
<div className="terminal-line" style={{ marginLeft: 18 }}> Active Agents: 1000/1000</div>
<div className="terminal-line" style={{ marginLeft: 18 }}> Memory Journals: 1000 active streams</div>
<div className="terminal-line" style={{ marginLeft: 18 }}> Event Bus: 847 events/sec</div>
<div className="terminal-line" style={{ marginLeft: 18 }}> System Health: 99.95%</div>
<div className="terminal-line" style={{ marginTop: 10 }}>
<span className="terminal-prompt">blackroad@cecilia:~$</span> roadchain sync
</div>
<div className="terminal-line" style={{ marginLeft: 18 }}>Syncing with RoadChain network...</div>
<div className="terminal-line" style={{ marginLeft: 18 }}> Block height: 1,247,891</div>
<div className="terminal-line" style={{ marginLeft: 18 }}> Peers: 2847 connected</div>
<div className="terminal-line" style={{ marginTop: 10 }}>
<span className="terminal-prompt">blackroad@cecilia:~$</span>
<span className="terminal-cursor"></span>
</div>
</div>
</div>
</WindowFrame>
);
}

View File

@@ -0,0 +1,52 @@
import React from 'react';
import WindowFrame from '../shared/WindowFrame';
import { WalletStats, WindowState } from '../../hooks/useWindowManager';
type Props = {
state: WindowState;
stats: WalletStats;
onClose: (id: string) => void;
onMinimize: (id: string) => void;
onMaximize: (id: string) => void;
onDragStart: (id: string, event: React.MouseEvent) => void;
onFocus: (id: string) => void;
};
export default function WalletWindow({ state, stats, ...handlers }: Props) {
return (
<WindowFrame id="wallet" title="RoadCoin Wallet" icon="💰" state={state} {...handlers}>
<div className="window-inner">
<div className="content-header">
<h2 style={{ fontSize: 26 }}>{stats.balanceRC.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} RC</h2>
<p> ${stats.balanceUSD.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} USD Synced with RoadChain</p>
</div>
<div className="content-body">
<div style={{ display: 'flex', gap: 8, marginBottom: 10 }}>
<button className="btn-primary">📤 Send</button>
<button className="btn-primary" style={{ background: 'rgba(255,255,255,0.06)', color: 'var(--br-white)' }}>
📥 Receive
</button>
</div>
<div className="card">
<div className="stat-row">
<div>
<div style={{ fontWeight: 600, marginBottom: 2 }}>Received</div>
<div style={{ fontSize: 11, color: 'var(--br-muted)' }}>Mining rewards 2h ago</div>
</div>
<div style={{ color: '#22c55e', fontWeight: 600 }}>+47.23 RC</div>
</div>
</div>
<div className="card">
<div className="stat-row">
<div>
<div style={{ fontWeight: 600, marginBottom: 2 }}>Sent</div>
<div style={{ fontSize: 11, color: 'var(--br-muted)' }}>Payment to Alice Yesterday</div>
</div>
<div style={{ color: '#ef4444', fontWeight: 600 }}>12.50 RC</div>
</div>
</div>
</div>
</div>
</WindowFrame>
);
}

View File

@@ -0,0 +1,412 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
export type WindowState = {
id: string;
position: { x: number; y: number };
size: { width: number; height: number };
isOpen: boolean;
isMaximized: boolean;
zIndex: number;
};
export type LucidiaStats = {
status: string;
activeAgents: number;
totalAgents: number;
memoryJournals: number;
eventBusRate: number;
uptime: number;
};
export type RoadChainStats = {
currentBlock: number;
networkHashrate: string;
activeNodes: number;
yourHashrate: string;
shares: string;
dailyEarnings: string;
};
export type WalletStats = {
balanceRC: number;
balanceUSD: number;
};
export type MinerStats = {
hashRate: string;
sharesAccepted: number;
poolName: string;
};
export type WindowId =
| 'lucidia'
| 'agents'
| 'roadchain'
| 'wallet'
| 'terminal'
| 'roadmail'
| 'social'
| 'blackstream'
| 'roadview'
| 'pi'
| 'miner'
| 'roadcraft';
const API_BASE = '/api/br95';
const WINDOW_PRESETS: Record<WindowId, WindowState> = {
lucidia: { id: 'lucidia', position: { x: 60, y: 90 }, size: { width: 680, height: 420 }, isOpen: false, isMaximized: false, zIndex: 10 },
agents: { id: 'agents', position: { x: 120, y: 120 }, size: { width: 760, height: 460 }, isOpen: false, isMaximized: false, zIndex: 10 },
roadchain: { id: 'roadchain', position: { x: 180, y: 80 }, size: { width: 760, height: 440 }, isOpen: false, isMaximized: false, zIndex: 10 },
wallet: { id: 'wallet', position: { x: 220, y: 130 }, size: { width: 520, height: 380 }, isOpen: false, isMaximized: false, zIndex: 10 },
terminal: { id: 'terminal', position: { x: 140, y: 180 }, size: { width: 720, height: 420 }, isOpen: false, isMaximized: false, zIndex: 10 },
roadmail: { id: 'roadmail', position: { x: 80, y: 80 }, size: { width: 640, height: 380 }, isOpen: false, isMaximized: false, zIndex: 10 },
social: { id: 'social', position: { x: 160, y: 90 }, size: { width: 640, height: 380 }, isOpen: false, isMaximized: false, zIndex: 10 },
blackstream: { id: 'blackstream', position: { x: 200, y: 100 }, size: { width: 720, height: 420 }, isOpen: false, isMaximized: false, zIndex: 10 },
roadview: { id: 'roadview', position: { x: 120, y: 70 }, size: { width: 820, height: 460 }, isOpen: false, isMaximized: false, zIndex: 10 },
pi: { id: 'pi', position: { x: 220, y: 140 }, size: { width: 540, height: 320 }, isOpen: false, isMaximized: false, zIndex: 10 },
miner: { id: 'miner', position: { x: 260, y: 120 }, size: { width: 560, height: 320 }, isOpen: false, isMaximized: false, zIndex: 10 },
roadcraft: { id: 'roadcraft', position: { x: 300, y: 160 }, size: { width: 600, height: 360 }, isOpen: false, isMaximized: false, zIndex: 10 },
};
export function useWindowManager() {
const [windowStates, setWindowStates] = useState<Record<WindowId, WindowState>>(WINDOW_PRESETS);
const [openWindows, setOpenWindows] = useState<WindowId[]>([]);
const [activeWindow, setActiveWindow] = useState<WindowId | null>(null);
const [roadMenuOpen, setRoadMenuOpen] = useState(false);
const [shellReady, setShellReady] = useState(false);
const [clock, setClock] = useState(() => new Date());
const zIndexRef = useRef(10);
const dragRef = useRef<{ id: WindowId; offsetX: number; offsetY: number } | null>(null);
const menuRef = useRef<HTMLDivElement | null>(null);
const menuButtonRef = useRef<HTMLDivElement | null>(null);
const wsRef = useRef<WebSocket | null>(null);
const reconnectRef = useRef<NodeJS.Timeout | null>(null);
const [lucidiaStats, setLucidiaStats] = useState<LucidiaStats>({
status: 'OPERATIONAL',
activeAgents: 1000,
totalAgents: 1000,
memoryJournals: 1000,
eventBusRate: 847,
uptime: 99.95,
});
const [roadchainStats, setRoadchainStats] = useState<RoadChainStats>({
currentBlock: 1247891,
networkHashrate: '847.3 TH/s',
activeNodes: 2847,
yourHashrate: '1.2 GH/s',
shares: '8,423 accepted',
dailyEarnings: '47.23 RC',
});
const [walletStats, setWalletStats] = useState<WalletStats>({
balanceRC: 1247.89,
balanceUSD: 18705,
});
const [minerStats, setMinerStats] = useState<MinerStats>({
hashRate: '1.2 GH/s',
sharesAccepted: 8423,
poolName: 'BRGlobal01',
});
useEffect(() => {
const timer = setTimeout(() => setShellReady(true), 2400);
return () => clearTimeout(timer);
}, []);
useEffect(() => {
const interval = setInterval(() => setClock(new Date()), 1000);
return () => clearInterval(interval);
}, []);
useEffect(() => {
const handleClick = (event: MouseEvent) => {
if (!roadMenuOpen) return;
const target = event.target as Node;
if (menuRef.current?.contains(target) || menuButtonRef.current?.contains(target)) {
return;
}
setRoadMenuOpen(false);
};
document.addEventListener('click', handleClick);
return () => document.removeEventListener('click', handleClick);
}, [roadMenuOpen]);
const focusWindow = useCallback((id: WindowId) => {
setWindowStates((prev) => {
const current = prev[id];
if (!current) return prev;
const nextZ = ++zIndexRef.current;
return { ...prev, [id]: { ...current, zIndex: nextZ } };
});
setActiveWindow(id);
}, []);
const openWindow = useCallback((id: WindowId) => {
setWindowStates((prev) => {
const current = prev[id];
if (!current) return prev;
const nextZ = ++zIndexRef.current;
return { ...prev, [id]: { ...current, isOpen: true, zIndex: nextZ } };
});
setActiveWindow(id);
setOpenWindows((prev) => (prev.includes(id) ? prev : [...prev, id]));
setRoadMenuOpen(false);
}, []);
const closeWindow = useCallback((id: WindowId) => {
setWindowStates((prev) => {
const current = prev[id];
if (!current) return prev;
return { ...prev, [id]: { ...current, isOpen: false } };
});
setOpenWindows((prev) => prev.filter((win) => win !== id));
setActiveWindow((prev) => (prev === id ? null : prev));
}, []);
const minimizeWindow = useCallback((id: WindowId) => {
setWindowStates((prev) => {
const current = prev[id];
if (!current) return prev;
return { ...prev, [id]: { ...current, isOpen: false } };
});
setActiveWindow((prev) => (prev === id ? null : prev));
}, []);
const maximizeWindow = useCallback((id: WindowId) => {
setWindowStates((prev) => {
const current = prev[id];
if (!current) return prev;
const nextZ = ++zIndexRef.current;
return { ...prev, [id]: { ...current, isMaximized: !current.isMaximized, zIndex: nextZ } };
});
setActiveWindow(id);
}, []);
const startDrag = useCallback((id: WindowId, event: React.MouseEvent) => {
event.preventDefault();
setWindowStates((prev) => {
const current = prev[id];
if (!current || current.isMaximized) return prev;
dragRef.current = {
id,
offsetX: event.clientX - current.position.x,
offsetY: event.clientY - current.position.y,
};
const nextZ = ++zIndexRef.current;
return { ...prev, [id]: { ...current, zIndex: nextZ } };
});
setActiveWindow(id);
}, []);
useEffect(() => {
const handleMove = (event: MouseEvent) => {
if (!dragRef.current) return;
setWindowStates((prev) => {
const current = prev[dragRef.current!.id];
if (!current || current.isMaximized) return prev;
const nextPosition = {
x: event.clientX - dragRef.current!.offsetX,
y: event.clientY - dragRef.current!.offsetY,
};
return { ...prev, [current.id]: { ...current, position: nextPosition } };
});
};
const handleUp = () => {
dragRef.current = null;
};
window.addEventListener('mousemove', handleMove);
window.addEventListener('mouseup', handleUp);
return () => {
window.removeEventListener('mousemove', handleMove);
window.removeEventListener('mouseup', handleUp);
};
}, []);
const toggleRoadMenu = useCallback(() => {
setRoadMenuOpen((prev) => !prev);
}, []);
const taskbarToggle = useCallback(
(id: WindowId) => {
if (windowStates[id]?.isOpen && activeWindow === id) {
minimizeWindow(id);
} else {
openWindow(id);
}
},
[activeWindow, minimizeWindow, openWindow, windowStates],
);
const clockText = useMemo(() => clock.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }), [clock]);
const fetchLucidia = useCallback(async () => {
try {
const response = await fetch(`${API_BASE}/lucidia`);
const data = await response.json();
setLucidiaStats((prev) => ({
status: data.status ? String(data.status).toUpperCase() : prev.status,
activeAgents: data.active_agents ?? prev.activeAgents,
totalAgents: data.total_agents ?? prev.totalAgents,
memoryJournals: data.memory_journals ?? prev.memoryJournals,
eventBusRate: data.event_bus_rate ?? prev.eventBusRate,
uptime: data.system_health ?? prev.uptime,
}));
} catch (error) {
console.error('Failed to fetch Lucidia stats:', error);
}
}, []);
const fetchRoadchain = useCallback(async () => {
try {
const response = await fetch(`${API_BASE}/roadchain`);
const data = await response.json();
setRoadchainStats((prev) => ({
currentBlock: data.current_block ?? prev.currentBlock,
networkHashrate: data.network_hashrate ?? prev.networkHashrate,
activeNodes: data.active_nodes ?? prev.activeNodes,
yourHashrate: data.network_hashrate ?? prev.yourHashrate,
shares: prev.shares,
dailyEarnings: prev.dailyEarnings,
}));
} catch (error) {
console.error('Failed to fetch RoadChain stats:', error);
}
}, []);
const fetchWallet = useCallback(async () => {
try {
const response = await fetch(`${API_BASE}/wallet`);
const data = await response.json();
setWalletStats((prev) => ({
balanceRC: data.balance_rc ?? prev.balanceRC,
balanceUSD: data.balance_usd ?? prev.balanceUSD,
}));
} catch (error) {
console.error('Failed to fetch Wallet stats:', error);
}
}, []);
const fetchMiner = useCallback(async () => {
try {
const response = await fetch(`${API_BASE}/miner`);
const data = await response.json();
setMinerStats((prev) => ({
hashRate: data.hash_rate ?? prev.hashRate,
sharesAccepted: data.shares_accepted ?? prev.sharesAccepted,
poolName: data.pool_name ?? prev.poolName,
}));
} catch (error) {
console.error('Failed to fetch Miner stats:', error);
}
}, []);
const connectWebSocket = useCallback(() => {
if (typeof window === 'undefined') return;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}${API_BASE}/ws`;
const socket = new WebSocket(wsUrl);
wsRef.current = socket;
socket.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
switch (message.type) {
case 'miner_update':
setMinerStats((prev) => ({
...prev,
hashRate: message.data?.hash_rate ?? prev.hashRate,
sharesAccepted: message.data?.shares_accepted ?? prev.sharesAccepted,
}));
break;
case 'roadchain_update':
setRoadchainStats((prev) => ({
...prev,
currentBlock: message.data?.current_block ?? prev.currentBlock,
activeNodes: message.data?.active_nodes ?? prev.activeNodes,
}));
break;
case 'wallet_update':
setWalletStats((prev) => ({
...prev,
balanceRC: message.data?.balance_rc ?? prev.balanceRC,
balanceUSD: message.data?.balance_usd ?? prev.balanceUSD,
}));
break;
default:
break;
}
} catch (error) {
console.error('Failed to parse WebSocket message:', error);
}
};
socket.onclose = () => {
if (reconnectRef.current) {
clearTimeout(reconnectRef.current);
}
reconnectRef.current = setTimeout(connectWebSocket, 5000);
};
socket.onerror = (error) => {
console.error('WebSocket error:', error);
};
}, []);
useEffect(() => {
if (!shellReady) return;
let lucidiaInterval: NodeJS.Timeout | null = null;
const startApis = () => {
fetchLucidia();
fetchRoadchain();
fetchWallet();
fetchMiner();
connectWebSocket();
lucidiaInterval = setInterval(fetchLucidia, 30000);
};
const timer = setTimeout(startApis, 3000);
return () => {
clearTimeout(timer);
if (lucidiaInterval) {
clearInterval(lucidiaInterval);
}
if (reconnectRef.current) {
clearTimeout(reconnectRef.current);
}
wsRef.current?.close();
};
}, [connectWebSocket, fetchLucidia, fetchMiner, fetchRoadchain, fetchWallet, shellReady]);
return {
windowStates,
openWindows,
activeWindow,
roadMenuOpen,
shellReady,
clockText,
lucidiaStats,
roadchainStats,
walletStats,
minerStats,
menuRef,
menuButtonRef,
openWindow,
closeWindow,
minimizeWindow,
maximizeWindow,
startDrag,
focusWindow,
toggleRoadMenu,
taskbarToggle,
};
}

5
br95/next-env.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

9
br95/next.config.mjs Normal file
View File

@@ -0,0 +1,9 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
experimental: {
appDir: true,
},
};
export default nextConfig;

21
br95/package.json Normal file
View File

@@ -0,0 +1,21 @@
{
"name": "br95",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "14.2.3",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@types/node": "20.12.7",
"@types/react": "18.2.79",
"typescript": "5.4.3"
}
}

21
br95/tsconfig.json Normal file
View File

@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"baseUrl": "."
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}