FULL ON WEBSITE REMAKE
This commit is contained in:
parent
d2f99c87b9
commit
a14df3f3b3
11 changed files with 1140 additions and 830 deletions
|
|
@ -1,13 +1,38 @@
|
|||
"use client";
|
||||
|
||||
import { useRef, useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { APPS, APP_BY_ID } from "./apps";
|
||||
import WinWindow, { type WinState } from "./WinWindow";
|
||||
import Clock from "./Clock";
|
||||
import { useTheme } from "./useTheme";
|
||||
import { WALLPAPER } from "../data";
|
||||
import { WALLPAPER, friends } from "../data";
|
||||
|
||||
const TASKBAR_HEIGHT = 40;
|
||||
const TASKBAR_HEIGHT = 48;
|
||||
|
||||
const LEFT_APPS = APPS.filter((a) => a.id !== "services" && a.id !== "friends");
|
||||
|
||||
const SERVICE_SHORTCUTS: {
|
||||
id: string;
|
||||
icon: string;
|
||||
label: string;
|
||||
url?: string;
|
||||
appId?: string;
|
||||
}[] = [
|
||||
{ id: "svc-proxmox", icon: "🖥️", label: "Proxmox", appId: "alacritty" },
|
||||
{ id: "svc-ai", icon: "🦾", label: "Llama.cpp", url: "https://ai.dvorakmortonxd.dev/" },
|
||||
{ id: "svc-jellyfin", icon: "🎬", label: "Jellyfin", url: "https://jelly.dvorakmortonxd.dev/" },
|
||||
{ id: "svc-smp", icon: "🎮", label: "SMP", url: "https://discord.gg/vyvhk7d8bX" },
|
||||
{ id: "svc-navidrome", icon: "🎸", label: "Navidrome", url: "https://navidrome.dvorakmortonxd.dev/" },
|
||||
{ id: "svc-gitdxd", icon: "🐙", label: "GitDXD", url: "https://git.dvorakmortonxd.dev" },
|
||||
];
|
||||
|
||||
const FRIEND_SHORTCUTS = friends.map((f, i) => ({
|
||||
id: `friend-${i}`,
|
||||
label: f.name,
|
||||
url: f.realUrl || f.url,
|
||||
img: f.img,
|
||||
}));
|
||||
|
||||
export default function WindowsDesktop() {
|
||||
const { theme, toggle } = useTheme();
|
||||
|
|
@ -23,6 +48,7 @@ export default function WindowsDesktop() {
|
|||
z: 10,
|
||||
minimized: false,
|
||||
maximized: false,
|
||||
centered: true,
|
||||
},
|
||||
]);
|
||||
const [activeId, setActiveId] = useState<number | null>(1);
|
||||
|
|
@ -31,7 +57,6 @@ export default function WindowsDesktop() {
|
|||
|
||||
const idRef = useRef(2);
|
||||
const zRef = useRef(11);
|
||||
const spawnRef = useRef(1);
|
||||
|
||||
const focus = (id: number) => {
|
||||
setActiveId(id);
|
||||
|
|
@ -54,12 +79,14 @@ export default function WindowsDesktop() {
|
|||
}
|
||||
const app = APP_BY_ID[appId];
|
||||
if (!app) return;
|
||||
const n = spawnRef.current++;
|
||||
const id = idRef.current++;
|
||||
const maxX = Math.max(20, (typeof window !== "undefined" ? window.innerWidth : 1280) - app.width - 20);
|
||||
const maxY = Math.max(20, (typeof window !== "undefined" ? window.innerHeight : 800) - TASKBAR_HEIGHT - app.height - 20);
|
||||
const x = Math.min(maxX, 90 + (n % 8) * 30);
|
||||
const y = Math.min(maxY, 56 + (n % 8) * 28);
|
||||
const vw = window.innerWidth;
|
||||
const vh = window.innerHeight - TASKBAR_HEIGHT;
|
||||
const cascade = Math.min(wins.length, 8) * 30;
|
||||
let x = Math.round((vw - app.width) / 2) + cascade;
|
||||
let y = Math.round((vh - app.height) / 2) + cascade;
|
||||
x = Math.max(10, Math.min(x, vw - app.width - 10));
|
||||
y = Math.max(10, Math.min(y, vh - app.height - 10));
|
||||
setWins((prev) => [
|
||||
...prev,
|
||||
{
|
||||
|
|
@ -72,6 +99,7 @@ export default function WindowsDesktop() {
|
|||
z: ++zRef.current,
|
||||
minimized: false,
|
||||
maximized: false,
|
||||
centered: false,
|
||||
},
|
||||
]);
|
||||
setActiveId(id);
|
||||
|
|
@ -86,14 +114,17 @@ export default function WindowsDesktop() {
|
|||
prev.map((w) => (w.id === id ? { ...w, maximized: !w.maximized } : w))
|
||||
);
|
||||
const moveWin = (id: number, x: number, y: number) => {
|
||||
const maxX =
|
||||
(typeof window !== "undefined" ? window.innerWidth : 1280) - 120;
|
||||
const maxY =
|
||||
(typeof window !== "undefined" ? window.innerHeight : 800) - TASKBAR_HEIGHT - 20;
|
||||
const maxX = (typeof window !== "undefined" ? window.innerWidth : 1280) - 120;
|
||||
const maxY = (typeof window !== "undefined" ? window.innerHeight : 800) - TASKBAR_HEIGHT;
|
||||
setWins((prev) =>
|
||||
prev.map((w) =>
|
||||
w.id === id
|
||||
? { ...w, x: Math.max(-w.w + 120, Math.min(maxX, x)), y: Math.max(0, Math.min(maxY, y)) }
|
||||
? {
|
||||
...w,
|
||||
centered: false,
|
||||
x: Math.max(-w.w + 120, Math.min(maxX, x)),
|
||||
y: Math.max(0, Math.min(maxY, y)),
|
||||
}
|
||||
: w
|
||||
)
|
||||
);
|
||||
|
|
@ -129,13 +160,15 @@ export default function WindowsDesktop() {
|
|||
|
||||
{/* Desktop icons */}
|
||||
<div className="win-icons" onPointerDown={(e) => e.stopPropagation()}>
|
||||
{APPS.map((app) => (
|
||||
{LEFT_APPS.map((app) => (
|
||||
<button
|
||||
key={app.id}
|
||||
type="button"
|
||||
className={"win-icon" + (selectedIcon === app.id ? " selected" : "")}
|
||||
onClick={() => setSelectedIcon(app.id)}
|
||||
onDoubleClick={() => openApp(app.id)}
|
||||
onClick={() => {
|
||||
setSelectedIcon(app.id);
|
||||
openApp(app.id);
|
||||
}}
|
||||
title={`Open ${app.title}`}
|
||||
>
|
||||
<span className="win-icon-glyph" style={{ background: app.color }}>
|
||||
|
|
@ -146,6 +179,61 @@ export default function WindowsDesktop() {
|
|||
))}
|
||||
</div>
|
||||
|
||||
{/* Services - top right */}
|
||||
<div
|
||||
className="win-icons win-icons-tr"
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
{SERVICE_SHORTCUTS.map((s) =>
|
||||
s.appId ? (
|
||||
<button
|
||||
key={s.id}
|
||||
type="button"
|
||||
className="win-icon"
|
||||
title={s.label}
|
||||
onClick={() => openApp(s.appId as string)}
|
||||
>
|
||||
<span className="win-icon-glyph">{s.icon}</span>
|
||||
<span className="win-icon-label">{s.label}</span>
|
||||
</button>
|
||||
) : (
|
||||
<a
|
||||
key={s.id}
|
||||
href={s.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="win-icon"
|
||||
title={s.label}
|
||||
>
|
||||
<span className="win-icon-glyph">{s.icon}</span>
|
||||
<span className="win-icon-label">{s.label}</span>
|
||||
</a>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Friends - bottom right */}
|
||||
<div
|
||||
className="win-icons win-icons-br"
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
{FRIEND_SHORTCUTS.map((f) => (
|
||||
<a
|
||||
key={f.id}
|
||||
href={f.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="win-icon"
|
||||
title={f.label}
|
||||
>
|
||||
<span className="win-icon-glyph">
|
||||
<Image src={f.img} alt={f.label} width={38} height={38} className="win-icon-img" />
|
||||
</span>
|
||||
<span className="win-icon-label">{f.label}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Windows */}
|
||||
<div className="win-layer">
|
||||
{wins.map((w) => (
|
||||
|
|
@ -163,52 +251,84 @@ export default function WindowsDesktop() {
|
|||
))}
|
||||
</div>
|
||||
|
||||
{/* Start menu */}
|
||||
{/* Application launcher */}
|
||||
{startOpen && (
|
||||
<div className="win-startmenu" onPointerDown={(e) => e.stopPropagation()}>
|
||||
<div className="win-startmenu-user">
|
||||
<span className="win-startmenu-avatar">🧑💻</span>
|
||||
<span>Martin Dvořák</span>
|
||||
</div>
|
||||
<div className="win-startmenu-list">
|
||||
{APPS.map((app) => (
|
||||
<button
|
||||
key={app.id}
|
||||
type="button"
|
||||
className="win-startmenu-item"
|
||||
onClick={() => openApp(app.id)}
|
||||
>
|
||||
<span className="win-startmenu-icon" style={{ background: app.color }}>
|
||||
{app.icon}
|
||||
</span>
|
||||
{app.title}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="win-startmenu-foot">
|
||||
<button
|
||||
type="button"
|
||||
className="win-startmenu-power"
|
||||
onClick={() => {
|
||||
toggle();
|
||||
setStartOpen(false);
|
||||
}}
|
||||
>
|
||||
⏻ Switch to {theme === "skeuo" ? "Terminal" : "Aero"}
|
||||
</button>
|
||||
<div className="kickoff" onPointerDown={(e) => e.stopPropagation()}>
|
||||
<div className="kickoff-content">
|
||||
<div className="kickoff-section-title">Applications</div>
|
||||
<div className="kickoff-list">
|
||||
{LEFT_APPS.map((app) => (
|
||||
<button
|
||||
key={app.id}
|
||||
type="button"
|
||||
className="kickoff-item"
|
||||
onClick={() => openApp(app.id)}
|
||||
>
|
||||
<span className="kickoff-icon">{app.icon}</span>
|
||||
<span className="kickoff-label">{app.title}</span>
|
||||
</button>
|
||||
))}
|
||||
{SERVICE_SHORTCUTS.map((s) =>
|
||||
s.appId ? (
|
||||
<button
|
||||
key={s.id}
|
||||
type="button"
|
||||
className="kickoff-item"
|
||||
onClick={() => openApp(s.appId as string)}
|
||||
>
|
||||
<span className="kickoff-icon">{s.icon}</span>
|
||||
<span className="kickoff-label">{s.label}</span>
|
||||
</button>
|
||||
) : (
|
||||
<a
|
||||
key={s.id}
|
||||
className="kickoff-item"
|
||||
href={s.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<span className="kickoff-icon">{s.icon}</span>
|
||||
<span className="kickoff-label">{s.label}</span>
|
||||
</a>
|
||||
)
|
||||
)}
|
||||
{FRIEND_SHORTCUTS.map((f) => (
|
||||
<a
|
||||
key={f.id}
|
||||
className="kickoff-item"
|
||||
href={f.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<span className="kickoff-icon">
|
||||
<Image
|
||||
src={f.img}
|
||||
alt={f.label}
|
||||
width={20}
|
||||
height={20}
|
||||
className="kickoff-icon-img"
|
||||
/>
|
||||
</span>
|
||||
<span className="kickoff-label">{f.label}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Taskbar */}
|
||||
{/* Plasma panel */}
|
||||
<div className="win-taskbar" onPointerDown={(e) => e.stopPropagation()}>
|
||||
<button
|
||||
type="button"
|
||||
className={"win-start" + (startOpen ? " open" : "")}
|
||||
onClick={() => setStartOpen((v) => !v)}
|
||||
aria-label="Start"
|
||||
aria-label="Application Launcher"
|
||||
title="Application Launcher"
|
||||
>
|
||||
<span className="win-start-orb">⊞</span>
|
||||
<span className="win-start-orb">
|
||||
<Image src="/gentoo.svg" alt="Gentoo" width={26} height={27} className="gentoo-logo" />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div className="win-tb-buttons">
|
||||
|
|
@ -236,37 +356,24 @@ export default function WindowsDesktop() {
|
|||
</div>
|
||||
|
||||
<div className="win-tray">
|
||||
<form
|
||||
className="win-search"
|
||||
action="https://search.dvorakmortonxd.dev/search"
|
||||
method="GET"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<span className="win-search-glyph" aria-hidden="true">
|
||||
⌕
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
name="q"
|
||||
placeholder="Search the web..."
|
||||
aria-label="Search the web"
|
||||
/>
|
||||
</form>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="win-theme-toggle"
|
||||
onClick={toggle}
|
||||
title={`Switch to ${theme === "skeuo" ? "terminal" : "Aero"} theme`}
|
||||
title={`Switch to ${theme === "skeuo" ? "gruvbox" : "Desktop"} theme`}
|
||||
>
|
||||
<span aria-hidden="true">{theme === "skeuo" ? "▚" : "❖"}</span>
|
||||
<span>{theme === "skeuo" ? "Terminal" : "Aero"}</span>
|
||||
<span>{theme === "skeuo" ? "Gruvbox" : "Desktop"}</span>
|
||||
</button>
|
||||
|
||||
<Clock className="win-clock" />
|
||||
|
||||
<button type="button" className="win-showdesktop" aria-label="Show desktop" onClick={() => setWins((prev) => prev.map((w) => ({ ...w, minimized: true })))} />
|
||||
<button
|
||||
type="button"
|
||||
className="win-showdesktop"
|
||||
aria-label="Show desktop"
|
||||
onClick={() => setWins((prev) => prev.map((w) => ({ ...w, minimized: true })))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue