"use client"; import { useEffect, useState } from "react"; import Image from "next/image"; import Link from "next/link"; import { loadCurrentBook, type CurrentBook } from "../tisch/currentBook"; import ChatBody from "./ChatBody"; import type { WindowLabels, WindowRequest } from "./types"; const EMAIL = "contact@dk0.dev"; const btn = "inline-flex items-center gap-2 rounded-full bg-ink px-4 py-2.5 text-sm font-semibold text-paper transition-transform duration-150 ease-out active:scale-[0.97]"; const ghost = "inline-flex items-center gap-2 rounded-full px-4 py-2.5 text-sm font-semibold shadow-[inset_0_0_0_1.5px_rgb(var(--plakat-ink))] transition-[background-color,color,transform] duration-150 ease-out hover:bg-ink hover:text-paper active:scale-[0.97]"; /** Titelzeile eines Fensters, auch für die Ablage-Leiste. */ export function windowTitle(req: WindowRequest, labels: WindowLabels): string { switch (req.kind) { case "project": return req.project.year ? `${req.project.title} · ${req.project.year}` : req.project.title; case "now": return labels.now.title; case "server": return labels.server.title; case "read": return labels.read.title; case "about": return labels.about.title; case "contact": return labels.contact.title; case "chat": return labels.chat.title; } } /** Farbe des Punkts in der Ablage-Leiste */ export function windowAccent(req: WindowRequest): string { if (req.kind === "project") return req.project.color; return { now: "rgb(var(--plakat-red))", server: "rgb(var(--plakat-ok))", read: "#7a5240", about: "#2f5bea", contact: "rgb(var(--plakat-red))", chat: "#1d1b3d" }[req.kind]; } interface WindowContentProps { req: WindowRequest; labels: WindowLabels; /** Schließt das Fenster, z. B. wenn ein Link auf der Seite weiterführt */ onClose: () => void; } export default function WindowContent({ req, labels, onClose }: WindowContentProps) { switch (req.kind) { case "project": { const p = req.project; return ( <>
{p.tags.slice(0, 4).join(" · ")} {p.title}

{p.description}

{p.year && {p.year}} {p.tags.length > 0 && {p.tags.join(", ")}}
{labels.projectPage} {p.liveUrl && ( {labels.live} )} {p.githubUrl && ( {labels.source} )}
); } case "now": return ; case "server": return ( <>

{labels.server.headline}

{labels.server.rows.map((r) => (
))}

{labels.server.note}

); case "read": return ; case "about": return ( <> {labels.about.paragraphs.map((text) => (

{text}

))}
{labels.about.facts.map((f) => ( {f.value} ))}
); case "contact": return ; case "chat": return ; } } function Row({ label, children }: { label: string; children: React.ReactNode }) { return (
{label}
{children}
); } // Abgabe der Masterarbeit; auf dem Client gerechnet, damit das Fenster nie einen veralteten Stand zeigt const THESIS_DEADLINE = new Date("2027-02-15T12:00:00+01:00"); function NowBody({ labels }: { labels: WindowLabels }) { const days = Math.max(0, Math.ceil((THESIS_DEADLINE.getTime() - Date.now()) / 86_400_000)); return ( <>
{days}

{labels.now.days}

{labels.now.rows.map((r) => ( {r.text} ))}
); } function ReadBody({ labels }: { labels: WindowLabels }) { const [book, setBook] = useState(undefined); useEffect(() => { let alive = true; loadCurrentBook().then((b) => alive && setBook(b)); return () => { alive = false; }; }, []); const progress = book && Number.isFinite(book.progress) ? Math.max(0, Math.min(100, book.progress)) : 0; return ( <> {book === undefined ? (