feat: enhance user interface with CRT effects, update AboutMe and Projects sections, and add new Collab app
All checks were successful
Deploy / deploy (push) Successful in 14s

This commit is contained in:
M1ngdaXie
2026-05-07 21:42:06 +08:00
parent 38bc80c566
commit 74554210dd
11 changed files with 310 additions and 37 deletions

View File

@@ -37,3 +37,43 @@ html, body, #root {
font-smooth: never;
image-rendering: pixelated;
}
/* CRT scanlines + faint phosphor glow over the entire desktop */
#root::after {
content: '';
position: fixed;
inset: 0;
pointer-events: none;
z-index: 9998;
background:
repeating-linear-gradient(
0deg,
rgba(0, 255, 255, 0.025) 0px,
rgba(0, 255, 255, 0.025) 1px,
transparent 1px,
transparent 3px
);
mix-blend-mode: screen;
animation: crt-flicker 4.2s steps(60) infinite;
}
@keyframes crt-flicker {
0%, 100% { opacity: 0.85; }
47% { opacity: 0.92; }
50% { opacity: 0.6; }
53% { opacity: 0.9; }
}
@keyframes blink-caret {
0%, 49% { opacity: 1; }
50%, 100% { opacity: 0; }
}
.cypher-caret {
display: inline-block;
width: 0.55em;
margin-left: 2px;
background: var(--pixel-cyan);
color: transparent;
animation: blink-caret 1s steps(1) infinite;
}

View File

@@ -1,35 +1,177 @@
export default function AboutMe() {
return (
<div style={{ padding: '24px', color: '#fff', fontFamily: 'var(--font-ui)' }}>
<div style={{ display: 'flex', gap: 20, alignItems: 'flex-start', marginBottom: 20 }}>
<div style={{
width: 72, height: 72, borderRadius: '50%',
background: 'linear-gradient(135deg, #0a84ff, #bf5af2)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: 36, flexShrink: 0
}}>👤</div>
<div
style={{ padding: "24px", color: "#fff", fontFamily: "var(--font-ui)" }}
>
<div
style={{
fontFamily: "var(--font-mono)",
fontSize: 12,
color: "var(--pixel-cyan)",
marginBottom: 16,
letterSpacing: 0.5,
}}
>
<span style={{ opacity: 0.5 }}>mingda@doitou-sv:~$ </span>
whoami<span className="cypher-caret"></span>
</div>
<div
style={{
display: "flex",
gap: 20,
alignItems: "flex-start",
marginBottom: 20,
}}
>
<div
style={{
width: 72,
height: 72,
borderRadius: "50%",
background: "linear-gradient(135deg, #0a84ff, #bf5af2)",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: 36,
flexShrink: 0,
}}
>
👤
</div>
<div>
<h2 style={{ fontSize: 20, fontWeight: 600, marginBottom: 4 }}>Mingda Xie <span style={{ color: 'rgba(255,255,255,0.4)', fontWeight: 400, fontSize: 14 }}></span></h2>
<p style={{ fontSize: 13, color: 'rgba(255,255,255,0.6)', lineHeight: 1.5 }}>Backend Developer · CS Master's @ Northeastern University</p>
<h2 style={{ fontSize: 20, fontWeight: 600, marginBottom: 4 }}>
Mingda Xie{" "}
<span
style={{
color: "rgba(255,255,255,0.4)",
fontWeight: 400,
fontSize: 14,
}}
>
</span>
</h2>
<p
style={{
fontSize: 13,
color: "rgba(255,255,255,0.6)",
lineHeight: 1.5,
}}
>
Backend Developer · CS Master's @ Northeastern University
</p>
</div>
</div>
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginBottom: 20 }}>
{['Java/Spring Cloud', 'Go', 'TypeScript/Bun', 'Redis', 'k3s', 'Microservices'].map(tag => (
<span key={tag} style={{
padding: '3px 10px', borderRadius: 20,
background: 'rgba(10, 132, 255, 0.15)',
border: '1px solid rgba(10,132,255,0.3)',
fontSize: 12, color: '#64d2ff'
}}>{tag}</span>
<div
style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 20 }}
>
{[
"Java/Spring Cloud",
"Go",
"TypeScript/Bun",
"Redis",
"k3s",
"Microservices",
].map((tag) => (
<span
key={tag}
style={{
padding: "2px 8px",
borderRadius: 0,
background: "rgba(0,255,255,0.06)",
border: "1px solid rgba(0,255,255,0.25)",
fontSize: 11,
fontFamily: "var(--font-mono)",
color: "var(--pixel-cyan)",
letterSpacing: 0.3,
}}
>
[{tag}]
</span>
))}
</div>
<p style={{ fontSize: 13, color: 'rgba(255,255,255,0.7)', lineHeight: 1.7, marginBottom: 16 }}>
Backend engineer who runs a personal k3s cluster on Vultr, ships microservices, and builds things that occasionally work in production.
Pixel art enjoyer. Stardew Valley veteran. Huge fan of 土屋アンナ.
<p
style={{
fontSize: 13,
fontFamily: "var(--font-mono)",
color: "rgba(255,255,255,0.7)",
lineHeight: 1.7,
marginBottom: 16,
}}
>
<span style={{ color: "rgba(0,255,255,0.45)" }}>// </span>
Backend engineer. Distributed systems, infra, and the long tail of
things that go wrong at 3am. Trust the math, not the institution.
</p>
<p style={{ fontSize: 12, color: 'rgba(255,255,255,0.3)', fontStyle: 'italic' }}>
{/* <p style={{ fontSize: 12, color: 'rgba(255,255,255,0.3)', fontStyle: 'italic' }}>
"I am the dragon scroll, bitch." Genuine mastery over shortcuts.
</p>
</p> */}
<div
style={{
marginTop: 8,
padding: "12px 14px",
background: "rgba(0,0,0,0.25)",
border: "1px solid rgba(255,255,255,0.08)",
borderRadius: 8,
}}
>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 8,
}}
>
<span
style={{
fontSize: 11,
color: "rgba(255,255,255,0.4)",
letterSpacing: 0.5,
textTransform: "uppercase",
}}
>
Signed Identity
</span>
<a
href="/pgp.asc"
target="_blank"
rel="noopener noreferrer"
style={{
fontSize: 11,
color: "#64d2ff",
textDecoration: "none",
}}
>
pgp.asc
</a>
</div>
<pre
style={{
margin: 0,
fontFamily: "var(--font-mono)",
fontSize: 11,
lineHeight: 1.5,
color: "rgba(255,255,255,0.65)",
whiteSpace: "pre-wrap",
wordBreak: "break-all",
}}
>{`-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
identity: Mingda Xie
email: xiemingda2020@gmail.com
pgp: 8C50 AD5D CE0E 949E 39FA F19D 609C 704E 544A 0BFD
-----BEGIN PGP SIGNATURE-----
iJEEARYKADkWIQSNntaQmSEMuam1aJhRySHSOW66vwUCafyKpRsUgAAAAAAEAA5t
YW51MiwyLjUrMS4xMiwwLDMACgkQUckh0jluur8brQD/RzlXBuVuQnGioNsibNDm
Rm85/5ed2BROS5gaH4FCBrQBALjMapIlyCYBNvIZNQX1eFCY/WNfZQXe3woeZYlP
E/IF
=k1lO
-----END PGP SIGNATURE-----`}</pre>
</div>
</div>
);
}

View File

@@ -1,7 +1,6 @@
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' },
];

View File

@@ -1,5 +1,5 @@
import { useState, useRef, useEffect } from 'react';
import type { KeyboardEvent } from 'react';
import { useEffect, useRef, useState } from 'react';
interface Line { type: 'input' | 'output'; text: string; key: number }
@@ -8,12 +8,34 @@ const COMMANDS: Record<string, string[]> = {
'Available commands:',
' help — show this list',
' about — who is mingda',
' whoami — current identity',
' projects — list projects',
' skills — tech stack',
' contact — contact info',
' pgp — public key fingerprint',
' motd — message of the day',
' neofetch — system info',
' clear — clear terminal',
],
whoami: [
'mingda',
'uid=1000 groups=(wheel,crypto,sudoers)',
'shell=/bin/zsh tty=/dev/pts/0',
],
pgp: [
'PGP fingerprint:',
' 8C50 AD5D CE0E 949E 39FA F19D 609C 704E 544A 0BFD',
'',
'Public key: /pgp.asc',
'Verify before you trust.',
],
motd: [
'cypherpunks write code.',
'privacy is necessary for an open society in the electronic age.',
'we cannot expect governments, corporations, or other large,',
'faceless organizations to grant us privacy out of their beneficence.',
' — eric hughes, 1993',
],
about: [
'Mingda Xie (解明达)',
'Backend Developer · CS Master\'s @ Northeastern University',
@@ -28,7 +50,6 @@ const COMMANDS: Record<string, string[]> = {
'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',
],
@@ -55,7 +76,7 @@ const COMMANDS: Record<string, string[]> = {
' .::!!!::. Kernel: Ubuntu 24.04',
' .:::!!!:::. RAM: 4GB',
' .::::!!!::::. Shell: zsh',
' .:::::!!!:::::. Services: Alfred, Chengyu, k3s, VLESS',
' .:::::!!!:::::. Services: Alfred, Chengyu, k3s',
'',
' nginx + k3s + let\'s encrypt',
],
@@ -68,8 +89,9 @@ function nextKey() { return ++lineCounter; }
export default function Terminal() {
const [lines, setLines] = useState<Line[]>([
{ type: 'output', text: 'MingdaOS Terminal v1.0', key: nextKey() },
{ type: 'output', text: 'Type "help" to see available commands.', key: nextKey() },
{ type: 'output', text: 'MingdaOS Terminal v1.0 [secure tty]', key: nextKey() },
{ type: 'output', text: 'kernel: linux 6.x | tls: 1.3 | pgp: 8C50AD5D...0BFD', key: nextKey() },
{ type: 'output', text: 'cypherpunks write code. type "help" for commands.', key: nextKey() },
{ type: 'output', text: '', key: nextKey() },
]);
const [input, setInput] = useState('');

View File

@@ -50,12 +50,19 @@ export default function Desktop() {
gridRow: app.desktopPosition.row + 1,
}}
>
<DesktopIcon app={app} onOpen={() => openWindow(app.id)} />
<DesktopIcon
app={app}
onOpen={() =>
app.externalUrl
? window.open(app.externalUrl, '_blank', 'noopener,noreferrer')
: openWindow(app.id)
}
/>
</div>
))}
</div>
{APPS.map(app => (
{APPS.filter(app => !app.externalUrl).map(app => (
<Window key={app.id} app={app} />
))}

View File

@@ -10,7 +10,9 @@ export default function DesktopIcon({ app, onOpen }: Props) {
return (
<div className="desktop-icon" onDoubleClick={onOpen}>
<div className="desktop-icon-img" style={{ background: app.iconGradient }}>
<span>{app.emoji}</span>
{app.iconImage
? <img src={app.iconImage} alt={app.title} style={{ width: '70%', height: '70%', objectFit: 'contain' }} />
: <span>{app.emoji}</span>}
</div>
<span className="desktop-icon-label">{app.title}</span>
</div>

View File

@@ -1,31 +1,52 @@
import { useEffect, useState } from 'react';
import './MenuBar.css';
const MENU_ITEMS = ['File', 'Edit', 'View', 'Go', 'Help'];
const MENU_ITEMS = ['encrypt', 'sign', 'verify', 'wipe', 'about'];
const BOOT_AT = Date.now();
export default function MenuBar() {
const [time, setTime] = useState(() => formatTime(new Date()));
const [uptime, setUptime] = useState(() => formatUptime(0));
useEffect(() => {
const id = setInterval(() => setTime(formatTime(new Date())), 1000);
const id = setInterval(() => {
setTime(formatTime(new Date()));
setUptime(formatUptime(Date.now() - BOOT_AT));
}, 1000);
return () => clearInterval(id);
}, []);
return (
<div className="menubar">
<div className="menubar-left">
<span className="menubar-logo">&#63743;</span>
<span className="menubar-logo">[ MingdaOS ]</span>
{MENU_ITEMS.map(item => (
<span key={item} className="menubar-item">{item}</span>
))}
</div>
<div className="menubar-right">
<span className="menubar-clock">{time}</span>
<span className="menubar-clock" style={{ opacity: 0.55, marginRight: 12 }}>
up {uptime}
</span>
<span className="menubar-clock">
{time}<span className="cypher-caret"></span>
</span>
</div>
</div>
);
}
function formatTime(d: Date) {
return d.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
const hh = String(d.getUTCHours()).padStart(2, '0');
const mm = String(d.getUTCMinutes()).padStart(2, '0');
const ss = String(d.getUTCSeconds()).padStart(2, '0');
return `${hh}:${mm}:${ss} UTC`;
}
function formatUptime(ms: number) {
const s = Math.floor(ms / 1000);
const h = Math.floor(s / 3600);
const m = Math.floor((s % 3600) / 60);
const sec = s % 60;
return `${String(h).padStart(2, '0')}h${String(m).padStart(2, '0')}m${String(sec).padStart(2, '0')}s`;
}

View File

@@ -89,6 +89,18 @@ export const APPS: AppConfig[] = [
component: Trash,
desktopPosition: { col: 1, row: 3 },
},
{
id: 'collab',
title: 'Collab',
emoji: '',
iconGradient: 'linear-gradient(135deg, rgba(255,255,255,0.06), rgba(255,255,255,0.02))',
iconImage: '/docnest-icon-32.png',
defaultSize: { width: 400, height: 360 },
defaultPosition: { x: 0, y: 0 },
component: Links,
desktopPosition: { col: 0, row: 4 },
externalUrl: 'https://collab.m1ngdaxie.com/',
},
];
export const WALLPAPERS = [

View File

@@ -21,6 +21,8 @@ export interface AppConfig {
defaultPosition: { x: number; y: number };
component: ComponentType;
desktopPosition: { col: number; row: number };
externalUrl?: string;
iconImage?: string;
}
export interface OSState {