implement shell command interface and add basic commands (echo, ls, pwd, cd, touch, cat, rm) with syscall integration
This commit is contained in:
parent
2817165386
commit
f39333cae6
@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Vite + React + TS</title>
|
<title>demo</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
152
package-lock.json
generated
152
package-lock.json
generated
@ -8,6 +8,7 @@
|
|||||||
"name": "vite-react-typescript-starter",
|
"name": "vite-react-typescript-starter",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^1.7.9",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"lucide-react": "^0.344.0",
|
"lucide-react": "^0.344.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
@ -27,7 +28,7 @@
|
|||||||
"eslint-plugin-react-refresh": "^0.4.11",
|
"eslint-plugin-react-refresh": "^0.4.11",
|
||||||
"globals": "^15.9.0",
|
"globals": "^15.9.0",
|
||||||
"postcss": "^8.4.35",
|
"postcss": "^8.4.35",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.17",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.3",
|
||||||
"typescript-eslint": "^8.3.0",
|
"typescript-eslint": "^8.3.0",
|
||||||
"vite": "^5.4.2"
|
"vite": "^5.4.2"
|
||||||
@ -1698,6 +1699,11 @@
|
|||||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||||
|
},
|
||||||
"node_modules/autoprefixer": {
|
"node_modules/autoprefixer": {
|
||||||
"version": "10.4.20",
|
"version": "10.4.20",
|
||||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
||||||
@ -1735,6 +1741,16 @@
|
|||||||
"postcss": "^8.1.0"
|
"postcss": "^8.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.7.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
|
||||||
|
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.6",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bail": {
|
"node_modules/bail": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
|
||||||
@ -1964,6 +1980,17 @@
|
|||||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"dependencies": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/comma-separated-tokens": {
|
"node_modules/comma-separated-tokens": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
|
||||||
@ -2059,6 +2086,14 @@
|
|||||||
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
|
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dequal": {
|
"node_modules/dequal": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||||
@ -2566,6 +2601,25 @@
|
|||||||
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
|
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||||
|
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/foreground-child": {
|
"node_modules/foreground-child": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
|
||||||
@ -2582,6 +2636,19 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/form-data": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fraction.js": {
|
"node_modules/fraction.js": {
|
||||||
"version": "4.3.7",
|
"version": "4.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
|
||||||
@ -3087,12 +3154,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lilconfig": {
|
"node_modules/lilconfig": {
|
||||||
"version": "2.1.0",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
||||||
"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
|
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antonk52"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lines-and-columns": {
|
"node_modules/lines-and-columns": {
|
||||||
@ -4003,6 +4073,25 @@
|
|||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
@ -4241,9 +4330,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
@ -4375,18 +4464,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-load-config/node_modules/lilconfig": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/antonk52"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/postcss-nested": {
|
"node_modules/postcss-nested": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
|
||||||
@ -4449,6 +4526,11 @@
|
|||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||||
|
},
|
||||||
"node_modules/punycode": {
|
"node_modules/punycode": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
@ -4981,33 +5063,33 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "3.4.13",
|
"version": "3.4.17",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
|
||||||
"integrity": "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==",
|
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alloc/quick-lru": "^5.2.0",
|
"@alloc/quick-lru": "^5.2.0",
|
||||||
"arg": "^5.0.2",
|
"arg": "^5.0.2",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.6.0",
|
||||||
"didyoumean": "^1.2.2",
|
"didyoumean": "^1.2.2",
|
||||||
"dlv": "^1.1.3",
|
"dlv": "^1.1.3",
|
||||||
"fast-glob": "^3.3.0",
|
"fast-glob": "^3.3.2",
|
||||||
"glob-parent": "^6.0.2",
|
"glob-parent": "^6.0.2",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
"jiti": "^1.21.0",
|
"jiti": "^1.21.6",
|
||||||
"lilconfig": "^2.1.0",
|
"lilconfig": "^3.1.3",
|
||||||
"micromatch": "^4.0.5",
|
"micromatch": "^4.0.8",
|
||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
"object-hash": "^3.0.0",
|
"object-hash": "^3.0.0",
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.1.1",
|
||||||
"postcss": "^8.4.23",
|
"postcss": "^8.4.47",
|
||||||
"postcss-import": "^15.1.0",
|
"postcss-import": "^15.1.0",
|
||||||
"postcss-js": "^4.0.1",
|
"postcss-js": "^4.0.1",
|
||||||
"postcss-load-config": "^4.0.1",
|
"postcss-load-config": "^4.0.2",
|
||||||
"postcss-nested": "^6.0.1",
|
"postcss-nested": "^6.2.0",
|
||||||
"postcss-selector-parser": "^6.0.11",
|
"postcss-selector-parser": "^6.1.2",
|
||||||
"resolve": "^1.22.2",
|
"resolve": "^1.22.8",
|
||||||
"sucrase": "^3.32.0"
|
"sucrase": "^3.35.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"tailwind": "lib/cli.js",
|
"tailwind": "lib/cli.js",
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^1.7.9",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"lucide-react": "^0.344.0",
|
"lucide-react": "^0.344.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
@ -29,7 +30,7 @@
|
|||||||
"eslint-plugin-react-refresh": "^0.4.11",
|
"eslint-plugin-react-refresh": "^0.4.11",
|
||||||
"globals": "^15.9.0",
|
"globals": "^15.9.0",
|
||||||
"postcss": "^8.4.35",
|
"postcss": "^8.4.35",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.17",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.3",
|
||||||
"typescript-eslint": "^8.3.0",
|
"typescript-eslint": "^8.3.0",
|
||||||
"vite": "^5.4.2"
|
"vite": "^5.4.2"
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Project 1: Personal Portfolio
|
## Project 1: Personal Portfolio
|
||||||
- **Tech Stack:** React, TypeScript, Tailwind CSS
|
- **Tech Stack:** React, TypeScript, Tailwind CSS
|
||||||
- **Description:** A retro TV-themed portfolio website showcasing my work and skills
|
- **Description:** A retro TV-themed portfolio website
|
||||||
- [View Source](https://github.com/username/portfolio)
|
- [View Source](https://github.com/username/portfolio)
|
||||||
|
|
||||||
## Project 2: Task Manager
|
## Project 2: Task Manager
|
||||||
|
16
src/App.tsx
16
src/App.tsx
@ -3,6 +3,7 @@ import { Terminal } from 'lucide-react';
|
|||||||
import TerminalButton from './components/TerminalButton';
|
import TerminalButton from './components/TerminalButton';
|
||||||
import ProfileContent from './components/ProfileContent';
|
import ProfileContent from './components/ProfileContent';
|
||||||
import ProjectsContent from './components/ProjectsContent';
|
import ProjectsContent from './components/ProjectsContent';
|
||||||
|
import TerminalShell from './components/TerminalShell';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [activeSection, setActiveSection] = React.useState('about');
|
const [activeSection, setActiveSection] = React.useState('about');
|
||||||
@ -10,7 +11,7 @@ function App() {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-900 flex items-center justify-center overflow-hidden">
|
<div className="min-h-screen bg-gray-900 flex items-center justify-center overflow-hidden">
|
||||||
{/* TV Container */}
|
{/* TV Container */}
|
||||||
<div className="w-[95%] max-w-[1400px] relative h-[80vh]">
|
<div className="w-[95%] max-w-[1400px] relative h-[90vh]">
|
||||||
{/* TV Frame */}
|
{/* TV Frame */}
|
||||||
<div className="absolute inset-0 tv-frame">
|
<div className="absolute inset-0 tv-frame">
|
||||||
{/* Screen Container with Convex Effect */}
|
{/* Screen Container with Convex Effect */}
|
||||||
@ -47,18 +48,18 @@ function App() {
|
|||||||
>
|
>
|
||||||
SKILLS
|
SKILLS
|
||||||
</TerminalButton>
|
</TerminalButton>
|
||||||
<TerminalButton
|
|
||||||
onClick={() => setActiveSection('contact')}
|
|
||||||
isSelected={activeSection === 'contact'}
|
|
||||||
>
|
|
||||||
CONTACT
|
|
||||||
</TerminalButton>
|
|
||||||
<TerminalButton
|
<TerminalButton
|
||||||
onClick={() => setActiveSection('resume')}
|
onClick={() => setActiveSection('resume')}
|
||||||
isSelected={activeSection === 'resume'}
|
isSelected={activeSection === 'resume'}
|
||||||
>
|
>
|
||||||
RESUME
|
RESUME
|
||||||
</TerminalButton>
|
</TerminalButton>
|
||||||
|
<TerminalButton
|
||||||
|
onClick={() => setActiveSection('shell')}
|
||||||
|
isSelected={activeSection === 'shell'}
|
||||||
|
>
|
||||||
|
SHELL
|
||||||
|
</TerminalButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Vertical Separator */}
|
{/* Vertical Separator */}
|
||||||
@ -70,6 +71,7 @@ function App() {
|
|||||||
{activeSection === 'projects' && (
|
{activeSection === 'projects' && (
|
||||||
<ProjectsContent markdownPath="/content/projects.md" />
|
<ProjectsContent markdownPath="/content/projects.md" />
|
||||||
)}
|
)}
|
||||||
|
{activeSection === 'shell' && <TerminalShell />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
59
src/components/TerminalShell.tsx
Normal file
59
src/components/TerminalShell.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
|
import { Terminal } from 'lucide-react';
|
||||||
|
import { System } from '../shell/system';
|
||||||
|
|
||||||
|
const TerminalShell: React.FC = () => {
|
||||||
|
const [history, setHistory] = useState<string[]>(['Welcome to the terminal! Type "help" for commands.']);
|
||||||
|
const [currentCommand, setCurrentCommand] = useState('');
|
||||||
|
const bottomRef = useRef<HTMLDivElement>(null);
|
||||||
|
const system = System.getInstance(); // Use singleton instead of creating new instance
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||||
|
}, [history]);
|
||||||
|
|
||||||
|
const handleCommand = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
const command = currentCommand.trim();
|
||||||
|
const [cmdName, ...args] = command.split(' ');
|
||||||
|
|
||||||
|
if (system.knowsCommand(cmdName)) {
|
||||||
|
const output = system.executeCommand(cmdName, args);
|
||||||
|
|
||||||
|
const newLines = output.split('\\n').filter(l => l);
|
||||||
|
|
||||||
|
setHistory(prev => [...prev, `$ ${command}`, ...newLines]);
|
||||||
|
} else {
|
||||||
|
setHistory(prev => [...prev, `$ ${command}`, `Command not found: ${cmdName}`]);
|
||||||
|
}
|
||||||
|
setCurrentCommand('');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full flex flex-col">
|
||||||
|
<div className="flex-1 overflow-y-auto mb-4">
|
||||||
|
{history.map((line, index) => (
|
||||||
|
<div key={index} className="mb-2">
|
||||||
|
{line}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div ref={bottomRef} />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Terminal className="mr-2" size={18} />
|
||||||
|
<span className="mr-2">$</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={currentCommand}
|
||||||
|
onChange={(e) => setCurrentCommand(e.target.value)}
|
||||||
|
onKeyDown={handleCommand}
|
||||||
|
className="flex-1 bg-transparent border-none outline-none text-[#00FF00]"
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TerminalShell;
|
@ -173,7 +173,7 @@
|
|||||||
transparent 100%
|
transparent 100%
|
||||||
);
|
);
|
||||||
animation: glare 3s ease-in-out infinite;
|
animation: glare 3s ease-in-out infinite;
|
||||||
opacity: 0.5;
|
opacity: 0.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.screen-glare {
|
.screen-glare {
|
||||||
@ -187,6 +187,7 @@
|
|||||||
transparent 100%
|
transparent 100%
|
||||||
);
|
);
|
||||||
animation: horizontal-glare 4s linear infinite;
|
animation: horizontal-glare 4s linear infinite;
|
||||||
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
109
src/shell/FileSystem.ts
Normal file
109
src/shell/FileSystem.ts
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
interface INode {
|
||||||
|
name: string;
|
||||||
|
isDirectory: boolean;
|
||||||
|
content?: string;
|
||||||
|
children?: Map<string, INode>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FileSystem {
|
||||||
|
private root: INode;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
console.log('[FileSystem] Initializing new filesystem');
|
||||||
|
this.root = {
|
||||||
|
name: '/',
|
||||||
|
isDirectory: true,
|
||||||
|
children: new Map()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
createDirectory(path: string): void {
|
||||||
|
console.log(`[FileSystem] Creating directory: ${path}`);
|
||||||
|
const parts = path.split('/').filter(p => p);
|
||||||
|
let current = this.root;
|
||||||
|
|
||||||
|
for (const part of parts) {
|
||||||
|
if (!current.children!.has(part)) {
|
||||||
|
console.log(`[FileSystem] Creating new directory node: ${part}`);
|
||||||
|
current.children!.set(part, {
|
||||||
|
name: part,
|
||||||
|
isDirectory: true,
|
||||||
|
children: new Map()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
current = current.children!.get(part)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFile(path: string, content: string): void {
|
||||||
|
console.log(`[FileSystem] Writing file: ${path}`);
|
||||||
|
const parts = path.split('/').filter(p => p);
|
||||||
|
const fileName = parts.pop()!;
|
||||||
|
let current = this.root;
|
||||||
|
|
||||||
|
for (const part of parts) {
|
||||||
|
if (!current.children!.has(part)) {
|
||||||
|
console.log(`[FileSystem] Creating parent directory: ${part}`);
|
||||||
|
this.createDirectory(part);
|
||||||
|
}
|
||||||
|
current = current.children!.get(part)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.children!.set(fileName, {
|
||||||
|
name: fileName,
|
||||||
|
isDirectory: false,
|
||||||
|
content
|
||||||
|
});
|
||||||
|
console.log(`[FileSystem] File written: ${fileName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
listDirectory(path: string): string[] {
|
||||||
|
console.log(`[FileSystem] Listing directory: ${path}`);
|
||||||
|
const parts = path.split('/').filter(p => p);
|
||||||
|
let current = this.root;
|
||||||
|
|
||||||
|
for (const part of parts) {
|
||||||
|
if (!current.children!.has(part)) return [];
|
||||||
|
current = current.children!.get(part)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = Array.from(current.children!.keys());
|
||||||
|
console.log(`[FileSystem] Found ${result.length} entries`);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
readFile(path: string): string | null {
|
||||||
|
console.log(`[FileSystem] Reading file: ${path}`);
|
||||||
|
const parts = path.split('/').filter(p => p);
|
||||||
|
const fileName = parts.pop()!;
|
||||||
|
let current = this.root;
|
||||||
|
|
||||||
|
for (const part of parts) {
|
||||||
|
if (!current.children!.has(part)) return null;
|
||||||
|
current = current.children!.get(part)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = current.children!.get(fileName);
|
||||||
|
if (!file || file.isDirectory) {
|
||||||
|
console.log(`[FileSystem] File not found or is directory: ${path}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
console.log(`[FileSystem] File read successfully: ${path}`);
|
||||||
|
return file.content!;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteFile(path: string): void {
|
||||||
|
console.log(`[FileSystem] Deleting file: ${path}`);
|
||||||
|
const parts = path.split('/').filter(p => p);
|
||||||
|
const fileName = parts.pop()!;
|
||||||
|
let current = this.root;
|
||||||
|
|
||||||
|
for (const part of parts) {
|
||||||
|
if (!current.children!.has(part)) return;
|
||||||
|
current = current.children!.get(part)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.children!.delete(fileName);
|
||||||
|
console.log(`[FileSystem] File deleted: ${path}`);
|
||||||
|
}
|
||||||
|
}
|
43
src/shell/ShellSyscall.ts
Normal file
43
src/shell/ShellSyscall.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { FileSystem } from "./FileSystem";
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export class ShellSyscall {
|
||||||
|
constructor(
|
||||||
|
private fs: FileSystem,
|
||||||
|
private cwd: string
|
||||||
|
) {}
|
||||||
|
|
||||||
|
getCurrentDirectory(): string {
|
||||||
|
return this.cwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
listCurrentDirectory(): string[] {
|
||||||
|
return this.fs.listDirectory(this.cwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
changeDirectory(path: string): boolean {
|
||||||
|
// For now, just accept any path
|
||||||
|
this.cwd = path;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
createFile(filename: string): void {
|
||||||
|
this.fs.writeFile(`${this.cwd}/${filename}`, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
readFile(filename: string): string {
|
||||||
|
return this.fs.readFile(`${this.cwd}/${filename}`) || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFile(filename: string, content: string) {
|
||||||
|
this.fs.writeFile(`${this.cwd}/${filename}`, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchUrl(url: string) {
|
||||||
|
return await axios.get(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteFile(path: string): void {
|
||||||
|
this.fs.deleteFile(`${this.cwd}/${path}`);
|
||||||
|
}
|
||||||
|
}
|
4
src/shell/commands/IShellCommand.ts
Normal file
4
src/shell/commands/IShellCommand.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export interface IShellCommand {
|
||||||
|
getName(): string;
|
||||||
|
execute(input: string | string[]): string;
|
||||||
|
}
|
15
src/shell/commands/cat.ts
Normal file
15
src/shell/commands/cat.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { IShellCommand } from "./IShellCommand";
|
||||||
|
import { ShellSyscall } from "../ShellSyscall";
|
||||||
|
|
||||||
|
export class cat implements IShellCommand {
|
||||||
|
constructor(private syscall: ShellSyscall) {}
|
||||||
|
|
||||||
|
getName(): string {
|
||||||
|
return "cat";
|
||||||
|
}
|
||||||
|
|
||||||
|
execute(args: string[]): string {
|
||||||
|
if (args.length === 0) return "cat: missing file operand";
|
||||||
|
return this.syscall.readFile(args[0]);
|
||||||
|
}
|
||||||
|
}
|
16
src/shell/commands/cd.ts
Normal file
16
src/shell/commands/cd.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { IShellCommand } from "./IShellCommand";
|
||||||
|
import { ShellSyscall } from "../ShellSyscall";
|
||||||
|
|
||||||
|
export class cd implements IShellCommand {
|
||||||
|
constructor(private syscall: ShellSyscall) {}
|
||||||
|
|
||||||
|
getName(): string {
|
||||||
|
return "cd";
|
||||||
|
}
|
||||||
|
|
||||||
|
execute(args: string[]): string {
|
||||||
|
const path = args[0] || '/';
|
||||||
|
const success = this.syscall.changeDirectory(path);
|
||||||
|
return success ? '' : 'Invalid directory';
|
||||||
|
}
|
||||||
|
}
|
14
src/shell/commands/echo.ts
Normal file
14
src/shell/commands/echo.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { IShellCommand } from "./IShellCommand";
|
||||||
|
|
||||||
|
export class echo implements IShellCommand {
|
||||||
|
getName(): string {
|
||||||
|
return 'echo';
|
||||||
|
}
|
||||||
|
|
||||||
|
execute(input: string | string[]): string {
|
||||||
|
if (Array.isArray(input)) {
|
||||||
|
return input.join(' ').trim();
|
||||||
|
}
|
||||||
|
return input.trim();
|
||||||
|
}
|
||||||
|
}
|
15
src/shell/commands/ls.ts
Normal file
15
src/shell/commands/ls.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { IShellCommand } from "./IShellCommand";
|
||||||
|
import { ShellSyscall } from "../ShellSyscall";
|
||||||
|
|
||||||
|
export class ls implements IShellCommand {
|
||||||
|
constructor(private syscall: ShellSyscall) {}
|
||||||
|
|
||||||
|
getName(): string {
|
||||||
|
return 'ls';
|
||||||
|
}
|
||||||
|
|
||||||
|
execute(dir: string[]): string {
|
||||||
|
const files = this.syscall.listCurrentDirectory();
|
||||||
|
return files.join('\n');
|
||||||
|
}
|
||||||
|
}
|
14
src/shell/commands/pwd.ts
Normal file
14
src/shell/commands/pwd.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { IShellCommand } from "./IShellCommand";
|
||||||
|
import { ShellSyscall } from "../ShellSyscall";
|
||||||
|
|
||||||
|
export class pwd implements IShellCommand {
|
||||||
|
constructor(private syscall: ShellSyscall) {}
|
||||||
|
|
||||||
|
getName(): string {
|
||||||
|
return "pwd";
|
||||||
|
}
|
||||||
|
|
||||||
|
execute(_: string[]): string {
|
||||||
|
return this.syscall.getCurrentDirectory();
|
||||||
|
}
|
||||||
|
}
|
24
src/shell/commands/rm.ts
Normal file
24
src/shell/commands/rm.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { IShellCommand } from "./IShellCommand";
|
||||||
|
import { ShellSyscall } from "../ShellSyscall";
|
||||||
|
|
||||||
|
export class rm implements IShellCommand {
|
||||||
|
private syscall: ShellSyscall;
|
||||||
|
|
||||||
|
constructor(syscall: ShellSyscall) {
|
||||||
|
this.syscall = syscall;
|
||||||
|
}
|
||||||
|
|
||||||
|
getName(): string {
|
||||||
|
return "rm";
|
||||||
|
}
|
||||||
|
|
||||||
|
execute(args: string[]): string {
|
||||||
|
if (args.length < 1) {
|
||||||
|
return "Usage: rm <filename>";
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename = args[0];
|
||||||
|
this.syscall.deleteFile(filename);
|
||||||
|
return `Removed ${filename}`;
|
||||||
|
}
|
||||||
|
}
|
16
src/shell/commands/touch.ts
Normal file
16
src/shell/commands/touch.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { IShellCommand } from "./IShellCommand";
|
||||||
|
import { ShellSyscall } from "../ShellSyscall";
|
||||||
|
|
||||||
|
export class touch implements IShellCommand {
|
||||||
|
constructor(private syscall: ShellSyscall) {}
|
||||||
|
|
||||||
|
getName(): string {
|
||||||
|
return "touch";
|
||||||
|
}
|
||||||
|
|
||||||
|
execute(args: string[]): string {
|
||||||
|
if (args.length === 0) return "touch: missing file operand";
|
||||||
|
this.syscall.createFile(args[0]);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
58
src/shell/system.ts
Normal file
58
src/shell/system.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { echo } from "./commands/echo";
|
||||||
|
import { ls } from "./commands/ls";
|
||||||
|
import { pwd } from "./commands/pwd";
|
||||||
|
import { cd } from "./commands/cd";
|
||||||
|
import { touch } from "./commands/touch";
|
||||||
|
import { cat } from "./commands/cat";
|
||||||
|
import { rm } from "./commands/rm";
|
||||||
|
import { IShellCommand } from "./commands/IShellCommand";
|
||||||
|
import { FileSystem } from "./FileSystem";
|
||||||
|
import { ShellSyscall } from "./ShellSyscall";
|
||||||
|
|
||||||
|
// System class to manage commands
|
||||||
|
class System {
|
||||||
|
private static instance: System | null = null;
|
||||||
|
private commands: Array<IShellCommand>;
|
||||||
|
private fs: FileSystem;
|
||||||
|
private cwd: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.commands = new Array<IShellCommand>();
|
||||||
|
this.fs = new FileSystem();
|
||||||
|
this.cwd = '/';
|
||||||
|
this.initializeCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
private initializeCommands(): void {
|
||||||
|
const syscall = new ShellSyscall(this.fs, this.cwd);
|
||||||
|
this.commands.push(new echo());
|
||||||
|
this.commands.push(new ls(syscall));
|
||||||
|
this.commands.push(new pwd(syscall));
|
||||||
|
this.commands.push(new cd(syscall));
|
||||||
|
this.commands.push(new touch(syscall));
|
||||||
|
this.commands.push(new cat(syscall));
|
||||||
|
this.commands.push(new rm(syscall));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getInstance(): System {
|
||||||
|
if (!System.instance) {
|
||||||
|
System.instance = new System();
|
||||||
|
}
|
||||||
|
return System.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
knowsCommand(commandName: string): boolean {
|
||||||
|
return this.commands.some(c => c.getName() === commandName);
|
||||||
|
}
|
||||||
|
|
||||||
|
executeCommand(commandName: string, args: string[]): string {
|
||||||
|
const command = this.commands.find(c => c.getName() === commandName);
|
||||||
|
if (command) {
|
||||||
|
return command.execute(args);
|
||||||
|
}
|
||||||
|
return `Command not found: ${commandName}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export the System class and getInstance for use in other files
|
||||||
|
export { System };
|
Loading…
x
Reference in New Issue
Block a user