diff --git a/src/apps/AboutMe.tsx b/src/apps/AboutMe.tsx
new file mode 100644
index 0000000..542fe0e
--- /dev/null
+++ b/src/apps/AboutMe.tsx
@@ -0,0 +1,36 @@
+export default function AboutMe() {
+ return (
+
+
+
👤
+
+
Mingda Xie 解明达
+
Backend Developer · CS Master's @ Northeastern University
+
Shenzhen-bound · Class of 2026
+
+
+
+ {['Java/Spring Cloud', 'Go', 'TypeScript/Bun', 'Redis', 'k3s', 'Microservices'].map(tag => (
+ {tag}
+ ))}
+
+
+ Backend engineer who runs a personal k3s cluster on Vultr, ships microservices, and builds things that occasionally work in production.
+ LeetCode 400+. Pixel art enjoyer. Stardew Valley veteran. Huge fan of 土屋アンナ.
+
+
+ "I am the dragon scroll, bitch." — Genuine mastery over shortcuts.
+
+
+ );
+}
diff --git a/src/apps/Alfred.tsx b/src/apps/Alfred.tsx
new file mode 100644
index 0000000..0a52e9e
--- /dev/null
+++ b/src/apps/Alfred.tsx
@@ -0,0 +1,28 @@
+export default function Alfred() {
+ return (
+
+
+
☠️
+
+
Alfred
+
Personal AI Agent
+
+
+
+ Alfred is my self-hosted AI agent running on k3s. It handles task management, reminders,
+ Telegram notifications, and acts as a personal ops layer for my homelab stack.
+
+
+ {['Task & reminder management', 'Telegram bot interface', 'Home automation bridge', 'LLM-powered command routing'].map(f => (
+
+ ▶ {f}
+
+ ))}
+
+
+ );
+}
diff --git a/src/apps/Chengyu.tsx b/src/apps/Chengyu.tsx
new file mode 100644
index 0000000..078bd68
--- /dev/null
+++ b/src/apps/Chengyu.tsx
@@ -0,0 +1,32 @@
+export default function Chengyu() {
+ return (
+
+
🀄
+
成语填空
+
Chinese Idiom Guessing Game
+
Wordle-style · Daily puzzle · 4-character idioms
+
+ Guess the 4-character Chinese idiom (成语) in 6 tries.
+ Correct characters turn green, misplaced turn yellow.
+ A new puzzle drops every day at midnight.
+
+
+ Launch Game →
+
+
+ );
+}
diff --git a/src/apps/Links.tsx b/src/apps/Links.tsx
new file mode 100644
index 0000000..0a20638
--- /dev/null
+++ b/src/apps/Links.tsx
@@ -0,0 +1,41 @@
+const LINKS = [
+ { label: 'GitHub', sub: 'github.com/m1ngdaxie', emoji: '🐙', url: 'https://github.com/m1ngdaxie', color: '#30d158' },
+ { label: 'Homepage', sub: 'm1ngdaxie.com', emoji: '🌐', url: 'https://m1ngdaxie.com', color: '#0a84ff' },
+ { label: '成语填空', sub: 'chengyu.m1ngdaxie.com', emoji: '🀄', url: 'https://chengyu.m1ngdaxie.com', color: '#ff453a' },
+ { label: 'Email', sub: 'mingda@m1ngdaxie.com', emoji: '✉️', url: 'mailto:mingda@m1ngdaxie.com', color: '#bf5af2' },
+];
+
+export default function Links() {
+ return (
+
+ );
+}
diff --git a/src/apps/Poker.tsx b/src/apps/Poker.tsx
new file mode 100644
index 0000000..a15f00d
--- /dev/null
+++ b/src/apps/Poker.tsx
@@ -0,0 +1,16 @@
+export default function Poker() {
+ return (
+
+
🎲
+
Poker
+
🚧 Under Construction
+
+ A Texas Hold'em practice tool is in the works. Check back later.
+
+
+ );
+}
diff --git a/src/apps/Projects.tsx b/src/apps/Projects.tsx
new file mode 100644
index 0000000..57bb691
--- /dev/null
+++ b/src/apps/Projects.tsx
@@ -0,0 +1,38 @@
+const PROJECTS = [
+ { name: 'CampusNest', desc: 'Campus housing platform with Spring Cloud microservices, Redis caching, and k3s deployment.', tags: ['Java', 'Spring Cloud', 'Redis', 'k3s'], color: '#0a84ff' },
+ { name: 'Alfred Bot', desc: 'Personal AI agent — task management, reminders, home automation bridge.', tags: ['Go', 'TypeScript', 'Bun'], color: '#bf5af2' },
+ { name: 'Warden / Bastion', desc: 'Self-hosted auth and proxy layer running on k3s with Let\'s Encrypt.', tags: ['Nginx', 'k3s', 'VLESS'], color: '#30d158' },
+ { name: 'Collab Platform', desc: 'Real-time collaborative workspace with WebSocket rooms and conflict resolution.', tags: ['TypeScript', 'Bun', 'WebSocket'], color: '#ffd60a' },
+ { name: '成语填空', desc: 'Daily Chinese idiom guessing game — Wordle-style, runs at chengyu.m1ngdaxie.com.', tags: ['React', 'TypeScript'], color: '#ff453a' },
+];
+
+export default function Projects() {
+ return (
+
+ {PROJECTS.map(p => (
+
+
+
{p.desc}
+
+ {p.tags.map(t => (
+ {t}
+ ))}
+
+
+ ))}
+
+ );
+}
diff --git a/src/apps/Terminal.tsx b/src/apps/Terminal.tsx
new file mode 100644
index 0000000..6593421
--- /dev/null
+++ b/src/apps/Terminal.tsx
@@ -0,0 +1,178 @@
+import { useState, useRef, useEffect } from 'react';
+import type { KeyboardEvent } from 'react';
+
+interface Line { type: 'input' | 'output'; text: string; key: number }
+
+const COMMANDS: Record = {
+ help: [
+ 'Available commands:',
+ ' help — show this list',
+ ' about — who is mingda',
+ ' projects — list projects',
+ ' skills — tech stack',
+ ' contact — contact info',
+ ' neofetch — system info',
+ ' clear — clear terminal',
+ ],
+ about: [
+ 'Mingda Xie (解明达)',
+ 'Backend Developer · CS Master\'s @ Northeastern University',
+ 'Shenzhen-bound · Class of 2026',
+ '',
+ 'Runs a personal k3s cluster. Writes Go and Java at 2am.',
+ 'LeetCode 400+. Pixel art. Stardew Valley. 土屋アンナ.',
+ '',
+ '"I am the dragon scroll, bitch."',
+ ],
+ projects: [
+ 'Projects:',
+ ' CampusNest — Spring Cloud housing platform',
+ ' Alfred Bot — Self-hosted AI agent',
+ ' Warden/Bastion — k3s auth + proxy layer',
+ ' Collab Platform— Real-time collaborative workspace',
+ ' 成语填空 — Daily Chinese idiom game',
+ ],
+ skills: [
+ 'Tech Stack:',
+ ' Languages : Java, Go, TypeScript, Python',
+ ' Frameworks : Spring Cloud, Bun, React',
+ ' Infra : k3s, Nginx, Redis, Docker',
+ ' Cloud : Vultr, Let\'s Encrypt',
+ ' Other : Microservices, WebSocket, LLM APIs',
+ ],
+ contact: [
+ 'Contact:',
+ ' GitHub : github.com/m1ngdaxie',
+ ' Web : m1ngdaxie.com',
+ ' Game : chengyu.m1ngdaxie.com',
+ ' Email : mingda@m1ngdaxie.com',
+ ],
+ neofetch: [
+ ' . mingda@doitou-sv',
+ ' .:. ----------------',
+ ' .:!:. OS: MingdaOS 1.0',
+ ' .:!!!:. Host: Vultr VPS',
+ ' .::!!!::. Kernel: Ubuntu 24.04',
+ ' .:::!!!:::. RAM: 4GB',
+ ' .::::!!!::::. Shell: zsh',
+ ' .:::::!!!:::::. Services: Alfred, Chengyu, k3s, VLESS',
+ '',
+ ' nginx + k3s + let\'s encrypt',
+ ],
+};
+
+const PROMPT = 'mingda@doitou-sv ~ % ';
+
+let lineCounter = 0; // module-level counter for stable unique keys
+function nextKey() { return ++lineCounter; }
+
+export default function Terminal() {
+ const [lines, setLines] = useState([
+ { type: 'output', text: 'MingdaOS Terminal v1.0', key: nextKey() },
+ { type: 'output', text: 'Type "help" to see available commands.', key: nextKey() },
+ { type: 'output', text: '', key: nextKey() },
+ ]);
+ const [input, setInput] = useState('');
+ const [history, setHistory] = useState([]);
+ const [histIdx, setHistIdx] = useState(-1);
+ const bottomRef = useRef(null);
+ const inputRef = useRef(null);
+
+ useEffect(() => {
+ bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
+ }, [lines]);
+
+ useEffect(() => {
+ inputRef.current?.focus();
+ }, []);
+
+ function handleSubmit() {
+ const cmd = input.trim().toLowerCase();
+ const newLines: Line[] = [...lines, { type: 'input', text: PROMPT + input, key: nextKey() }];
+
+ if (cmd === '') {
+ setLines([...newLines, { type: 'output', text: '', key: nextKey() }]);
+ } else if (cmd === 'clear') {
+ setLines([]);
+ } else if (COMMANDS[cmd]) {
+ COMMANDS[cmd].forEach(l => newLines.push({ type: 'output', text: l, key: nextKey() }));
+ newLines.push({ type: 'output', text: '', key: nextKey() });
+ setLines(newLines);
+ } else {
+ newLines.push({ type: 'output', text: `zsh: command not found: ${cmd}`, key: nextKey() });
+ newLines.push({ type: 'output', text: '', key: nextKey() });
+ setLines(newLines);
+ }
+
+ if (cmd) setHistory(h => [cmd, ...h].slice(0, 50));
+ setHistIdx(-1);
+ setInput('');
+ }
+
+ function handleKeyDown(e: KeyboardEvent) {
+ if (e.key === 'Enter') {
+ handleSubmit();
+ } else if (e.key === 'ArrowUp') {
+ e.preventDefault();
+ const next = Math.min(histIdx + 1, history.length - 1);
+ setHistIdx(next);
+ if (history[next] !== undefined) setInput(history[next]);
+ } else if (e.key === 'ArrowDown') {
+ e.preventDefault();
+ const next = Math.max(histIdx - 1, -1);
+ setHistIdx(next);
+ setInput(next === -1 ? '' : history[next]);
+ }
+ }
+
+ return (
+ inputRef.current?.focus()}
+ >
+
+ {lines.map((line) => (
+
+ {line.text}
+
+ ))}
+
+
+
+ {PROMPT}
+ setInput(e.target.value)}
+ onKeyDown={handleKeyDown}
+ style={{
+ flex: 1,
+ background: 'transparent',
+ border: 'none',
+ outline: 'none',
+ color: '#e2e8f0',
+ fontFamily: 'var(--font-mono)',
+ fontSize: 13,
+ caretColor: '#64d2ff',
+ }}
+ spellCheck={false}
+ autoComplete="off"
+ />
+
+
+ );
+}
diff --git a/src/apps/Trash.tsx b/src/apps/Trash.tsx
new file mode 100644
index 0000000..8b37383
--- /dev/null
+++ b/src/apps/Trash.tsx
@@ -0,0 +1,12 @@
+export default function Trash() {
+ return (
+
+
🗑️
+
Trash
+
The trash is empty.
+
+ just like my social life
+
+
+ );
+}