The portfolio of Amandeep Yadav, reimagined as TerminusOS — a no-scroll, spatial "operating system" you navigate instead of scroll. Built with the Next.js App Router, TypeScript, Tailwind CSS, and shadcn-style UI primitives, with an Asimov's Foundation theme throughout.
Instead of the usual hero → work → skills → footer page, visitors land on a boot/lock screen, then "enter the system" into a windowed desktop (or a mobile home screen on small viewports). Each piece of the portfolio — profile, case files, skills, résumé — is an app you open, move, resize, and arrange.
- Boot / lock screen shows the essentials (contact, focus, proof) while it warms the OS shell and installed-app chunks in the background. "Enter System" stays disabled until the systems check completes.
- Desktop shell (≥ 820px): a window manager with a magnifying dock, menu bar, right-click context menus, window snapping, and session restore.
- Mobile shell (< 820px): a home screen of app icons with full-screen app frames. App state is preserved when resizing across the breakpoint.
- System apps (always present): Operator (profile), Case Files (projects), Activity (skills), System Log, Résumé, Settings, App Store.
- User apps (installable from the App Store): Terminal, Notes, Colophon, Psychohistory, Labs (interactive engineering-systems exhibits), plus games — Psychohistory Sim (Game of Life), Trantor 2048, Hyperwave (Snake), Seldon's Grid (Lights Out), Codebreaker, and Cipher.
- Personalization persists in the browser: theme, wallpaper, accent color, UI density, dock position, installed apps, reduced motion, and horizontal-scroll behavior.
Install dependencies:
npm installCreate local environment variables:
cp .env.example .env.localFill in the contact and social fields in .env.local:
NEXT_PUBLIC_SITE_URL=https://your-domain.com
CONTACT_EMAIL=you@example.com
CONTACT_PHONE="+91 00000 00000"
SOCIAL_GITHUB=https://github.com/your-handle
SOCIAL_LINKEDIN=https://www.linkedin.com/in/your-handle
SOCIAL_LEETCODE=https://leetcode.com/your-handleRun the development server:
npm run devOpen http://localhost:3000 to view the site.
app/— Next.js App Router entrypoints.layout.tsxwires fonts, metadata/SEO, theme providers, and the cosmic background.page.tsxresolves the contact block and rendersTerminusOS.providers.tsxsets upnext-themes.not-found.tsxanderror.tsxrender branded 404 and 500 screens viaPageShell.robots.tsandsitemap.tsgenerate SEO crawler metadata.
components/portfolio/os/— the TerminusOS shell.TerminusOS.tsxis the root: it boots settings, shows the lock screen, and lazily loads the desktop/mobile shells.LockScreen.tsxis the boot/login screen that preloads shell and app chunks.osStore.tsxis the window-manager state (open/close/focus/move/resize, snapping, session restore).osSettings.tsxholds persisted user settings and applies them (theme, accent, density, dock, etc.).notifications.tsxis the system toast layer;AppRuntime.tsxkeeps app state alive across the desktop/mobile reflow.desktop/(DesktopOS,WindowManager,Window,Dock,MenuBar,ContextMenu) andmobile/(MobileOS,HomeScreen,AppFrame,StatusBar) are the two shells.wallpaper/holds the Foundation-themed wallpapers (with dark/light variants).apps/systemApps/andapps/userApps/hold the apps, one folder each (see below).appRegistry.tsx,apps/appManifest.ts, and the generatedapps/registry.generated.tsassemble the app list automatically.
components/portfolio/icons/— shared icon and motif components (Foundation motifs, tech/brand badges, social glyphs).components/portfolio/shared/— thePageShellframe (used by 404/500) and the cosmic background.components/ui/— shadcn-style UI primitives.lib/resume.ts— single source of truth for profile, summary, skills, education, experience, and featured work.lib/resume-files.ts— metadata for the single downloadable résumé PDF used by the Résumé app.lib/contact.ts— resolves the contact/social block from environment variables at request time.scripts/generate-app-registry.mjs— scans the app folders and writesapps/registry.generated.ts.scripts/generate-resume.tsx— renders the downloadable ATS PDF fromlib/resume.ts(runs beforedev/build).public/— static assets used by metadata and browsers.
Most portfolio copy and structured content lives in lib/resume.ts. Update that file first when changing:
- Name, title, focus, professional summary, and highlights
- Skills and education
- Experience history and project descriptions
- Featured project cards
- Contact environment variable keys
App-specific copy lives in a data.ts file inside the relevant app folder under components/portfolio/os/apps/.
Each feature of the OS is a self-contained app under components/portfolio/os/apps/:
userApps/— optional modules a visitor can install/uninstall from the App Store.systemApps/— built-in apps that are always present and cannot be removed.
The registry is generated automatically from the folder structure, so you never edit a central list to add an app. Just create a folder with an index.tsx entry point:
components/portfolio/os/apps/userApps/StopwatchApp/
index.tsx # required — default-exports the app component
icon.tsx # optional — default-exports the dock icon (falls back to a generic one)
manifest.ts # optional — id, title, size, etc. (defaults derived from the folder name)
1. The component (index.tsx) is the only required file. Default-export a React component. It optionally receives AppComponentProps (payload, windowKey):
// index.tsx
export default function StopwatchApp() {
return <div className="p-4">Hello from a new app.</div>;
}2. The icon (icon.tsx) is co-located so the app stays self-contained. Default-export an SVG component that accepts className:
// icon.tsx
export default function Icon({ className }: { className?: string }) {
return (
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
aria-hidden
className={className ?? "size-4"}
>
<circle cx="12" cy="13" r="7" />
<path d="M12 13V9M10 2h4" />
</svg>
);
}3. The manifest (manifest.ts) customises how the app appears. Without it, the id and title are derived from the folder name and a generic icon is used.
// manifest.ts
import Icon from "./icon";
import type { AppManifest } from "../../appManifest";
export const manifest: AppManifest = {
id: "stopwatch", // stable, persisted in user storage — don't change casually
title: "Stopwatch",
shortLabel: "Watch", // optional — used where space is tight
description: "A tiny timer.", // shown in the App Store
Icon,
defaultSize: { w: 420, h: 360 }, // optional default window size
order: 200, // optional — lower sorts earlier in the dock/store
};That's it. The kind (system vs user) is inferred from which folder the app lives in. The registry is regenerated on npm run dev and npm run build; to regenerate it manually after adding a folder while the dev server is running, run:
npm run gen:appsThis writes components/portfolio/os/apps/registry.generated.ts — a generated file you should not edit by hand.
- Code splitting: the OS shells and every app body are separate chunks loaded on demand. The initial bundle only carries lightweight metadata and icons; the lock screen warms what it needs while the visitor reads, and installing an app fetches its chunk.
- Two shells, one state: desktop and mobile are distinct UIs, but
AppRuntimereparents live app DOM via portals so state survives crossing the breakpoint. - Persistence: user settings and the open-window session are stored in
localStorageand re-validated against the current build (stale or unknown app ids are dropped). - Accessibility: the OS honors
prefers-reduced-motion, and interactive surfaces support keyboard navigation. - Résumé, one source:
lib/resume.tsfeeds both a fully styled in-OS résumé (the Résumé app renders summary, highlights, experience, skills, and education in-window) and a single ATS-friendly PDF generated at build time (scripts/generate-resume.tsx→public/Amandeep_Yadav_Resume.pdf), which the app offers as a download. Contact details come from env, so the app and the PDF always match.
npm run gen:appsregenerates the app registry from theapps/folder structure (runs automatically beforedevandbuild).npm run gen:resumeregenerates the downloadable ATS résumé PDF fromlib/resume.ts(also runs automatically beforedevandbuild).npm run devstarts the local development server.npm run buildcreates a production build.npm run startstarts the built app.npm run lintruns ESLint.npm run typecheckruns the TypeScript compiler with no emit.npm run formatformats the codebase with Prettier (npm run format:checkverifies formatting).
Before publishing changes, run:
npm run lint
npm run typecheck
npm run buildThis project is ready for a standard Vercel deployment. Configure the same environment variables from .env.example in the hosting provider before publishing.