
Problem
The hard part was not the styling. It was building a command surface that could handle state, shared integrations, and client/server boundaries without collapsing into one large component.
Context / users
This is an application layer inside the portfolio. It exposes real site behavior through commands: resume browsing, Codex content, AI chat, weather lookup, contact capture, mini-games, and shared dice rolling.
My role
I designed and built the terminal page, shell behavior, command router, stateful modes, shared integrations, and supporting API usage. That includes parsing, history and cursor behavior, content modes, weather and contact flows, dice command integration, and the shared AI backend.
Solution
I split it into a client-only terminal shell, modular command handlers, and mode-specific helpers instead of treating the whole feature as one monolith. Where possible, I reused existing site logic rather than duplicating behavior: the `roll` command uses the shared dice engine, and the `chat` command posts into the shared `/api/chat` route.
- Command-driven portfolio navigation with help, resume, Codex, weather, roll, chat, contact, and game commands
- Stateful interaction modes for Resume, Codex, Zork, Blackjack, contact capture, and weather selection
- Shared dice notation support through the same roller logic used by the dedicated RPG Dice Roller
- Shared AI chat integration through the site’s protected `/api/chat` backend
- Guided contact intake flow that collects name, email, and message from inside the terminal
- Keyboard-oriented UX with command history, cursor movement, focus recovery, and terminal-style output rendering
Architecture
This sits on top of the Next.js app as a client-rendered interaction layer. The terminal page dynamically imports the heavy client-only pieces, which keeps server-rendering concerns separate from keyboard-driven UI logic. Supporting flows are split into hooks and data modules. Server-backed features cross into protected API routes, where validation, rate limiting, sanitization, and bot checks live.
Engineering Details
- • Separated page shell, terminal runtime, command handlers, hooks, and content modules instead of putting all behavior in one file
- • Used dynamic imports for client-only terminal pieces to keep interactive shell logic out of the server-rendered path
- • Implemented mode-aware command routing so active experiences like Codex, Resume, and games can intercept input before the standard command switch
- • Sanitized rendered terminal HTML on the client and protected server routes with validation, rate limiting, same-origin checks, and bot-detection signals
- • Reused a shared dice engine that parses notation and uses stronger randomness via `crypto.getRandomValues` when available
- • Connected terminal chat to a server-controlled AI route with fallback provider handling and prompt-protection logic instead of exposing prompt behavior directly in the client
- • Added UX details that matter in a command interface: duplicate-command suppression, scroll control, local appearance persistence, and deliberate focus management
Outcome
- Turned the portfolio into a more distinctive interaction system without abandoning maintainability
- Created a reusable command surface that shows frontend systems thinking rather than only visual styling
- Reduced duplication by reusing shared chat and dice logic instead of building terminal-only versions
- Produced a project that signals engineering range: input handling, component architecture, client/server boundaries, and defensive API design
Tradeoffs / Limits
- • This is a browser terminal interface, not a full shell or virtual filesystem, and the case study should stay honest about that scope
- • The main terminal component still carries a lot of responsibility, even though the architecture is more modular than a one-file implementation
- • Weather lookup is a client-side integration, which is simpler to wire but less ideal than fully proxying it through the server
- • I found test tooling in the repo, but I would not claim strong terminal-specific automated coverage without adding or pointing to those tests explicitly
- • Some extensibility hooks appear prepared for future use, but not every one of them is clearly exercised in the current app
Why It Matters
It turns a gimmick-prone idea into an engineered interface.
Like what you see?
Feel free to reach out if you have questions about this project or want to chat about working together.