From 74554210dd62cfd7925fe5c89effb57c7f30553f Mon Sep 17 00:00:00 2001 From: M1ngdaXie <156019134+M1ngdaXie@users.noreply.github.com> Date: Thu, 7 May 2026 21:42:06 +0800 Subject: [PATCH] feat: enhance user interface with CRT effects, update AboutMe and Projects sections, and add new Collab app --- public/docnest-icon-32.png | Bin 0 -> 744 bytes public/pgp.asc | 26 +++ src/App.css | 40 +++++ src/apps/AboutMe.tsx | 188 ++++++++++++++++++--- src/apps/Projects.tsx | 1 - src/apps/Terminal.tsx | 32 +++- src/components/Desktop/Desktop.tsx | 11 +- src/components/DesktopIcon/DesktopIcon.tsx | 4 +- src/components/MenuBar/MenuBar.tsx | 31 +++- src/config/apps.ts | 12 ++ src/types/index.ts | 2 + 11 files changed, 310 insertions(+), 37 deletions(-) create mode 100644 public/docnest-icon-32.png create mode 100644 public/pgp.asc diff --git a/public/docnest-icon-32.png b/public/docnest-icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..3d736b0b307ae6e630395a3eabae65ecbeea21b7 GIT binary patch literal 744 zcmVP)8lSXp1%4}mVrWW`szc6(^ns^CPhOhrIz9`I~lKnM=w6?qLt-Hj?hQuGa^YS znrh46$EHb6)A~QD7CpLtl#x`I8R4>=Q~=VGQquh#mt9j9?>I`d!{o?x!0D?G!88uf z!o^vLb_oLmgS?gvUipXDjxc=t@rU91xdRN>?mdH9w%=4+-VuuwQH6U zRAZ3yfnjeeD}#xa93iFC7#J8xa{$axkT1D7P_=@@V8FThH^YS~2tGc*LhUHXcR0(? z)z-|w#c_oC4k)Xt#VbE`$xbj0N(00K7Ge~^f;>CM9VMtiVlX*u(Lyx`a6t_w6a~mp zK~)F9fSY#!1GNDQF^WLJD^*|2@T|EPYcP`={6sqdWO(R5S+FBMbWS8HH&E39AYh=c z%V5Bu%Rs4xNZ=ocU?Dc%K{?>@%a24`c;?z8LQ0rG1i>m3RB?z4^D__wUcUXv08$I0 zk-!1;y5P}^5C1`GOts}135LMT`Kuxg^mUgrkO9n%G#Ri}M92wxW}u6qn~B%ea+=ovr!3w9ugj77Ak1Hu*MKYs zqhWF|c__Q|^woy~a+=l~plTYR4g$9qa5?}KN63bM{r(&70Ccrj@P1@DC_4dJFVq2F zL2M=@!TB(DQ_pM^N9ET}K(=&0o;D)57J|y-tdT%b!N9;!3?fKtI8p&11%0xqw){MJ z2!Ozp#XIKz|Noz6jyiqy;Z_(MWI2dt#N)~trrPqERI(TprztSLiIyA_BO@a?BpC5% z`VWmOiUFuRfB{h9gDl60PvfH(ALL)Y{dkLkfq@N+^lxY)W=0bSl``0h&TLa{c~C=i aZ~*`~ina${L#QhN0000r literal 0 HcmV?d00001 diff --git a/public/pgp.asc b/public/pgp.asc new file mode 100644 index 0000000..7a3b4d7 --- /dev/null +++ b/public/pgp.asc @@ -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----- diff --git a/src/App.css b/src/App.css index 4834b10..ecaa212 100644 --- a/src/App.css +++ b/src/App.css @@ -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; +} diff --git a/src/apps/AboutMe.tsx b/src/apps/AboutMe.tsx index e1a769f..608e7c4 100644 --- a/src/apps/AboutMe.tsx +++ b/src/apps/AboutMe.tsx @@ -1,35 +1,177 @@ export default function AboutMe() { return ( -
-
-
👤
+
+
+ mingda@doitou-sv:~$ + whoami +
+
+
+ 👤 +
-

Mingda Xie 解明达

-

Backend Developer · CS Master's @ Northeastern University

+

+ Mingda Xie{" "} + + 解明达 + +

+

+ Backend Developer · CS Master's @ Northeastern University +

-
- {['Java/Spring Cloud', 'Go', 'TypeScript/Bun', 'Redis', 'k3s', 'Microservices'].map(tag => ( - {tag} +
+ {[ + "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. - Pixel art enjoyer. Stardew Valley veteran. Huge fan of 土屋アンナ. +

+ // + Backend engineer. Distributed systems, infra, and the long tail of + things that go wrong at 3am. Trust the math, not the institution.

-

+ {/*

"I am the dragon scroll, bitch." — Genuine mastery over shortcuts. -

+

*/} +
+
+ + Signed Identity + + + pgp.asc ↗ + +
+
{`-----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-----`}
+
); } diff --git a/src/apps/Projects.tsx b/src/apps/Projects.tsx index 57bb691..bf08a6b 100644 --- a/src/apps/Projects.tsx +++ b/src/apps/Projects.tsx @@ -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' }, ]; diff --git a/src/apps/Terminal.tsx b/src/apps/Terminal.tsx index fe2e900..ff4782d 100644 --- a/src/apps/Terminal.tsx +++ b/src/apps/Terminal.tsx @@ -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 = { '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 = { '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 = { ' .::!!!::. 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([ - { 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(''); diff --git a/src/components/Desktop/Desktop.tsx b/src/components/Desktop/Desktop.tsx index e9ccbed..bc69de4 100644 --- a/src/components/Desktop/Desktop.tsx +++ b/src/components/Desktop/Desktop.tsx @@ -50,12 +50,19 @@ export default function Desktop() { gridRow: app.desktopPosition.row + 1, }} > - openWindow(app.id)} /> + + app.externalUrl + ? window.open(app.externalUrl, '_blank', 'noopener,noreferrer') + : openWindow(app.id) + } + />
))}
- {APPS.map(app => ( + {APPS.filter(app => !app.externalUrl).map(app => ( ))} diff --git a/src/components/DesktopIcon/DesktopIcon.tsx b/src/components/DesktopIcon/DesktopIcon.tsx index 8330f8d..33b40fb 100644 --- a/src/components/DesktopIcon/DesktopIcon.tsx +++ b/src/components/DesktopIcon/DesktopIcon.tsx @@ -10,7 +10,9 @@ export default function DesktopIcon({ app, onOpen }: Props) { return (
- {app.emoji} + {app.iconImage + ? {app.title} + : {app.emoji}}
{app.title}
diff --git a/src/components/MenuBar/MenuBar.tsx b/src/components/MenuBar/MenuBar.tsx index aa3c37f..fa7e9e4 100644 --- a/src/components/MenuBar/MenuBar.tsx +++ b/src/components/MenuBar/MenuBar.tsx @@ -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 (
- + [ MingdaOS ] {MENU_ITEMS.map(item => ( {item} ))}
- {time} + + up {uptime} + + + {time} +
); } 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`; } diff --git a/src/config/apps.ts b/src/config/apps.ts index 680ade4..d30588b 100644 --- a/src/config/apps.ts +++ b/src/config/apps.ts @@ -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 = [ diff --git a/src/types/index.ts b/src/types/index.ts index 78044ba..b586db3 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -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 {