mirror of
https://github.com/timmypidashev/web.git
synced 2026-04-14 11:03:50 +00:00
Add background to content; update layouts; add more content:
This commit is contained in:
@@ -4,22 +4,22 @@ import { Code2, BookOpen, RocketIcon, Compass } from 'lucide-react';
|
|||||||
export default function CurrentFocus() {
|
export default function CurrentFocus() {
|
||||||
const recentProjects = [
|
const recentProjects = [
|
||||||
{
|
{
|
||||||
title: "Project Name 1",
|
title: "Darkbox",
|
||||||
description: "Short description of the project",
|
description: "My gruvbox theme, with a pure black background",
|
||||||
href: "/projects/project-1",
|
href: "/projects/darkbox",
|
||||||
tech: ["React", "TypeScript", "Node.js"]
|
tech: ["Neovim", "Lua"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Project Name 2",
|
title: "Revive Auto Parts",
|
||||||
description: "Short description of the project",
|
description: "A car parts listing site built for a client",
|
||||||
href: "/projects/project-2",
|
href: "/projects/reviveauto",
|
||||||
tech: ["Python", "Django", "PostgreSQL"]
|
tech: ["Tanstack", "React Query", "Fastapi"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Project Name 3",
|
title: "Fhccenter",
|
||||||
description: "Short description of the project",
|
description: "Website made for a private school",
|
||||||
href: "/projects/project-3",
|
href: "/projects/fhccenter",
|
||||||
tech: ["Next.js", "Tailwind", "Prisma"]
|
tech: ["Nextjs", "Typescript", "Prisma"]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -24,13 +24,22 @@ interface Grid {
|
|||||||
offsetY: number;
|
offsetY: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface BackgroundProps {
|
||||||
|
layout?: 'index' | 'sidebar';
|
||||||
|
position?: 'left' | 'right';
|
||||||
|
}
|
||||||
|
|
||||||
const CELL_SIZE = 25;
|
const CELL_SIZE = 25;
|
||||||
const TRANSITION_SPEED = 0.1;
|
const TRANSITION_SPEED = 0.1;
|
||||||
const SCALE_SPEED = 0.15;
|
const SCALE_SPEED = 0.15;
|
||||||
const CYCLE_FRAMES = 120;
|
const CYCLE_FRAMES = 120;
|
||||||
const INITIAL_DENSITY = 0.15;
|
const INITIAL_DENSITY = 0.15;
|
||||||
|
const SIDEBAR_WIDTH = 240;
|
||||||
|
|
||||||
const Background: React.FC = () => {
|
const Background: React.FC<BackgroundProps> = ({
|
||||||
|
layout = 'index',
|
||||||
|
position = 'left'
|
||||||
|
}) => {
|
||||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
const gridRef = useRef<Grid>();
|
const gridRef = useRef<Grid>();
|
||||||
const animationFrameRef = useRef<number>();
|
const animationFrameRef = useRef<number>();
|
||||||
@@ -50,14 +59,10 @@ const Background: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const calculateGridDimensions = (width: number, height: number) => {
|
const calculateGridDimensions = (width: number, height: number) => {
|
||||||
// Calculate number of complete cells that fit in the viewport
|
|
||||||
const cols = Math.floor(width / CELL_SIZE);
|
const cols = Math.floor(width / CELL_SIZE);
|
||||||
const rows = Math.floor(height / CELL_SIZE);
|
const rows = Math.floor(height / CELL_SIZE);
|
||||||
|
|
||||||
// Calculate offsets to center the grid
|
|
||||||
const offsetX = Math.floor((width - (cols * CELL_SIZE)) / 2);
|
const offsetX = Math.floor((width - (cols * CELL_SIZE)) / 2);
|
||||||
const offsetY = Math.floor((height - (rows * CELL_SIZE)) / 2);
|
const offsetY = Math.floor((height - (rows * CELL_SIZE)) / 2);
|
||||||
|
|
||||||
return { cols, rows, offsetX, offsetY };
|
return { cols, rows, offsetX, offsetY };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -138,7 +143,6 @@ const Background: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const computeNextState = (grid: Grid) => {
|
const computeNextState = (grid: Grid) => {
|
||||||
// First pass: compute next state without applying it
|
|
||||||
for (let i = 0; i < grid.cols; i++) {
|
for (let i = 0; i < grid.cols; i++) {
|
||||||
for (let j = 0; j < grid.rows; j++) {
|
for (let j = 0; j < grid.rows; j++) {
|
||||||
const cell = grid.cells[i][j];
|
const cell = grid.cells[i][j];
|
||||||
@@ -153,11 +157,9 @@ const Background: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark cells that need to transition
|
|
||||||
if (cell.alive !== cell.next && !cell.transitioning) {
|
if (cell.alive !== cell.next && !cell.transitioning) {
|
||||||
cell.transitioning = true;
|
cell.transitioning = true;
|
||||||
cell.transitionComplete = false;
|
cell.transitionComplete = false;
|
||||||
// For dying cells, start the shrinking animation
|
|
||||||
if (!cell.next) {
|
if (!cell.next) {
|
||||||
cell.targetScale = 0;
|
cell.targetScale = 0;
|
||||||
cell.targetOpacity = 0;
|
cell.targetOpacity = 0;
|
||||||
@@ -172,13 +174,10 @@ const Background: React.FC = () => {
|
|||||||
for (let j = 0; j < grid.rows; j++) {
|
for (let j = 0; j < grid.rows; j++) {
|
||||||
const cell = grid.cells[i][j];
|
const cell = grid.cells[i][j];
|
||||||
|
|
||||||
// Update animation properties
|
|
||||||
cell.opacity += (cell.targetOpacity - cell.opacity) * TRANSITION_SPEED;
|
cell.opacity += (cell.targetOpacity - cell.opacity) * TRANSITION_SPEED;
|
||||||
cell.scale += (cell.targetScale - cell.scale) * SCALE_SPEED;
|
cell.scale += (cell.targetScale - cell.scale) * SCALE_SPEED;
|
||||||
|
|
||||||
// Handle transition states
|
|
||||||
if (cell.transitioning) {
|
if (cell.transitioning) {
|
||||||
// Check if shrinking animation is complete for dying cells
|
|
||||||
if (!cell.next && cell.scale < 0.05) {
|
if (!cell.next && cell.scale < 0.05) {
|
||||||
cell.alive = false;
|
cell.alive = false;
|
||||||
cell.transitioning = false;
|
cell.transitioning = false;
|
||||||
@@ -186,7 +185,6 @@ const Background: React.FC = () => {
|
|||||||
cell.scale = 0;
|
cell.scale = 0;
|
||||||
cell.opacity = 0;
|
cell.opacity = 0;
|
||||||
}
|
}
|
||||||
// Check if growing animation is complete for new cells
|
|
||||||
else if (cell.next && !cell.alive && !cell.transitionComplete) {
|
else if (cell.next && !cell.alive && !cell.transitionComplete) {
|
||||||
cell.alive = true;
|
cell.alive = true;
|
||||||
cell.transitioning = false;
|
cell.transitioning = false;
|
||||||
@@ -194,7 +192,6 @@ const Background: React.FC = () => {
|
|||||||
cell.targetScale = 1;
|
cell.targetScale = 1;
|
||||||
cell.targetOpacity = 1;
|
cell.targetOpacity = 1;
|
||||||
}
|
}
|
||||||
// Start growing animation for new cells once old cells have shrunk
|
|
||||||
else if (cell.next && !cell.alive && cell.transitionComplete) {
|
else if (cell.next && !cell.alive && cell.transitionComplete) {
|
||||||
cell.transitioning = true;
|
cell.transitioning = true;
|
||||||
cell.targetScale = 1;
|
cell.targetScale = 1;
|
||||||
@@ -214,17 +211,21 @@ const Background: React.FC = () => {
|
|||||||
|
|
||||||
const resizeCanvas = () => {
|
const resizeCanvas = () => {
|
||||||
const dpr = window.devicePixelRatio || 1;
|
const dpr = window.devicePixelRatio || 1;
|
||||||
const displayWidth = window.innerWidth;
|
let displayWidth: number;
|
||||||
const displayHeight = window.innerHeight;
|
let displayHeight: number;
|
||||||
|
|
||||||
|
if (layout === 'index') {
|
||||||
|
displayWidth = window.innerWidth;
|
||||||
|
displayHeight = window.innerHeight;
|
||||||
|
} else {
|
||||||
|
displayWidth = SIDEBAR_WIDTH;
|
||||||
|
displayHeight = window.innerHeight;
|
||||||
|
}
|
||||||
|
|
||||||
// Set canvas size accounting for device pixel ratio
|
|
||||||
canvas.width = displayWidth * dpr;
|
canvas.width = displayWidth * dpr;
|
||||||
canvas.height = displayHeight * dpr;
|
canvas.height = displayHeight * dpr;
|
||||||
|
|
||||||
// Scale the context to ensure correct drawing operations
|
|
||||||
ctx.scale(dpr, dpr);
|
ctx.scale(dpr, dpr);
|
||||||
|
|
||||||
// Set CSS size
|
|
||||||
canvas.style.width = `${displayWidth}px`;
|
canvas.style.width = `${displayWidth}px`;
|
||||||
canvas.style.height = `${displayHeight}px`;
|
canvas.style.height = `${displayHeight}px`;
|
||||||
|
|
||||||
@@ -232,7 +233,6 @@ const Background: React.FC = () => {
|
|||||||
gridRef.current = initGrid(displayWidth, displayHeight);
|
gridRef.current = initGrid(displayWidth, displayHeight);
|
||||||
isInitialized.current = true;
|
isInitialized.current = true;
|
||||||
} else if (gridRef.current) {
|
} else if (gridRef.current) {
|
||||||
// Update grid dimensions and offsets on resize
|
|
||||||
const { cols, rows, offsetX, offsetY } = calculateGridDimensions(displayWidth, displayHeight);
|
const { cols, rows, offsetX, offsetY } = calculateGridDimensions(displayWidth, displayHeight);
|
||||||
gridRef.current.cols = cols;
|
gridRef.current.cols = cols;
|
||||||
gridRef.current.rows = rows;
|
gridRef.current.rows = rows;
|
||||||
@@ -264,7 +264,6 @@ const Background: React.FC = () => {
|
|||||||
const xOffset = (cellSize - scaledSize) / 2;
|
const xOffset = (cellSize - scaledSize) / 2;
|
||||||
const yOffset = (cellSize - scaledSize) / 2;
|
const yOffset = (cellSize - scaledSize) / 2;
|
||||||
|
|
||||||
// Add grid offsets to center the animation
|
|
||||||
const x = grid.offsetX + i * CELL_SIZE + (CELL_SIZE - cellSize) / 2 + xOffset;
|
const x = grid.offsetX + i * CELL_SIZE + (CELL_SIZE - cellSize) / 2 + xOffset;
|
||||||
const y = grid.offsetY + j * CELL_SIZE + (CELL_SIZE - cellSize) / 2 + yOffset;
|
const y = grid.offsetY + j * CELL_SIZE + (CELL_SIZE - cellSize) / 2 + yOffset;
|
||||||
const scaledRoundness = roundness * cell.scale;
|
const scaledRoundness = roundness * cell.scale;
|
||||||
@@ -312,10 +311,21 @@ const Background: React.FC = () => {
|
|||||||
cancelAnimationFrame(animationFrameRef.current);
|
cancelAnimationFrame(animationFrameRef.current);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
}, [layout]);
|
||||||
|
|
||||||
|
const getContainerClasses = () => {
|
||||||
|
if (layout === 'index') {
|
||||||
|
return 'fixed inset-0 -z-10';
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseClasses = 'fixed top-0 bottom-0 hidden lg:block -z-10';
|
||||||
|
return position === 'left'
|
||||||
|
? `${baseClasses} left-0`
|
||||||
|
: `${baseClasses} right-0`;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 -z-10">
|
<div className={getContainerClasses()}>
|
||||||
<canvas
|
<canvas
|
||||||
ref={canvasRef}
|
ref={canvasRef}
|
||||||
className="w-full h-full bg-black"
|
className="w-full h-full bg-black"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Links } from "@/components/header/links";
|
import { Links } from "@/components/header/links";
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
@@ -8,11 +8,9 @@ export default function Header() {
|
|||||||
const [currentPath, setCurrentPath] = useState("");
|
const [currentPath, setCurrentPath] = useState("");
|
||||||
const [shouldAnimate, setShouldAnimate] = useState(false);
|
const [shouldAnimate, setShouldAnimate] = useState(false);
|
||||||
|
|
||||||
// Handle client-side initialization
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsClient(true);
|
setIsClient(true);
|
||||||
setCurrentPath(document.location.pathname);
|
setCurrentPath(document.location.pathname);
|
||||||
// Trigger initial animation after a brief delay
|
|
||||||
setTimeout(() => setShouldAnimate(true), 50);
|
setTimeout(() => setShouldAnimate(true), 50);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -44,7 +42,11 @@ export default function Header() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={link.id}
|
key={link.id}
|
||||||
className={`relative inline-block ${link.color}`}
|
className={`
|
||||||
|
relative inline-block
|
||||||
|
${link.color}
|
||||||
|
${!isIndexPage ? 'bg-black rounded' : ''}
|
||||||
|
`}
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href={link.href}
|
href={link.href}
|
||||||
@@ -87,12 +89,18 @@ export default function Header() {
|
|||||||
font-bold
|
font-bold
|
||||||
transition-transform duration-300
|
transition-transform duration-300
|
||||||
${visible ? "translate-y-0" : "-translate-y-full"}
|
${visible ? "translate-y-0" : "-translate-y-full"}
|
||||||
${!isIndexPage ? "bg-black" : ""}
|
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<div className="flex flex-row pt-1 px-2 text-lg lg:pt-2 lg:text-3xl md:text-2xl items-center justify-between md:justify-center space-x-2 md:space-x-10 lg:space-x-20">
|
<div className="flex flex-row items-center justify-center h-full">
|
||||||
|
<div className={`
|
||||||
|
flex flex-row pt-1 px-2 text-lg lg:pt-2 lg:text-3xl md:text-2xl
|
||||||
|
items-center justify-between md:justify-center
|
||||||
|
space-x-2 md:space-x-10 lg:space-x-20
|
||||||
|
${!isIndexPage ? 'bg-black' : ''}
|
||||||
|
`}>
|
||||||
{headerLinks}
|
{headerLinks}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,58 @@
|
|||||||
---
|
---
|
||||||
title: "Iridescent"
|
title: "Iridescent"
|
||||||
description: "An open-source graphics engine."
|
description: "An open-source graphics engine"
|
||||||
githubUrl: "https://github.com/timmypidashev/iridescent"
|
githubUrl: "https://github.com/timmypidashev/iridescent"
|
||||||
techStack: ["Cmake", "Glad", "Imgui"]
|
techStack: ["Cmake", "Glad", "Imgui"]
|
||||||
date: "2024-05-03"
|
date: "2024-05-03"
|
||||||
image: "/projects/iridescent/thumbnail.jpeg"
|
image: "/projects/iridescent/thumbnail.jpeg"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
A open-source graphics engine concept created for my highschool senior project.
|
||||||
|
Built to expand my understanding of the low level programming world, and further
|
||||||
|
my reach of graphics and c++ programming.
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
* **Dockable Windows**: A quality of life feature utilizing imgui which allows
|
||||||
|
to efficiently use monitor space, moving logging and certain tools into their own
|
||||||
|
windows outside of the engines viewspace.
|
||||||
|
|
||||||
|
* **Layer Stack**: Deterministic and fast stack which determines the order in which
|
||||||
|
all layers of the application are drawn, from the ui all the way to the polygons making
|
||||||
|
up the scene.
|
||||||
|
|
||||||
|
* **Input Polling**: A realtime implementation polling all input events on the keyboard
|
||||||
|
and mouse for an extremely low latency between input events and scene events.
|
||||||
|
|
||||||
|
* **Detailed Logging**: A detailed logging system which logs events from the scene all the
|
||||||
|
way down to the system.
|
||||||
|
|
||||||
|
* **Shading**: The crème de la crème of the renderer, adding shading to the entire scene.
|
||||||
|
|
||||||
|
## Development Highlights
|
||||||
|
Some of the best highlights for me during this project was quite literally every time I managed
|
||||||
|
to fix a compilation issue. The exhileration after each successful step forward was just mesmerizing,
|
||||||
|
and I knew the moment I began my obsession wouldn't end until the engine was in a complete state.
|
||||||
|
Since I was going into this blind during my senior year at highschool, I knew almost nothing about
|
||||||
|
c++ or the build systems that accompany it, so learning all of that at once was both a breath of fresh
|
||||||
|
air and a frustration. However, after several days of work, I became very comfortable with cmake, my build
|
||||||
|
system of choice, and most basic concepts of c++, allowing me to begin working on the engine itself.
|
||||||
|
It was at this point where I started hitting some real knowledge barriers and ended up going to multiple
|
||||||
|
resources to learn and understand the inner workings of a graphics renderer. Resources such as the
|
||||||
|
[Hazel Engine Series](https://www.youtube.com/watch?v=JxIZbV_XjAs&list=PLlrATfBNZ98dC-V-N3m0Go4deliWHPFwT),
|
||||||
|
[Learn C++ Website](https://www.learncpp.com/learn-cpp-site-index/), and [OpenGL Tutorials](https://www.opengl.org/sdk/docs/tutorials/)
|
||||||
|
helped me tremendously here.
|
||||||
|
|
||||||
|
## Challenges and Roadblocks
|
||||||
|
Some of the biggest challenges I faced was overcoming the knowledge barrier I had on the inner workings
|
||||||
|
of graphics rendering and low level programming overall. Thankfuly, being stubborn and obsessed can
|
||||||
|
sometimes help, and in this case after numerous attempts, I began meticoulouly learning every aspect
|
||||||
|
of the engine as I developed it, and over time it began paying off, snowballing into a very fun experience.
|
||||||
|
Some food for thought, after you overcome the biggest barrier in your journey, the rest seems laughably easy.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
Looking back, working on Iridescent was some of the most fun I have ever had programming as of yet,
|
||||||
|
and I am still craving for the next project to top this one. It was only by working on this engine
|
||||||
|
that I realized just how much I love working on the little details, obsessing over each little system
|
||||||
|
in the engine. Definitely something I will have to do again, maybe write a little game, but thats for
|
||||||
|
future me to decide :D
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "Revive Auto Parts"
|
title: "Revive Auto Parts"
|
||||||
description: "A car parts listing site built for a client."
|
description: "A car parts listing site built for a client"
|
||||||
demoUrl: "https://reviveauto.parts"
|
demoUrl: "https://reviveauto.parts"
|
||||||
techStack: ["Tanstack", "React Query", "Fastapi"]
|
techStack: ["Tanstack", "React Query", "Fastapi"]
|
||||||
date: "2025-01-04"
|
date: "2025-01-04"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import "@/style/globals.css";
|
import "@/style/globals.css";
|
||||||
import Header from "@/components/header";
|
import Header from "@/components/header";
|
||||||
import Footer from "@/components/footer";
|
import Footer from "@/components/footer";
|
||||||
|
import Background from "@/components/background";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -23,7 +24,9 @@ const { title, description, permalink, current } = Astro.props;
|
|||||||
<Header client:load />
|
<Header client:load />
|
||||||
<main>
|
<main>
|
||||||
<div class="max-w-5xl mx-auto pt-12 px-4 py-8">
|
<div class="max-w-5xl mx-auto pt-12 px-4 py-8">
|
||||||
|
<Background layout="content" position="right" client:only="react" />
|
||||||
<slot />
|
<slot />
|
||||||
|
<Background layout="content" position="left" client:only="react" />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<Footer client:load />
|
<Footer client:load />
|
||||||
@@ -5,7 +5,7 @@ import "@/style/globals.css";
|
|||||||
|
|
||||||
import Header from "@/components/header";
|
import Header from "@/components/header";
|
||||||
import Footer from "@/components/footer";
|
import Footer from "@/components/footer";
|
||||||
import Background from "@/components/hero/background";
|
import Background from "@/components/background";
|
||||||
---
|
---
|
||||||
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@@ -13,12 +13,13 @@ import Background from "@/components/hero/background";
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
|
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||||
<title>{content.title}</title>
|
<title>{content.title}</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-background text-foreground">
|
<body class="bg-background text-foreground">
|
||||||
<Header client:load />
|
<Header client:load />
|
||||||
<main>
|
<main>
|
||||||
<Background client:only="react" />
|
<Background layout="index" client:only="react" />
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
</main>
|
||||||
<Footer client:load fixed=true />
|
<Footer client:load fixed=true />
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
const { content } = Astro.props;
|
|
||||||
|
|
||||||
import "@/style/globals.css";
|
|
||||||
|
|
||||||
import Header from "@/components/header";
|
|
||||||
import Footer from "@/components/footer";
|
|
||||||
---
|
|
||||||
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
||||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
|
||||||
<title>{content.title}</title>
|
|
||||||
</head>
|
|
||||||
<body class="bg-background text-foreground">
|
|
||||||
<Header client:load />
|
|
||||||
<main>
|
|
||||||
<slot />
|
|
||||||
</main>
|
|
||||||
<Footer client:load />
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
---
|
|
||||||
import "@/style/globals.css";
|
|
||||||
import Header from "@/components/header";
|
|
||||||
import Footer from "@/components/footer";
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
title: string;
|
|
||||||
description?: string;
|
|
||||||
permalink?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { title, description, permalink } = Astro.props;
|
|
||||||
---
|
|
||||||
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
||||||
<title>{title}</title>
|
|
||||||
{description && <meta name="description" content={description} />}
|
|
||||||
{permalink && <link rel="canonical" href={permalink} />}
|
|
||||||
</head>
|
|
||||||
<body class="bg-background text-foreground">
|
|
||||||
<Header client:load />
|
|
||||||
<main>
|
|
||||||
<div class="max-w-5xl mx-auto pt-12 px-4 py-8">
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
<Footer client:load />
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,9 +1,18 @@
|
|||||||
---
|
---
|
||||||
import MainLayout from "@/layouts/main.astro";
|
import IndexLayout from "@/layouts/index.astro";
|
||||||
|
|
||||||
const title = "404 Not Found";
|
const title = "404 Not Found";
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout content={{ title: "404 | Timothy Pidashev" }}>
|
<IndexLayout content={{ title: "404 | Timothy Pidashev" }}>
|
||||||
<main>404 not found</main>
|
<main class="min-h-screen flex flex-col items-center justify-center p-4 text-center">
|
||||||
</MainLayout>
|
<h1 class="text-6xl font-bold mb-4">404</h1>
|
||||||
|
<p class="text-xl mb-8">Whoops! This page doesn't exist.</p>
|
||||||
|
<button
|
||||||
|
onclick="window.history.back()"
|
||||||
|
class="underline hover:opacity-70 transition-opacity"
|
||||||
|
>
|
||||||
|
go back
|
||||||
|
</button>
|
||||||
|
</main>
|
||||||
|
</IndexLayout>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
import "@/style/globals.css"
|
import "@/style/globals.css"
|
||||||
import MainLayout from "@/layouts/main.astro";
|
import ContentLayout from "@/layouts/content.astro";
|
||||||
import Intro from "@/components/about/intro";
|
import Intro from "@/components/about/intro";
|
||||||
import Timeline from "@/components/about/timeline";
|
import Timeline from "@/components/about/timeline";
|
||||||
import CurrentFocus from "@/components/about/current-focus";
|
import CurrentFocus from "@/components/about/current-focus";
|
||||||
import OutsideCoding from "@/components/about/outside-coding";
|
import OutsideCoding from "@/components/about/outside-coding";
|
||||||
---
|
---
|
||||||
<MainLayout content={{ title: "About | Timothy Pidashev" }}>
|
<ContentLayout content={{ title: "About | Timothy Pidashev" }}>
|
||||||
<div class="min-h-screen">
|
<div class="min-h-screen">
|
||||||
<section class="h-screen flex items-center justify-center">
|
<section class="h-screen flex items-center justify-center">
|
||||||
<Intro client:load />
|
<Intro client:load />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
import { CollectionEntry, getCollection } from "astro:content";
|
import { CollectionEntry, getCollection } from "astro:content";
|
||||||
import { Image } from "astro:assets";
|
import { Image } from "astro:assets";
|
||||||
import BlogLayout from "@/layouts/blog.astro";
|
import ContentLayout from "@/layouts/content.astro";
|
||||||
import { getArticleSchema } from "@/lib/structuredData";
|
import { getArticleSchema } from "@/lib/structuredData";
|
||||||
import { blogWebsite } from "@/lib/structuredData";
|
import { blogWebsite } from "@/lib/structuredData";
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ const jsonLd = {
|
|||||||
};
|
};
|
||||||
---
|
---
|
||||||
|
|
||||||
<BlogLayout>
|
<ContentLayout>
|
||||||
<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
|
<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
|
||||||
<div class="relative max-w-8xl mx-auto">
|
<div class="relative max-w-8xl mx-auto">
|
||||||
<article class="prose prose-lg mx-auto max-w-4xl">
|
<article class="prose prose-lg mx-auto max-w-4xl">
|
||||||
@@ -74,4 +74,4 @@ const jsonLd = {
|
|||||||
<Content />
|
<Content />
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</BlogLayout>
|
</ContentLayout>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
import { getCollection } from "astro:content";
|
import { getCollection } from "astro:content";
|
||||||
import MainLayout from "@/layouts/main.astro";
|
import ContentLayout from "@/layouts/content.astro";
|
||||||
|
|
||||||
import { BlogPostList } from "@/components/blog/post-list";
|
import { BlogPostList } from "@/components/blog/post-list";
|
||||||
|
|
||||||
@@ -11,6 +11,6 @@ const posts = (await getCollection("blog", ({ data }) => {
|
|||||||
);
|
);
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout content={{ title: "Blog | Timothy Pidashev" }}>
|
<ContentLayout content={{ title: "Blog | Timothy Pidashev" }}>
|
||||||
<BlogPostList posts={posts} client:load />
|
<BlogPostList posts={posts} client:load />
|
||||||
</MainLayout>
|
</ContentLayout>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
import { getCollection } from "astro:content";
|
import { getCollection } from "astro:content";
|
||||||
import ProjectsLayout from "@/layouts/projects.astro";
|
import ContentLayout from "@/layouts/content.astro";
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const projects = await getCollection("projects");
|
const projects = await getCollection("projects");
|
||||||
@@ -14,7 +14,7 @@ const { project } = Astro.props;
|
|||||||
const { Content } = await project.render();
|
const { Content } = await project.render();
|
||||||
---
|
---
|
||||||
|
|
||||||
<ProjectsLayout title={`${project.data.title} | Timothy Pidashev`}>
|
<ContentLayout title={`${project.data.title} | Timothy Pidashev`}>
|
||||||
<article class="w-full mx-auto px-4 pt-6 sm:pt-12">
|
<article class="w-full mx-auto px-4 pt-6 sm:pt-12">
|
||||||
{/* Image Section */}
|
{/* Image Section */}
|
||||||
{project.data.image && (
|
{project.data.image && (
|
||||||
@@ -60,4 +60,4 @@ const { Content } = await project.render();
|
|||||||
<Content />
|
<Content />
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</ProjectsLayout>
|
</ContentLayout>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
import { getCollection } from "astro:content";
|
import { getCollection } from "astro:content";
|
||||||
import ProjectsLayout from "@/layouts/projects.astro";
|
import ContentLayout from "@/layouts/content.astro";
|
||||||
import { ProjectList } from "@/components/projects/project-list";
|
import { ProjectList } from "@/components/projects/project-list";
|
||||||
|
|
||||||
const projects = (await getCollection("projects", ({ data }) => {
|
const projects = (await getCollection("projects", ({ data }) => {
|
||||||
@@ -10,6 +10,6 @@ const projects = (await getCollection("projects", ({ data }) => {
|
|||||||
);
|
);
|
||||||
---
|
---
|
||||||
|
|
||||||
<ProjectsLayout title="Projects | Timothy Pidashev">
|
<ContentLayout title="Projects | Timothy Pidashev">
|
||||||
<ProjectList projects={projects} client:load />
|
<ProjectList projects={projects} client:load />
|
||||||
</ProjectsLayout>
|
</ContentLayout>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
import "@/style/globals.css"
|
import "@/style/globals.css"
|
||||||
|
|
||||||
import MainLayout from "@/layouts/main.astro";
|
import ContentLayout from "@/layouts/content.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout content={{ title: "Resume | Timothy Pidashev" }}>
|
<ContentLayout content={{ title: "Resume | Timothy Pidashev" }}>
|
||||||
<div class="flex items-center justify-center h-screen w-full">
|
<div class="flex items-center justify-center h-screen w-full">
|
||||||
<h1 class="text-4xl text-blue font-bold">Resume</h1>
|
<h1 class="text-4xl text-blue font-bold">Resume</h1>
|
||||||
</div>
|
</div>
|
||||||
</MainLayout>
|
</ContentLayout>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user