šŸ”® UI pattern Ā· React state

Terminal Oracle

The complete CLI/chat interface skeleton — the exact pattern behind every AI chat product, ready to wire to a real backend.

Use it for: AI chat products Ā· developer tools Ā· easter eggs

oracle@koah.ing

KOAH OS v17.85 — the oracle is listening

type "help" to see what it knows

āÆ

The complete source

This is the exact file rendering the demo above — a single self-contained React client component with its styles inline. Copy or download it, drop it into any React / Next.js project, and it runs. No extra dependencies.

oracle.jsx
1"use client";
2import { useEffect, useRef, useState } from "react";
3
4/* ============================================================
5 TERMINAL ORACLE — a complete CLI-style interface pattern:
6 command parsing, history (↑/↓), typewriter output, and a
7 blinking caret. The skeleton for any "AI chat" or developer
8 tool aesthetic. Pure React state — no terminal libraries.
9 ============================================================ */
10
11const BANNER = [
12 "KOAH OS v17.85 — the oracle is listening",
13 'type "help" to see what it knows',
14];
15
16const RESPONSES = {
17 help: [
18 "available commands:",
19 " about — who is Koah Ing?",
20 " magic — receive one (1) act of digital magic",
21 " skills — the 2026 web platform codex",
22 " clear — wipe the slate",
23 ],
24 about: [
25 "Koah Ing: teacher Ā· koach Ā· performer Ā· inventor.",
26 "Thinks in multiple dimensions. Makes your thING genius.",
27 ],
28 magic: [
29 "šŸŽ© watch closely...",
30 "...your card was the seven of stars. correct?",
31 "(the oracle is never wrong. occasionally early.)",
32 ],
33 skills: [
34 "WebGPU (Baseline 2026) Ā· scroll-driven CSS Ā· view transitions",
35 "anchor positioning Ā· ::scroll-button carousels Ā· @property",
36 "all demonstrated, with source, in this very Lab.",
37 ],
38};
39
40export default function Oracle() {
41 const [lines, setLines] = useState(BANNER.map((t) => ({ t, who: "sys" })));
42 const [input, setInput] = useState("");
43 const [history, setHistory] = useState([]);
44 const [hIdx, setHIdx] = useState(-1);
45 const scroller = useRef(null);
46 const typing = useRef(false);
47
48 // typewriter: reveal queued lines character by character
49 const typeOut = (queue) => {
50 typing.current = true;
51 let li = 0;
52 let ci = 0;
53 const tick = () => {
54 if (li >= queue.length) {
55 typing.current = false;
56 return;
57 }
58 ci++;
59 const partial = queue[li].slice(0, ci);
60 setLines((prev) => {
61 const next = [...prev];
62 if (next[next.length - 1]?.who === "oracle-typing") next.pop();
63 return [...next, { t: partial, who: ci >= queue[li].length ? "oracle" : "oracle-typing" }];
64 });
65 if (ci >= queue[li].length) {
66 li++;
67 ci = 0;
68 }
69 setTimeout(tick, 14);
70 };
71 tick();
72 };
73
74 const run = (raw) => {
75 const cmd = raw.trim().toLowerCase();
76 if (!cmd) return;
77 setLines((p) => [...p, { t: `āÆ ${raw}`, who: "you" }]);
78 setHistory((h) => [raw, ...h]);
79 setHIdx(-1);
80 if (cmd === "clear") {
81 setTimeout(() => setLines([]), 120);
82 return;
83 }
84 const out = RESPONSES[cmd] || [
85 `unknown command: "${cmd}"`,
86 'the oracle suggests: "help"',
87 ];
88 typeOut(out);
89 };
90
91 const onKey = (e) => {
92 if (e.key === "Enter" && !typing.current) {
93 run(input);
94 setInput("");
95 } else if (e.key === "ArrowUp") {
96 e.preventDefault();
97 const ni = Math.min(hIdx + 1, history.length - 1);
98 if (history[ni]) {
99 setHIdx(ni);
100 setInput(history[ni]);
101 }
102 } else if (e.key === "ArrowDown") {
103 e.preventDefault();
104 const ni = hIdx - 1;
105 setHIdx(ni);
106 setInput(ni >= 0 ? history[ni] : "");
107 }
108 };
109
110 useEffect(() => {
111 scroller.current?.scrollTo({ top: scroller.current.scrollHeight });
112 }, [lines]);
113
114 return (
115 <div
116 className="rounded-3xl border border-white/15 overflow-hidden font-mono"
117 style={{ background: "#070710" }}
118 onClick={() => document.getElementById("oracle-input")?.focus()}
119 >
120 <div className="flex items-center gap-2 px-5 py-3 border-b border-white/10">
121 <span className="w-3 h-3 rounded-full bg-rose-500/80" />
122 <span className="w-3 h-3 rounded-full bg-amber-400/80" />
123 <span className="w-3 h-3 rounded-full bg-emerald-400/80" />
124 <span className="ml-3 text-[1.2rem] text-white/40">oracle@koah.ing</span>
125 </div>
126 <div ref={scroller} className="h-[44svh] overflow-y-auto px-5 py-4 text-[1.4rem] leading-relaxed">
127 {lines.map((l, i) => (
128 <p
129 key={i}
130 className={
131 l.who === "you"
132 ? "text-cyan-300"
133 : l.who === "sys"
134 ? "text-white/40"
135 : "text-emerald-300"
136 }
137 >
138 {l.t}
139 {l.who === "oracle-typing" && <span className="oracle-caret">ā–‹</span>}
140 </p>
141 ))}
142 </div>
143 <div className="flex items-center gap-3 px-5 py-4 border-t border-white/10">
144 <span className="text-violet-400 text-[1.4rem]">āÆ</span>
145 <input
146 id="oracle-input"
147 value={input}
148 onChange={(e) => setInput(e.target.value)}
149 onKeyDown={onKey}
150 placeholder='try "magic" — ↑ for history'
151 className="flex-1 bg-transparent outline-none text-white text-[1.4rem] placeholder:text-white/25"
152 autoComplete="off"
153 spellCheck={false}
154 />
155 </div>
156 <style>{`
157 .oracle-caret { animation: oracle-blink 1s steps(1) infinite; }
158 @keyframes oracle-blink { 50% { opacity: 0; } }
159 `}</style>
160 </div>
161 );
162}
163