// Timeline-Illustrationen: Hologramm-Szenen im Stil des Hero-Bilds // (dunkler Raum, Neon-Deckenlicht, Glas-Panels, Projektor auf dem Tisch). // Bewusst themenunabhängig dunkel – wie ein Foto. export type TimelineArtId = | 'lochkarte' | 'pc' | 'atari' | 'unix' | 'linux' | 'www' | 'server' | 'ki' const MONO = { fontFamily: 'var(--font-jetbrains-mono), ui-monospace, monospace', whiteSpace: 'pre' } as const const CY = '#5ff3ff' const CY_TXT = '#a8fbff' const CY_DIM = '#4fc3d4' const MAG = '#ff4fd8' const SCREEN_DARK = '#062a33' const SCREEN_LIGHT = '#d9fbff' type U = (name: string) => string function holo(u: U) { return { stroke: CY, fill: 'none', strokeWidth: 1.1, strokeLinecap: 'round' as const, strokeLinejoin: 'round' as const, filter: `url(#${u('glow')})`, } } function T({ x, y, size = 6, color = CY_TXT, anchor, className, children, }: { x: number y: number size?: number color?: string anchor?: 'start' | 'middle' | 'end' className?: string children: React.ReactNode }) { return ( {children} ) } /* === Gemeinsame Bausteine: Raum, Projektor, Glas-Panel === */ function Defs({ u }: { u: U }) { return ( ) } const BOKEH: [number, number, number, string, number][] = [ [26, 70, 12, '#ff7a3d', 0.35], [46, 128, 7, '#ff3d6e', 0.3], [18, 186, 9, '#7a5cff', 0.25], [374, 84, 11, '#3dd6ff', 0.3], [356, 150, 6, '#ff7a3d', 0.22], [388, 200, 8, MAG, 0.2], ] function Backdrop({ u }: { u: U }) { const rays = [-240, -120, 0, 100, 200, 300, 400, 520, 640] return ( <> {/* Rack-Silhouetten an der Rückwand */} {[8, 36, 348, 376].map((x) => ( ))} {BOKEH.map(([x, y, r, c, o], i) => ( ))} {/* Decke in Perspektive mit Neon-Leisten */} {rays.map((x) => ( ))} {/* Tisch */} {[-200, -60, 80, 200, 320, 460, 600].map((x) => ( ))} ) } function Overlay({ u, caption }: { u: U; caption: string }) { return ( <> {caption} ) } function Emitter({ u, cx, w = 100, top = 30 }: { u: U; cx: number; w?: number; top?: number }) { return ( ) } function GlassPanel({ u, x, y, w, h, tilt, title, lines = [], size = 5.6, children, }: { u: U x: number y: number w: number h: number tilt: number title: string lines?: string[] size?: number children?: React.ReactNode }) { return ( {[6, 12, 18].map((cx) => ( ))} {title} {lines.map((line, i) => ( {line} ))} {children} ) } /* === 1976: Lochkarte === */ const CARD_TEXT = 'C JAMULIX 1976 FORTRAN IV'.padEnd(28, ' ') // Zeilenindex: 0 = Zone 12, 1 = Zone 11, 2 = Ziffer 0, 3..11 = Ziffern 1..9 function hollerith(ch: string): number[] { if (ch >= '0' && ch <= '9') return [2 + Number(ch)] const c = ch.charCodeAt(0) - 65 if (c >= 0 && c < 9) return [0, 3 + c] if (c >= 9 && c < 18) return [1, 3 + c - 9] if (c >= 18 && c < 26) return [2, 4 + c - 18] return [] } function Lochkarte({ u }: { u: U }) { return ( <> {CARD_TEXT.split('').map((ch, i) => { const x = 14 + i * 7.2 const holes = hollerith(ch) return ( {ch} {Array.from({ length: 12 }, (_, r) => { const y = 17 + r * 7.3 return holes.includes(r) ? ( ) : ( ) })} ) })} {/* Bandspulen */} {[322, 362].map((cx, i) => ( ))} ) } /* === 1984: PC === */ function Pc({ u }: { u: U }) { return ( <> C:\>dir /w TP.EXE DBASE.EXE 123.COM NC.EXE WP.EXE QBASIC.EXE C:\> {Array.from({ length: 3 }, (_, r) => Array.from({ length: 14 }, (_, i) => ( )), )} {['A', 'B', 'C'].map((c, i) => ( {c} ))} {[ ['Miete', '450', '450'], ['Disks', '89', '178'], ['Modem', '398', '398'], ['RAM', '640', '0'], ['Summe', '1577', '1026'], ].map((row, r) => ( {row.map((v, i) => ( {v} ))} ))} {/* 5,25"-Diskette */} ) } /* === 1986: Atari ST === */ function Atari({ u }: { u: U }) { return ( <> Desk File View Options move.l d0,(a1)+ dbra d7,loop trap #1 {[160, 171, 182].map((x) => ( ))} OK {Array.from({ length: 10 }, (_, i) => ( ))} {Array.from({ length: 3 }, (_, r) => Array.from({ length: 15 }, (_, i) => ( )), )} 520ST {/* Akustikkoppler */} {[0, 0.4, 0.8].map((d, i) => ( ))} 300 BAUD ) } /* === 1990: Unix === */ function Unix({ u }: { u: U }) { const tools = ['grep', 'sort', 'uniq', 'wc'] return ( <> {tools.map((t, i) => { const y = 52 + i * 44 return ( {t} {i < tools.length - 1 && ( <> | )} ) })} Do one thing and do it well. ) } /* === 1992: Linux === */ function Disk({ i, label }: { i: number; label?: boolean }) { return ( {label && ( <> SLACKWARE disk a1 / 40 )} ) } function Linux({ u }: { u: U }) { return ( <> {[0, 1, 2, 3, 4].map((i) => ( ))} 40 × 1,44 MB · Kernel 0.99 {/* Tux, „echt“ auf dem Tisch */} ) } /* === 1995: WWW === */ const CHIRP = Array.from({ length: 110 }, (_, i) => { const t = i / 109 const x = 244 + i * 1.25 const y = 212 + Math.sin(i * (0.25 + t * 0.9)) * (3 + 7 * t) return `${x.toFixed(1)},${y.toFixed(1)}` }).join(' ') function Www({ u }: { u: U }) { const cx = 128 const cy = 128 const r = 74 const nodes: [number, number][] = [ [96, 92], [160, 84], [178, 150], [110, 176], [134, 128], ] return ( <> {[0, 1.5, 3, 4.5].map((d) => ( ))} {[-0.75, -0.4, 0, 0.4, 0.75].map((lat) => { const y = cy + lat * r const w = Math.sqrt(1 - lat * lat) * r return })} {nodes.map(([x, y], i) => ( ))} http://www.linix.de/ {[48, 55, 62, 69].map((y, i) => ( ))} <a href="links.html"> UNDER CONSTRUCTION Besucher: 000042 ATDT … 28.8k CONNECT ) } /* === 2000er: Server === */ function IsoBox({ cx, y0, s, label }: { cx: number; y0: number; s: number; label: string }) { return ( {label} ) } function Server({ u }: { u: U }) { return ( <> {Array.from({ length: 7 }, (_, i) => { const y = 48 + i * 25.5 return ( ) })} ) } /* === 2022: KI === */ const BRAIN_NODES: [number, number][] = [ [78, 112], [96, 84], [122, 72], [150, 62], [176, 72], [200, 86], [214, 112], [204, 140], [178, 146], [150, 156], [118, 152], [92, 138], [110, 108], [140, 96], [168, 108], [132, 130], [160, 132], [188, 120], ] const BRAIN_EDGES: [number, number][] = [] BRAIN_NODES.forEach(([x1, y1], i) => BRAIN_NODES.forEach(([x2, y2], j) => { if (j > i && Math.hypot(x2 - x1, y2 - y1) < 38) BRAIN_EDGES.push([i, j]) }), ) function Ki({ u }: { u: U }) { const probs: [string, number][] = [ ['Linux', 0.71], ['Code', 0.17], ['Kaffee', 0.08], ['Windows', 0.01], ] return ( <> {/* Großhirn */} {/* Kleinhirn und Hirnstamm */} {/* Furchen */} {BRAIN_EDGES.map(([a, b]) => ( ))} {BRAIN_NODES.map(([x, y], i) => ( ))} > Je schlauer, desto {probs.map(([word, p], i) => { const y = 44 + i * 24 return ( {word} {p.toFixed(2)} ) })} temperature 0.7 · top_p 0.9 ) } const SCENES: Record React.JSX.Element }> = { lochkarte: { caption: 'FIG.01 // 1976 // LOCHKARTE · HOLLERITH-CODE', alt: 'Hologramm einer Lochkarte mit gestanzter Zeile „C JAMULIX 1976 FORTRAN IV“, daneben ein Fortran-Listing und Bandspulen', Scene: Lochkarte, }, pc: { caption: 'FIG.02 // 1984 // PC UNTER DOS', alt: 'Hologramm eines PCs mit DOS-Prompt, daneben schwebende Panels mit Lotus-Tabelle und dBASE', Scene: Pc, }, atari: { caption: 'FIG.03 // 1986 // ATARI ST · 68000 · DFÜ', alt: 'Hologramm eines Atari ST mit GEM-Desktop und Bomben-Dialog, 68000-Assembler-Panel und Akustikkoppler', Scene: Atari, }, unix: { caption: 'FIG.04 // 1990 // UNIX-SHELL UND PIPES', alt: 'Schwebendes Terminal mit Unix-Shell-Befehlen und eine Pipeline aus grep, sort, uniq und wc als Hologramm', Scene: Unix, }, linux: { caption: 'FIG.05 // 1992 // SLACKWARE AUF 40 DISKETTEN', alt: 'Hologramm gefächerter Slackware-Disketten mit Installationsfortschritt, daneben der Linux-Pinguin Tux auf dem Tisch', Scene: Linux, }, www: { caption: 'FIG.06 // 1995 // WORLD WIDE WEB', alt: 'Rotierender Hologramm-Globus mit Hyperlinks, Browser-Panel von linix.de mit Besucherzähler und Modem-Signal', Scene: Www, }, server: { caption: 'FIG.07 // 2000ER // RACK · CONTAINER · AUTOMATISIERUNG', alt: 'Hologramm eines Server-Racks mit blinkenden LEDs, verbunden mit Containern, daneben ein SSH-Panel', Scene: Server, }, ki: { caption: 'FIG.08 // 2022 // SPRACHMODELL · NÄCHSTES TOKEN', alt: 'Gehirn aus Netzwerkknoten als Hologramm; ein Panel zeigt „Linux“ mit 71 Prozent als nächstes Token nach „Je schlauer, desto“', Scene: Ki, }, } export function TimelineHolo({ id, className = '' }: { id: TimelineArtId; className?: string }) { const { caption, alt, Scene } = SCENES[id] const u: U = (name) => `holo-${id}-${name}` return (
) }