Compare commits
3 Commits
92597f4278
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ba0a2057d | |||
| 4b7257c884 | |||
| 5d7043b8d9 |
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
46
README.md
Normal file
46
README.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Getting Started with Create React App
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `yarn start`
|
||||
|
||||
Runs the app in the development mode.\
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||
|
||||
The page will reload if you make edits.\
|
||||
You will also see any lint errors in the console.
|
||||
|
||||
### `yarn test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.\
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `yarn build`
|
||||
|
||||
Builds the app for production to the `build` folder.\
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.\
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `yarn eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||
|
||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||
|
||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
2
desc.d.ts
vendored
Normal file
2
desc.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
declare module "@mavrin/remark-typograf";
|
||||
declare module "react-markdown";
|
||||
73
package.json
Normal file
73
package.json
Normal file
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"name": "peroxy_site_v2",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@agentofuser/remark-oembed": "^1.0.4",
|
||||
"@mavrin/remark-typograf": "^2.2.0",
|
||||
"@react-three/drei": "^9.57.3",
|
||||
"@react-three/fiber": "^8.12.0",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^13.0.0",
|
||||
"@testing-library/user-event": "^13.2.1",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^18.15.3",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@types/react-syntax-highlighter": "^15.5.6",
|
||||
"@types/three": "^0.149.0",
|
||||
"framer-motion": "^10.6.0",
|
||||
"gray-matter": "^4.0.3",
|
||||
"parse-numeric-range": "^1.3.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.8.0",
|
||||
"react-markdown": "^8.0.5",
|
||||
"react-router-dom": "6",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-youtube": "^10.1.0",
|
||||
"rehype-raw": "^6.1.1",
|
||||
"rehype-sanitize": "^5.0.1",
|
||||
"remark": "^14.0.2",
|
||||
"remark-directive": "^2.0.1",
|
||||
"remark-directive-rehype": "^0.4.2",
|
||||
"remark-emoji": "^3.1.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-html": "^15.0.2",
|
||||
"remark-smartypants": "^2.0.0",
|
||||
"three": "^0.150.1",
|
||||
"typescript": "^4.4.2",
|
||||
"web-vitals": "^2.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.14",
|
||||
"postcss": "^8.4.21",
|
||||
"tailwindcss": "^3.2.7"
|
||||
}
|
||||
}
|
||||
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
43
public/index.html
Normal file
43
public/index.html
Normal file
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
BIN
public/logo192.png
Normal file
BIN
public/logo192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
BIN
public/logo512.png
Normal file
BIN
public/logo512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
25
public/manifest.json
Normal file
25
public/manifest.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
3
public/robots.txt
Normal file
3
public/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
29
src/App.tsx
Normal file
29
src/App.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { MainLayout } from "./layouts/MainLayout";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<MainLayout>
|
||||
<div className="grid lg:grid-cols-2 grid-cols-1 xl:mt-10 gap-10 items-center">
|
||||
<div className="bg-primary-dark/30 border-white/30 border p-10 rounded-lg flex flex-col gap-10 h-fit 3xl:w-2/3 w-full text-lg text-primary-text/90 shadow-lg shadow-gradient-dark/20">
|
||||
<div>
|
||||
<span className="font-bold text-xl text-primary-text">
|
||||
Lorem ipsum dolor{" "}
|
||||
</span>{" "}
|
||||
sit amet consectetur adipisicing elit. Laboriosam ab quia quae
|
||||
reiciendis. Nostrum corrupti laboriosam quae vero repudiandae
|
||||
dolorem quos dolores laborum recusandae atque itaque illo autem
|
||||
neque odit, in soluta sit alias necessitatibus fugit veniam sunt at.
|
||||
</div>
|
||||
<button className="px-4 py-2 rounded-lg bg-primary hover:bg-primary/50 transition-all ease-out duration-150 font-bold w-fit text-white">
|
||||
Action
|
||||
</button>
|
||||
</div>
|
||||
<div id="showcase" className="w-full h-96 bg-primary-text">
|
||||
<div className="text-primary-dark m-auto w-fit">showcase</div>
|
||||
</div>
|
||||
</div>
|
||||
</MainLayout>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
41
src/components/ImageSlide.tsx
Normal file
41
src/components/ImageSlide.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { useState } from "react";
|
||||
import { AiFillLeftCircle } from "react-icons/ai";
|
||||
|
||||
export const ImageSlide = ({ children }: any) => {
|
||||
const [current, setCurrent] = useState(0);
|
||||
const length = children.length;
|
||||
|
||||
const nextSlide = () => {
|
||||
setCurrent(current === length - 1 ? 0 : current + 2);
|
||||
};
|
||||
|
||||
const prevSlide = () => {
|
||||
setCurrent(current === 0 ? length - 1 : current - 2);
|
||||
};
|
||||
if (children.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="slider">
|
||||
<AiFillLeftCircle className="left-arrow" onClick={prevSlide} />
|
||||
<AiFillLeftCircle className="right-arrow" onClick={nextSlide} />
|
||||
{children.map((slide: any, index: any) => {
|
||||
return (
|
||||
<div
|
||||
className={index === current ? "slide active" : "slide"}
|
||||
key={index}
|
||||
>
|
||||
{index === current && (
|
||||
<img
|
||||
src={slide.props.children[0]}
|
||||
alt="travel image"
|
||||
className="image"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
102
src/components/Logo.tsx
Normal file
102
src/components/Logo.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
export const Logo = () => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="svg166"
|
||||
version="1.1"
|
||||
className="fill-action group-hover:fill-primary dark:group-hover:fill-secondary transition-all ease-in duration-100 flex-no-shrink xl:w-12 xl:h-12 w-8 h-8"
|
||||
viewBox="0 0 100 100"
|
||||
>
|
||||
<g id="167217677994600400">
|
||||
<path
|
||||
id="path119"
|
||||
d="M108.79 15a1 1 0 0 0 .59 1.29c9.72 3.6 15.07 8.12 15.07 12.72a1 1 0 0 0 2 0c0-5.57-5.66-10.62-16.37-14.6a1 1 0 0 0-1.29.59Z"
|
||||
/>
|
||||
<path
|
||||
id="path121"
|
||||
d="M120.92 35.51a1 1 0 0 0-1.13 1.65c3.79 2.59 5.71 5.32 5.71 8.09a1 1 0 0 0 2 0c0-3.47-2.21-6.75-6.58-9.74z"
|
||||
/>
|
||||
<path
|
||||
id="path123"
|
||||
d="M112.9 48.45a1 1 0 1 0-.75 1.85c8.61 3.49 13.35 7.77 13.35 12.06a1 1 0 1 0 2 0c0-5.23-5.05-10.04-14.6-13.91z"
|
||||
/>
|
||||
<path
|
||||
id="path125"
|
||||
d="M125.45 80.52a1 1 0 0 0 1-1c0-6.42-7.62-12.2-21.46-16.28a1 1 0 1 0-.56 1.91c12.72 3.76 20 9 20 14.37a1 1 0 0 0 1.02 1z"
|
||||
/>
|
||||
<path
|
||||
id="path127"
|
||||
d="M32.89 98.39h.19a1 1 0 0 0 .37-.08h.08a.83.83 0 0 0 .22-.14l.09-.1a.76.76 0 0 0 .12-.19.84.84 0 0 0 .06-.14v-.12a.27.27 0 0 0 0-.09V62.47c3.92-.71 8.06-1.27 12.35-1.68v11.07a1 1 0 0 0 .22.63l8.2 10.09v10a93.19 93.19 0 0 0-23.09 11.2 101.78 101.78 0 0 0-18.31 15.17 1 1 0 0 0 .72 1.69 1 1 0 0 0 .73-.31 99.86 99.86 0 0 1 18-14.88 88.68 88.68 0 0 1 32-13.37 1.0179 1.0179 0 0 0-.38-2c-2.67.53-5.19 1.16-7.58 1.86v-9.71a1 1 0 0 0-.22-.63l-8.2-10.1V59.64a.83.83 0 0 0 0-.15.74.74 0 0 0 0-.14v-.08a.76.76 0 0 0-.08-.11l-.27-.16a.27.27 0 0 1-.08-.07l-2.93-2 3.27-7.05a.58.58 0 0 0 0-.19.71.71 0 0 0 0-.2V47a16.75 16.75 0 1 1 33.5 0v46.3a1 1 0 1 0 2 0v-3.65c11.28-.13 19.27 2.68 23.73 8.35a20 20 0 0 1 3.87 9.31v11.22H30.38a1 1 0 1 0 0 2h82.15a1 1 0 0 0 1-1v-12.29a.78.78 0 0 0 0-.16 21.86 21.86 0 0 0-4.28-10.32c-4.86-6.18-13.38-9.24-25.3-9.11v-26.5a136.18 136.18 0 0 1 15.95 2.78h.23a1 1 0 0 0 1-.76 1 1 0 0 0-.74-1.21c-1.84-.45-3.78-.85-5.74-1.23V45.44a106.09 106.09 0 0 1 11.71 2.81 1 1 0 0 0 .29 0 1 1 0 0 0 .3-1.95 108 108 0 0 0-12.3-2.94V28.33l1.73.32a80 80 0 0 1 19.57 6.24.93.93 0 0 0 .45.11 1 1 0 0 0 .89-.55 1 1 0 0 0-.44-1.34 81.26 81.26 0 0 0-20.08-6.42l-2.12-.4v-14c2.23.44 4.42.91 6.51 1.45a1.0017 1.0017 0 1 0 .5-1.94 157.17 157.17 0 0 0-38.19-4.33C28.16 7.47.5 16.92.5 29v50.52c0 8.03 12.11 15.08 32.39 18.87zM63.47 9.47a167.36 167.36 0 0 1 29.14 2.41v48.51c-2.79-.49-5.69-.91-8.68-1.25V46.83a18.7502 18.7502 0 0 0-37.5.17v2.14l-3.53 7.63a1 1 0 0 0 .34 1.23l1.38 1c-3.64.38-7.17.88-10.54 1.48V46.77a1.16 1.16 0 0 0 0-.18 1.08 1.08 0 0 0-.08-.19 1.16 1.16 0 0 0-.25-.29l-.19-.11a1.89 1.89 0 0 0-.21-.09h-.09C14.57 42.41 2.5 35.77 2.5 29c0-10.59 27.92-19.53 60.97-19.53Zm-61 25c4.14 5.57 14.55 10.29 29.58 13.2V96.2c-18-3.55-29.58-10-29.58-16.68Z"
|
||||
/>
|
||||
<path
|
||||
id="path129"
|
||||
d="M56.59 63.16a1.0098 1.0098 0 0 0 .28 2 1 1 0 0 0 .29 0 8.51 8.51 0 0 0 3.92-2.43c2.29-2.55 3.39-6.87 1.16-9.41a6.25 6.25 0 0 0-4.9-1.82 1 1 0 0 0 0 2 4.36 4.36 0 0 1 3.35 1.14c1.43 1.64.54 4.87-1.15 6.75a6.55 6.55 0 0 1-2.95 1.77z"
|
||||
/>
|
||||
<path
|
||||
id="path131"
|
||||
d="M40.93 30a1 1 0 0 0 .75-.33 1 1 0 0 0-.08-1.41L38 25.07a1 1 0 1 0-1.33 1.49l3.55 3.18a1 1 0 0 0 .71.26Z"
|
||||
/>
|
||||
<path
|
||||
id="path133"
|
||||
d="M43.86 30.27a1 1 0 1 0-1.33 1.49L45 33.92a1 1 0 0 0 .67.26 1 1 0 0 0 .75-.34 1 1 0 0 0-.08-1.41z"
|
||||
/>
|
||||
<path
|
||||
id="path135"
|
||||
d="M16.82 25.61 27 31a1 1 0 0 0 .46.12 1 1 0 0 0 .89-.53 1 1 0 0 0-.42-1.36l-8.13-4.28a98.46 98.46 0 0 1 11.66-3l3.15 2.8a1 1 0 0 0 1.33-1.49l-2-1.79c3.2-.58 6.54-1.07 10-1.45.92-.1 1.87-.18 2.8-.27l1.2 3.83a1 1 0 0 0 .95.7 1 1 0 0 0 .3 0 1 1 0 0 0 .66-1.26l-1.08-3.42c2.37-.2 4.79-.33 7.22-.42l.16 1.81a1 1 0 0 0 1 .91h.09a1 1 0 0 0 .9-1.09L58 19c1.82-.06 3.66-.09 5.5-.09a1 1 0 0 0 0-2c-2.22 0-4.42.05-6.61.13h-.12c-4.44.16-8.81.46-13 .93a115.63 115.63 0 0 0-26.76 5.84 1 1 0 0 0-.65.87 1 1 0 0 0 .46.93z"
|
||||
/>
|
||||
<path
|
||||
id="path137"
|
||||
d="M33.79 34.56a1.12 1.12 0 0 0 .47.11 1 1 0 0 0 .88-.53 1 1 0 0 0-.41-1.35l-3.66-1.93a1.002 1.002 0 0 0-.94 1.77z"
|
||||
/>
|
||||
<path
|
||||
id="path139"
|
||||
d="m44.28 37.82-6-3.18a1 1 0 1 0-.93 1.77l6 3.18a.92.92 0 0 0 .46.11 1 1 0 0 0 .47-1.88z"
|
||||
/>
|
||||
<path
|
||||
id="path141"
|
||||
d="m9 32.13 4.68 2a1.09 1.09 0 0 0 .39.08 1 1 0 0 0 .39-1.92l-4.68-2A1 1 0 0 0 9 32.13Z"
|
||||
/>
|
||||
<path
|
||||
id="path143"
|
||||
d="M43.08 44.33 20 34.6a1 1 0 1 0-.77 1.84l23.07 9.74a1 1 0 0 0 .38.08 1 1 0 0 0 .39-1.93z"
|
||||
/>
|
||||
<path
|
||||
id="path145"
|
||||
d="M51 26.68a1.001 1.001 0 0 0-1.91.6l.72 2.28a1 1 0 0 0 1 .71 1 1 0 0 0 .3-.05 1 1 0 0 0 .62-1.22z"
|
||||
/>
|
||||
<path
|
||||
id="path147"
|
||||
d="M57.32 22.93a1 1 0 0 0-.9 1.09l.28 3.05a1 1 0 0 0 1 .91h.09a1 1 0 0 0 .9-1.09l-.28-3.06a1 1 0 0 0-1.09-.9z"
|
||||
/>
|
||||
<path
|
||||
id="path149"
|
||||
d="M82.93 105.09a1 1 0 0 0 1-1l.13-4.31a1.0004 1.0004 0 0 0-2-.06l-.13 4.31a1 1 0 0 0 1 1z"
|
||||
/>
|
||||
<path
|
||||
id="path151"
|
||||
d="M25.12 55c0 .07.06.12.09.18l.13.15a1.21 1.21 0 0 0 .32.22 1 1 0 0 0 .39.07 1 1 0 0 0 .7-.29l.13-.15c0-.06.06-.11.09-.18l.06-.18a1.36 1.36 0 0 0 0-.2 1 1 0 0 0-.3-.7.87.87 0 0 0-.32-.22 1 1 0 0 0-.77 0 .87.87 0 0 0-.32.22 1 1 0 0 0-.29.7.68.68 0 0 0 0 .2 1.21 1.21 0 0 0 .09.18z"
|
||||
/>
|
||||
<path
|
||||
id="path153"
|
||||
d="M25.66 60.29a1 1 0 0 0 .39.07 1 1 0 1 0-.39-1.92 1 1 0 0 0-.32.21 1 1 0 0 0 0 1.42 1.21 1.21 0 0 0 .32.22z"
|
||||
/>
|
||||
<path
|
||||
id="path155"
|
||||
d="M25.66 65.06a1.09 1.09 0 0 0 .39.08 1 1 0 0 0 1-1 1 1 0 0 0-.08-.38.91.91 0 0 0-.22-.33 1 1 0 0 0-1.09-.21 1 1 0 0 0-.32.21 1.06 1.06 0 0 0-.22.33 1 1 0 0 0-.07.38 1 1 0 0 0 .29.71 1 1 0 0 0 .32.21z"
|
||||
/>
|
||||
<path
|
||||
id="path157"
|
||||
d="M78.41 53.24a1.51 1.51 0 0 0 .18.09.64.64 0 0 0 .18.06h.2a1 1 0 0 0 .71-.3 1 1 0 0 0 .29-.7 1 1 0 0 0-.29-.71 1.19 1.19 0 0 0-.33-.22 1 1 0 0 0-.58 0h-.18l-.18.1a.79.79 0 0 0-.15.12 1 1 0 0 0-.29.71 1 1 0 0 0 .29.7z"
|
||||
/>
|
||||
<path
|
||||
id="path159"
|
||||
d="m78.26 57.89.15.13.18.09.18.06h.2a1 1 0 0 0 .71-.3 1 1 0 0 0 .29-.7 1 1 0 0 0-.29-.71 1.19 1.19 0 0 0-.33-.22 1 1 0 0 0-.58 0h-.18l-.18.09-.15.13a1 1 0 0 0-.29.71 1 1 0 0 0 .29.72z"
|
||||
/>
|
||||
<path
|
||||
id="path161"
|
||||
d="M78 62.16a.7.7 0 0 0 .06.19.56.56 0 0 0 .09.17.88.88 0 0 0 .12.15l.15.13.18.09.18.06H79a1 1 0 0 0 .38-.07 1.19 1.19 0 0 0 .33-.22l.12-.15a.56.56 0 0 0 .09-.17.7.7 0 0 0 .06-.19A1.23 1.23 0 0 0 80 62a1 1 0 0 0-.08-.39 1 1 0 0 0-.21-.32 1.19 1.19 0 0 0-.33-.22 1 1 0 0 0-1.09.22 1 1 0 0 0-.21.32.88.88 0 0 0-.08.39 1.23 1.23 0 0 0 0 .16z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
71
src/components/Navbar.tsx
Normal file
71
src/components/Navbar.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { IoMenu as MenuIcon, IoClose as CloseIcon } from "react-icons/io5";
|
||||
import { useState } from "react";
|
||||
import { Logo } from "./Logo";
|
||||
import { Link } from "react-router-dom";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
|
||||
const links = [
|
||||
{ name: "Blog", href: "/blog" },
|
||||
{ name: "Projects", href: "/projects" },
|
||||
{ name: "Gitea", href: "https://git.peroxy.dev/kookroach" },
|
||||
];
|
||||
export const Navbar = () => {
|
||||
const [showMenu, setShowMenu] = useState(false);
|
||||
|
||||
return (
|
||||
<nav className="py-4 xl:px-10 px-4 flex flex-row justify-between sticky top-0 z-50 bg-gradient-to-b from-black/50 to-black/30">
|
||||
<Link to="/" id="logo" className="flex flex-row gap-2 items-center group">
|
||||
<Logo />
|
||||
<div className="font-bold xl:text-4xl text-xl font-mono transition-all ease-in duration-100 text-action group-hover:text-primary dark:group-hover:text-secondary">
|
||||
peroxy
|
||||
<span className="xl:text-sm text-xs">.dev</span>
|
||||
</div>
|
||||
</Link>
|
||||
<div className="hidden xl:flex flex-row gap-10 items-center font-medium font-mono">
|
||||
{links.map(({ name, href }) => (
|
||||
<Link to={href} key={name}>
|
||||
<button className="px-10 py-1 rounded-sm bg-gradient-to-r from-indigo-500/30 to-fuchsia-500/20 hover:bg-primary/50 transition-all duration-150 ease-out text-white">
|
||||
{name}
|
||||
</button>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div className="xl:hidden flex">
|
||||
<button onClick={() => setShowMenu(true)} className="text-2xl">
|
||||
<MenuIcon />
|
||||
</button>
|
||||
</div>
|
||||
<AnimatePresence>
|
||||
{showMenu && (
|
||||
<div className="fixed h-screen w-screen top-0 left-0">
|
||||
<motion.div
|
||||
className="absolute left-0 top-0 h-full w-full bg-black/70 z-10"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
/>
|
||||
<motion.div
|
||||
className={`z-40 absolute w-1/2 bg-blue-900 text-primary-text
|
||||
right-0 top-0 px-10 py-6 h-full`}
|
||||
initial={{ x: "100%" }}
|
||||
animate={{ x: 0 }}
|
||||
exit={{ x: "100%" }}
|
||||
transition={{ ease: "easeInOut", duration: 0.4 }}
|
||||
>
|
||||
<button onClick={() => setShowMenu(false)} className="text-2xl">
|
||||
<CloseIcon />
|
||||
</button>
|
||||
<div className="flex flex-col gap-10 mt-10 font-bold font-mono">
|
||||
{links.map(({ name, href }) => (
|
||||
<Link to={href} key={name}>
|
||||
<button>{name}</button>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
53
src/components/PostHeader.tsx
Normal file
53
src/components/PostHeader.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Link } from "react-router-dom";
|
||||
import { FrontMatter } from "../types/types";
|
||||
|
||||
export interface BasicArticleProps extends FrontMatter {
|
||||
title: string;
|
||||
description: string;
|
||||
date: string;
|
||||
author: string;
|
||||
authorLink: string;
|
||||
thumbnail: string;
|
||||
}
|
||||
export const PostHeader = ({
|
||||
frontMatter,
|
||||
estTime,
|
||||
}: {
|
||||
frontMatter: FrontMatter;
|
||||
estTime: number;
|
||||
}) => {
|
||||
const { title, author, date, description, authorLink } =
|
||||
frontMatter as BasicArticleProps;
|
||||
|
||||
return (
|
||||
<div className="flex items-start gap-2 flex-col">
|
||||
<div className="lg:text-5xl text-3xl font-bold mt-2 text-gray-50">
|
||||
{title}
|
||||
</div>
|
||||
<div className="mt-2 text-gray-600dark:text-gray-400 text-sm">
|
||||
{description}
|
||||
</div>
|
||||
<div className="mt-2 mb-10 flex lg:flex-row flex-col gap-2 items-start">
|
||||
{author && (
|
||||
<div className="font-medium ">
|
||||
By{" "}
|
||||
{authorLink && (
|
||||
<Link to={authorLink} className="dark:text-action text-primary">
|
||||
@{author}
|
||||
</Link>
|
||||
)}
|
||||
{!authorLink && (
|
||||
<span className="dark:text-action text-primary">@{author}</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-sm font-medium text-gray-500 lg:before:content-['•'] lg:after:content-['•'] lg:before:pr-2 lg:after:pl-2">
|
||||
{date}
|
||||
</div>
|
||||
<div className="text-sm font-medium text-gray-500">
|
||||
{estTime} min read
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
66
src/components/R3Background.tsx
Normal file
66
src/components/R3Background.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import { useRef, useMemo } from "react";
|
||||
import { Canvas, useFrame } from "@react-three/fiber";
|
||||
import { BufferGeometry, Material, MathUtils, Mesh } from "three";
|
||||
import { vertexShader } from "./Shaders/Background/vertex";
|
||||
import { fragmentShader } from "./Shaders/Background/fragment";
|
||||
|
||||
const Fragment = () => {
|
||||
// This reference will give us direct access to the mesh
|
||||
const meshRef = useRef<Mesh<BufferGeometry, Material | Material[]>>(null);
|
||||
|
||||
const uniforms = useMemo(
|
||||
() => ({
|
||||
u_intensity: {
|
||||
value: 1.0,
|
||||
},
|
||||
u_time: {
|
||||
value: 0.0,
|
||||
},
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
useFrame((state) => {
|
||||
if (!meshRef.current) {
|
||||
return;
|
||||
}
|
||||
const { clock } = state;
|
||||
//@ts-ignore
|
||||
meshRef.current.material.uniforms.u_time.value =
|
||||
(0.4 * clock.getElapsedTime()) / 5;
|
||||
//@ts-ignore
|
||||
meshRef.current.material.uniforms.u_intensity.value = MathUtils.lerp(
|
||||
//@ts-ignore
|
||||
meshRef.current.material.uniforms.u_intensity.value,
|
||||
1.0,
|
||||
0.02
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<mesh
|
||||
ref={meshRef}
|
||||
position={[0, 0, 0]}
|
||||
rotation={[-Math.PI / 17, Math.PI / 20, 0]}
|
||||
scale={1.5}
|
||||
>
|
||||
<planeGeometry args={[30, 30, 200, 200]} />
|
||||
<shaderMaterial
|
||||
fragmentShader={fragmentShader}
|
||||
vertexShader={vertexShader}
|
||||
uniforms={uniforms}
|
||||
wireframe={false}
|
||||
/>
|
||||
</mesh>
|
||||
);
|
||||
};
|
||||
|
||||
export const R3Gradient = () => {
|
||||
return (
|
||||
<div className="-z-40 h-screen w-screen fixed bg-black opacity-60 top-0 left-0 blur">
|
||||
<Canvas camera={{ position: [0.0, 0.0, 5.0] }}>
|
||||
<Fragment />
|
||||
</Canvas>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
75
src/components/Shaders/Background/fragment.tsx
Normal file
75
src/components/Shaders/Background/fragment.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
|
||||
export const fragmentShader = `
|
||||
uniform float u_intensity;
|
||||
uniform float u_time;
|
||||
uniform int width;
|
||||
uniform int height;
|
||||
|
||||
varying vec2 vUv;
|
||||
varying float vDisplacement;
|
||||
|
||||
//from Hash Functions for GPU Rendering (Jarzynski et al.)
|
||||
//http://www.jcgt.org/published/0009/03/02/
|
||||
vec3 pcg3d(uvec3 v)
|
||||
{
|
||||
v = v * 1664525u + 1013904223u;
|
||||
v.x += v.y*v.z; v.y += v.z*v.x; v.z += v.x*v.y;
|
||||
v ^= v >> 16u;
|
||||
v.x += v.y*v.z; v.y += v.z*v.x; v.z += v.x*v.y;
|
||||
return vec3(v) * (1.0/float(0xffffffffu));
|
||||
}
|
||||
|
||||
// convert texture coordinates to pixel
|
||||
float t2p(float t, int noOfPixels){
|
||||
return t * float(noOfPixels) - 0.5;
|
||||
}
|
||||
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
vec2 randomGradient(uvec3 p){
|
||||
vec3 _uv = pcg3d(p);
|
||||
float r = sqrt(_uv[0]);
|
||||
float phi = 2.0 * M_PI * _uv[1];
|
||||
return vec2(r* cos(phi), r * sin(phi));
|
||||
}
|
||||
|
||||
|
||||
vec3 gradientNoise(vec2 pos, float gridSize){
|
||||
vec2 gridPos = pos * gridSize;
|
||||
uvec2 i = uvec2(gridPos);
|
||||
vec2 f = fract(gridPos);
|
||||
|
||||
vec2 g11 = randomGradient(uvec3(i.x,i.y,1));
|
||||
vec2 g12 = randomGradient(uvec3(i.x + 1u,i.y,1));
|
||||
vec2 g21 = randomGradient(uvec3(i.x,i.y + 1u,1));
|
||||
vec2 g22 = randomGradient(uvec3(i.x + 1u,i.y +1u,1));
|
||||
|
||||
float d11 = dot(g11, f);
|
||||
float d12 = dot(g12, f - vec2(1.0, 0.0));
|
||||
float d21 = dot(g21, f - vec2(0.0, 1.0));
|
||||
float d22 = dot(g22, f - vec2(1.0, 1.0));
|
||||
|
||||
vec3 f11 = pcg3d(uvec3(i.x,i.y,0)) * (d11 + 1.0);
|
||||
vec3 f12 = pcg3d(uvec3(i.x + 1u,i.y,0))* (d12 + 1.0);
|
||||
vec3 f21 = pcg3d(uvec3(i.x,i.y + 1u,0))* (d21 + 1.0);
|
||||
vec3 f22 = pcg3d(uvec3(i.x + 1u,i.y +1u,0))* (d22 + 1.0);
|
||||
|
||||
f = smoothstep(0.0, 1.0, f);
|
||||
|
||||
vec3 q1 = mix(f11, f12, vec3(f.x));
|
||||
vec3 q2 = mix(f21, f22, vec3(f.x));
|
||||
vec3 p = mix(q1, q2, vec3(f.y));
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
void main() {
|
||||
float distort = 2.0 * vDisplacement + u_intensity;
|
||||
vec2 pos = vec2(u_time) /5.0 + vUv;
|
||||
vec3 texColor = gradientNoise(pos, 4.0);
|
||||
texColor.y = 0.0;
|
||||
gl_FragColor = vec4(texColor, 1.0);
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
108
src/components/Shaders/Background/vertex.tsx
Normal file
108
src/components/Shaders/Background/vertex.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
export const vertexShader = `
|
||||
uniform float u_intensity;
|
||||
uniform float u_time;
|
||||
|
||||
varying vec2 vUv;
|
||||
varying float vDisplacement;
|
||||
|
||||
|
||||
// Classic Perlin 3D Noise
|
||||
// by Stefan Gustavson
|
||||
//
|
||||
vec4 permute(vec4 x) {
|
||||
return mod(((x*34.0)+1.0)*x, 289.0);
|
||||
}
|
||||
|
||||
vec4 taylorInvSqrt(vec4 r) {
|
||||
return 1.79284291400159 - 0.85373472095314 * r;
|
||||
}
|
||||
|
||||
vec3 fade(vec3 t) {
|
||||
return t*t*t*(t*(t*6.0-15.0)+10.0);
|
||||
}
|
||||
|
||||
float cnoise(vec3 P) {
|
||||
vec3 Pi0 = floor(P); // Integer part for indexing
|
||||
vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1
|
||||
Pi0 = mod(Pi0, 289.0);
|
||||
Pi1 = mod(Pi1, 289.0);
|
||||
vec3 Pf0 = fract(P); // Fractional part for interpolation
|
||||
vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
|
||||
vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
|
||||
vec4 iy = vec4(Pi0.yy, Pi1.yy);
|
||||
vec4 iz0 = Pi0.zzzz;
|
||||
vec4 iz1 = Pi1.zzzz;
|
||||
|
||||
vec4 ixy = permute(permute(ix) + iy);
|
||||
vec4 ixy0 = permute(ixy + iz0);
|
||||
vec4 ixy1 = permute(ixy + iz1);
|
||||
|
||||
vec4 gx0 = ixy0 / 7.0;
|
||||
vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5;
|
||||
gx0 = fract(gx0);
|
||||
vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
|
||||
vec4 sz0 = step(gz0, vec4(0.0));
|
||||
gx0 -= sz0 * (step(0.0, gx0) - 0.5);
|
||||
gy0 -= sz0 * (step(0.0, gy0) - 0.5);
|
||||
|
||||
vec4 gx1 = ixy1 / 7.0;
|
||||
vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5;
|
||||
gx1 = fract(gx1);
|
||||
vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
|
||||
vec4 sz1 = step(gz1, vec4(0.0));
|
||||
gx1 -= sz1 * (step(0.0, gx1) - 0.5);
|
||||
gy1 -= sz1 * (step(0.0, gy1) - 0.5);
|
||||
|
||||
vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
|
||||
vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
|
||||
vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
|
||||
vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
|
||||
vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
|
||||
vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
|
||||
vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
|
||||
vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
|
||||
|
||||
vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
|
||||
g000 *= norm0.x;
|
||||
g010 *= norm0.y;
|
||||
g100 *= norm0.z;
|
||||
g110 *= norm0.w;
|
||||
vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
|
||||
g001 *= norm1.x;
|
||||
g011 *= norm1.y;
|
||||
g101 *= norm1.z;
|
||||
g111 *= norm1.w;
|
||||
|
||||
float n000 = dot(g000, Pf0);
|
||||
float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
|
||||
float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
|
||||
float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
|
||||
float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
|
||||
float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
|
||||
float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
|
||||
float n111 = dot(g111, Pf1);
|
||||
|
||||
vec3 fade_xyz = fade(Pf0);
|
||||
vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
|
||||
vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
|
||||
float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
|
||||
return 2.2 * n_xyz;
|
||||
}
|
||||
|
||||
// End of Perlin Noise Code
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
|
||||
vDisplacement = cnoise(position + vec3(2.0 * u_time));
|
||||
|
||||
vec3 newPosition = position + normal * (u_intensity * vDisplacement);
|
||||
|
||||
vec4 modelPosition = modelMatrix * vec4(newPosition, 1.0);
|
||||
vec4 viewPosition = viewMatrix * modelPosition;
|
||||
vec4 projectedPosition = projectionMatrix * viewPosition;
|
||||
|
||||
gl_Position = projectedPosition;
|
||||
}
|
||||
|
||||
`
|
||||
217
src/components/StyledMarkdown.tsx
Normal file
217
src/components/StyledMarkdown.tsx
Normal file
@@ -0,0 +1,217 @@
|
||||
import rangeParser from "parse-numeric-range";
|
||||
import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import tsx from "react-syntax-highlighter/dist/cjs/languages/prism/tsx";
|
||||
import typescript from "react-syntax-highlighter/dist/cjs/languages/prism/typescript";
|
||||
import scss from "react-syntax-highlighter/dist/cjs/languages/prism/scss";
|
||||
import bash from "react-syntax-highlighter/dist/cjs/languages/prism/bash";
|
||||
import markdown from "react-syntax-highlighter/dist/cjs/languages/prism/markdown";
|
||||
import json from "react-syntax-highlighter/dist/cjs/languages/prism/json";
|
||||
import { coldarkDark as defaulttheme } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
||||
import { BiCopy } from "react-icons/bi";
|
||||
import { Components } from "react-markdown";
|
||||
import { generateSlug } from "../utils/general";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
// @ts-ignore
|
||||
import remarkTypograf from "@mavrin/remark-typograf";
|
||||
import smartypants from "remark-smartypants";
|
||||
import rehypeRaw from "rehype-raw";
|
||||
import emoji from "remark-emoji";
|
||||
import oembed from "@agentofuser/remark-oembed";
|
||||
import remarkDirective from "remark-directive";
|
||||
import remarkDirectiveRehype from "remark-directive-rehype";
|
||||
import { YouTubeVideo } from "./Youtube";
|
||||
import { ImageSlide } from "./ImageSlide";
|
||||
// import rehypeSanitize from "rehype-sanitize";
|
||||
|
||||
SyntaxHighlighter.registerLanguage("tsx", tsx);
|
||||
SyntaxHighlighter.registerLanguage("typescript", typescript);
|
||||
SyntaxHighlighter.registerLanguage("scss", scss);
|
||||
SyntaxHighlighter.registerLanguage("bash", bash);
|
||||
SyntaxHighlighter.registerLanguage("markdown", markdown);
|
||||
SyntaxHighlighter.registerLanguage("json", json);
|
||||
|
||||
export const StyledMarkdown = ({ html }: { html: string }) => {
|
||||
const MarkdownComponents: Components = {
|
||||
h1: (props: any) => {
|
||||
const arr = props.children;
|
||||
let heading = "";
|
||||
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i]?.type !== undefined) {
|
||||
for (let j = 0; j < arr[i].props.children.length; j++) {
|
||||
heading += arr[i]?.props?.children[0];
|
||||
}
|
||||
} else heading += arr[i];
|
||||
}
|
||||
|
||||
const slug = generateSlug(heading);
|
||||
return (
|
||||
<h1 id={slug}>
|
||||
<a href={`#${slug}`} {...props}></a>
|
||||
</h1>
|
||||
);
|
||||
},
|
||||
h2: (props: any) => {
|
||||
const arr = props.children;
|
||||
let heading = "";
|
||||
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i]?.type !== undefined) {
|
||||
for (let j = 0; j < arr[i].props.children.length; j++) {
|
||||
heading += arr[i]?.props?.children[0];
|
||||
}
|
||||
} else heading += arr[i];
|
||||
}
|
||||
|
||||
const slug = generateSlug(heading);
|
||||
return (
|
||||
<h2 id={slug}>
|
||||
<a href={`#${slug}`} {...props}></a>
|
||||
</h2>
|
||||
);
|
||||
},
|
||||
h3: (props: any) => {
|
||||
const arr = props.children;
|
||||
let heading = "";
|
||||
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i]?.type !== undefined) {
|
||||
for (let j = 0; j < arr[i].props.children.length; j++) {
|
||||
heading += arr[i]?.props?.children[0];
|
||||
}
|
||||
} else heading += arr[i];
|
||||
}
|
||||
|
||||
const slug = generateSlug(heading);
|
||||
return (
|
||||
<h3 id={slug} className="text-red-500">
|
||||
<a href={`#${slug}`} {...props}></a>
|
||||
</h3>
|
||||
);
|
||||
},
|
||||
h4: (props: any) => {
|
||||
const arr = props.children;
|
||||
let heading = "";
|
||||
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i]?.type !== undefined) {
|
||||
for (let j = 0; j < arr[i].props.children.length; j++) {
|
||||
heading += arr[i]?.props?.children[0];
|
||||
}
|
||||
} else heading += arr[i];
|
||||
}
|
||||
|
||||
const slug = generateSlug(heading);
|
||||
return (
|
||||
<h4 id={slug}>
|
||||
<a href={`#${slug}`} {...props}></a>
|
||||
</h4>
|
||||
);
|
||||
},
|
||||
code({ node, inline, className, ...props }: any) {
|
||||
const match = /language-(\w+)/.exec(className || "");
|
||||
const hasMeta = node?.data?.meta;
|
||||
|
||||
const applyHighlights: object = (applyHighlights: number) => {
|
||||
if (hasMeta) {
|
||||
const RE = /{([\d,-]+)}/;
|
||||
const metadata = node.data.meta?.replace(/\s/g, "");
|
||||
const strlineNumbers = RE?.test(metadata)
|
||||
? // @ts-ignore
|
||||
RE?.exec(metadata)[1]
|
||||
: "0";
|
||||
|
||||
const highlightLines = rangeParser(strlineNumbers);
|
||||
const highlight = highlightLines;
|
||||
const data = highlight.includes(applyHighlights) ? "highlight" : null;
|
||||
return { data };
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
const applyStartingNumber = () => {
|
||||
if (hasMeta) {
|
||||
const metadata = node.data.meta?.replace(/\s/g, "");
|
||||
const RE = /line=(\d+)/;
|
||||
const start = RE?.test(metadata) && RE.exec(metadata);
|
||||
return start ? +start[1] : 1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
const getTitle = () => {
|
||||
if (hasMeta) {
|
||||
const metadata = node.data.meta;
|
||||
const RE = /title=\"(.*)\"/;
|
||||
const title = RE?.test(metadata) && RE.exec(metadata);
|
||||
return title ? title[1] : "Code";
|
||||
}
|
||||
return "Code";
|
||||
};
|
||||
|
||||
return match ? (
|
||||
<div>
|
||||
<div className="w-full flex flex-row items-center gap-4">
|
||||
<button
|
||||
className="w-fit ml-2 important"
|
||||
onClick={() => navigator.clipboard.writeText(props.children)}
|
||||
>
|
||||
<BiCopy />
|
||||
</button>
|
||||
{<div>{getTitle()}</div>}
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
style={defaulttheme}
|
||||
language={match[1]}
|
||||
PreTag="div"
|
||||
className="codeStyle"
|
||||
showLineNumbers={true}
|
||||
wrapLines={hasMeta ? true : false}
|
||||
useInlineStyles={true}
|
||||
lineProps={applyHighlights}
|
||||
startingLineNumber={applyStartingNumber()}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<code className={className} {...props} />
|
||||
);
|
||||
},
|
||||
//custom directives
|
||||
//@ts-ignore
|
||||
yt: YouTubeVideo,
|
||||
"img-slide": ImageSlide,
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="
|
||||
prose
|
||||
prose-code:font-mono prose-code:text-gray-100 prose-code:bg-black/20 prose-code:p-1 prose-code:m-1 prose-code:rounded-md
|
||||
prose-headings:text-gray-800 dark:prose-headings:text-gray-100 prose-p:text-gray-600 dark:prose-p:text-gray-300
|
||||
prose-img:w-full prose-img:h-auto prose-img:object-cover
|
||||
prose-li:text-gray-600 dark:prose-li:text-gray-200 prose-td:text-gray-600 dark:prose-td:text-gray-300
|
||||
prose-a:text-primary prose-strong:text-gray-900 dark:prose-strong:text-gray-50
|
||||
dark:prose-hr:bg-gray-200 prose-hr:bg-gray-400
|
||||
"
|
||||
>
|
||||
<ReactMarkdown
|
||||
components={MarkdownComponents}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
remarkPlugins={[
|
||||
remarkDirective,
|
||||
remarkDirectiveRehype,
|
||||
oembed,
|
||||
remarkGfm,
|
||||
smartypants,
|
||||
emoji,
|
||||
remarkTypograf,
|
||||
]}
|
||||
children={html}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
5
src/components/Youtube.tsx
Normal file
5
src/components/Youtube.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
export const YouTubeVideo = ({ id, children }: any) => (
|
||||
<iframe src={"https://www.youtube.com/embed/" + id} width="640" height="360">
|
||||
{children}
|
||||
</iframe>
|
||||
);
|
||||
21
src/content/blogs/first-blog.md
Normal file
21
src/content/blogs/first-blog.md
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
title: "My First Blog"
|
||||
date: 27.12.2022
|
||||
author: kookroach
|
||||
authorLink: https://git.peroxy.dev/kookroach
|
||||
description: Short description
|
||||
---
|
||||
|
||||
# First blog
|
||||
|
||||
Insane
|
||||
|
||||
test change
|
||||
|
||||
# Header 1 test
|
||||
|
||||
## Header 2 test
|
||||
|
||||
### Header 3 test
|
||||
|
||||
#### Header 4 test
|
||||
10
src/content/blogs/second-blog.md
Normal file
10
src/content/blogs/second-blog.md
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: "My Second Blog"
|
||||
date: 27.12.2022
|
||||
author: kookroach
|
||||
description: Short description
|
||||
---
|
||||
|
||||
# Secoond blog
|
||||
|
||||
Insane
|
||||
10
src/content/blogs/third-blog.md
Normal file
10
src/content/blogs/third-blog.md
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: "My Third Blog (TEST)"
|
||||
date: 27.12.2022
|
||||
author: kookroach
|
||||
description: Short description
|
||||
---
|
||||
|
||||
# Third blog
|
||||
|
||||
Insane
|
||||
27
src/content/projects/ConceptArt and Game Design.md
Normal file
27
src/content/projects/ConceptArt and Game Design.md
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: "Concept Art and Game Design"
|
||||
date: 17.01.2022
|
||||
author: kookroach
|
||||
authorLink: https://git.peroxy.dev/kookroach
|
||||
description: "Creative basics for professional game development. A Workshop by Dr.-Ing. Wolfgang Höhl."
|
||||
thumbnail: https://docs.peroxy.dev/uploads/f001b7e2-4e85-4335-8302-40cbda9c3490.png
|
||||
---
|
||||
|
||||
From concept drawing to modeling to game engine. A neo-noir Game Concept inspired from Games like Max Payne and Luke Cage Noir comics.
|
||||
|
||||
### Sketches
|
||||
|
||||
#### Character Concept
|
||||
::img-slide[https://wiki.tum.de/download/attachments/1030785260/Character%20Design%201%20rework.png?version=1&modificationDate=1642430318357&api=v2 https://wiki.tum.de/download/attachments/1030785260/Character%20Design%202.png?version=1&modificationDate=1642430320190&api=v2]{}
|
||||
|
||||
#### Environment Concept
|
||||
::img-slide[https://wiki.tum.de/download/attachments/1030785260/Env-Design%20Skizze.png?version=1&modificationDate=1642430334223&api=v2 https://wiki.tum.de/download/attachments/1030785260/Env-Design%20Final.png?version=1&modificationDate=1642430333137&api=v2]{}
|
||||
|
||||
### Models
|
||||
::img-slide[https://docs.peroxy.dev/uploads/251bda2e-05e2-4fe4-828c-e3a747e5c9e8.png https://docs.peroxy.dev/uploads/7a9e94b3-cb00-43bb-85ad-7fdf25e047fd.png https://docs.peroxy.dev/uploads/b8c07d01-11d6-4771-af21-4f208e443216.png https://docs.peroxy.dev/uploads/99cec6be-6239-46f3-a350-9ef9bfb60932.png https://docs.peroxy.dev/uploads/a3f69410-3e32-41a0-9f9a-c87f35d4cbfa.png https://docs.peroxy.dev/uploads/8b4cf730-48e4-422c-8cf5-590efd3e79b7.png]{}
|
||||
|
||||
### Final Render
|
||||

|
||||
|
||||
### Final Result in Unity Engine
|
||||

|
||||
8
src/content/projects/MadTrax.md
Normal file
8
src/content/projects/MadTrax.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: "MadTrax"
|
||||
date: 11.12.2022
|
||||
author: kookroach
|
||||
authorLink: https://git.peroxy.dev/kookroach
|
||||
description: "A game jam game made entirely from scratch in just 42 hours using the Godot 4 game engine."
|
||||
thumbnail: https://docs.peroxy.dev/uploads/b968afc4-059c-41fd-b948-67c9b32c2cc2.png
|
||||
---
|
||||
8
src/content/projects/RE-Chess.md
Normal file
8
src/content/projects/RE-Chess.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: "RE:Chess"
|
||||
date: 19.06.2022
|
||||
author: kookroach
|
||||
authorLink: https://git.peroxy.dev/kookroach
|
||||
description: "A game jam game made in just 42 hours using the Unity engine. Redefine the rules of chess to solve the re:chess puzzles in this unique chess game."
|
||||
thumbnail: https://docs.peroxy.dev/uploads/4806dc24-3fe7-4349-8121-f230e7a02359.png
|
||||
---
|
||||
45
src/content/projects/SpaceMadness.md
Normal file
45
src/content/projects/SpaceMadness.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: "Space Madness"
|
||||
date: 06.06.2020
|
||||
author: kookroach
|
||||
authorLink: https://git.peroxy.dev/kookroach
|
||||
description: "Navigate through an endless battlefield and destroy turrets while avoiding being killed. See if you can beat the current high score of 7471."
|
||||
thumbnail: https://docs.peroxy.dev/uploads/cd22a289-ebe7-4d82-98e3-b639d1df2ea6.png
|
||||
---
|
||||
|
||||
```
|
||||
_____ ___ ___ _
|
||||
/ ___| | \/ | | |
|
||||
\ `--. _ __ __ _ ___ ___ | . . | __ _ __| | _ __ ___ ___ ___
|
||||
`--. \| '_ \ / _` | / __| / _ \ | |\/| | / _` | / _` || '_ \ / _ \/ __|/ __|
|
||||
/\__/ /| |_) || (_| || (__ | __/ | | | || (_| || (_| || | | || __/\__ \\__ \
|
||||
\____/ | .__/ \__,_| \___| \___| \_| |_/ \__,_| \__,_||_| |_| \___||___/|___/
|
||||
| |
|
||||
|_|
|
||||
```
|
||||
|
||||
> :warning: **Assets and Sounds of this game are not self made**
|
||||
|
||||
### About this Project
|
||||
|
||||
My introduction to game development began with the completion of my first project, "Space Madness", which I created using the Unity Engine. This experience sparked my passion for the field and led me to pursue a degree in Informatics: Games Engineering at the Technical University of Munich.
|
||||
|
||||
A Challenging mobile endless space shooter game.
|
||||
|
||||
### Technical Features
|
||||
|
||||
- Procedual Terrain generation
|
||||
- Chunk rendering with LOD
|
||||
- Endless Level generation
|
||||
- Basic Boss Fight Logic
|
||||
- Touch input support
|
||||
|
||||
|
||||
### Gameplay Video
|
||||
::yt[Video of a cat in a box]{#1qquNEJrA10}
|
||||
|
||||
## Links
|
||||
|
||||
Repository [here](https://gitlab.com/moungoslukas/stonks)
|
||||
|
||||
Download Windows Build [here](https://cloud.peroxy.dev/s/c5PzG37xtqyD8gZ) (playable by using the Mouse and Left Mouse Button)
|
||||
253
src/content/projects/markdown-test.md
Normal file
253
src/content/projects/markdown-test.md
Normal file
@@ -0,0 +1,253 @@
|
||||
---
|
||||
title: "Markdown test"
|
||||
date: 29.12.2022
|
||||
author: kookroach
|
||||
authorLink: https://git.peroxy.dev/kookroach
|
||||
description: Short description
|
||||
---
|
||||
|
||||
# USE ONLY H3 TAGS FOR HEADER HYPERLINKS
|
||||
### Test heading
|
||||
|
||||
|
||||
|
||||
## Horizontal Rules
|
||||
|
||||
___
|
||||
|
||||
---
|
||||
|
||||
***
|
||||
|
||||
|
||||
## Typographic replacements
|
||||
|
||||
Enable typographer option to see result.
|
||||
|
||||
(c) (C) (r) (R) (tm) (TM) (p) (P) +-
|
||||
|
||||
|
||||
test.. test... test.... test..... test?..... test!....
|
||||
|
||||
|
||||
!!!!!! ???? ,, -- ---
|
||||
|
||||
"Smartypants, double quotes" and 'single quotes'
|
||||
|
||||
|
||||
## Emphasis
|
||||
|
||||
**This is bold text**
|
||||
|
||||
__This is bold text__
|
||||
|
||||
*This is italic text*
|
||||
|
||||
_This is italic text_
|
||||
|
||||
~~Strikethrough~~
|
||||
|
||||
|
||||
## Blockquotes
|
||||
|
||||
|
||||
> Blockquotes can also be nested...
|
||||
>> ...by using additional greater-than signs right next to each other...
|
||||
> > > ...or with spaces between arrows.
|
||||
|
||||
|
||||
## Lists
|
||||
|
||||
Unordered
|
||||
|
||||
+ Create a list by starting a line with `+`, `-`, or `*`
|
||||
+ Sub-lists are made by indenting 2 spaces:
|
||||
- Marker character change forces new list start:
|
||||
* Ac tristique libero volutpat at
|
||||
+ Facilisis in pretium nisl aliquet
|
||||
- Nulla volutpat aliquam velit
|
||||
+ Very easy!
|
||||
|
||||
Ordered
|
||||
|
||||
1. Lorem ipsum dolor sit amet
|
||||
2. Consectetur adipiscing elit
|
||||
3. Integer molestie lorem at massa
|
||||
|
||||
|
||||
1. You can use sequential numbers...
|
||||
1. ...or keep all the numbers as `1.`
|
||||
|
||||
Start numbering with offset:
|
||||
|
||||
57. foo
|
||||
1. bar
|
||||
|
||||
|
||||
https://www.youtube.com/watch?v=lrBRV3WK2x4
|
||||
|
||||
|
||||
## Code
|
||||
|
||||
Inline `code`
|
||||
|
||||
Indented code
|
||||
|
||||
// Some comments
|
||||
line 1 of code
|
||||
line 2 of code
|
||||
line 3 of code
|
||||
|
||||
|
||||
Block code "fences"
|
||||
|
||||
```
|
||||
Sample text here...
|
||||
```
|
||||
|
||||
Syntax highlighting
|
||||
|
||||
```tsx
|
||||
var foo = function (bar) {
|
||||
return bar++;
|
||||
};
|
||||
|
||||
console.log(foo(5));
|
||||
```
|
||||
|
||||
```tsx line=200, title="pidhsome je"
|
||||
var foo = function (bar) {
|
||||
return bar++;
|
||||
};
|
||||
|
||||
console.log(foo(5));
|
||||
```
|
||||
|
||||
## Tables
|
||||
|
||||
| Option | Description |
|
||||
| ------ | ----------- |
|
||||
| data | path to data files to supply the data that will be passed into templates. |
|
||||
| engine | engine to be used for processing templates. Handlebars is the default. |
|
||||
| ext | extension to be used for dest files. |
|
||||
|
||||
Right aligned columns
|
||||
|
||||
| Option | Description |
|
||||
| ------:| -----------:|
|
||||
| data | path to data files to supply the data that will be passed into templates. |
|
||||
| engine | engine to be used for processing templates. Handlebars is the default. |
|
||||
| ext | extension to be used for dest files. |
|
||||
|
||||
|
||||
## Links
|
||||
|
||||
[link text](http://dev.nodeca.com)
|
||||
|
||||
[link with title](http://nodeca.github.io/pica/demo/ "title text!")
|
||||
|
||||
Autoconverted link https://github.com/nodeca/pica (enable linkify to see)
|
||||
|
||||
|
||||
## Images
|
||||
|
||||

|
||||

|
||||
|
||||
Like links, Images also have a footnote style syntax
|
||||
|
||||
![Alt text][id]
|
||||
|
||||
With a reference later in the document defining the URL location:
|
||||
|
||||
[id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat"
|
||||
|
||||
|
||||
## Plugins
|
||||
|
||||
The killer feature of `markdown-it` is very effective support of
|
||||
[syntax plugins](https://www.npmjs.org/browse/keyword/markdown-it-plugin).
|
||||
|
||||
|
||||
### [Emojies](https://github.com/markdown-it/markdown-it-emoji)
|
||||
|
||||
> Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum:
|
||||
>
|
||||
> Shortcuts (emoticons): :-) :-( 8-) ;)
|
||||
|
||||
see [how to change output](https://github.com/markdown-it/markdown-it-emoji#change-output) with twemoji.
|
||||
|
||||
|
||||
### [Subscript](https://github.com/markdown-it/markdown-it-sub) / [Superscript](https://github.com/markdown-it/markdown-it-sup)
|
||||
|
||||
- 19^th^
|
||||
- H~2~O
|
||||
|
||||
|
||||
### [\<ins>](https://github.com/markdown-it/markdown-it-ins)
|
||||
|
||||
++Inserted text++
|
||||
|
||||
|
||||
### [\<mark>](https://github.com/markdown-it/markdown-it-mark)
|
||||
|
||||
==Marked text==
|
||||
|
||||
|
||||
### [Footnotes](https://github.com/markdown-it/markdown-it-footnote)
|
||||
|
||||
Footnote 1 link[^first].
|
||||
|
||||
Footnote 2 link[^second].
|
||||
|
||||
Inline footnote^[Text of inline footnote] definition.
|
||||
|
||||
Duplicated footnote reference[^second].
|
||||
|
||||
[^first]: Footnote **can have markup**
|
||||
|
||||
and multiple paragraphs.
|
||||
|
||||
[^second]: Footnote text.
|
||||
|
||||
|
||||
### [Definition lists](https://github.com/markdown-it/markdown-it-deflist)
|
||||
|
||||
Term 1
|
||||
|
||||
: Definition 1
|
||||
with lazy continuation.
|
||||
|
||||
Term 2 with *inline markup*
|
||||
|
||||
: Definition 2
|
||||
|
||||
{ some code, part of Definition 2 }
|
||||
|
||||
Third paragraph of definition 2.
|
||||
|
||||
_Compact style:_
|
||||
|
||||
Term 1
|
||||
~ Definition 1
|
||||
|
||||
Term 2
|
||||
~ Definition 2a
|
||||
~ Definition 2b
|
||||
|
||||
|
||||
### [Abbreviations](https://github.com/markdown-it/markdown-it-abbr)
|
||||
|
||||
This is HTML abbreviation example.
|
||||
|
||||
It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on.
|
||||
|
||||
*[HTML]: Hyper Text Markup Language
|
||||
|
||||
### [Custom containers](https://github.com/markdown-it/markdown-it-container)
|
||||
|
||||
::: warning
|
||||
*here be dragons*
|
||||
:::
|
||||
|
||||
[go to 3rd heading](#test-heading)
|
||||
120
src/index.css
Normal file
120
src/index.css
Normal file
@@ -0,0 +1,120 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html {
|
||||
scroll-padding-top: 5rem;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 800px !important;
|
||||
margin: auto !important;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@apply dark:bg-gradient-dark dark:text-primary-text bg-slate-100 text-gradient-dark w-full mx-auto;
|
||||
}
|
||||
|
||||
pre {
|
||||
@apply dark:bg-black/30 bg-slate-200 text-slate-800 dark:text-slate-200 !important;
|
||||
}
|
||||
|
||||
pre > code {
|
||||
@apply bg-transparent text-gray-700 dark:text-slate-300 !important;
|
||||
}
|
||||
|
||||
pre > div > div > button > svg:hover {
|
||||
@apply hover:text-action transition-all ease-in-out duration-100 !important;
|
||||
}
|
||||
|
||||
h1 > a,
|
||||
h2 > a,
|
||||
h3 > a,
|
||||
h4 > a {
|
||||
@apply cursor-pointer relative text-gray-800 dark:text-gray-100 decoration-transparent font-bold !important;
|
||||
}
|
||||
|
||||
h1 > a:hover::before,
|
||||
h2 > a:hover::before,
|
||||
h3 > a:hover::before,
|
||||
h4 > a:hover::before {
|
||||
@apply content-['#'] absolute -left-8 text-gray-300 dark:text-gray-400;
|
||||
}
|
||||
|
||||
h2 > a:hover::before {
|
||||
@apply -left-6;
|
||||
}
|
||||
|
||||
h3 > a:hover::before {
|
||||
@apply -left-5;
|
||||
}
|
||||
|
||||
h4 > a:hover::before {
|
||||
@apply -left-4;
|
||||
}
|
||||
|
||||
div > code,
|
||||
pre > code {
|
||||
@apply p-0 m-0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.codeStyle {
|
||||
@apply bg-transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
.prose {
|
||||
margin: 0;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 1000px;
|
||||
height: 600px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.right-arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0%;
|
||||
font-size: 2rem;
|
||||
rotate: 180deg;
|
||||
color: #bdbdbd;
|
||||
z-index: 10;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.left-arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: -5%;
|
||||
font-size: 2rem;
|
||||
color: #bdbdbd;
|
||||
z-index: 10;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.slide {
|
||||
opacity: 0;
|
||||
transition-duration: 0.5s ease;
|
||||
}
|
||||
|
||||
.slide.active {
|
||||
opacity: 1;
|
||||
transition-duration: 0.5s;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
21
src/index.tsx
Normal file
21
src/index.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: "/",
|
||||
element: <App />,
|
||||
},
|
||||
]);
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById("root") as HTMLElement
|
||||
);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<RouterProvider router={router} />
|
||||
</React.StrictMode>
|
||||
);
|
||||
13
src/layouts/MainLayout.tsx
Normal file
13
src/layouts/MainLayout.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { R3Gradient } from '../components/R3Background';
|
||||
import { Navbar } from "../components/Navbar";
|
||||
|
||||
export const MainLayout = ({ children }: PropsWithChildren) => {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<Navbar />
|
||||
<R3Gradient />
|
||||
<main className="xl:p-5 p-4 px-5 xl:mx-0">{children}</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
52
src/pages/blog/index.tsx
Normal file
52
src/pages/blog/index.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { MainLayout } from "../../layouts/MainLayout";
|
||||
import { Post, getAllBlogsFrontMatter } from "../../utils/markdown";
|
||||
import { formatDate } from "../../utils/general";
|
||||
import { BasicArticleProps } from "../../components/PostHeader";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
const BlogCard = ({
|
||||
blog,
|
||||
slug,
|
||||
}: {
|
||||
blog: BasicArticleProps;
|
||||
slug: string;
|
||||
}) => {
|
||||
return (
|
||||
<div className="p-4 rounded-md border border-gray-200 shadow-md shadow-gray-200 dark:shadow-gray-900 dark:border-gray-700 bg-black bg-opacity-60">
|
||||
<div className="text-sm font-medium text-gray-500">
|
||||
{formatDate(blog.date)}
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold">{blog.title}</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-sm">
|
||||
{blog.description}
|
||||
</p>
|
||||
<Link to={`blog/${slug}`}>
|
||||
<button className="bg-action px-2 py-1 rounded-md mt-4 hover:bg-action/60 transition-all ease-in duration-100 font-bold text-white">
|
||||
Read more
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Blog = ({ posts }: { posts: Post[] }) => {
|
||||
return (
|
||||
<MainLayout>
|
||||
<h1 className="text-3xl font-bold text-gray-800 dark:text-gray-200">
|
||||
Blog
|
||||
</h1>
|
||||
<div className="w-full h-0.5 bg-violet-200 dark:bg-white/20 my-4 rounded-full" />
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{posts.map((post, index) => (
|
||||
<BlogCard
|
||||
key={index}
|
||||
blog={post.frontMatter as BasicArticleProps}
|
||||
slug={post.slug}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</MainLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Blog;
|
||||
1
src/react-app-env.d.ts
vendored
Normal file
1
src/react-app-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
||||
13
src/types/types.d.ts
vendored
Normal file
13
src/types/types.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
export interface FrontMatter {
|
||||
[prop: string]: string;
|
||||
}
|
||||
|
||||
export interface MarkdownDocument {
|
||||
frontMatter: FrontMatter;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface MarkdownRenderingResult {
|
||||
frontMatter: FrontMatter;
|
||||
html: string;
|
||||
}
|
||||
35
src/utils/general.ts
Normal file
35
src/utils/general.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
export const formatDate = (date: string) => {
|
||||
if (!date) return null;
|
||||
const [day, month, year] = date.split(".");
|
||||
const d = new Date(`${year}-${month}-${day}`);
|
||||
return d.toLocaleDateString("en-US", {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
export const generateSlug = (str: string) => {
|
||||
str = str?.replace(/^\s+|\s+$/g, "");
|
||||
str = str?.toLowerCase();
|
||||
const from = "àáãäâèéëêìíïîòóöôùúüûñç·/_,:;";
|
||||
const to = "aaaaaeeeeiiiioooouuuunc------";
|
||||
|
||||
for (let i = 0, l = from.length; i < l; i++) {
|
||||
str = str.replace(new RegExp(from.charAt(i), "g"), to.charAt(i));
|
||||
}
|
||||
|
||||
str = str
|
||||
?.replace(/[^a-z0-9 -]/g, "")
|
||||
.replace(/\s+/g, "-")
|
||||
.replace(/-+/g, "-");
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
export const readingTime = (content: string) => {
|
||||
const wpm = 225;
|
||||
const words = content.trim().split(/\s+/).length;
|
||||
const time = Math.ceil(words / wpm);
|
||||
return time;
|
||||
};
|
||||
104
src/utils/markdown.ts
Normal file
104
src/utils/markdown.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import matter from "gray-matter";
|
||||
import { join } from "path";
|
||||
import fs from "fs";
|
||||
import { FrontMatter, MarkdownDocument } from "../types/types";
|
||||
import { remark } from "remark";
|
||||
import html from "remark-html";
|
||||
|
||||
export const BLOGS_PATH = join(process.cwd(), "content/blogs");
|
||||
export const PROJECTS_PATH = join(process.cwd(), "content/projects");
|
||||
export interface Post {
|
||||
frontMatter: FrontMatter;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
const getDate = (date: string) => {
|
||||
if (typeof date === "string") {
|
||||
const d = date.split(".").map((v) => +v);
|
||||
return new Date(d[2], d[1] - 1, d[0]);
|
||||
}
|
||||
return new Date();
|
||||
};
|
||||
|
||||
export const getBlogContentBySlug = (slug: string): MarkdownDocument => {
|
||||
const filepath = join(BLOGS_PATH, `${slug}.md` || `${slug}.mdx`);
|
||||
const filecontents = fs.readFileSync(filepath);
|
||||
|
||||
const { data, content } = matter(filecontents);
|
||||
|
||||
return {
|
||||
frontMatter: data,
|
||||
content: content,
|
||||
};
|
||||
};
|
||||
export const getProjectContentBySlug = (slug: string): MarkdownDocument => {
|
||||
const filepath = join(PROJECTS_PATH, `${slug}.md` || `${slug}.mdx`);
|
||||
const filecontents = fs.readFileSync(filepath);
|
||||
|
||||
const { data, content } = matter(filecontents);
|
||||
|
||||
return {
|
||||
frontMatter: data,
|
||||
content: content,
|
||||
};
|
||||
};
|
||||
|
||||
export const getAllBlogsFrontMatter = (): Post[] => {
|
||||
const files = fs.readdirSync(BLOGS_PATH);
|
||||
|
||||
const blogs = files.reduce((allPosts: Post[], postSlug: string) => {
|
||||
const slug = postSlug.replace(".md", "");
|
||||
const post = getBlogContentBySlug(slug);
|
||||
|
||||
return [{ frontMatter: post.frontMatter, slug }, ...allPosts];
|
||||
}, []);
|
||||
|
||||
return blogs.sort(
|
||||
//@ts-ignore
|
||||
(a, b) => getDate(b.frontMatter.date) - getDate(a.frontMatter.date)
|
||||
);
|
||||
};
|
||||
export const getAllProjectsFrontMatter = (): Post[] => {
|
||||
const files = fs.readdirSync(PROJECTS_PATH);
|
||||
|
||||
const projects = files.reduce((allPosts: Post[], postSlug: string) => {
|
||||
const slug = postSlug.replace(".md", "");
|
||||
const post = getProjectContentBySlug(slug);
|
||||
|
||||
return [{ frontMatter: post.frontMatter, slug }, ...allPosts];
|
||||
}, []);
|
||||
|
||||
return projects.sort(
|
||||
//@ts-ignore
|
||||
(a, b) => getDate(b.frontMatter.date) - getDate(a.frontMatter.date)
|
||||
);
|
||||
};
|
||||
|
||||
export const getHeadings = (markdown: string): any => {
|
||||
const headingOne = markdown.match(/^(#{1})\s(.*)/gm);
|
||||
const headingTwo = markdown.match(/^(#{2})\s(.*)/gm);
|
||||
const headingThree = markdown.match(/^(#{3})\s(.*)/gm);
|
||||
const headingFour = markdown.match(/^(#{4})\s(.*)/gm);
|
||||
|
||||
const headings = markdown.match(/^(#{1,4})\s(.*)/gm);
|
||||
var tree: any[] = [];
|
||||
var prev = "";
|
||||
headings?.forEach((heading) => {
|
||||
if (headingOne?.includes(heading)) {
|
||||
tree.push(heading);
|
||||
}
|
||||
});
|
||||
|
||||
return tree;
|
||||
};
|
||||
|
||||
export async function markdownToHtml(markdown: any) {
|
||||
const result = await remark().use(html).process(markdown);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
export const renderMarkdown = async (
|
||||
markdownContent: string
|
||||
): Promise<string> => {
|
||||
return await markdownToHtml(markdownContent || "");
|
||||
};
|
||||
18
tailwind.config.js
Normal file
18
tailwind.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
"gradient-dark": "#161926",
|
||||
"gradient-light": "#1b2034",
|
||||
primary: "#1E96FC",
|
||||
"primary-dark": "#1B264F",
|
||||
action: "#FF3366",
|
||||
secondary: "#FFE600",
|
||||
"primary-text": "#D9D9D9",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
26
tsconfig.json
Normal file
26
tsconfig.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user