179 lines
8.0 KiB
TypeScript
179 lines
8.0 KiB
TypeScript
// Schaubild „So funktioniert dein System" für die Anleitung.
|
|
// Zwei Teile: (1) Landkarte — wer redet mit wem, (2) Kreislauf — wie die Box
|
|
// von allein arbeitet und wo DEIN Klick sitzt. Reines Inline-SVG mit den
|
|
// Design-Tokens (hsl(var(--…))), damit es automatisch zum Theme passt.
|
|
|
|
const T = {
|
|
text: "hsl(var(--foreground))",
|
|
mut: "hsl(var(--muted-foreground))",
|
|
border: "hsl(var(--border))",
|
|
card: "hsl(var(--card) / 0.9)",
|
|
bg: "hsl(var(--background) / 0.4)",
|
|
primary: "hsl(var(--primary))",
|
|
teal: "#2dd4bf",
|
|
sky: "#38bdf8",
|
|
violet: "#a78bfa",
|
|
rose: "#fb7185",
|
|
amber: "#fbbf24",
|
|
emerald: "#34d399",
|
|
indigo: "#818cf8",
|
|
}
|
|
|
|
// Kleine Bausteine ------------------------------------------------------------
|
|
|
|
function NodeBox({ x, y, w, h, color, title, sub, sub2, strong }: {
|
|
x: number; y: number; w: number; h: number; color: string
|
|
title: string; sub?: string; sub2?: string; strong?: boolean
|
|
}) {
|
|
const cx = x + w / 2
|
|
return (
|
|
<g>
|
|
<rect x={x} y={y} width={w} height={h} rx={12} fill={T.card}
|
|
stroke={color} strokeOpacity={strong ? 0.9 : 0.45} strokeWidth={strong ? 2 : 1.25} />
|
|
<rect x={x} y={y} width={w} height={3} rx={1.5} fill={color} opacity={0.8} />
|
|
<text x={cx} y={y + (sub ? 24 : h / 2 + 4)} textAnchor="middle" fill={T.text}
|
|
fontSize={12.5} fontWeight={700}>{title}</text>
|
|
{sub && (
|
|
<text x={cx} y={y + 40} textAnchor="middle" fill={T.mut} fontSize={10}>{sub}</text>
|
|
)}
|
|
{sub2 && (
|
|
<text x={cx} y={y + 53} textAnchor="middle" fill={T.mut} fontSize={10}>{sub2}</text>
|
|
)}
|
|
</g>
|
|
)
|
|
}
|
|
|
|
function Arrow({ from, to, color = T.border, dash }: {
|
|
from: [number, number]; to: [number, number]; color?: string; dash?: boolean
|
|
}) {
|
|
const [x1, y1] = from, [x2, y2] = to
|
|
const mx = (x1 + x2) / 2
|
|
return (
|
|
<g stroke={color} strokeOpacity={0.7} fill="none" strokeWidth={1.5}
|
|
strokeDasharray={dash ? "5 4" : undefined}>
|
|
<path d={`M ${x1} ${y1} C ${mx} ${y1}, ${mx} ${y2}, ${x2} ${y2}`} />
|
|
<path d={`M ${x2 - 6} ${y2 - 4} L ${x2} ${y2} L ${x2 - 6} ${y2 + 4}`}
|
|
fill={color} fillOpacity={0.7} stroke="none" />
|
|
</g>
|
|
)
|
|
}
|
|
|
|
// (1) Landkarte ---------------------------------------------------------------
|
|
|
|
function Landkarte() {
|
|
// Türen links, Box rechts — Pfeile: Du → Tür → Box; Box → PC-Executor (Lucy-Bau).
|
|
const tuer = [
|
|
{ y: 34, title: "Diese Oberfläche", sub: "Browser · Cockpit & Klick-Gates", color: T.teal },
|
|
{ y: 128, title: "Lucy", sub: "Stimme & Begleiterin am PC", color: T.rose },
|
|
{ y: 222, title: "Hermes Desktop", sub: "IDE für Projekte (PC + remote)", color: T.violet },
|
|
{ y: 316, title: "Telegram", sub: "unterwegs, gleiche Erinnerung", color: T.sky },
|
|
]
|
|
return (
|
|
<svg viewBox="0 0 880 432" role="img"
|
|
aria-label="Landkarte: Du erreichst die Box über vier Türen; in der Box arbeiten Mission Control, der Hermes-Agent, der Modell-Stall, das Gedächtnis und der Wissens-Vault."
|
|
style={{ width: "100%", height: "auto", fontFamily: "var(--font-sans)" }}>
|
|
|
|
{/* Du */}
|
|
<NodeBox x={16} y={150} w={120} h={92} color={T.primary} strong title="Du" sub="entscheidest —" sub2="die Box arbeitet" />
|
|
|
|
{/* PC-Executor */}
|
|
<NodeBox x={16} y={330} w={150} h={68} color={T.emerald} title="PC-Executor" sub="baut & startet Lucy" sub2="nach deiner Annahme" />
|
|
|
|
{/* Türen */}
|
|
{tuer.map((t) => (
|
|
<NodeBox key={t.title} x={196} y={t.y} w={186} h={64} color={t.color} title={t.title} sub={t.sub} />
|
|
))}
|
|
{tuer.map((t) => (
|
|
<Arrow key={t.title} from={[136, 196]} to={[196, t.y + 32]} />
|
|
))}
|
|
|
|
{/* Die Box */}
|
|
<rect x={420} y={14} width={444} height={404} rx={16} fill={T.bg} stroke={T.border} strokeOpacity={0.7} />
|
|
<text x={642} y={38} textAnchor="middle" fill={T.text} fontSize={13} fontWeight={800} letterSpacing={0.5}>
|
|
DIE BOX · 192.168.178.151
|
|
</text>
|
|
|
|
<NodeBox x={444} y={54} w={396} h={62} color={T.teal} title="Mission Control (:9001)"
|
|
sub="Cockpit · Auftragsbuch (Klick-Gate) · Chronik · Wissen" />
|
|
<NodeBox x={444} y={126} w={396} h={62} color={T.rose} title="Hermes-Agent"
|
|
sub="denkt, nutzt Werkzeuge, betreibt die Werkstatt & Nacht-Crons" />
|
|
<NodeBox x={444} y={198} w={396} h={78} color={T.sky} title="Modell-Stall (llama-swap)"
|
|
sub="Hirn · Groß · Coder · Augen · Späher · Kritiker"
|
|
sub2="+ Embedding & Sortierer fürs Gedächtnis" />
|
|
<NodeBox x={444} y={286} w={190} h={56} color={T.indigo} title="Natives Gedächtnis" sub="Hermes v0.20 SQLite & Embed" />
|
|
<NodeBox x={650} y={286} w={190} h={56} color={T.amber} title="Wissens-Vault" sub="Träume & Notizen (git)" />
|
|
<NodeBox x={444} y={352} w={396} h={52} color={T.emerald} title="Selbstheilung & Backups"
|
|
sub="Health-Checks · Auto-Revert bei Rot · PBS-Sicherung" />
|
|
|
|
{/* Türen → Box */}
|
|
{tuer.map((t) => (
|
|
<Arrow key={t.title} from={[382, t.y + 32]} to={[420, t.y + 46]} />
|
|
))}
|
|
|
|
{/* Box → PC-Executor (Lucy-Annahme) */}
|
|
<Arrow from={[420, 396]} to={[166, 364]} color={T.emerald} dash />
|
|
<text x={272} y={412} textAnchor="middle" fill={T.mut} fontSize={9.5} fontStyle="italic">
|
|
Lucy-Karte angenommen → PC baut neu
|
|
</text>
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
// (2) Kreislauf ---------------------------------------------------------------
|
|
|
|
function Kreislauf() {
|
|
const stationen = [
|
|
{ x: 14, color: T.amber, title: "Idee", sub: "von dir oder vom", sub2: "Nacht-Radar" },
|
|
{ x: 158, color: T.violet, title: "Ideen-Queue", sub: "Kanban sammelt,", sub2: "Specifier plant" },
|
|
{ x: 302, color: T.rose, title: "Werkstatt", sub: "Agent baut den", sub2: "Vorschlag (Branch)" },
|
|
{ x: 446, color: T.sky, title: "Auftragsbuch", sub: "Karte + Stempel", sub2: "vom Gutachter" },
|
|
{ x: 590, color: T.primary, title: "DEIN Klick", sub: "annehmen oder", sub2: "ablehnen (mit Grund)", strong: true },
|
|
{ x: 734, color: T.emerald, title: "Live", sub: "Merge + Deploy,", sub2: "Rot = Auto-Rückbau" },
|
|
]
|
|
return (
|
|
<svg viewBox="0 0 880 246" role="img"
|
|
aria-label="Kreislauf: Idee, Ideen-Queue, Werkstatt, Auftragsbuch, dein Klick, live — Chronik und Morgenlage berichten zurück."
|
|
style={{ width: "100%", height: "auto", fontFamily: "var(--font-sans)" }}>
|
|
|
|
{stationen.map((s) => (
|
|
<NodeBox key={s.title} x={s.x} y={38} w={132} h={66} color={s.color}
|
|
title={s.title} sub={s.sub} sub2={s.sub2} strong={s.strong} />
|
|
))}
|
|
{stationen.slice(0, -1).map((s, i) => (
|
|
<Arrow key={s.title} from={[s.x + 132, 71]} to={[stationen[i + 1].x, 71]} />
|
|
))}
|
|
|
|
{/* Rückweg: Live → Idee (Bericht & Lern-Futter) */}
|
|
<g stroke={T.border} strokeOpacity={0.7} fill="none" strokeWidth={1.5} strokeDasharray="5 4">
|
|
<path d="M 800 104 C 800 150, 720 150, 440 150 C 160 150, 80 150, 80 112" />
|
|
<path d="M 76 118 L 80 108 L 84 118" fill={T.border} fillOpacity={0.7} stroke="none" />
|
|
</g>
|
|
<text x={440} y={144} textAnchor="middle" fill={T.mut} fontSize={10} fontStyle="italic">
|
|
Chronik & Morgenlage berichten · Ablehnungs-Gründe werden Lern-Futter
|
|
</text>
|
|
|
|
{/* Nacht-Fahrplan */}
|
|
<rect x={14} y={176} width={852} height={54} rx={12} fill={T.bg} stroke={T.border} strokeOpacity={0.5} />
|
|
<text x={440} y={198} textAnchor="middle" fill={T.text} fontSize={11} fontWeight={700}>
|
|
Nachts, ganz allein:
|
|
</text>
|
|
<text x={440} y={216} textAnchor="middle" fill={T.mut} fontSize={10}>
|
|
03:15 Traum · 03:35 Selbst-Inventur · 03:45 Idle-Radar · 04:00 Karten-Gutachter · 04:10 Bagatellen · 04:30 Morgenlage · Mo 05:15 Release-Radar
|
|
</text>
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
export function SystemSchaubild() {
|
|
return (
|
|
<div className="space-y-5">
|
|
<div className="overflow-x-auto">
|
|
<div className="min-w-[680px]"><Landkarte /></div>
|
|
</div>
|
|
<div className="border-t border-border/20 pt-4 overflow-x-auto">
|
|
<div className="min-w-[680px]"><Kreislauf /></div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|