Compare commits
86 Commits
e46339c7a7
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| dcfad5fcd6 | |||
| 9b8b59d4e6 | |||
|
|
dc7e82b143 | ||
|
|
703b611dfd | ||
|
|
dd4f848071 | ||
|
|
46097b8390 | ||
| e78bc12c49 | |||
| aa3bb66d5e | |||
| c9f5dfcba7 | |||
| 4902006130 | |||
| 71f679c3ca | |||
| f7c6d3ae16 | |||
| f884ac1d1d | |||
| 9b58851c42 | |||
| 6142b6c9cc | |||
| 3e5bb19970 | |||
| 22755e66e5 | |||
| ff8536f8c6 | |||
| ad99601407 | |||
| 2d88365a69 | |||
| 3140f9b666 | |||
| 9d98b3ac69 | |||
| 9a48d20a16 | |||
|
|
50358c43b9 | ||
| cfdecde769 | |||
|
|
839defdd06 | ||
|
|
b4e6631f07 | ||
|
|
b0a87b5592 | ||
| 957ff33555 | |||
| 8ce6560482 | |||
| c5018b3bb9 | |||
| f76edaec72 | |||
| 4d9092ae3d | |||
| d397503c66 | |||
| e545a596ec | |||
| 446c4a4241 | |||
| ba2edb6c38 | |||
| 437a79877e | |||
| 94ca95821d | |||
| ceee45409d | |||
| 7eed2ee59a | |||
| 9a7e9048f2 | |||
| de224373ed | |||
| a1a0e15f6d | |||
| b200528007 | |||
| 083fb3fd48 | |||
| 75e24a6a08 | |||
| 5a9e65396c | |||
| 406183536e | |||
| 4be09b8100 | |||
| d71f599f0d | |||
| be1b6268e7 | |||
| d229d639fd | |||
| 74aa0ab925 | |||
| 281e0f4170 | |||
| e37f2f0212 | |||
| 144020b6d1 | |||
| 1c165a68cb | |||
| bbe50c4790 | |||
| 4c4e762a4a | |||
| d19966baba | |||
| c3f3a4914a | |||
| c714281330 | |||
| bf0e8e2c02 | |||
| a83e68ea73 | |||
| 49fc655de9 | |||
| 2a6198aa1f | |||
| e1e9a41d59 | |||
| 2abb8a1bfc | |||
| da4c565cb3 | |||
| 31c7e8ea27 | |||
| 25cd9c75ab | |||
| 2913688e63 | |||
| 77ae0a746b | |||
| 9ee61716d4 | |||
| 2ff40d856a | |||
| f1f84aec3d | |||
| a609afcefc | |||
| c77371b0a4 | |||
| 5f2853083f | |||
| 2cfcc3c892 | |||
| 611fe5f12b | |||
| 8f6b36eca1 | |||
| a833d2666a | |||
| 0e18750370 | |||
| d97e329f4c |
48
.drone.yml
48
.drone.yml
@@ -1,15 +1,41 @@
|
|||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: default
|
name: prod-pipe
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: now
|
- name: prod
|
||||||
image: one000mph/drone-now # Use one000mph's version as lucaperret's is outdated
|
image: docker/compose
|
||||||
environment:
|
volumes:
|
||||||
NOW_TOKEN:
|
- name: docker_socket
|
||||||
from_secret: now_token # Refers to a secret in your drone repo titled "NOW_TOKEN"
|
path: /var/run/docker.sock
|
||||||
settings:
|
commands:
|
||||||
secret: [now_token] # Refers to the above environment variable
|
- docker-compose -f docker-compose.yml up --build --force-recreate -d
|
||||||
deploy_name: peroxy-dev # The name of your vercel project
|
trigger:
|
||||||
prod: true # Leave this if you want to deploy to production, remove to disable production
|
branch:
|
||||||
# directory: public # Only if you've pre-rendered the page. Normally you let vercel handle this
|
- main
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: docker_socket
|
||||||
|
host:
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: dev-pipe
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: dev
|
||||||
|
image: docker/compose
|
||||||
|
volumes:
|
||||||
|
- name: docker_socket
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
commands:
|
||||||
|
- docker-compose -f docker-compose.dev.yml up --build --force-recreate -d
|
||||||
|
trigger:
|
||||||
|
branch:
|
||||||
|
- dev
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: docker_socket
|
||||||
|
host:
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
/coverage
|
||||||
|
/content/projects/markdown-test.md
|
||||||
|
|
||||||
# next.js
|
# next.js
|
||||||
/.next/
|
/.next/
|
||||||
|
|||||||
2
.vscode/settings.json
vendored
Normal file
2
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
{
|
||||||
|
}
|
||||||
31
Dockerfile
Normal file
31
Dockerfile
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#Creates a layer from node:alpine image.
|
||||||
|
FROM node:alpine
|
||||||
|
|
||||||
|
#Creates directories
|
||||||
|
RUN mkdir -p /usr/src/app
|
||||||
|
|
||||||
|
#Sets an environment variable
|
||||||
|
ENV PORT 3000
|
||||||
|
|
||||||
|
#Sets the working directory for any RUN, CMD, ENTRYPOINT, COPY, and ADD commands
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
#Copy new files or directories into the filesystem of the container
|
||||||
|
COPY package.json /usr/src/app
|
||||||
|
#COPY package-lock.json /usr/src/app
|
||||||
|
|
||||||
|
#Execute commands in a new layer on top of the current image and commit the results
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
##Copy new files or directories into the filesystem of the container
|
||||||
|
COPY . /usr/src/app
|
||||||
|
|
||||||
|
#Execute commands in a new layer on top of the current image and commit the results
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
#Informs container runtime that the container listens on the specified network ports at runtime
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
#Allows you to configure a container that will run as an executable
|
||||||
|
ENTRYPOINT ["npm", "run"]
|
||||||
|
|
||||||
41
components/ImageSlide.tsx
Normal file
41
components/ImageSlide.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import React, { 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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -4,7 +4,7 @@ export const Logo = () => {
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
id="svg166"
|
id="svg166"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
className="fill-action group-hover:fill-secondary transition-all ease-in duration-100 flex-no-shrink xl:w-12 xl:h-12 w-8 h-8"
|
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"
|
viewBox="0 0 100 100"
|
||||||
>
|
>
|
||||||
<g id="167217677994600400">
|
<g id="167217677994600400">
|
||||||
|
|||||||
@@ -5,22 +5,22 @@ import { motion, AnimatePresence } from "framer-motion";
|
|||||||
import { Logo } from "./Logo";
|
import { Logo } from "./Logo";
|
||||||
|
|
||||||
const links = [
|
const links = [
|
||||||
{ name: "blog", href: "/blog" },
|
{ name: "Blog", href: "/blog" },
|
||||||
{ name: "projects", href: "/projects" },
|
{ name: "Projects", href: "/projects" },
|
||||||
{ name: "github", href: "https://git.peroxy.dev/kookroach" },
|
{ name: "Gitea", href: "https://git.peroxy.dev/kookroach" },
|
||||||
];
|
];
|
||||||
export const Navbar = () => {
|
export const Navbar = () => {
|
||||||
const [showMenu, setShowMenu] = useState(false);
|
const [showMenu, setShowMenu] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="lg:p-4 p-2 lg:py-10 mx-6 flex flex-row justify-between">
|
<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
|
<Link
|
||||||
href="/"
|
href="/"
|
||||||
id="logo"
|
id="logo"
|
||||||
className="flex flex-row gap-2 items-center group"
|
className="flex flex-row gap-2 items-center group"
|
||||||
>
|
>
|
||||||
<Logo />
|
<Logo />
|
||||||
<div className="font-bold xl:text-4xl text-xl font-mono transition-all ease-in duration-100 text-action group-hover:text-secondary">
|
<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
|
peroxy
|
||||||
<span className="xl:text-sm text-xs">.dev</span>
|
<span className="xl:text-sm text-xs">.dev</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -28,7 +28,7 @@ export const Navbar = () => {
|
|||||||
<div className="hidden xl:flex flex-row gap-10 items-center font-medium font-mono">
|
<div className="hidden xl:flex flex-row gap-10 items-center font-medium font-mono">
|
||||||
{links.map(({ name, href }) => (
|
{links.map(({ name, href }) => (
|
||||||
<Link href={href} key={name}>
|
<Link href={href} key={name}>
|
||||||
<button className="px-10 py-1 rounded-sm bg-primary hover:bg-primary/50 transition-all duration-150 ease-out">
|
<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}
|
{name}
|
||||||
</button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -49,7 +49,7 @@ export const Navbar = () => {
|
|||||||
exit={{ opacity: 0 }}
|
exit={{ opacity: 0 }}
|
||||||
/>
|
/>
|
||||||
<motion.div
|
<motion.div
|
||||||
className={`z-40 absolute w-1/2 bg-blue-900
|
className={`z-40 absolute w-1/2 bg-blue-900 text-primary-text
|
||||||
right-0 top-0 px-10 py-6 h-full`}
|
right-0 top-0 px-10 py-6 h-full`}
|
||||||
initial={{ x: "100%" }}
|
initial={{ x: "100%" }}
|
||||||
animate={{ x: 0 }}
|
animate={{ x: 0 }}
|
||||||
|
|||||||
51
components/PostHeader.tsx
Normal file
51
components/PostHeader.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
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 href={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
components/R3Background.tsx
Normal file
66
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
components/Shaders/Background/fragment.tsx
Normal file
75
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
components/Shaders/Background/vertex.tsx
Normal file
108
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
216
components/StyledMarkdown.tsx
Normal file
216
components/StyledMarkdown.tsx
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
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";
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
};
|
||||||
9
components/Youtube.tsx
Normal file
9
components/Youtube.tsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export const YouTubeVideo = ({id, children} : any) => (
|
||||||
|
<iframe
|
||||||
|
src={'https://www.youtube.com/embed/' + id}
|
||||||
|
width="640"
|
||||||
|
height="360"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</iframe>
|
||||||
|
)
|
||||||
@@ -9,3 +9,13 @@ description: Short description
|
|||||||
# First blog
|
# First blog
|
||||||
|
|
||||||
Insane
|
Insane
|
||||||
|
|
||||||
|
test change
|
||||||
|
|
||||||
|
# Header 1 test
|
||||||
|
|
||||||
|
## Header 2 test
|
||||||
|
|
||||||
|
### Header 3 test
|
||||||
|
|
||||||
|
#### Header 4 test
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: "My Third Blog"
|
title: "My Third Blog (TEST)"
|
||||||
date: 27.12.2022
|
date: 27.12.2022
|
||||||
author: kookroach
|
author: kookroach
|
||||||
description: Short description
|
description: Short description
|
||||||
|
|||||||
27
content/projects/ConceptArt and Game Design.md
Normal file
27
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
content/projects/MadTrax.md
Normal file
8
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
content/projects/RE-Chess.md
Normal file
8
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
content/projects/SpaceMadness.md
Normal file
45
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)
|
||||||
25
docker-compose.dev.yml
Normal file
25
docker-compose.dev.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
peroxy_site_dev:
|
||||||
|
container_name: peroxy_site_dev
|
||||||
|
command: start
|
||||||
|
restart: unless-stopped
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.peroxy_site_dev.entrypoints=https"
|
||||||
|
- "traefik.http.routers.peroxy_site_dev.rule=Host(`test.peroxy.dev`)"
|
||||||
|
- "traefik.http.routers.peroxy_site_dev.tls=true"
|
||||||
|
- "traefik.http.routers.peroxy_site_dev.tls.certresolver=http"
|
||||||
|
- "traefik.http.routers.peroxy_site_dev.service=peroxy_site_dev-service"
|
||||||
|
- "traefik.http.services.peroxy_site_dev-service.loadbalancer.server.port=3000"
|
||||||
|
networks:
|
||||||
|
- proxy
|
||||||
|
volumes:
|
||||||
|
- ./dev_site:/var/lib/website/dev
|
||||||
|
|
||||||
|
networks:
|
||||||
|
proxy:
|
||||||
|
external: true
|
||||||
25
docker-compose.yml
Normal file
25
docker-compose.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
peroxy_site:
|
||||||
|
container_name: peroxy_site
|
||||||
|
command: start
|
||||||
|
restart: unless-stopped
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.peroxy_site.entrypoints=https"
|
||||||
|
- "traefik.http.routers.peroxy_site.rule=Host(`peroxy.dev`, `www.peroxy.dev`)"
|
||||||
|
- "traefik.http.routers.peroxy_site.tls=true"
|
||||||
|
- "traefik.http.routers.peroxy_site.tls.certresolver=http"
|
||||||
|
- "traefik.http.routers.peroxy_site.service=peroxy_site-service"
|
||||||
|
- "traefik.http.services.peroxy_site-service.loadbalancer.server.port=3000"
|
||||||
|
networks:
|
||||||
|
- proxy
|
||||||
|
volumes:
|
||||||
|
- ./dev_site:/var/lib/website/prod
|
||||||
|
|
||||||
|
networks:
|
||||||
|
proxy:
|
||||||
|
external: true
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
import { PropsWithChildren } from "react";
|
import { PropsWithChildren } from "react";
|
||||||
|
import { R3Gradient } from '../components/R3Background';
|
||||||
import { Navbar } from "../components/Navbar";
|
import { Navbar } from "../components/Navbar";
|
||||||
|
|
||||||
export const MainLayout = ({ children }: PropsWithChildren) => {
|
export const MainLayout = ({ children }: PropsWithChildren) => {
|
||||||
return (
|
return (
|
||||||
<div className="w-11/12 lg:w-2/3 m-auto overflow-x-hidden">
|
<div className="w-full">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<main className="xl:p-10 p-6 xl:mt-10 mt-4">{children}</main>
|
<R3Gradient />
|
||||||
|
<main className="xl:p-5 p-4 px-5 xl:mx-0">{children}</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
7597
package-lock.json
generated
Normal file
7597
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@@ -9,21 +9,39 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@agentofuser/remark-oembed": "^1.0.4",
|
||||||
|
"@mavrin/remark-typograf": "^2.2.0",
|
||||||
|
"@react-three/drei": "^9.56.19",
|
||||||
|
"@react-three/fiber": "^8.11.0",
|
||||||
"@tailwindcss/typography": "^0.5.8",
|
"@tailwindcss/typography": "^0.5.8",
|
||||||
"@types/node": "18.11.11",
|
"@types/node": "18.11.11",
|
||||||
"@types/react": "18.0.26",
|
"@types/react": "18.0.26",
|
||||||
"@types/react-dom": "18.0.9",
|
"@types/react-dom": "18.0.9",
|
||||||
|
"@types/three": "^0.149.0",
|
||||||
"framer-motion": "^8.0.2",
|
"framer-motion": "^8.0.2",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"next": "13.0.6",
|
"next": "13.0.6",
|
||||||
|
"parse-numeric-range": "^1.3.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-icons": "^4.7.1",
|
"react-icons": "^4.7.1",
|
||||||
|
"react-markdown": "^8.0.4",
|
||||||
|
"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": "^14.0.2",
|
||||||
|
"remark-directive": "^2.0.1",
|
||||||
|
"remark-directive-rehype": "^0.4.2",
|
||||||
|
"remark-emoji": "^3.1.0",
|
||||||
|
"remark-gfm": "^3.0.1",
|
||||||
"remark-html": "^15.0.1",
|
"remark-html": "^15.0.1",
|
||||||
|
"remark-smartypants": "^2.0.0",
|
||||||
|
"three": "^0.149.0",
|
||||||
"typescript": "4.9.3"
|
"typescript": "4.9.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/react-syntax-highlighter": "^15.5.5",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"postcss": "^8.4.19",
|
"postcss": "^8.4.19",
|
||||||
"tailwindcss": "^3.2.4"
|
"tailwindcss": "^3.2.4"
|
||||||
|
|||||||
@@ -1,46 +1,30 @@
|
|||||||
import { GetStaticPaths, GetStaticProps } from "next";
|
import { GetStaticPaths, GetStaticProps } from "next";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { ParsedUrlQuery } from "querystring";
|
import { ParsedUrlQuery } from "querystring";
|
||||||
import {
|
import { BLOGS_PATH, getBlogContentBySlug } from "../../utils/markdown";
|
||||||
BLOGS_PATH,
|
|
||||||
getFileContentBySlug,
|
|
||||||
renderMarkdown,
|
|
||||||
} from "../../utils/markdown";
|
|
||||||
import { MarkdownRenderingResult } from "../../types/types";
|
import { MarkdownRenderingResult } from "../../types/types";
|
||||||
import { MainLayout } from "../../layouts/MainLayout";
|
import { MainLayout } from "../../layouts/MainLayout";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { IoMdArrowRoundBack as BackIcon } from "react-icons/io";
|
import { IoMdArrowRoundBack as BackIcon } from "react-icons/io";
|
||||||
import { BasicBlogProps } from ".";
|
import { PostHeader } from "../../components/PostHeader";
|
||||||
|
import { readingTime } from "../../utils/general";
|
||||||
|
import { StyledMarkdown } from "../../components/StyledMarkdown";
|
||||||
|
|
||||||
export const BlogArticle = ({ frontMatter, html }: MarkdownRenderingResult) => {
|
export const BlogArticle = ({ frontMatter, html }: MarkdownRenderingResult) => {
|
||||||
const { title, author, date, description, authorLink } =
|
|
||||||
frontMatter as BasicBlogProps;
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
|
<div className="rounded-md border border-gray-200 shadow-md shadow-gray-200 dark:shadow-gray-900 dark:border-gray-700 gap-4 bg-black bg-opacity-30 round">
|
||||||
|
<div className="mx-10 mt-5 mb-10">
|
||||||
<Link href="/blog">
|
<Link href="/blog">
|
||||||
<button className="flex flex-row items-center gap-2 text-secondary hover:text-secondary/50 transition-all ease-in duration-75">
|
<button className="flex flex-row items-center gap-2 text-primary hover:text-primary-dark dark:text-secondary dark:hover:text-secondary/50 transition-all ease-in duration-75">
|
||||||
<BackIcon />
|
<BackIcon />
|
||||||
<span>Go Back</span>
|
<span>Go Back</span>
|
||||||
</button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="lg:mt-10 mt-4 mb-2 text-sm font-medium text-gray-500">
|
<PostHeader frontMatter={frontMatter} estTime={readingTime(html)} />
|
||||||
{date}
|
<StyledMarkdown html={html} />
|
||||||
</div>
|
</div>
|
||||||
<div className="lg:text-5xl font-bold">{title}</div>
|
|
||||||
<div className="mt-2 font-medium">
|
|
||||||
By{" "}
|
|
||||||
{authorLink && (
|
|
||||||
<Link href={authorLink} className="text-action">
|
|
||||||
@{author}
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
{!authorLink && <span className="text-action">@{author}</span>}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2">{description}</div>
|
|
||||||
<div
|
|
||||||
className="mt-4 prose prose-headings:text-primary-text prose-p:text-gray-400"
|
|
||||||
dangerouslySetInnerHTML={{ __html: html }}
|
|
||||||
/>
|
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -62,12 +46,11 @@ export const getStaticPaths: GetStaticPaths<BlogProps> = async () => {
|
|||||||
export const getStaticProps: GetStaticProps<MarkdownRenderingResult> = async ({
|
export const getStaticProps: GetStaticProps<MarkdownRenderingResult> = async ({
|
||||||
params,
|
params,
|
||||||
}) => {
|
}) => {
|
||||||
const markdownContent = getFileContentBySlug(params?.slug as string);
|
const markdownContent = getBlogContentBySlug(params?.slug as string);
|
||||||
const renderedHTML = await renderMarkdown(markdownContent.content);
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
frontMatter: markdownContent.frontMatter,
|
frontMatter: markdownContent.frontMatter,
|
||||||
html: renderedHTML,
|
html: markdownContent.content,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,34 +1,26 @@
|
|||||||
import { GetServerSideProps } from "next";
|
import { GetServerSideProps } from "next";
|
||||||
import { MainLayout } from "../../layouts/MainLayout";
|
import { MainLayout } from "../../layouts/MainLayout";
|
||||||
import { BlogPost, getAllFilesFrontMatter } from "../../utils/markdown";
|
import { Post, getAllBlogsFrontMatter } from "../../utils/markdown";
|
||||||
import { FrontMatter } from "../../types/types";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { formatDate } from "../../utils/general";
|
||||||
|
import { BasicArticleProps } from "../../components/PostHeader";
|
||||||
|
|
||||||
export interface BasicBlogProps extends FrontMatter {
|
const BlogCard = ({
|
||||||
title: string;
|
blog,
|
||||||
description: string;
|
slug,
|
||||||
date: string;
|
}: {
|
||||||
author: string;
|
blog: BasicArticleProps;
|
||||||
authorLink: string;
|
slug: string;
|
||||||
}
|
}) => {
|
||||||
|
|
||||||
const BlogCard = ({ blog, slug }: { blog: BasicBlogProps; slug: string }) => {
|
|
||||||
const format = (date: string) => {
|
|
||||||
const [day, month, year] = date.split(".");
|
|
||||||
const d = new Date(`${year}-${month}-${day}`);
|
|
||||||
return d.toLocaleDateString("en-US", {
|
|
||||||
month: "long",
|
|
||||||
day: "numeric",
|
|
||||||
year: "numeric",
|
|
||||||
});
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4 rounded-md border border-gray-700">
|
<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">
|
<div className="text-sm font-medium text-gray-500">
|
||||||
{format(blog.date)}
|
{formatDate(blog.date)}
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl font-bold">{blog.title}</h2>
|
<h2 className="text-2xl font-bold">{blog.title}</h2>
|
||||||
<p className="text-gray-400 text-lg">{blog.description}</p>
|
<p className="text-gray-600 dark:text-gray-400 text-sm">
|
||||||
|
{blog.description}
|
||||||
|
</p>
|
||||||
<Link href={`blog/${slug}`}>
|
<Link href={`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">
|
<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
|
Read more
|
||||||
@@ -38,16 +30,18 @@ const BlogCard = ({ blog, slug }: { blog: BasicBlogProps; slug: string }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Blog = ({ posts }: { posts: BlogPost[] }) => {
|
const Blog = ({ posts }: { posts: Post[] }) => {
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
<h1 className="text-3xl font-bold mt-10">Blog</h1>
|
<h1 className="text-3xl font-bold text-gray-800 dark:text-gray-200">
|
||||||
<div className="w-full h-0.5 bg-white/50 my-4 rounded-full" />
|
Blog
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
</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) => (
|
{posts.map((post, index) => (
|
||||||
<BlogCard
|
<BlogCard
|
||||||
key={index}
|
key={index}
|
||||||
blog={post.frontMatter as BasicBlogProps}
|
blog={post.frontMatter as BasicArticleProps}
|
||||||
slug={post.slug}
|
slug={post.slug}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@@ -57,7 +51,7 @@ const Blog = ({ posts }: { posts: BlogPost[] }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async () => {
|
export const getServerSideProps: GetServerSideProps = async () => {
|
||||||
const posts = getAllFilesFrontMatter();
|
const posts = getAllBlogsFrontMatter();
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
posts,
|
posts,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ export default function Home() {
|
|||||||
return (
|
return (
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
<div className="grid lg:grid-cols-2 grid-cols-1 xl:mt-10 gap-10 items-center">
|
<div className="grid lg:grid-cols-2 grid-cols-1 xl:mt-10 gap-10 items-center">
|
||||||
<div className="bg-primary-dark 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 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>
|
<div>
|
||||||
<span className="font-bold text-xl text-primary-text">
|
<span className="font-bold text-xl text-primary-text">
|
||||||
Lorem ipsum dolor{" "}
|
Lorem ipsum dolor{" "}
|
||||||
|
|||||||
62
pages/project/[slug].tsx
Normal file
62
pages/project/[slug].tsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { GetStaticPaths, GetStaticProps } from "next";
|
||||||
|
import fs from "fs";
|
||||||
|
import { ParsedUrlQuery } from "querystring";
|
||||||
|
import { getProjectContentBySlug, PROJECTS_PATH } from "../../utils/markdown";
|
||||||
|
import { MarkdownRenderingResult } from "../../types/types";
|
||||||
|
import { MainLayout } from "../../layouts/MainLayout";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { IoMdArrowRoundBack as BackIcon } from "react-icons/io";
|
||||||
|
import { readingTime } from "../../utils/general";
|
||||||
|
import { PostHeader } from "../../components/PostHeader";
|
||||||
|
import { StyledMarkdown } from "../../components/StyledMarkdown";
|
||||||
|
|
||||||
|
export const ProjectArticle = ({
|
||||||
|
frontMatter,
|
||||||
|
html,
|
||||||
|
}: MarkdownRenderingResult) => {
|
||||||
|
return (
|
||||||
|
<MainLayout>
|
||||||
|
<div className="rounded-md border border-gray-200 shadow-md shadow-gray-200 dark:shadow-gray-900 dark:border-gray-700 gap-4 bg-black bg-opacity-30 round">
|
||||||
|
<div className="mx-10 my-10">
|
||||||
|
<Link href="/projects">
|
||||||
|
<button className="flex flex-row items-center gap-2 text-primary hover:text-primary-dark dark:text-secondary dark:hover:text-secondary/50 transition-all ease-in duration-75">
|
||||||
|
<BackIcon />
|
||||||
|
<span>Go Back</span>
|
||||||
|
</button>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<PostHeader frontMatter={frontMatter} estTime={readingTime(html)} />
|
||||||
|
|
||||||
|
<StyledMarkdown html={html} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</MainLayout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
interface ProjectProps extends ParsedUrlQuery {
|
||||||
|
slug: string;
|
||||||
|
}
|
||||||
|
export const getStaticPaths: GetStaticPaths<ProjectProps> = async () => {
|
||||||
|
const paths = fs
|
||||||
|
.readdirSync(PROJECTS_PATH)
|
||||||
|
.map((path) => path.replace(/\.mdx?$/, ""))
|
||||||
|
.map((slug) => ({ params: { slug } }));
|
||||||
|
|
||||||
|
return {
|
||||||
|
paths,
|
||||||
|
fallback: false,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStaticProps: GetStaticProps<MarkdownRenderingResult> = async ({
|
||||||
|
params,
|
||||||
|
}) => {
|
||||||
|
const markdownContent = getProjectContentBySlug(params?.slug as string);
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
frontMatter: markdownContent.frontMatter,
|
||||||
|
html: markdownContent.content,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export default ProjectArticle;
|
||||||
71
pages/projects/index.tsx
Normal file
71
pages/projects/index.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { GetServerSideProps } from "next";
|
||||||
|
import { MainLayout } from "../../layouts/MainLayout";
|
||||||
|
import { Post, getAllProjectsFrontMatter } from "../../utils/markdown";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { formatDate } from "../../utils/general";
|
||||||
|
import { BasicArticleProps } from "../../components/PostHeader";
|
||||||
|
|
||||||
|
const ProjectCard = ({
|
||||||
|
project,
|
||||||
|
slug,
|
||||||
|
}: {
|
||||||
|
project: 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 grid grid-cols-1 xl:grid-cols-2 items-center gap-4 bg-black bg-opacity-60">
|
||||||
|
<div className="order-last xl:order-1">
|
||||||
|
<div className="text-sm font-medium text-gray-500">
|
||||||
|
{formatDate(project.date)}
|
||||||
|
</div>
|
||||||
|
<h2 className="text-2xl font-bold">{project.title}</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 text-sm">
|
||||||
|
{project.description}
|
||||||
|
</p>
|
||||||
|
<Link href={`project/${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>
|
||||||
|
{project.thumbnail && (
|
||||||
|
<img
|
||||||
|
src={project.thumbnail}
|
||||||
|
alt={`${slug}-thumbnail`}
|
||||||
|
className="w-full xl:h-40 h-auto object-cover rounded-md order-1 xl:order-last"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Projects = ({ posts }: { posts: Post[] }) => {
|
||||||
|
return (
|
||||||
|
<MainLayout>
|
||||||
|
<h1 className="text-3xl font-bold text-gray-800 dark:text-gray-100">
|
||||||
|
Projects
|
||||||
|
</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 gap-4">
|
||||||
|
{posts.map((post, index) => (
|
||||||
|
<ProjectCard
|
||||||
|
key={index}
|
||||||
|
project={post.frontMatter as BasicArticleProps}
|
||||||
|
slug={post.slug}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</MainLayout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps = async () => {
|
||||||
|
const posts = getAllProjectsFrontMatter();
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
posts,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Projects;
|
||||||
@@ -2,6 +2,109 @@
|
|||||||
@import "tailwindcss/components";
|
@import "tailwindcss/components";
|
||||||
@import "tailwindcss/utilities";
|
@import "tailwindcss/utilities";
|
||||||
|
|
||||||
body {
|
html {
|
||||||
@apply dark:bg-gradient-dark dark:text-primary-text bg-white text-gradient-dark;
|
scroll-padding-top: 5rem;
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply dark:bg-gradient-dark dark:text-primary-text bg-slate-100 text-gradient-dark w-full mx-auto;
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
max-width: 800px !important;
|
||||||
|
margin: auto !important;
|
||||||
|
}
|
||||||
|
.prose {
|
||||||
|
margin: 0;
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeStyle {
|
||||||
|
@apply bg-transparent !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
|
||||||
|
}
|
||||||
|
|
||||||
|
.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);
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ module.exports = {
|
|||||||
secondary: "#FFE600",
|
secondary: "#FFE600",
|
||||||
"primary-text": "#D9D9D9",
|
"primary-text": "#D9D9D9",
|
||||||
},
|
},
|
||||||
}
|
|
||||||
},
|
},
|
||||||
plugins: [require('@tailwindcss/typography')],
|
},
|
||||||
|
plugins: [require("@tailwindcss/typography")],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,11 @@
|
|||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"incremental": true
|
"incremental": true
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
"include": [
|
||||||
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
"desc.d.ts"
|
||||||
|
],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|||||||
35
utils/general.ts
Normal file
35
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;
|
||||||
|
};
|
||||||
@@ -6,8 +6,21 @@ import { remark } from "remark";
|
|||||||
import html from "remark-html";
|
import html from "remark-html";
|
||||||
|
|
||||||
export const BLOGS_PATH = join(process.cwd(), "content/blogs");
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
export const getFileContentBySlug = (slug: string): MarkdownDocument => {
|
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 filepath = join(BLOGS_PATH, `${slug}.md` || `${slug}.mdx`);
|
||||||
const filecontents = fs.readFileSync(filepath);
|
const filecontents = fs.readFileSync(filepath);
|
||||||
|
|
||||||
@@ -18,21 +31,47 @@ export const getFileContentBySlug = (slug: string): MarkdownDocument => {
|
|||||||
content: content,
|
content: content,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
export const getProjectContentBySlug = (slug: string): MarkdownDocument => {
|
||||||
|
const filepath = join(PROJECTS_PATH, `${slug}.md` || `${slug}.mdx`);
|
||||||
|
const filecontents = fs.readFileSync(filepath);
|
||||||
|
|
||||||
export interface BlogPost {
|
const { data, content } = matter(filecontents);
|
||||||
frontMatter: FrontMatter;
|
|
||||||
slug: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getAllFilesFrontMatter = (): BlogPost[] => {
|
return {
|
||||||
|
frontMatter: data,
|
||||||
|
content: content,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllBlogsFrontMatter = (): Post[] => {
|
||||||
const files = fs.readdirSync(BLOGS_PATH);
|
const files = fs.readdirSync(BLOGS_PATH);
|
||||||
|
|
||||||
return files.reduce((allPosts: BlogPost[], postSlug: string) => {
|
const blogs = files.reduce((allPosts: Post[], postSlug: string) => {
|
||||||
const slug = postSlug.replace(".md", "");
|
const slug = postSlug.replace(".md", "");
|
||||||
const post = getFileContentBySlug(slug);
|
const post = getBlogContentBySlug(slug);
|
||||||
|
|
||||||
return [{ frontMatter: post.frontMatter, slug }, ...allPosts];
|
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 async function markdownToHtml(markdown: any) {
|
export async function markdownToHtml(markdown: any) {
|
||||||
|
|||||||
Reference in New Issue
Block a user