new portfolio yay!
This commit is contained in:
parent
88bd40c942
commit
786c414eb3
3
.eslintrc.json
Normal file
3
.eslintrc.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "next/core-web-vitals"
|
||||||
|
}
|
45
.gitignore
vendored
45
.gitignore
vendored
@ -1,21 +1,36 @@
|
|||||||
node_modules
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
# Output
|
# dependencies
|
||||||
.output
|
/node_modules
|
||||||
.vercel
|
/.pnp
|
||||||
/.svelte-kit
|
.pnp.js
|
||||||
|
.yarn/install-state.gz
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
/build
|
/build
|
||||||
|
|
||||||
# OS
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
*.pem
|
||||||
|
|
||||||
# Env
|
# debug
|
||||||
.env
|
npm-debug.log*
|
||||||
.env.*
|
yarn-debug.log*
|
||||||
!.env.example
|
yarn-error.log*
|
||||||
!.env.test
|
|
||||||
|
|
||||||
# Vite
|
# local env files
|
||||||
vite.config.js.timestamp-*
|
.env*.local
|
||||||
vite.config.ts.timestamp-*
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
# Package Managers
|
|
||||||
package-lock.json
|
|
||||||
pnpm-lock.yaml
|
|
||||||
yarn.lock
|
|
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"useTabs": true,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"printWidth": 100,
|
|
||||||
"plugins": ["prettier-plugin-svelte"],
|
|
||||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
|
||||||
}
|
|
52
README.md
52
README.md
@ -1,38 +1,36 @@
|
|||||||
# create-svelte
|
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||||
|
|
||||||
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
|
## Getting Started
|
||||||
|
|
||||||
## Creating a project
|
First, run the development server:
|
||||||
|
|
||||||
If you're seeing this, you've probably already done this step. Congrats!
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# create a new project in the current directory
|
|
||||||
npm create svelte@latest
|
|
||||||
|
|
||||||
# create a new project in my-app
|
|
||||||
npm create svelte@latest my-app
|
|
||||||
```
|
|
||||||
|
|
||||||
## Developing
|
|
||||||
|
|
||||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run dev
|
npm run dev
|
||||||
|
# or
|
||||||
# or start the server and open the app in a new browser tab
|
yarn dev
|
||||||
npm run dev -- --open
|
# or
|
||||||
|
pnpm dev
|
||||||
|
# or
|
||||||
|
bun dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## Building
|
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||||
|
|
||||||
To create a production version of your app:
|
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||||
|
|
||||||
```bash
|
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
You can preview the production build with `npm run preview`.
|
## Learn More
|
||||||
|
|
||||||
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
To learn more about Next.js, take a look at the following resources:
|
||||||
|
|
||||||
|
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||||
|
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||||
|
|
||||||
|
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||||
|
|
||||||
|
## Deploy on Vercel
|
||||||
|
|
||||||
|
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||||
|
|
||||||
|
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
import js from '@eslint/js';
|
|
||||||
import ts from 'typescript-eslint';
|
|
||||||
import svelte from 'eslint-plugin-svelte';
|
|
||||||
import prettier from 'eslint-config-prettier';
|
|
||||||
import globals from 'globals';
|
|
||||||
|
|
||||||
/** @type {import('eslint').Linter.FlatConfig[]} */
|
|
||||||
export default [
|
|
||||||
js.configs.recommended,
|
|
||||||
...ts.configs.recommended,
|
|
||||||
...svelte.configs['flat/recommended'],
|
|
||||||
prettier,
|
|
||||||
...svelte.configs['flat/prettier'],
|
|
||||||
{
|
|
||||||
languageOptions: {
|
|
||||||
globals: {
|
|
||||||
...globals.browser,
|
|
||||||
...globals.node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['**/*.svelte'],
|
|
||||||
languageOptions: {
|
|
||||||
parserOptions: {
|
|
||||||
parser: ts.parser
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ignores: ['build/', '.svelte-kit/', 'dist/']
|
|
||||||
}
|
|
||||||
];
|
|
4
next.config.mjs
Normal file
4
next.config.mjs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {};
|
||||||
|
|
||||||
|
export default nextConfig;
|
5574
package-lock.json
generated
Normal file
5574
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
57
package.json
57
package.json
@ -1,40 +1,31 @@
|
|||||||
{
|
{
|
||||||
"name": "portfolio",
|
"name": "portfolio",
|
||||||
"version": "0.0.1",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "next dev",
|
||||||
"build": "vite build",
|
"build": "next build",
|
||||||
"preview": "vite preview",
|
"start": "next start",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"lint": "next lint"
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
},
|
||||||
"lint": "prettier --check . && eslint .",
|
"dependencies": {
|
||||||
"format": "prettier --write .",
|
"@react-three/drei": "^9.108.4",
|
||||||
"model-pipeline:run": "node scripts/model-pipeline.js"
|
"@react-three/fiber": "^8.16.8",
|
||||||
|
"@types/three": "^0.166.0",
|
||||||
|
"next": "14.2.5",
|
||||||
|
"react": "^18",
|
||||||
|
"react-dom": "^18",
|
||||||
|
"three": "^0.166.1",
|
||||||
|
"three-stdlib": "^2.30.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "^3.0.0",
|
"@types/node": "^20",
|
||||||
"@sveltejs/kit": "^2.0.0",
|
"@types/react": "^18",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
"@types/react-dom": "^18",
|
||||||
"@types/eslint": "^8.56.7",
|
"eslint": "^8",
|
||||||
"eslint": "^9.0.0",
|
"eslint-config-next": "14.2.5",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"postcss": "^8",
|
||||||
"eslint-plugin-svelte": "^2.36.0",
|
"tailwindcss": "^3.4.1",
|
||||||
"globals": "^15.0.0",
|
"typescript": "^5"
|
||||||
"prettier": "^3.1.1",
|
|
||||||
"prettier-plugin-svelte": "^3.1.2",
|
|
||||||
"svelte": "^4.2.7",
|
|
||||||
"svelte-check": "^3.6.0",
|
|
||||||
"tslib": "^2.4.1",
|
|
||||||
"typescript": "^5.0.0",
|
|
||||||
"typescript-eslint": "^8.0.0-alpha.20",
|
|
||||||
"vite": "^5.0.3",
|
|
||||||
"@types/three": "^0.159.0"
|
|
||||||
},
|
|
||||||
"type": "module",
|
|
||||||
"dependencies": {
|
|
||||||
"three": "^0.159.0",
|
|
||||||
"@threlte/core": "^7.3.1",
|
|
||||||
"@threlte/extras": "^8.11.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
8
postcss.config.mjs
Normal file
8
postcss.config.mjs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/** @type {import('postcss-load-config').Config} */
|
||||||
|
const config = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
BIN
public/bootup.mp3
Normal file
BIN
public/bootup.mp3
Normal file
Binary file not shown.
BIN
public/enter.mp3
Normal file
BIN
public/enter.mp3
Normal file
Binary file not shown.
BIN
public/key.mp3
Normal file
BIN
public/key.mp3
Normal file
Binary file not shown.
BIN
public/shrimp_smol.glb
Normal file
BIN
public/shrimp_smol.glb
Normal file
Binary file not shown.
BIN
public/welcome.mp3
Normal file
BIN
public/welcome.mp3
Normal file
Binary file not shown.
@ -1,149 +0,0 @@
|
|||||||
import { execSync } from 'node:child_process'
|
|
||||||
import { readdirSync, copyFileSync, unlinkSync, mkdirSync, existsSync } from 'node:fs'
|
|
||||||
import { join, resolve } from 'node:path'
|
|
||||||
import { exit } from 'node:process'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This script is used to transform gltf and glb files into Threlte components.
|
|
||||||
* It uses the `@threlte/gltf` package to do so.
|
|
||||||
* It works in two steps:
|
|
||||||
* 1. Transform the gltf/glb files located in the sourceDir directory
|
|
||||||
* 2. Move the Threlte components to the targetDir directory
|
|
||||||
*/
|
|
||||||
const configuration = {
|
|
||||||
sourceDir: resolve(join('static', 'models')),
|
|
||||||
targetDir: resolve(join('src', 'lib', 'components', 'models')),
|
|
||||||
overwrite: false,
|
|
||||||
root: '/models/',
|
|
||||||
types: true,
|
|
||||||
keepnames: false,
|
|
||||||
meta: false,
|
|
||||||
shadows: false,
|
|
||||||
printwidth: 120,
|
|
||||||
precision: 2,
|
|
||||||
draco: null,
|
|
||||||
preload: false,
|
|
||||||
suspense: false,
|
|
||||||
isolated: false,
|
|
||||||
transform: {
|
|
||||||
enabled: false,
|
|
||||||
resolution: 1024,
|
|
||||||
simplify: {
|
|
||||||
enabled: false,
|
|
||||||
weld: 0.0001,
|
|
||||||
ratio: 0.75,
|
|
||||||
error: 0.001
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the target directory doesn't exist, create it
|
|
||||||
mkdirSync(configuration.targetDir, { recursive: true })
|
|
||||||
|
|
||||||
// throw error if source directory doesn't exist
|
|
||||||
if (!existsSync(configuration.sourceDir)) {
|
|
||||||
throw new Error(`Source directory ${configuration.sourceDir} doesn't exist.`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// read the directory, filter for .glb and .gltf files and files *not* ending
|
|
||||||
// with -transformed.gltf or -transformed.glb as these should not be transformed
|
|
||||||
// again.
|
|
||||||
const gltfFiles = readdirSync(configuration.sourceDir).filter((file) => {
|
|
||||||
return (
|
|
||||||
(file.endsWith('.glb') || file.endsWith('.gltf')) &&
|
|
||||||
!file.endsWith('-transformed.gltf') &&
|
|
||||||
!file.endsWith('-transformed.glb')
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (gltfFiles.length === 0) {
|
|
||||||
console.log('No gltf or glb files found.')
|
|
||||||
exit()
|
|
||||||
}
|
|
||||||
|
|
||||||
const filteredGltfFiles = gltfFiles.filter((file) => {
|
|
||||||
if (!configuration.overwrite) {
|
|
||||||
const componentFilename = file.split('.').slice(0, -1).join('.') + '.svelte'
|
|
||||||
const componentPath = join(configuration.targetDir, componentFilename)
|
|
||||||
if (existsSync(componentPath)) {
|
|
||||||
console.error(`File ${componentPath} already exists, skipping.`)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
if (filteredGltfFiles.length === 0) {
|
|
||||||
console.log('No gltf or glb files to process.')
|
|
||||||
exit()
|
|
||||||
}
|
|
||||||
|
|
||||||
filteredGltfFiles.forEach((file) => {
|
|
||||||
// run the gltf transform command on every file
|
|
||||||
const path = join(configuration.sourceDir, file)
|
|
||||||
|
|
||||||
// parse the configuration
|
|
||||||
const args = []
|
|
||||||
if (configuration.root) args.push(`--root ${configuration.root}`)
|
|
||||||
if (configuration.types) args.push('--types')
|
|
||||||
if (configuration.keepnames) args.push('--keepnames')
|
|
||||||
if (configuration.meta) args.push('--meta')
|
|
||||||
if (configuration.shadows) args.push('--shadows')
|
|
||||||
args.push(`--printwidth ${configuration.printwidth}`)
|
|
||||||
args.push(`--precision ${configuration.precision}`)
|
|
||||||
if (configuration.draco) args.push(`--draco ${configuration.draco}`)
|
|
||||||
if (configuration.preload) args.push('--preload')
|
|
||||||
if (configuration.suspense) args.push('--suspense')
|
|
||||||
if (configuration.isolated) args.push('--isolated')
|
|
||||||
if (configuration.transform.enabled) {
|
|
||||||
args.push(`--transform`)
|
|
||||||
args.push(`--resolution ${configuration.transform.resolution}`)
|
|
||||||
if (configuration.transform.simplify.enabled) {
|
|
||||||
args.push(`--simplify`)
|
|
||||||
args.push(`--weld ${configuration.transform.simplify.weld}`)
|
|
||||||
args.push(`--ratio ${configuration.transform.simplify.ratio}`)
|
|
||||||
args.push(`--error ${configuration.transform.simplify.error}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const formattedArgs = args.join(' ')
|
|
||||||
|
|
||||||
// run the command
|
|
||||||
const cmd = `npx @threlte/gltf@latest ${path} ${formattedArgs}`
|
|
||||||
try {
|
|
||||||
execSync(cmd, {
|
|
||||||
cwd: configuration.sourceDir
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error transforming model: ${error}`)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// read dir again, but search for .svelte files only.
|
|
||||||
const svelteFiles = readdirSync(configuration.sourceDir).filter((file) => file.endsWith('.svelte'))
|
|
||||||
|
|
||||||
svelteFiles.forEach((file) => {
|
|
||||||
// now move every file to /src/components/models
|
|
||||||
const path = join(configuration.sourceDir, file)
|
|
||||||
const newPath = join(configuration.targetDir, file)
|
|
||||||
copyFile: try {
|
|
||||||
// Sanity check, we checked earlier if the file exists. Still, the CLI takes
|
|
||||||
// a while, so who knows what happens in the meantime.
|
|
||||||
if (!configuration.overwrite) {
|
|
||||||
// check if file already exists
|
|
||||||
if (existsSync(newPath)) {
|
|
||||||
console.error(`File ${newPath} already exists, skipping.`)
|
|
||||||
break copyFile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
copyFileSync(path, newPath)
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error copying file: ${error}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove the file from /static/models
|
|
||||||
try {
|
|
||||||
unlinkSync(path)
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error removing file: ${error}`)
|
|
||||||
}
|
|
||||||
})
|
|
13
src/app.d.ts
vendored
13
src/app.d.ts
vendored
@ -1,13 +0,0 @@
|
|||||||
// See https://kit.svelte.dev/docs/types#app
|
|
||||||
// for information about these interfaces
|
|
||||||
declare global {
|
|
||||||
namespace App {
|
|
||||||
// interface Error {}
|
|
||||||
// interface Locals {}
|
|
||||||
// interface PageData {}
|
|
||||||
// interface PageState {}
|
|
||||||
// interface Platform {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {};
|
|
12
src/app.html
12
src/app.html
@ -1,12 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
%sveltekit.head%
|
|
||||||
</head>
|
|
||||||
<body data-sveltekit-preload-data="hover">
|
|
||||||
<div style="display: contents">%sveltekit.body%</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
20
src/app/globals.css
Normal file
20
src/app/globals.css
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
.shrimp {
|
||||||
|
position: absolute !important;
|
||||||
|
top: 0 !important;
|
||||||
|
left: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
padding: 0% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Global styles adjustment */
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
min-width: 100vw;
|
||||||
|
background-color: black; /* Ensure the background color is set here for full coverage */
|
||||||
|
}
|
28
src/app/home/page.tsx
Normal file
28
src/app/home/page.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import Shrimp from '@/components/ShrimpRender';
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<div className='min-h-screen min-w-screen'>
|
||||||
|
<Shrimp />
|
||||||
|
<div className='absolute z-[1] bottom-0 left-1/2 transform -translate-x-1/2 text-white font-mono pb-10 text-center'>
|
||||||
|
<p>SticksDev</p>
|
||||||
|
<p>In shrimp, we trust.</p>
|
||||||
|
|
||||||
|
{/* Contact link */}
|
||||||
|
<div className='text-blue-400 flex flex-row space-x-2'>
|
||||||
|
<a href='/term' className='hover:text-blue-500 duration-200'>
|
||||||
|
Open Terminal to Learn More
|
||||||
|
</a>
|
||||||
|
<p className='text-white'>or</p>
|
||||||
|
<a
|
||||||
|
href='mailto:sticks@teamhydra.dev'
|
||||||
|
className='hover:text-blue-500 duration-200'
|
||||||
|
>
|
||||||
|
Get in touch
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
55
src/app/layout.tsx
Normal file
55
src/app/layout.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import type { Metadata } from 'next';
|
||||||
|
import { Inter } from 'next/font/google';
|
||||||
|
import './globals.css';
|
||||||
|
|
||||||
|
const inter = Inter({ subsets: ['latin'] });
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: Readonly<{
|
||||||
|
children: React.ReactNode;
|
||||||
|
}>) {
|
||||||
|
return (
|
||||||
|
<html lang='en'>
|
||||||
|
<head>
|
||||||
|
<title>Stick's Portfolio</title>
|
||||||
|
<meta name='title' content="Stick's Portfolio" />
|
||||||
|
<meta
|
||||||
|
name='description'
|
||||||
|
content='Sometimes I make the computers do the things.'
|
||||||
|
/>
|
||||||
|
<meta name='theme-color' content='#0047AB'></meta>
|
||||||
|
|
||||||
|
<meta property='og:type' content='website' />
|
||||||
|
<meta property='og:url' content='https://sticksdev.tech' />
|
||||||
|
<meta property='og:title' content="Stick's Portfolio" />
|
||||||
|
<meta
|
||||||
|
property='og:description'
|
||||||
|
content='Sometimes I make the computers do the things.'
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property='og:image'
|
||||||
|
content='https://img.sticks.ovh/stickspfpnew.png'
|
||||||
|
/>
|
||||||
|
|
||||||
|
<meta property='twitter:card' content='summary_large_image' />
|
||||||
|
<meta property='twitter:url' content='https://sticksdev.tech' />
|
||||||
|
<meta property='twitter:title' content="Stick's Portfolio" />
|
||||||
|
<meta
|
||||||
|
property='twitter:description'
|
||||||
|
content='Sometimes I make the computers do the things.'
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property='twitter:image'
|
||||||
|
content='https://sticksdev.techimages/meta-tags.png'
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<link
|
||||||
|
rel='icon'
|
||||||
|
type='image/png'
|
||||||
|
href='https://img.sticks.ovh/stickspfpnew.png'
|
||||||
|
/>
|
||||||
|
<body className={inter.className}>{children}</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
157
src/app/page.tsx
Normal file
157
src/app/page.tsx
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
'use client';
|
||||||
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
function Home() {
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
const bootMessages = [
|
||||||
|
'Initializing ShrimpBIOS v0.1.0... (c) 2024 SticksDev. All rights reserved.',
|
||||||
|
'Checking Memory...',
|
||||||
|
'...100KB OK',
|
||||||
|
'...512KB OK',
|
||||||
|
'...1024KB OK',
|
||||||
|
'...2048KB OK',
|
||||||
|
'...4096KB OK',
|
||||||
|
'Memory OK',
|
||||||
|
'Checking CPU...',
|
||||||
|
'...CPU OK. Found Intel Pentium II 400MHz',
|
||||||
|
'Running POST Cycle for Integrated Graphics...',
|
||||||
|
'...Graphics OK. Found Intel i740',
|
||||||
|
'Running POST Cycle for Audio...',
|
||||||
|
'...Audio OK. Found SoundBlaster 16',
|
||||||
|
'Running POST Cycle for Storage...',
|
||||||
|
'...Storage OK. Found 1x 40GB HDD',
|
||||||
|
'...Storage OK. Found 1x 1.44MB FDD',
|
||||||
|
'...Storage OK. Found 1x CD-ROM Drive',
|
||||||
|
'Preparing to boot from HDD...',
|
||||||
|
'Booting from HDD...',
|
||||||
|
'ShrimpOS v1.0.0 (c) 2024 SticksDev. All rights reserved.',
|
||||||
|
'Welcome to ShrimpOS!',
|
||||||
|
'In shrimp, we trust.',
|
||||||
|
'Sending you to the home in 3...',
|
||||||
|
'2...',
|
||||||
|
'1...',
|
||||||
|
'S:\\> home.exe',
|
||||||
|
'Inlining ShrimpOS Home...',
|
||||||
|
'Done!',
|
||||||
|
'Preparing assets...',
|
||||||
|
'Done!',
|
||||||
|
'Loading ShrimpOS Home...',
|
||||||
|
'Done!',
|
||||||
|
'Welcome to ShrimpOS Home!',
|
||||||
|
];
|
||||||
|
|
||||||
|
const [currentMessage, setCurrentMessage] = useState('');
|
||||||
|
const [messageIndex, setMessageIndex] = useState(0);
|
||||||
|
const [hasBootSequenceStarted, setHasBootSequenceStarted] = useState(false);
|
||||||
|
const [isBootSequenceDone, setIsBootSequenceDone] = useState(false);
|
||||||
|
const [skippedBootSequence, setSkippedBootSequence] = useState(false);
|
||||||
|
|
||||||
|
// Audio refs
|
||||||
|
const bootAudio = useRef<HTMLAudioElement | null>(null);
|
||||||
|
const welcomeAudio = useRef<HTMLAudioElement | null>(null);
|
||||||
|
|
||||||
|
function startBootSequence() {
|
||||||
|
const bootAudio = new Audio('/bootup.mp3');
|
||||||
|
bootAudio.play();
|
||||||
|
setMessageIndex(1);
|
||||||
|
setHasBootSequenceStarted(true);
|
||||||
|
|
||||||
|
// When bootAudio finsihes, play the welcome audio and goto /home
|
||||||
|
bootAudio.onended = () => {
|
||||||
|
const welcomeAudio = new Audio('/welcome.mp3');
|
||||||
|
|
||||||
|
// Fade out the main div
|
||||||
|
const mainDiv = document.getElementById('main');
|
||||||
|
if (!mainDiv) return;
|
||||||
|
mainDiv.style.opacity = '0';
|
||||||
|
|
||||||
|
welcomeAudio.play();
|
||||||
|
setIsBootSequenceDone(true);
|
||||||
|
welcomeAudio.onended = () => {
|
||||||
|
window.location.href = '/home';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSkipBootSequence() {
|
||||||
|
setSkippedBootSequence(true);
|
||||||
|
setMessageIndex(bootMessages.length);
|
||||||
|
|
||||||
|
// Kill all audios
|
||||||
|
bootAudio.current?.pause();
|
||||||
|
welcomeAudio.current?.pause();
|
||||||
|
|
||||||
|
window.location.href = '/home';
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!hasBootSequenceStarted) return;
|
||||||
|
if (messageIndex < bootMessages.length) {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setCurrentMessage(bootMessages[messageIndex]);
|
||||||
|
setMessageIndex(messageIndex + 1);
|
||||||
|
}, 588); // Change the delay here to speed up or slow down the sequence
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}
|
||||||
|
}, [messageIndex, bootMessages, hasBootSequenceStarted]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Load the bootup sound
|
||||||
|
bootAudio.current = new Audio('/bootup.mp3');
|
||||||
|
welcomeAudio.current = new Audio('/welcome.mp3');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='min-h-screen min-w-screen bg-black flex items-center justify-center'>
|
||||||
|
{!hasBootSequenceStarted && (
|
||||||
|
<div
|
||||||
|
className={`flex flex-col items-center justify-center text-center text-white font-mono ${
|
||||||
|
hasBootSequenceStarted ? 'hidden' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<p>You notice a mysterious computer in front of you.</p>
|
||||||
|
<p>It seems to be off.</p>
|
||||||
|
<button
|
||||||
|
onClick={() => startBootSequence()}
|
||||||
|
className='bg-blue-400 hover:bg-blue-500 text-white font-bold py-2 px-4 rounded mt-2'
|
||||||
|
>
|
||||||
|
Turn it on
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className='absolute top-0 left-0 text-white font-mono p-4 text-left transition-opacity duration-300'
|
||||||
|
id='main'
|
||||||
|
>
|
||||||
|
{hasBootSequenceStarted &&
|
||||||
|
bootMessages
|
||||||
|
.slice(0, messageIndex)
|
||||||
|
.map((message, index) => <p key={index}>{message}</p>)}
|
||||||
|
</div>
|
||||||
|
{/* Add skip boot sequence button in the right corner */}
|
||||||
|
<button
|
||||||
|
onClick={handleSkipBootSequence}
|
||||||
|
className={`absolute top-0 right-0 text-white font-mono p-4 text-right transition-opacity duration-300 ${
|
||||||
|
hasBootSequenceStarted ? '' : 'hidden' // Hide the button if the boot sequence hasn't started
|
||||||
|
} ${
|
||||||
|
skippedBootSequence ? 'hidden' : '' // Hide the button if the user has already skipped the boot sequence
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Skip Boot Sequence
|
||||||
|
</button>
|
||||||
|
{/* Center text to fade in once boot seq is done */}
|
||||||
|
<div
|
||||||
|
className={`flex flex-col items-center justify-center text-white text-center font-mono p-4 transition-opacity duration-500 ${
|
||||||
|
isBootSequenceDone ? 'opacity-100' : 'opacity-0 hidden'
|
||||||
|
}`}
|
||||||
|
id='center'
|
||||||
|
>
|
||||||
|
<h1 className='text-2xl font-bold'>ShrimpOS</h1>
|
||||||
|
<p className='text-gray-500'>Welcome.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Home;
|
257
src/app/term/page.tsx
Normal file
257
src/app/term/page.tsx
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import About from '@/components/About';
|
||||||
|
import { Contact } from '@/components/Contact';
|
||||||
|
import Experience from '@/components/Experince';
|
||||||
|
import Projects from '@/components/Projects';
|
||||||
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const [input, setInput] = useState('');
|
||||||
|
const [output, setOutput] = useState<React.ReactNode[]>([
|
||||||
|
'ShrimpTerm v1.0.0',
|
||||||
|
"Type 'help' for a list of commands.",
|
||||||
|
'Press Tab for autocomplete, Escape to clear the current input.',
|
||||||
|
]);
|
||||||
|
const [suggestion, setSuggestion] = useState('');
|
||||||
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
const keyAudio = useRef<HTMLAudioElement | null>(null);
|
||||||
|
const enterAudio = useRef<HTMLAudioElement | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Load the key press sound
|
||||||
|
keyAudio.current = new Audio('/key.mp3');
|
||||||
|
keyAudio.current.volume = 0.1;
|
||||||
|
enterAudio.current = new Audio('/enter.mp3');
|
||||||
|
enterAudio.current.volume = 0.1;
|
||||||
|
inputRef.current?.focus();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const commands = [
|
||||||
|
'help',
|
||||||
|
'about',
|
||||||
|
'clear',
|
||||||
|
'projects',
|
||||||
|
'experience',
|
||||||
|
'contact',
|
||||||
|
'back',
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleInput = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const { value } = e.target;
|
||||||
|
setInput(value);
|
||||||
|
|
||||||
|
// Generate suggestion only if 3 or more characters are entered
|
||||||
|
if (value.trim().length < 3) {
|
||||||
|
setSuggestion('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchedCommand = commands.find((command) =>
|
||||||
|
command.startsWith(value.trim().toLowerCase()),
|
||||||
|
);
|
||||||
|
setSuggestion(matchedCommand ? matchedCommand : '');
|
||||||
|
};
|
||||||
|
|
||||||
|
function processCommand(command: string) {
|
||||||
|
switch (command.trim().toLowerCase()) {
|
||||||
|
case 'help':
|
||||||
|
setOutput((prevOutput) => [
|
||||||
|
...prevOutput,
|
||||||
|
'Available commands (click to autocomplete):',
|
||||||
|
<div key='help'>
|
||||||
|
<button
|
||||||
|
key='clear'
|
||||||
|
onClick={() => {
|
||||||
|
setInput('help');
|
||||||
|
inputRef.current?.focus();
|
||||||
|
}}
|
||||||
|
className='text-blue-500 hover:underline'
|
||||||
|
>
|
||||||
|
Help (this command)
|
||||||
|
</button>
|
||||||
|
{' - Display this help message'}
|
||||||
|
</div>,
|
||||||
|
<div key='about'>
|
||||||
|
<button
|
||||||
|
key='clear'
|
||||||
|
onClick={() => {
|
||||||
|
setInput('about');
|
||||||
|
inputRef.current?.focus();
|
||||||
|
}}
|
||||||
|
className='text-blue-500 hover:underline'
|
||||||
|
>
|
||||||
|
About
|
||||||
|
</button>
|
||||||
|
{' - Learn more about Sticks and his projects'}
|
||||||
|
</div>,
|
||||||
|
<div key='projects'>
|
||||||
|
<button
|
||||||
|
key='clear'
|
||||||
|
onClick={() => {
|
||||||
|
setInput('projects');
|
||||||
|
inputRef.current?.focus();
|
||||||
|
}}
|
||||||
|
className='text-blue-500 hover:underline'
|
||||||
|
>
|
||||||
|
Projects
|
||||||
|
</button>
|
||||||
|
{' - List of projects created by Sticks'}
|
||||||
|
</div>,
|
||||||
|
<div key='experience'>
|
||||||
|
<button
|
||||||
|
key='clear'
|
||||||
|
onClick={() => {
|
||||||
|
setInput('experience');
|
||||||
|
inputRef.current?.focus();
|
||||||
|
}}
|
||||||
|
className='text-blue-500 hover:underline'
|
||||||
|
>
|
||||||
|
Experience
|
||||||
|
</button>
|
||||||
|
{" - View Sticks's experience and past positions"}
|
||||||
|
</div>,
|
||||||
|
<div key='contact'>
|
||||||
|
<button
|
||||||
|
key='clear'
|
||||||
|
onClick={() => {
|
||||||
|
setInput('contact');
|
||||||
|
inputRef.current?.focus();
|
||||||
|
}}
|
||||||
|
className='text-blue-500 hover:underline'
|
||||||
|
>
|
||||||
|
Contact
|
||||||
|
</button>
|
||||||
|
{' - Get in touch with Sticks'}
|
||||||
|
</div>,
|
||||||
|
<div key='back'>
|
||||||
|
<button
|
||||||
|
key='clear'
|
||||||
|
onClick={() => {
|
||||||
|
setInput('back');
|
||||||
|
inputRef.current?.focus();
|
||||||
|
}}
|
||||||
|
className='text-blue-500 hover:underline'
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
{' - Return to the home page'}
|
||||||
|
</div>,
|
||||||
|
<div key='clear'>
|
||||||
|
<button
|
||||||
|
key='clear'
|
||||||
|
onClick={() => {
|
||||||
|
setInput('clear');
|
||||||
|
inputRef.current?.focus();
|
||||||
|
}}
|
||||||
|
className='text-blue-500 hover:underline'
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
{' - Clear the screen'}
|
||||||
|
</div>,
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
case 'about':
|
||||||
|
setOutput((prevOutput) => [
|
||||||
|
...prevOutput,
|
||||||
|
<div key='about' className='flex flex-row'>
|
||||||
|
<About />
|
||||||
|
</div>,
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'projects':
|
||||||
|
setOutput((prevOutput) => [
|
||||||
|
...prevOutput,
|
||||||
|
<div key='projects' className='flex flex-row'>
|
||||||
|
<Projects />
|
||||||
|
</div>,
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
case 'experience':
|
||||||
|
setOutput((prevOutput) => [
|
||||||
|
...prevOutput,
|
||||||
|
<div key='experience' className='flex flex-row'>
|
||||||
|
<Experience />
|
||||||
|
</div>,
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
case 'back':
|
||||||
|
window.location.href = '/home';
|
||||||
|
break;
|
||||||
|
case 'contact':
|
||||||
|
setOutput((prevOutput) => [
|
||||||
|
...prevOutput,
|
||||||
|
<div key='contact'>
|
||||||
|
<Contact />
|
||||||
|
</div>,
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
case 'clear':
|
||||||
|
setOutput([
|
||||||
|
'ShrimpTerm v1.0.0',
|
||||||
|
"Type 'help' for a list of commands.",
|
||||||
|
'Press Tab for autocomplete, Escape to clear the current input.',
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setOutput((prevOutput) => [
|
||||||
|
...prevOutput,
|
||||||
|
<div key='error'>
|
||||||
|
<span className='text-red-500'>Error:</span> Bad command
|
||||||
|
or file name: {command}
|
||||||
|
</div>,
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCommandInput = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
enterAudio.current?.play();
|
||||||
|
setOutput((prevOutput) => [...prevOutput, `S:\\> ${input}.exe`]);
|
||||||
|
processCommand(input);
|
||||||
|
setInput(''); // Clear the input
|
||||||
|
setSuggestion(''); // Clear the suggestion
|
||||||
|
} else if (e.key === 'Tab' && suggestion) {
|
||||||
|
keyAudio.current?.play();
|
||||||
|
e.preventDefault(); // Prevent the default tab behavior
|
||||||
|
setInput(suggestion); // Set input to the full suggestion
|
||||||
|
setSuggestion(''); // Clear the suggestion
|
||||||
|
} else if (e.key === 'Escape') {
|
||||||
|
keyAudio.current?.play();
|
||||||
|
setInput(''); // Clear the input
|
||||||
|
setSuggestion(''); // Clear the suggestion
|
||||||
|
} else {
|
||||||
|
keyAudio.current?.play();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Adjustments to the component
|
||||||
|
return (
|
||||||
|
<div className='bg-black'>
|
||||||
|
<div className='absolute z-[1] top-0 left-0 text-white font-mono p-10 overflow-auto min-h-screen min-w-screen'>
|
||||||
|
{/* Back Link */}
|
||||||
|
<a href='/home' className='hover:text-blue-500 duration-200'>
|
||||||
|
< Back
|
||||||
|
</a>
|
||||||
|
{output.map((line, index) => (
|
||||||
|
<p key={index}>{line}</p>
|
||||||
|
))}
|
||||||
|
<p className='text-gray-500'>
|
||||||
|
{input && suggestion ? suggestion : ''}
|
||||||
|
</p>
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
className='bg-transparent text-white outline-none'
|
||||||
|
value={input}
|
||||||
|
onChange={handleInput}
|
||||||
|
onKeyDown={handleCommandInput}
|
||||||
|
placeholder='Type a command...'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
23
src/components/About.tsx
Normal file
23
src/components/About.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
const AboutTxt = `
|
||||||
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ---- Reading file about_me.inf ----
|
||||||
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ---- File read successful ----
|
||||||
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Name: SticksDev (Tanner Sommers)
|
||||||
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Age: 21
|
||||||
|
@@@@@@@@@@@****+++=@@@@@@@@@ Location: United States (UTC-5)
|
||||||
|
@@@@@@@@@=-@@@@@@@@@@@@@@@@@ Occupation: Software Engineer/Freelancer
|
||||||
|
@@@@@@@@@.:@@@@@@@@@@@@@@@@@ Skills: JavaScript, TypeScript, React, Svelte, Node.js, Python, C#, Java
|
||||||
|
@@@@@@@@@@@------@@@@@@@@@@@ Interests: Web Development, Game Development, Cybersecurity
|
||||||
|
@@@@@@@@@@@@@@@@@--@@@@@@@@@ Hobbies: Coding, Gaming, Chess, Music
|
||||||
|
@@@@@@@@@==+++***@@@@@@@@@@@ Bio:
|
||||||
|
@@@@@@@@@%%%%@@%@@@@@@@@@@@@ Hi! I'm Tanner, a software engineer and freelancer from the United States.
|
||||||
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@ I love coding, gaming, and learning new things. I'm always looking for new
|
||||||
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@ opportunities to grow and expand my skillset. Feel free to reach out to me
|
||||||
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if you have any questions or just want to chat!
|
||||||
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ---- End of file ----
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function About() {
|
||||||
|
return <pre className='text-white font-mono'>{AboutTxt}</pre>;
|
||||||
|
}
|
32
src/components/Contact.tsx
Normal file
32
src/components/Contact.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
export function Contact() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1 className='text-2xl font-bold font-mono'>Contact</h1>
|
||||||
|
<p className='text-gray-500'>
|
||||||
|
You can reach me at the following email address:
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href='mailto:tanner@teamhydra.dev'
|
||||||
|
className='text-blue-500 hover:underline'
|
||||||
|
>
|
||||||
|
tanner@teamhydra.dev
|
||||||
|
</a>
|
||||||
|
<br />
|
||||||
|
-- or --
|
||||||
|
<br />
|
||||||
|
<a
|
||||||
|
href='https://discord.gg/zira'
|
||||||
|
className='text-blue-500 hover:underline'
|
||||||
|
>
|
||||||
|
Via Discord
|
||||||
|
</a>
|
||||||
|
<p className='text-gray-500'>
|
||||||
|
Please use the #other-support channel to get in touch with me. My
|
||||||
|
username is sticksdev.
|
||||||
|
</p>
|
||||||
|
<p className='text-gray-500'>
|
||||||
|
I look forward to hearing from you soon :)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
86
src/components/Experince.tsx
Normal file
86
src/components/Experince.tsx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
'use strict';
|
||||||
|
'use client';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
const experience = [
|
||||||
|
`
|
||||||
|
@@@@@@@@@@
|
||||||
|
@@@@@*:@@@@@
|
||||||
|
@#*@+**-@:@@
|
||||||
|
@#@@@ @@@**@**@:*@@ @@@*@
|
||||||
|
@@+@@@:@@@@@*%*@**@*.%@@@@@:@@@+@@
|
||||||
|
@@@@@@@@@@@@@@*@*:#*@@@@@***:@#@@@**:*@*@@@@@@@@@@@%@@ Company Name: Team Hydra
|
||||||
|
@@@%%@@***********@@********@@**+*******+@@**@@@ Position: Software Developer
|
||||||
|
@@@#%@********+*@********@*************@@@ Start Date: 2020-09-01
|
||||||
|
@@@@%********.*@*%**@*@+.*********@@@@ End: Present
|
||||||
|
@@@@#**@*%**+@@*@@@@+@@+****@**+@@@@ About:
|
||||||
|
@@@@**@@*****@@*@@=@@*****@@%*@@@@ I've worked with team hydra on a variety of projects,
|
||||||
|
@ @@+*@@@*****@#@@*****@@@#*@@ @ including developing web applications, mobile apps, discord bots,
|
||||||
|
@@@ @@%%@@@@@***@@***@@@@@%@@@ @@@ and APIs. It's a great team to work with, and I've learned a lot.
|
||||||
|
@@@%@@@@*****+**@@@@%@@@ I highly recommend them to anyone looking for software development
|
||||||
|
@@@%%%***#@**@****%%%@@@ services and a career in software development.
|
||||||
|
@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||||
|
@@ @@
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
*###########*
|
||||||
|
################*
|
||||||
|
######## *####
|
||||||
|
###### *
|
||||||
|
*####*
|
||||||
|
##### #########
|
||||||
|
##### ######### ordon food services
|
||||||
|
#####* ######### Position: Network Engineer/IT Specialist
|
||||||
|
###### *#### Start Date: 2022-06-01
|
||||||
|
#######* =###### End: 2024-06-01
|
||||||
|
################# About: I worked with Gordon food services to help maintain their network infrastructure
|
||||||
|
############* and provide IT support to their employees. I was responsible for troubleshooting network
|
||||||
|
####### issues, setting up new network equipment, and providing support to employees with IT issues.
|
||||||
|
#### I was laid off, but I enjoyed my time there and learned a lot about network engineering and
|
||||||
|
* IT support.
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function Experience() {
|
||||||
|
const [index, setIndex] = useState(0);
|
||||||
|
|
||||||
|
const handleNext = () => {
|
||||||
|
if (index < experience.length - 1) {
|
||||||
|
setIndex(index + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePrev = () => {
|
||||||
|
if (index > 0) {
|
||||||
|
setIndex(index - 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1 className='text-2xl font-bold font-mono'>Experience</h1>
|
||||||
|
<p className='text-gray-500'>
|
||||||
|
Use the buttons below to navigate through my experience.
|
||||||
|
</p>
|
||||||
|
<div className='flex justify-between'>
|
||||||
|
<button
|
||||||
|
onClick={handlePrev}
|
||||||
|
className='text-blue-500 hover:underline'
|
||||||
|
disabled={index === 0}
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleNext}
|
||||||
|
className='text-blue-500 hover:underline'
|
||||||
|
disabled={index === experience.length - 1}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className=''>
|
||||||
|
<pre className='whitespace-pre-wrap'>{experience[index]}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
59
src/components/Projects.tsx
Normal file
59
src/components/Projects.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
export const projects = [
|
||||||
|
{
|
||||||
|
title: 'BambuConnect',
|
||||||
|
description: 'A simple 3rd party client for managing your 3D printer.',
|
||||||
|
link: 'https://github.com/SticksDev/BambuConnect',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'VRDCN_NetworkTest',
|
||||||
|
description:
|
||||||
|
'A simple network test for a VR Streaming service. Written in Go.',
|
||||||
|
link: 'https://github.com/SticksDev/VRCDN_NetworkTest',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Runic Spells',
|
||||||
|
description:
|
||||||
|
'A simple spell system for Minecraft using Java and PaperMC APIs.',
|
||||||
|
link: 'https://github.com/SticksDev/runic_spells',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function Projects() {
|
||||||
|
// Step 1: Calculate maximum lengths
|
||||||
|
const maxTitleLength = Math.max(
|
||||||
|
...projects.map((project) => project.title.length),
|
||||||
|
);
|
||||||
|
const maxDescriptionLength = Math.max(
|
||||||
|
...projects.map((project) => project.description.length),
|
||||||
|
);
|
||||||
|
const maxLinkLength = Math.max(
|
||||||
|
...projects.map((project) => project.link.length),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 2: Prepare projects with padded strings for rendering
|
||||||
|
const preparedProjects = projects.map((project) => ({
|
||||||
|
...project,
|
||||||
|
title: project.title.padEnd(maxTitleLength, ' '),
|
||||||
|
description: project.description.padEnd(maxDescriptionLength, ' '),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='bg-black font-mono text-white'>
|
||||||
|
<h1 className='text-white font-mono text-md mb-4'>
|
||||||
|
--- Reading database projects.db ---<br></br>
|
||||||
|
Found {projects.length} projects in database. Displaying all projects:
|
||||||
|
</h1>
|
||||||
|
{preparedProjects.map((project, index) => (
|
||||||
|
<div key={index} className='flex flex-row'>
|
||||||
|
<p>{project.title}</p> |
|
||||||
|
<p> {project.description}</p>|
|
||||||
|
<a href={project.link} className="hover:text-blue-500 duration-200" target="blank"> {project.link}</a>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<h1 className='text-white font-mono text-md mt-4'>
|
||||||
|
--- End of database ---
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
67
src/components/ShrimpRender.tsx
Normal file
67
src/components/ShrimpRender.tsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
'use client';
|
||||||
|
import { Canvas, useFrame } from '@react-three/fiber';
|
||||||
|
import * as THREE from 'three';
|
||||||
|
import React, { useRef } from 'react';
|
||||||
|
import { AsciiRenderer, OrbitControls, useGLTF } from '@react-three/drei';
|
||||||
|
import { GLTF } from 'three-stdlib';
|
||||||
|
|
||||||
|
type GLTFResult = GLTF & {
|
||||||
|
nodes: {
|
||||||
|
Mesh_Mesh_head_geo001_lambert2SG001: THREE.Mesh;
|
||||||
|
};
|
||||||
|
materials: {
|
||||||
|
['lambert2SG.001']: THREE.MeshStandardMaterial;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ShrimpModel(props: JSX.IntrinsicElements['group']) {
|
||||||
|
const { nodes, materials } = useGLTF('/shrimp_smol.glb') as GLTFResult;
|
||||||
|
const groupRef = useRef<THREE.Group>(null!);
|
||||||
|
useFrame((state, delta) => (groupRef.current.rotation.z += delta * .5))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<group {...props} ref={groupRef} dispose={null}>
|
||||||
|
<mesh
|
||||||
|
castShadow
|
||||||
|
receiveShadow
|
||||||
|
geometry={nodes.Mesh_Mesh_head_geo001_lambert2SG001.geometry}
|
||||||
|
material={materials['lambert2SG.001']}
|
||||||
|
rotation={[-Math.PI / 2, 0, 0]}
|
||||||
|
/>
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
useGLTF.preload('/shrimp.glb');
|
||||||
|
|
||||||
|
function CameraHelper() {
|
||||||
|
const camera = new THREE.PerspectiveCamera(60, 1.77, 1, 3);
|
||||||
|
return (
|
||||||
|
<group position={[0.5, 40, 8.5]} rotation={new THREE.Euler(-1.5, 0, 0)}>
|
||||||
|
<cameraHelper args={[camera]} />
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Shrimp() {
|
||||||
|
const eular = new THREE.Euler(-1.5, 0, 0);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Canvas
|
||||||
|
className='shrimp'
|
||||||
|
camera={{
|
||||||
|
position: [0.4, 40, 11],
|
||||||
|
rotation: eular,
|
||||||
|
fov: 60,
|
||||||
|
aspect: 1.77,
|
||||||
|
near: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<color attach='background' args={['black']} />
|
||||||
|
<ambientLight intensity={Math.PI / 2} />
|
||||||
|
<ShrimpModel position={[0, -1, 0]} rotation={[0, 0, 0]} />
|
||||||
|
<AsciiRenderer fgColor='red' />
|
||||||
|
</Canvas>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -1,8 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Canvas } from '@threlte/core'
|
|
||||||
import Scene from './Scene.svelte'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Canvas>
|
|
||||||
<Scene />
|
|
||||||
</Canvas>
|
|
@ -1,60 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { T, useFrame } from '@threlte/core'
|
|
||||||
|
|
||||||
let rotation = 0
|
|
||||||
useFrame(() => {
|
|
||||||
rotation += 0.001
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<T.Group rotation.y={rotation}>
|
|
||||||
<T.PerspectiveCamera
|
|
||||||
makeDefault
|
|
||||||
position={[-10, 10, 10]}
|
|
||||||
fov={15}
|
|
||||||
on:create={({ ref }) => {
|
|
||||||
ref.lookAt(0, 1, 0)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</T.Group>
|
|
||||||
|
|
||||||
<!-- Floor -->
|
|
||||||
<T.Mesh rotation.x={(90 * Math.PI) / 180}>
|
|
||||||
<T.CircleGeometry args={[3, 16]} />
|
|
||||||
<T.MeshBasicMaterial
|
|
||||||
color="#666666"
|
|
||||||
wireframe
|
|
||||||
/>
|
|
||||||
</T.Mesh>
|
|
||||||
|
|
||||||
<T.DirectionalLight
|
|
||||||
intensity={0.8}
|
|
||||||
position.x={5}
|
|
||||||
position.y={10}
|
|
||||||
/>
|
|
||||||
<T.AmbientLight intensity={0.2} />
|
|
||||||
|
|
||||||
<T.Mesh
|
|
||||||
position.y={1.2}
|
|
||||||
position.z={-0.75}
|
|
||||||
>
|
|
||||||
<T.BoxGeometry />
|
|
||||||
<T.MeshStandardMaterial color="#0059BA" />
|
|
||||||
</T.Mesh>
|
|
||||||
|
|
||||||
<T.Mesh
|
|
||||||
position={[1.2, 1.5, 0.75]}
|
|
||||||
rotation.x={5}
|
|
||||||
rotation.y={71}
|
|
||||||
>
|
|
||||||
<T.TorusKnotGeometry args={[0.5, 0.15, 100, 12, 2, 3]} />
|
|
||||||
<T.MeshStandardMaterial color="#F85122" />
|
|
||||||
</T.Mesh>
|
|
||||||
|
|
||||||
<T.Mesh
|
|
||||||
position={[-1.4, 1.5, 0.75]}
|
|
||||||
rotation={[-5, 128, 10]}
|
|
||||||
>
|
|
||||||
<T.IcosahedronGeometry />
|
|
||||||
<T.MeshStandardMaterial color="#F8EBCE" />
|
|
||||||
</T.Mesh>
|
|
@ -1,5 +0,0 @@
|
|||||||
# Threlte Model Pipeline Components
|
|
||||||
|
|
||||||
This directory holds automatically generated Threlte components from GLTF models.
|
|
||||||
|
|
||||||
Place your models in `static/models` and run `npm run model-pipeline:run` to generate the components.
|
|
@ -1 +0,0 @@
|
|||||||
// place files you want to import through the `$lib` alias in this folder.
|
|
@ -1,20 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import App from '$lib/components/App.svelte'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<App />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
:global(body) {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
background: rgb(13, 19, 32);
|
|
||||||
background: linear-gradient(180deg, rgba(13, 19, 32, 1) 0%, rgba(8, 12, 21, 1) 100%);
|
|
||||||
}
|
|
||||||
</style>
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
@ -1,18 +0,0 @@
|
|||||||
import adapter from '@sveltejs/adapter-auto';
|
|
||||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
|
||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Config} */
|
|
||||||
const config = {
|
|
||||||
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
|
||||||
// for more information about preprocessors
|
|
||||||
preprocess: vitePreprocess(),
|
|
||||||
|
|
||||||
kit: {
|
|
||||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
|
||||||
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
|
||||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
|
||||||
adapter: adapter()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
|
20
tailwind.config.ts
Normal file
20
tailwind.config.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import type { Config } from "tailwindcss";
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
content: [
|
||||||
|
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
backgroundImage: {
|
||||||
|
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
|
||||||
|
"gradient-conic":
|
||||||
|
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
|
export default config;
|
@ -1,19 +1,26 @@
|
|||||||
{
|
{
|
||||||
"extends": "./.svelte-kit/tsconfig.json",
|
"compilerOptions": {
|
||||||
"compilerOptions": {
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"checkJs": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"strict": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"noEmit": true,
|
||||||
"resolveJsonModule": true,
|
"esModuleInterop": true,
|
||||||
"skipLibCheck": true,
|
"module": "esnext",
|
||||||
"sourceMap": true,
|
"moduleResolution": "bundler",
|
||||||
"strict": true,
|
"resolveJsonModule": true,
|
||||||
"moduleResolution": "bundler"
|
"isolatedModules": true,
|
||||||
}
|
"jsx": "preserve",
|
||||||
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
"incremental": true,
|
||||||
// except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
|
"plugins": [
|
||||||
//
|
{
|
||||||
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
"name": "next"
|
||||||
// from the referenced tsconfig.json - TypeScript does not merge them in
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
import { sveltekit } from '@sveltejs/kit/vite';
|
|
||||||
import { defineConfig } from 'vite';
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [sveltekit()]
|
|
||||||
});
|
|
Loading…
x
Reference in New Issue
Block a user