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

BIN
public/docnest-icon-32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 B

26
public/pgp.asc Normal file
View File

@@ -0,0 +1,26 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mDMEafxj9hYJKwYBBAHaRw8BAQdAuem8vfNFdu8MdEgkufBSWXIyYZofTxx/hNqM
WKAoi3O0JE1pbmdkYSBYaWUgPHhpZW1pbmdkYTIwMjBAZ21haWwuY29tPoi1BBMW
CgBdFiEEjFCtXc4OlJ45+vGdYJxwTlRKC/0FAmn8Y/YbFIAAAAAABAAObWFudTIs
Mi41KzEuMTIsMCwzAhsBBQkJZgGABQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheA
AAoJEGCccE5USgv9iugA/2BRU4ooaWF89QigzIyCK0g//qzw//0ZqT0dsKsYhYwX
AQCv+9lJnAJSpV+CsypiKtAWp7Rf3ryim2ZnSE0Jxiy8C7gzBGn8aooWCSsGAQQB
2kcPAQEHQJej8vLd6Zp/iC6j9rd/2jVPqDvS3HjH8eqNHYUpOVruiQERBBgWCgBC
FiEEjFCtXc4OlJ45+vGdYJxwTlRKC/0FAmn8aoobFIAAAAAABAAObWFudTIsMi41
KzEuMTIsMCwzAhsCBQkDwmcAAIEJEGCccE5USgv9diAEGRYKAB0WIQSNntaQmSEM
uam1aJhRySHSOW66vwUCafxqigAKCRBRySHSOW66v/LdAP9co9+y961vHGuVuif4
V5/Ngj9f3zGPJCS2726Dt7HH3gEAhUMV6kIUW0Ii9UXcTi54m+ldHEHcXVyOAlA4
GYtlLwkKDAD7B3FSt615NnFWE522VrmcA/UyrcvvyGMBJTIISuWz5X4A/3msKBYh
V3Ov5ir30JsHdq/34yKCKFDTKdqt3lXjf34MuDgEafxq7xIKKwYBBAGXVQEFAQEH
QHYOZ8u8g9QGrTkj9Hd4/A5JKHCLQb/72PiKrnaTmRB0AwEIB4iaBBgWCgBCFiEE
jFCtXc4OlJ45+vGdYJxwTlRKC/0FAmn8au8bFIAAAAAABAAObWFudTIsMi41KzEu
MTIsMCwzAhsMBQkDwmcAAAoJEGCccE5USgv9gbAA/AvmH45NgIyD8T1qQ+kEtRSv
O+CQLHbOt44R5wOuGMdmAQD/Elb2BuIHeGnFL/Q5x8+irKX59cJhxVWgRxPL0DW3
CLgzBGn8aygWCSsGAQQB2kcPAQEHQH3HCFluWlBCg+wE2z7IlCueCMOLK+ghOtIY
sBzwdVxIiJoEGBYKAEIWIQSMUK1dzg6Unjn68Z1gnHBOVEoL/QUCafxrKBsUgAAA
AAAEAA5tYW51MiwyLjUrMS4xMiwwLDMCGyAFCQPCZwAACgkQYJxwTlRKC/2PwAD+
KVRABLUlz27lVoY27Y54wyDIssIXYvw//2LU92a0SzsBAJLa2F1uihpBO6C5PgoL
F7lvu2C0si9LdM0JclC2UvUD
=opD7
-----END PGP PUBLIC KEY BLOCK-----

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 {