diff options
Diffstat (limited to 'src/pages')
| -rw-r--r-- | src/pages/404Page.tsx | 21 | ||||
| -rw-r--r-- | src/pages/Gear.tsx | 43 | ||||
| -rw-r--r-- | src/pages/Home.tsx | 31 | ||||
| -rw-r--r-- | src/pages/Homelab.tsx | 52 | ||||
| -rw-r--r-- | src/pages/Printing.tsx | 70 | ||||
| -rw-r--r-- | src/pages/Projects.tsx | 24 |
6 files changed, 241 insertions, 0 deletions
diff --git a/src/pages/404Page.tsx b/src/pages/404Page.tsx new file mode 100644 index 0000000..3eabe6b --- /dev/null +++ b/src/pages/404Page.tsx @@ -0,0 +1,21 @@ +import { useNavigate } from "react-router-dom"; +import notFoundImg from "../assets/404.png"; +import { useEffect } from "react"; + +export default function NotFoundPage() { + const navigate = useNavigate(); + + useEffect(() => { + const timer = setTimeout(() => { + navigate('/', { replace: true }); + }, 4000); + + return () => clearTimeout(timer); + }, [navigate]); + return( + <> + <h1>404 - Not found</h1> + <img src={notFoundImg} className="rounded-lg"/> + </> + ); +}
\ No newline at end of file diff --git a/src/pages/Gear.tsx b/src/pages/Gear.tsx new file mode 100644 index 0000000..dbbcdfb --- /dev/null +++ b/src/pages/Gear.tsx @@ -0,0 +1,43 @@ +import CardLink from '../components/CardLink'; + +import { + dailyDrivers, + desktopParts, + nasParts, + type Part, +} from '../data/gear'; + +function PartsGroup({ title, parts }: { title?: string; parts: Part[] }) { + return ( + <> + {title && <h2 className="text-2xl font-semibold my-8">{title}</h2>} + <ul className="space-y-2"> + {parts.map((p) => ( + <li key={p.name}> + <CardLink + title={p.name} + body={p.description} + href={p.url} + imgSrc={p.image} + /> + </li> + ))} + </ul> + </> + ); +} + +export default function GearPage() { + return ( + <> + <title>߸ gear</title> + <h1>Gear</h1> + + <PartsGroup parts={dailyDrivers} /> + + <PartsGroup title="Desktop" parts={desktopParts} /> + + <PartsGroup title="NAS" parts={nasParts} /> + </> + ); +}
\ No newline at end of file diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx new file mode 100644 index 0000000..912b0c5 --- /dev/null +++ b/src/pages/Home.tsx @@ -0,0 +1,31 @@ +import ImageGalleryGrid from "../components/ImageGalleryGrid"; + +import dsImg from "../assets/3ds.jpeg"; +import esp32Img from "../assets/esp32.jpeg"; +import riceImg from "../assets/rice.jpg"; +import setupImg from "../assets/setup.jpg"; + +export default function HomePage() { + return ( + <> + <title>߸ hi</title> + <h1>Hi, <span className="text-blue-500 dark:text-purple-500">I'm Leander.</span></h1> + + <p className="leading-relaxed mb-6"> + I have a passion for hardware and software, studying computer science. Currently building own 3d printing projects and learning nix. + </p> + + <ImageGalleryGrid images={[{ src: dsImg, alt: "Nintendo 3DS", id: 1}, { src: esp32Img, alt: "ESP 32", id: 2}, { src: riceImg, alt: "Linux rice", id: 3}, { src: setupImg, alt: "Setup", id: 4}]} /> + + <p className="mb-4">A few things I'm interrested in:</p> + + <ul className="list-disc pl-6 space-y-1"> + <li>Digital minimalism</li> + <li>*nix systems</li> + <li>3D printing</li> + <li>Homelab / self-hosting</li> + <li>Seinen manga</li> + </ul> + </> + ); +}
\ No newline at end of file diff --git a/src/pages/Homelab.tsx b/src/pages/Homelab.tsx new file mode 100644 index 0000000..580babc --- /dev/null +++ b/src/pages/Homelab.tsx @@ -0,0 +1,52 @@ +import nasImg from '../assets/nas.png'; +import piImg from '../assets/pi.png'; +import LinkWithIcon from '../components/LinkWithIcon'; + +const nasServices = [ + "Jellyfin: Media library", + "Kavita: Ebooks and manga", + "AdGuard Home: Ad blocking", + "Nginx: Reverse proxy", + "Ollama: Enough for small LLM testing", +]; + +export default function HomelabPage() { + return ( + <> + <title>߸ homelab</title> + <h1>Home lab</h1> + + <section className="mb-12"> + <h2>NAS</h2> + <img src={nasImg} alt="NAS" className="mx-auto mb-4 w-64 rounded-lg shadow" /> + <p className="mb-4 leading-relaxed"> + My custom-built NAS running <strong>Unraid</strong> hosts the following services. See <LinkWithIcon href='/gear'>gear</LinkWithIcon> for specs. + </p> + <ul className="list-disc pl-6 space-y-1"> + {nasServices.map((svc) => ( + <li key={svc}>{svc}</li> + ))} + </ul> + <p className="mb-4 leading-relaxed"> + For remote access, I use a VPN to connect to the machine. I also back up my MacBook using Time Machine, and for my desktop and VPS I use rsync. + </p> + </section> + + <section className="mb-12"> + <h2>Raspberry Pi</h2> + <img src={piImg} alt="Raspberry Pi 5" className="mx-auto mb-4 w-64 rounded-lg shadow" /> + <p className="mb-4 leading-relaxed"> + Raspberry Pi 5 (8GB) running Homebridge to integrate non-HomeKit devices. It also serves as a precision NTP server using a <LinkWithIcon href='https://store.uputronics.com/products/raspberry-pi-gps-rtc-expansion-board' target='_blank'>Uputronics GPS module</LinkWithIcon>. + </p> + </section> + + {/* VPS */} + <section> + <h2>VPS</h2> + <p className="mb-4 leading-relaxed"> + Cheap Ionos VPS for services exposed to the internet. Mainly using it for hosting this website and Nextcloud. + </p> + </section> + </> + ); +}
\ No newline at end of file diff --git a/src/pages/Printing.tsx b/src/pages/Printing.tsx new file mode 100644 index 0000000..61dbe71 --- /dev/null +++ b/src/pages/Printing.tsx @@ -0,0 +1,70 @@ +import a1Img from "../assets/a1.png"; +import CodeSnippet from "../components/CodeSnippet"; +import LinkWithIcon from "../components/LinkWithIcon"; + +export default function PrintingPage() { + const roboArmCode = `#include <Bluepad32.h> +#include <ESP32Servo.h> + +#define DEADZONE 30 +#define BASE_PIN 15 +#define SHOULDER_PIN 2 +#define ELBOW_PIN 4 +#define WRIST_PIN 16 +#define HAND_PIN 17 + +Servo base, shoulder, elbow, wrist, hand; +ControllerPtr pad; + +void onConnectedGamepad(ControllerPtr ctl) { + Serial.printf("Gamepad #%d verbunden\n", ctl->index()); + pad = ctl; +} + +void onDisconnectedGamepad(ControllerPtr ctl) { + Serial.printf("Gamepad getrennt\n"); +} + +int16_t mapAxis(int16_t v) { + if (abs(v) < DEADZONE) v = 0; + return map(v, -512, 512, 0, 180); +} +void setup() { + BP32.setup(&onConnectedGamepad, &onDisconnectedGamepad); + BP32.enableNewBluetoothConnections(true); + + base.attach(BASE_PIN); + shoulder.attach(SHOULDER_PIN); + elbow.attach(ELBOW_PIN); + wrist.attach(WRIST_PIN); + hand.attach(HAND_PIN); +} + +void loop() { + BP32.update(); + if (pad && pad->isConnected()) { + base.write(mapAxis(pad->axisX())); + shoulder.write(mapAxis(-pad->axisY())); + elbow.write(mapAxis(-pad->axisRY())); + hand.write(mapAxis(pad->throttle() - pad->brake())); + + if (pad->l1()) wrist.write(0); + else if (pad->r1()) wrist.write(180); + else wrist.write(90); + } + delay(15); +}`; + return ( + <> + <title>߸ 3d printing</title> + <h1>3D Printing</h1> + <img src={a1Img} alt="Bambu Lab A1" className="mx-auto mb-4 w-64 rounded-lg shadow"/> + <h2>Projects</h2> + <h3>Robot Arm</h3> + <p><LinkWithIcon href="https://makerworld.com/en/models/528885-robotic-arm#profileId-445995" target="_blank">3D Model</LinkWithIcon> changed the model to work with my servo motors.</p> + <CodeSnippet code={roboArmCode} initialLines={5} /> + <h3>Diy Drone</h3> + <p>WIP</p> + </> + ); +}
\ No newline at end of file diff --git a/src/pages/Projects.tsx b/src/pages/Projects.tsx new file mode 100644 index 0000000..d49a3d2 --- /dev/null +++ b/src/pages/Projects.tsx @@ -0,0 +1,24 @@ +import CardLink from '../components/CardLink'; +import { projects, type Project } from '../data/projects'; + +export default function ProjectsPage() { + return ( + <> + <title>߸ projects</title> + <h1>Projects</h1> + + <ul className="space-y-2"> + {projects.map((p: Project) => ( + <li key={p.name}> + <CardLink + title={p.name} + body={p.description} + href={p.url} + imgSrc={p.image} + /> + </li> + ))} + </ul> + </> + ); +}
\ No newline at end of file |
