diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4a7ea30 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..b38db2f --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +node_modules/ +build/ diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..d691799 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,26 @@ +module.exports = { + env: { + browser: true, + es2021: true + }, + extends: ["plugin:react/recommended", "plugin:prettier/recommended"], + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaFeatures: { + jsx: true + }, + ecmaVersion: 12, + sourceType: "module" + }, + settings: { + react: { + version: "detect" + } + }, + plugins: ["react", "@typescript-eslint"], + rules: { + "react-hooks/exhaustive-deps": "off", + "react/react-in-jsx-scope": "off", + "react/no-unescaped-entities": "off" + } +}; diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml new file mode 100644 index 0000000..6227023 --- /dev/null +++ b/.github/workflows/main.yaml @@ -0,0 +1,44 @@ +name: build and deploy to GitHub Pages + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Node + uses: actions/setup-node@v2.1.2 + with: + node-version: '12' + + - name: Get yarn cache + id: yarn-cache + run: echo "::set-output name=dir::$(yarn cache dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Build + run: | + yarn install --frozen-lockfile + yarn build + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: dist + cname: portfolio.zxh.io diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..90c657b --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.log +.DS_Store +.cache/ +node_modules +dist +.npmrc +.yarnrc +.idea diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..d2ae35e --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn lint-staged diff --git a/.prettierrc.yaml b/.prettierrc.yaml new file mode 100644 index 0000000..02db67b --- /dev/null +++ b/.prettierrc.yaml @@ -0,0 +1 @@ +trailingComma: none diff --git a/README.md b/README.md index 33fda9e..6e44f80 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ +# The Tech Devs + +Official portfolio website simulating macOS's GUI: http://thetechdevs.com + +Powered by [React](https://reactjs.org/) + [React Redux](https://react-redux.js.org/) + [Tailwind CSS](https://tailwindcss.com/) + [TypeScript](https://www.typescriptlang.org/) + [Vite](https://vitejs.dev/). + + + +
@@ -27,6 +36,13 @@
- Firebase
- Hosting (cPanel, Github pages, Netlify, Firebase, Vercel, Heroku, etc.)
+## Credits
+
+- [macOS Big Sur](https://www.apple.com/in/macos/big-sur/)
+- [macOS Catalina](https://www.apple.com/bw/macos/catalina/)
+- [macOS Icon Gallery](https://www.macosicongallery.com/)
+- [sindresorhus/file-icon-cli](https://github.com/sindresorhus/file-icon-cli)
+
### Members
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..88c026b
--- /dev/null
+++ b/index.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+ {app.title}
+
+
+
{app.appList}
+ {portfolio.appList}
+
+
{children}
+ );
+ }
+ };
+};
+
+const Sidebar = ({ cur, setMidBar }: SidebarProps) => {
+ return (
+
+ {bear.map((item, index) => (
+
+
+ {items.map((item: BearMdData, index: number) => (
+
+
+ )}
+
+
setGoURL(site.link)
+ : () => window.open(site.link)
+ }
+ />
+ ) : (
+
+
+ );
+ this.generateResultRow(this.curInputTimes, help);
+ };
+
+ autoComplete = (text: string): string => {
+ if (text === "") return text;
+
+ const input = text.split(" ");
+ const cmd = input[0];
+ const args = input[1];
+
+ let result = text;
+
+ if (args === undefined) {
+ const guess = Object.keys(this.commands).find((item) => {
+ return item.substring(0, cmd.length) === cmd;
+ });
+ if (guess !== undefined) result = guess;
+ } else if (cmd === "cd" || cmd === "cat") {
+ const type = cmd === "cd" ? "folder" : "file";
+ const guess = this.curChildren.find((item: TerminalData) => {
+ return (
+ item.type === type && item.title.substring(0, args.length) === args
+ );
+ });
+ if (guess !== undefined) result = cmd + " " + guess.title;
+ }
+ return result;
+ };
+
+ keyPress = (e: React.KeyboardEvent): void => {
+ const keyCode = e.key;
+ const $input = document.querySelector(
+ `#terminal-input-${this.curInputTimes}`
+ ) as HTMLInputElement;
+ const input_text = $input.value.trim();
+ const input = input_text.split(" ");
+
+ if (keyCode === "Enter") {
+ // ----------- run command -----------
+ this.history.push(input_text);
+
+ const cmd = input[0];
+ const args = input[1];
+
+ // we can't edit the past input
+ $input.setAttribute("readonly", "true");
+
+ if (input_text.substr(0, 6) === "rm -rf") this.setState({ rmrf: true });
+ else if (cmd && Object.keys(this.commands).includes(cmd)) {
+ this.commands[cmd](args);
+ } else {
+ this.generateResultRow(
+ this.curInputTimes,
+ {`zsh: command not found: ${cmd}`}
+ );
+ }
+
+ // point to the last history command
+ this.curHistory = this.history.length;
+
+ // generate new input row
+ this.curInputTimes += 1;
+ this.generateInputRow(this.curInputTimes);
+ } else if (keyCode === "ArrowUp") {
+ // ----------- previous history command -----------
+ if (this.history.length > 0) {
+ if (this.curHistory > 0) this.curHistory--;
+ const historyCommand = this.history[this.curHistory];
+ $input.value = historyCommand;
+ }
+ } else if (keyCode === "ArrowDown") {
+ // ----------- next history command -----------
+ if (this.history.length > 0) {
+ if (this.curHistory < this.history.length) this.curHistory++;
+ if (this.curHistory === this.history.length) $input.value = "";
+ else {
+ const historyCommand = this.history[this.curHistory];
+ $input.value = historyCommand;
+ }
+ }
+ } else if (keyCode === "Tab") {
+ // ----------- auto complete -----------
+ $input.value = this.autoComplete(input_text);
+ // prevent tab outside the terminal
+ e.preventDefault();
+ }
+ };
+
+ focusOnInput = (id: number): void => {
+ const input = document.querySelector(
+ `#terminal-input-${id}`
+ ) as HTMLInputElement;
+ input.focus();
+ };
+
+ generateInputRow = (id: number): void => {
+ const newRow = (
+ mouseX.set(e.nativeEvent.x)}
+ onMouseLeave={() => mouseX.set(null)}
+ style={{
+ height: `${(dockSize as number) + 15}px`
+ }}
+ >
+ {apps.map((app) => (
+
+
+
{props.children}
;
+};
+
+export { MenuItem, MenuItemGroup };
diff --git a/src/configs/apps.tsx b/src/configs/apps.tsx
new file mode 100644
index 0000000..00ef482
--- /dev/null
+++ b/src/configs/apps.tsx
@@ -0,0 +1,78 @@
+import FaceTime from "../components/apps/FaceTime";
+import Terminal from "../components/apps/Terminal";
+import Safari from "../components/apps/Safari";
+import Bear from "../components/apps/Bear";
+import VSCode from "../components/apps/VSCode";
+
+import type { AppsData } from "../types";
+
+const apps: AppsData[] = [
+ {
+ id: "launchpad",
+ title: "Launchpad",
+ desktop: false,
+ img: "img/icons/launchpad.png"
+ },
+ {
+ id: "bear",
+ title: "Bear",
+ desktop: true,
+ show: true,
+ width: 860,
+ height: 500,
+ img: "img/icons/bear.png",
+ content:
+
+ )
+ }
+ ]
+ },
+ {
+ id: "about-dream",
+ title: "my-dream.cpp",
+ type: "file",
+ content: (
+
+ {/*