14 Commits

Author SHA1 Message Date
ahmed 94a35b1ea6 Prompt regular browser
Publish Docker Image / Publish Docker Image (push) Successful in 5s
2025-07-28 18:29:17 -04:00
ahmed 44cbc48860 Fix mobile menu pointer events
Publish Docker Image / Publish Docker Image (push) Successful in 6s
2025-05-28 12:16:31 -04:00
ahmed 98cd4c465c Fallback link
Publish Docker Image / Publish Docker Image (push) Successful in 5s
2025-05-21 12:25:37 -04:00
ahmed 0e8a6d9e98 Documentation 2025-05-07 12:47:08 -04:00
ahmed d95ec6e036 [#2] Sidemenu for touchscreen devices
Publish Docker Image / Publish Docker Image (push) Successful in 5s
2025-05-06 22:22:48 -04:00
ahmed 219857106c [#1] Remove office phone from scene
Publish Docker Image / Publish Docker Image (push) Successful in 5s
2025-05-06 21:01:55 -04:00
ahmed 89f9194420 [#6] Name font change
Publish Docker Image / Publish Docker Image (push) Successful in 5s
2025-05-06 00:02:31 -04:00
ahmed 53fa9c815f [#5] Adjust render resolution dynamically
Publish Docker Image / Publish Docker Image (push) Successful in 5s
2025-05-05 22:45:37 -04:00
ahmed 25e55272d9 [#3] Delay loading bar
Publish Docker Image / Publish Docker Image (push) Successful in 5s
2025-05-04 22:53:32 -04:00
ahmed 2a1258a820 Fix broken PC UI on iOS
Publish Docker Image / Publish Docker Image (push) Successful in 5s
2025-05-02 22:44:35 -04:00
ahmed 01ac4d61cf Remove name from layout due to no longer being built at runtime
Publish Docker Image / Publish Docker Image (push) Successful in 5s
2025-05-02 17:09:43 -04:00
ahmed a6190c2694 Build once instead of at runtime
Publish Docker Image / Publish Docker Image (push) Successful in 17s
2025-05-02 16:55:55 -04:00
ahmed f4b7cef249 Revert CI, gitea bug
Publish Docker Image / Publish Docker Image (push) Successful in 51s
2025-05-02 13:41:50 -04:00
ahmed 4ff5e016b0 Embed version and fps
Publish Docker Image / build (push) Failing after 2m0s
2025-05-02 13:29:34 -04:00
23 changed files with 1117 additions and 355 deletions
+14 -6
View File
@@ -1,28 +1,36 @@
version: "1"
name: Publish Docker Image
on:
push:
tags:
- "v[0-9]+\\.[0-9]+\\.[0-9]+"
- "*"
jobs:
build:
name: Publish Docker Image
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v3
- name: Login to Registry
run: echo "${{ secrets.ACCESS_TOKEN }}" | docker login git.altaiar.dev -u "${{ secrets.USERNAME }}" --password-stdin
run: echo "${{ secrets.ACCESS_TOKEN }}" \
| docker login git.altaiar.dev -u "${{ secrets.USERNAME }}" --password-stdin
- name: Build & Tag Image
run: |
docker build -t git.altaiar.dev/${{ gitea.repository }}:${{ gitea.ref_name }} .
docker tag git.altaiar.dev/${{ gitea.repository }}:${{ gitea.ref_name }} git.altaiar.dev/${{ gitea.repository }}:latest
docker build \
--build-arg APP_VERSION=${{ gitea.ref_name }} \
--label org.opencontainers.image.version=${{ gitea.ref_name }} \
-t git.altaiar.dev/${{ gitea.repository }}:${{ gitea.ref_name }} .
docker tag \
git.altaiar.dev/${{ gitea.repository }}:${{ gitea.ref_name }} \
git.altaiar.dev/${{ gitea.repository }}:latest
- name: Push Images
run: |
+30 -9
View File
@@ -1,20 +1,41 @@
FROM node:lts-alpine AS deps
FROM node:lts-alpine AS builder
ARG APP_VERSION=dev
ENV NEXT_PUBLIC_APP_VERSION=$APP_VERSION
WORKDIR /app
RUN corepack enable && corepack prepare yarn@4.9.1 --activate
COPY package.json yarn.lock .yarnrc.yml ./
RUN yarn install --immutable
COPY . .
RUN yarn build
FROM node:lts-alpine AS runner
ARG APP_VERSION=dev
ENV NEXT_PUBLIC_APP_VERSION=$APP_VERSION \
APP_VERSION=$APP_VERSION \
NODE_ENV=production \
NEXT_TELEMETRY_DISABLED=1
LABEL org.opencontainers.image.version=$APP_VERSION
WORKDIR /app
RUN corepack enable && corepack prepare yarn@4.9.1 --activate \
&& addgroup -S nodejs -g 1001 \
&& adduser -S nextjs -u 1001
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/package.json ./package.json
COPY . .
RUN chown -R nextjs:nodejs /app
COPY --from=builder /app/yarn.lock ./yarn.lock
COPY --from=builder /app/.yarnrc.yml ./.yarnrc.yml
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
RUN mkdir -p /app/.yarn && chown -R nextjs:nodejs /app/.yarn
USER nextjs
ENV NODE_ENV=production \
NEXT_TELEMETRY_DISABLED=1 \
PORT=3000
EXPOSE 3000
CMD ["sh", "-c", "yarn build && yarn start -p $PORT"]
CMD ["yarn", "start"]
+33 -29
View File
@@ -1,36 +1,40 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
# Portfolio Website v2
## Getting Started
## Setup
First, run the development server:
### Domain Records
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
- Create one A record
### Reverse Proxy
- It doesn't matter what reverse proxy you use (Nginx, Apache, Traefik, Caddy, etc)
- Point the record to the web port (default: 3000)
### [Docker Compose](./docker-compose.yml)
```yaml
services:
portfolio:
container_name: portfolio
image: git.altaiar.dev/ahmed/portfolio-2:latest
network_mode: bridge
restart: unless-stopped
volumes:
- config:/app/public/config
- images:/app/public/images
ports:
- 3000:3000
volumes:
config: # Place config.json and resume.pdf here
images: # Place images here
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
(Optional) Replace volumes with bind mounts according to your setup
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
## Configuration
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
- The only required field is `name`, a two string array for first and last
- See [instrumentation.ts](./src/instrumentation.ts#L18-L89) or [config.d.ts](./src/types/config.d.ts) for possible values
- Validation is performed on container start, and will exit if anything is invalid
+15
View File
@@ -0,0 +1,15 @@
services:
portfolio:
container_name: portfolio
image: git.altaiar.dev/ahmed/portfolio-2:latest
network_mode: bridge
restart: unless-stopped
volumes:
- config:/app/public/config
- images:/app/public/images
ports:
- 3000:3000
volumes:
config: # Place config.json and resume.pdf here
images: # Place images here
+5 -1
View File
@@ -1,5 +1,9 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {};
const nextConfig: NextConfig = {
env: {
NEXT_PUBLIC_APP_VERSION: process.env.NEXT_PUBLIC_APP_VERSION || "dev",
},
};
export default nextConfig;
+3
View File
@@ -19,6 +19,7 @@
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"awesome-ajv-errors": "^5.1.0",
"lodash": "^4.17.21",
"next": "15.3.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
@@ -28,6 +29,7 @@
"devDependencies": {
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/lodash": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
@@ -35,6 +37,7 @@
"eslint": "^9",
"eslint-config-next": "15.3.0",
"tailwindcss": "^4",
"tailwindcss-text-fill-stroke": "^2.0.0-beta.3",
"typescript": "^5"
},
"packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538"
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+1
View File
@@ -1 +1,2 @@
@import "tailwindcss";
@plugin "tailwindcss-text-fill-stroke";
+1 -5
View File
@@ -1,7 +1,6 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import config from "../../public/config/config.json";
const inter = Inter({
variable: "--font-inter",
@@ -9,10 +8,7 @@ const inter = Inter({
preload: true,
});
export const metadata: Metadata = {
title: config.name.join(" "),
description: "Portfolio",
};
export const metadata: Metadata = { title: "Portfolio" };
export default function RootLayout({
children,
+34
View File
@@ -0,0 +1,34 @@
// https://github.com/f2etw/detect-inapp/blob/master/src/inapp.js
import { findKey } from "lodash";
const BROWSER = {
messenger: /\bFB[\w_]+\/(Messenger|MESSENGER)/,
facebook: /\bFB[\w_]+\//,
twitter: /\bTwitter/i,
line: /\bLine\//i,
wechat: /\bMicroMessenger\//i,
puffin: /\bPuffin/i,
miui: /\bMiuiBrowser\//i,
instagram: /\bInstagram/i,
chrome: /\bCrMo\b|CriOS|Android.*Chrome\/[.0-9]* (Mobile)?/,
safari: /Version.*Mobile.*Safari|Safari.*Mobile|MobileSafari/,
ie: /IEMobile|MSIEMobile/,
firefox:
/fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile|FxiOS/,
};
export const useInAppBrowser = (): boolean => {
const userAgent =
navigator.userAgent ||
navigator.vendor ||
(window as unknown as { opera: string }).opera;
const rules = ["WebView", "(iPhone|iPod|iPad)(?!.*Safari/)", "Android.*(wv)"];
const regex = new RegExp(`(${rules.join("|")})`, "ig");
return (
Boolean(userAgent.match(regex)) &&
findKey(BROWSER, (regex) => regex.test(userAgent)) !== undefined
);
};
-42
View File
@@ -106,9 +106,6 @@ export const Buttons = ({
: 0,
});
const printerMenuSpring = useThreeSpring<MeshStandardMaterial>({
opacity: currentView === View.PrinterView ? 1 : 0,
});
const desktopViewSpring = useThreeSpring<BufferGeometry>({
scale: hovered === View.DesktopView ? 1.25 : 1,
});
@@ -398,45 +395,6 @@ export const Buttons = ({
/>
</animated.mesh>
</group>
{/* Printer Menu */}
<group>
<Billboard position={[-3.25, 6.65, -2.75]}>
<AnimatedText
maxWidth={4.5}
font="/assets/inter.ttf"
fontSize={0.25}
outlineWidth={1 / 60}
fillOpacity={printerMenuSpring.opacity}
outlineOpacity={printerMenuSpring.opacity}
>
CAD and 3D printing
</AnimatedText>
</Billboard>
<Billboard position={[-2.75, 5.25, -2.25]}>
<AnimatedText
maxWidth={3}
textAlign="center"
font="/assets/inter.ttf"
fontSize={0.25}
outlineWidth={1 / 60}
fillOpacity={printerMenuSpring.opacity}
outlineOpacity={printerMenuSpring.opacity}
>
Building with electronics
</AnimatedText>
</Billboard>
<Billboard position={[-5.5, 3.175, -3]}>
<AnimatedText
font="/assets/inter.ttf"
fontSize={0.25}
outlineWidth={1 / 60}
fillOpacity={printerMenuSpring.opacity}
outlineOpacity={printerMenuSpring.opacity}
>
Gaming
</AnimatedText>
</Billboard>
</group>
</>
);
};
+313 -223
View File
@@ -1,6 +1,6 @@
"use client";
import { SpotLight } from "three";
import { MeshStandardMaterial, SpotLight } from "three";
import {
useGLTF,
PerspectiveCamera,
@@ -8,7 +8,8 @@ import {
Text,
Html,
type PerspectiveCameraProps,
useProgress,
PerformanceMonitor,
Billboard,
} from "@react-three/drei";
import {
animated as threeAnimated,
@@ -27,9 +28,11 @@ import {
useCallback,
type JSX,
} from "react";
import round from "lodash/round";
import Stack from "@/util/stack";
import isMobile from "@/util/isMobile";
import { Canvas } from "@react-three/fiber";
import { mdiArrowLeftBold } from "@mdi/js";
import { mdiArrowLeftBold, mdiClose, mdiOpenInNew } from "@mdi/js";
import { Icon } from "@mdi/react";
import { PDF } from "../util/PDF";
import { CellphoneUI } from "../ui/CellphoneUI";
@@ -42,7 +45,12 @@ import { Buttons } from "./Buttons";
import { Credits } from "../ui/Credits";
import { isWebGL2Available } from "@react-three/drei";
import { useWindowSize } from "../hooks/useWindowSize";
import { Info } from "../util/Info";
import { Loader } from "../ui/Loader";
import { MobileMenu } from "../ui/MobileMenu";
import { useInAppBrowser } from "../hooks/useInAppBrowser";
const AnimatedText = threeAnimated(Text);
const AnimatedCam = threeAnimated(PerspectiveCamera);
const AnimatedButton = webAnimated.button as React.ComponentType<
React.ButtonHTMLAttributes<HTMLButtonElement> & { children?: React.ReactNode }
@@ -50,11 +58,15 @@ const AnimatedButton = webAnimated.button as React.ComponentType<
export function Scene(props: JSX.IntrinsicElements["group"]) {
const { nodes, materials } = useGLTF("/scene.glb") as unknown as GLTFResult;
const { progress } = useProgress();
const { width, height } = useWindowSize();
const config = useConfig();
const mobile = isMobile();
const isInAppBrowser = useInAppBrowser();
// States
const [fps, setFps] = useState(0);
const [dpr, setDpr] = useState(2);
const [warningDismissed, setWarningDismissed] = useState(false);
const [pendingView, setPendingView] = useState<View | null>(null);
const [backHovered, setBackHovered] = useState(false);
const [backClicked, setBackClicked] = useState(false);
@@ -109,10 +121,13 @@ export function Scene(props: JSX.IntrinsicElements["group"]) {
precision: 0.0001,
friction: 80,
mass: 10,
clamp: true,
},
});
const printerMenuSpring = useThreeSpring<MeshStandardMaterial>({
opacity: currentView === View.PrinterView ? 1 : 0,
});
const { backScale, backOpacity } = useWebSpring({
backScale: backHovered ? 1.25 : 1,
backOpacity: menuTraversal.size() > 1 ? 1 : 0,
@@ -125,240 +140,315 @@ export function Scene(props: JSX.IntrinsicElements["group"]) {
{isWebGL2Available() ? (
width >= 1.5 * height ? (
<>
<div className="pointer-events-none fixed z-[999999999]">
<AnimatedButton
style={{
transform: backScale.to(
(s) => `scale(${s})`
) as unknown as string,
opacity: backOpacity as unknown as number,
pointerEvents: menuTraversal.size() > 1 ? "auto" : "none",
cursor: backHovered ? "pointer" : "auto",
}}
className="m-4"
onMouseEnter={() => setBackHovered(true)}
onMouseLeave={() => setBackHovered(false)}
onClick={() => {
setBackClicked(true);
goPreviousView();
}}
>
<Icon
path={mdiArrowLeftBold}
size={2}
color="white"
className="stroke-[0.75] stroke-black"
<Loader />
<div className="pointer-events-none fixed z-[999999999] size-full">
{mobile ? (
<MobileMenu
menuTraversal={menuTraversal}
setMenuTraversal={setMenuTraversal}
pendingView={pendingView}
setPendingView={setPendingView}
goPreviousView={goPreviousView}
currentView={currentView}
/>
</AnimatedButton>
) : (
<AnimatedButton
style={{
transform: backScale.to(
(s) => `scale(${s})`
) as unknown as string,
opacity: backOpacity as unknown as number,
pointerEvents: menuTraversal.size() > 1 ? "auto" : "none",
cursor: backHovered ? "pointer" : "auto",
}}
className="m-2"
onMouseEnter={() => setBackHovered(true)}
onMouseLeave={() => setBackHovered(false)}
onClick={() => {
setBackClicked(true);
goPreviousView();
}}
>
<Icon
path={mdiArrowLeftBold}
size={2}
color="white"
className="stroke-[0.75] stroke-black"
/>
</AnimatedButton>
)}
{!warningDismissed && isInAppBrowser ? (
<div className="absolute top-0 right-0 bg-amber-500 m-3 p-2 rounded-xl flex gap-2 items-center justify-between">
<p className="text-black text-md">
For the best experience, please open this page in a regular
browser
</p>
<button
className="transition-all duration-300 pointer-events-auto hover:scale-110 hover:bg-amber-600 rounded-lg"
onClick={() => setWarningDismissed(true)}
>
<Icon path={mdiClose} color="black" className="size-6" />
</button>
</div>
) : null}
<div className="min-w-12 text-white bottom-0 right-0 absolute m-3 p-1 text-xs text-right text-stroke-2 text-stroke-black paint-sfm">
<Info />
{fps > 0 && (
<p>
{fps.toFixed(0)} fps | {dpr}x
</p>
)}
{config.fallbackUrl && (
<div className="flex gap-1 justify-end items-center">
<Icon path={mdiOpenInNew} className="h-3" />
<a
href={config.fallbackUrl}
target="_blank"
className="hover:underline pointer-events-auto"
>
fallback
</a>
</div>
)}
</div>
</div>
<Canvas
shadows
dpr={dpr}
gl={{ localClippingEnabled: true, antialias: true, alpha: true }}
>
<Suspense
fallback={
<Html fullscreen>
<div className="pt-10 w-screen h-screen flex flex-col space-y-6 justify-center items-center text-white text-4xl pointer-events-none">
<p>Loading...</p>
<div className="w-48 bg-neutral-700 h-4 rounded-lg">
<div
className="h-full rounded-lg bg-neutral-100"
style={{
width: `${progress.toFixed(0)}%`,
}}
/>
</div>
</div>
</Html>
}
>
<Suspense fallback={null}>
<group {...props} dispose={null}>
<Environment preset="apartment" />
<AnimatedCam makeDefault {...cameraSpring} />
<Buttons
menuTraversal={menuTraversal}
setMenuTraversal={setMenuTraversal}
pendingView={pendingView}
setPendingView={setPendingView}
goPreviousView={goPreviousView}
currentView={currentView}
/>
<group>
<primitive
object={spotlight}
position={[-1.915, 20, 1.0925]}
/>
<primitive
object={spotlight.target}
position={[-1.915, 0, 1.0925]}
/>
</group>
<group
position={[2.50415, 0.12973, 3.47808]}
rotation={[0, -Math.PI / 2, 0]}
scale={0.01}
<PerformanceMonitor
ms={100}
onChange={(api) => {
setFps(api.fps);
setDpr(round(1.5 * api.factor + 0.5, 1));
}}
>
<mesh
castShadow
receiveShadow
geometry={nodes.Monitor.geometry}
material={materials.PlasticMaterial}
position={[-175, 415.25, -160]}
>
<Html
transform
receiveShadow
castShadow
pointerEvents={
currentView === View.PCView ? "auto" : "none"
}
occlude="blending"
className="w-[1452px] h-[810px] bg-neutral-400"
scale={10}
position={[0, 170, 0.25]}
raycast={
currentView === View.DesktopView
? () => null
: undefined
}
distanceFactor={10}
style={{
backgroundImage: "url(/assets/pc-bg.webp)",
backgroundColor: "#111535",
}}
>
<div className="w-full h-full backdrop-blur-md">
<PCUI />
</div>
</Html>
</mesh>
</group>
<group
position={[4.105, 4.2825, -2.085]}
rotation={[0, -Math.PI / 2, 0]}
>
<mesh
castShadow
receiveShadow
geometry={nodes.SecondaryMonitorMesh.geometry}
material={materials.SecondaryMonitorMaterial}
<Environment preset="apartment" />
<AnimatedCam makeDefault {...cameraSpring} />
{!mobile && (
<Buttons
menuTraversal={menuTraversal}
setMenuTraversal={setMenuTraversal}
pendingView={pendingView}
setPendingView={setPendingView}
goPreviousView={goPreviousView}
currentView={currentView}
/>
)}
<group>
<primitive
object={spotlight}
position={[-1.915, 20, 1.0925]}
/>
<primitive
object={spotlight.target}
position={[-1.915, 0, 1.0925]}
/>
</group>
<group
position={[2.50415, 0.12973, 3.47808]}
rotation={[0, -Math.PI / 2, 0]}
scale={0.01}
>
<Credits currentView={currentView} />
</mesh>
</group>
<group
position={[3.81024, 3.85444, 6.13972]}
rotation={[-Math.PI, -0.44331, -Math.PI]}
scale={0.035}
>
<mesh
castShadow
receiveShadow
geometry={nodes.Paper3.geometry}
material={materials.PaperMaterial}
>
<Html
transform
receiveShadow
<mesh
castShadow
pointerEvents={
currentView === View.ResumeView ? "auto" : "none"
}
occlude="blending"
className="w-[800px] h-[1074px] bg-white"
scale={15}
position={[28.7765, 12.431875, 3.725]}
rotation={[-Math.PI / 2, 0, (1 * Math.PI) / 9]}
raycast={
currentView === View.ResumeView
? () => null
: undefined
}
distanceFactor={1}
>
<PDF url="/config/resume.pdf" />
</Html>
</mesh>
</group>
<group
position={[2.58559, 4.69855, 3.29056]}
rotation={[-Math.PI, -0.43633, -Math.PI]}
scale={0.02}
>
<mesh
castShadow
receiveShadow
geometry={nodes.CellphoneMesh.geometry}
material={materials.CellphoneMaterial}
position={[-102.64982, -21.45192, 247.01788]}
>
<Html
transform
receiveShadow
castShadow
pointerEvents={
currentView === View.CellphoneView ? "auto" : "none"
}
occlude="blending"
className="w-[1008px] h-[1614px] bg-blue-100"
position={[0, 2.8, 0]}
rotation={[-Math.PI / 2, 0, Math.PI / 2]}
raycast={
currentView === View.CellphoneView
? () => null
: undefined
}
style={{
backgroundImage: "url('/assets/cellphone-bg.webp')",
backgroundColor: "black",
}}
distanceFactor={10}
geometry={nodes.Monitor.geometry}
material={materials.PlasticMaterial}
position={[-175, 415.25, -160]}
>
<CellphoneUI />
</Html>
</mesh>
</group>
<Text
position={[-2.415, 12.5, -6]}
font="/assets/inter-bold.ttf"
color="black"
fontSize={3}
>
{config.name[0]}
</Text>
<Text
position={[5.185, 12.5, 1.592]}
font="/assets/inter-bold.ttf"
rotation={[0, -Math.PI / 2, 0]}
color="black"
fontSize={3}
>
{config.name[1]}
</Text>
{config.status && (
<Html
transform
receiveShadow
castShadow
pointerEvents={
currentView === View.PCView ? "auto" : "none"
}
occlude="blending"
className="w-[1452px] h-[810px] bg-[#0e1838]"
scale={10}
position={[0, 170, 0.5]}
raycast={
currentView === View.DesktopView
? () => null
: undefined
}
distanceFactor={10}
>
<div className="size-full">
<PCUI />
</div>
</Html>
</mesh>
</group>
<group
position={[4.105, 4.2825, -2.085]}
rotation={[0, -Math.PI / 2, 0]}
>
<mesh
castShadow
receiveShadow
geometry={nodes.SecondaryMonitorMesh.geometry}
material={materials.SecondaryMonitorMaterial}
scale={0.01}
>
<Credits currentView={currentView} />
</mesh>
</group>
<group
position={[3.81024, 3.85444, 6.13972]}
rotation={[-Math.PI, -0.44331, -Math.PI]}
scale={0.035}
>
<mesh
castShadow
receiveShadow
geometry={nodes.Paper3.geometry}
material={materials.PaperMaterial}
>
<Html
transform
receiveShadow
castShadow
pointerEvents={
currentView === View.ResumeView ? "auto" : "none"
}
occlude="blending"
className="w-[800px] h-[1074px] bg-white"
scale={15}
position={[28.7765, 12.431875, 3.725]}
rotation={[-Math.PI / 2, 0, (1 * Math.PI) / 9]}
raycast={
currentView === View.ResumeView
? () => null
: undefined
}
distanceFactor={1}
>
<PDF url="/config/resume.pdf" />
</Html>
</mesh>
</group>
<group
position={[2.58559, 4.69855, 3.29056]}
rotation={[-Math.PI, -0.43633, -Math.PI]}
scale={0.02}
>
<mesh
castShadow
receiveShadow
geometry={nodes.CellphoneMesh.geometry}
material={materials.CellphoneMaterial}
position={[-102.64982, -21.45192, 247.01788]}
>
<Html
transform
receiveShadow
castShadow
pointerEvents={
currentView === View.CellphoneView ? "auto" : "none"
}
occlude="blending"
className="w-[1008px] h-[1614px] bg-blue-100"
position={[0, 2.85, 0]}
rotation={[-Math.PI / 2, 0, Math.PI / 2]}
raycast={
currentView === View.CellphoneView
? () => null
: undefined
}
style={{
backgroundImage: "url('/assets/cellphone-bg.webp')",
backgroundColor: "black",
}}
distanceFactor={10}
>
<CellphoneUI />
</Html>
</mesh>
</group>
<Text
position={[5.185, 10, 1.592]}
font="/assets/inter.ttf"
position={[-2.415, 12.5, -6]}
font="/assets/clashdisplay.ttf"
color="black"
fontSize={3}
>
{config.name[0]}
</Text>
<Text
position={[5.185, 12.5, 1.592]}
font="/assets/clashdisplay.ttf"
rotation={[0, -Math.PI / 2, 0]}
color="black"
fontSize={0.75}
fontSize={3}
>
{config.status}
{config.name[1]}
</Text>
)}
{config.location && (
<Text
position={[5.185, 8.5, 1.592]}
font="/assets/inter.ttf"
rotation={[0, -Math.PI / 2, 0]}
color="black"
fontSize={0.75}
>
📍&#8201;{config.location}
</Text>
)}
<StaticMeshes nodes={nodes} materials={materials} />
{config.status && (
<Text
position={[5.185, 10, 1.592]}
font="/assets/inter.ttf"
rotation={[0, -Math.PI / 2, 0]}
color="black"
fontSize={0.75}
>
{config.status}
</Text>
)}
{config.location && (
<Text
position={[5.185, 8.5, 1.592]}
font="/assets/inter.ttf"
rotation={[0, -Math.PI / 2, 0]}
color="black"
fontSize={0.75}
>
📍&#8201;{config.location}
</Text>
)}
<StaticMeshes nodes={nodes} materials={materials} />
<group>
<Billboard position={[-3.25, 6.65, -2.75]}>
<AnimatedText
maxWidth={4.5}
font="/assets/inter.ttf"
fontSize={0.25}
outlineWidth={1 / 60}
fillOpacity={printerMenuSpring.opacity}
outlineOpacity={printerMenuSpring.opacity}
>
CAD and 3D printing
</AnimatedText>
</Billboard>
<Billboard position={[-2.75, 5.25, -2.25]}>
<AnimatedText
maxWidth={3}
textAlign="center"
font="/assets/inter.ttf"
fontSize={0.25}
outlineWidth={1 / 60}
fillOpacity={printerMenuSpring.opacity}
outlineOpacity={printerMenuSpring.opacity}
>
Building with electronics
</AnimatedText>
</Billboard>
<Billboard position={[-5.5, 3.175, -3]}>
<AnimatedText
font="/assets/inter.ttf"
fontSize={0.25}
outlineWidth={1 / 60}
fillOpacity={printerMenuSpring.opacity}
outlineOpacity={printerMenuSpring.opacity}
>
Gaming
</AnimatedText>
</Billboard>
</group>
</PerformanceMonitor>
</group>
</Suspense>
</Canvas>
@@ -369,9 +459,9 @@ export function Scene(props: JSX.IntrinsicElements["group"]) {
config.fallbackUrl ? "pt-12" : ""
} flex flex-col space-y-6 w-screen h-screen text-center justify-center items-center text-white text-4xl`}
>
<p>Screen too small, please rotate</p>
<p className="mx-8">Screen too small, please rotate</p>
{config.fallbackUrl && (
<p className="text-base">
<p className="text-base mx-8">
or visit the{" "}
<a
href={config.fallbackUrl}
+1 -9
View File
@@ -15,7 +15,7 @@ export const Credits = ({ currentView }: CreditsProps) => {
occlude="blending"
className="w-[1452px] h-[810px] bg-neutral-400"
scale={10}
position={[0, 170, 0.25]}
position={[0, 170, 0.5]}
raycast={currentView === View.CreditsView ? () => null : undefined}
distanceFactor={10}
>
@@ -60,14 +60,6 @@ export const Credits = ({ currentView }: CreditsProps) => {
</a>
</li>
<li className="mb-4 text-black">
<a
className="hover:underline text-blue-800"
href="https://poly.pizza/m/YxfMuchpUF"
target="_blank"
>
Office Phone
</a>
{" and "}
<a
className="hover:underline text-blue-800"
href="https://poly.pizza/m/S5sNqsyyOs"
+92
View File
@@ -0,0 +1,92 @@
"use client";
import { useProgress } from "@react-three/drei";
import { animated, useSpring, useTransition } from "@react-spring/web";
import { useEffect, useState } from "react";
import { createPortal } from "react-dom";
export const Loader = ({ minDuration = 750, fadeMs = 600 }) => {
const [barDone, setBarDone] = useState(false);
const barStyles = useSpring({
from: { width: "0%" },
to: { width: "100%" },
config: { duration: minDuration },
onRest: () => setBarDone(true),
});
const { progress } = useProgress();
const [assetsDone, setAssetsDone] = useState(false);
useEffect(() => {
if (progress === 100) setAssetsDone(true);
}, [progress]);
const [visible, setVisible] = useState(true);
useEffect(() => {
if (barDone && assetsDone) {
const id = setTimeout(() => setVisible(false), fadeMs);
return () => clearTimeout(id);
}
}, [barDone, assetsDone, fadeMs]);
const overlay = useTransition(visible, {
from: { opacity: 1 },
leave: { opacity: 0 },
config: { duration: fadeMs, easing: (t) => t * t },
});
return createPortal(
overlay(
(styles, show) =>
show && (
<div>
{/* @ts-expect-error children not typed bug */}
<animated.div
style={{
...styles,
position: "fixed",
inset: 0,
background: "#1b1b1b",
zIndex: 1e9,
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
gap: "1.5rem",
fontSize: "2.25rem",
color: "#fff",
pointerEvents: "none",
paddingTop: "2.5rem",
}}
>
<p>Loading</p>
<div
style={{
width: 192,
height: 16,
background: "#4b4b4b",
borderRadius: 8,
overflow: "hidden",
}}
>
{/* @ts-expect-error children not typed bug */}
<animated.div
style={{ ...barStyles, height: "100%", background: "#fff" }}
>
{/* @ts-expect-error children not typed bug */}
<animated.div className="text-[#1b1b1b] text-xs flex h-full items-center justify-center">
{barStyles.width.to((w) => {
const p = parseInt(w) || 0;
return p > 5 ? p : "";
})}
</animated.div>
</animated.div>
</div>
</animated.div>
</div>
)
),
document.body
);
};
+113
View File
@@ -0,0 +1,113 @@
import type Stack from "@/util/stack";
import {
useCallback,
useEffect,
useState,
type Dispatch,
type SetStateAction,
} from "react";
import { View, viewToHash } from "../scene/consts";
import { animated, useSpring } from "@react-spring/web";
import Icon from "@mdi/react";
import { mdiMenu } from "@mdi/js";
interface MobileMenuProps {
menuTraversal: Stack<View>;
setMenuTraversal: Dispatch<SetStateAction<Stack<View>>>;
pendingView: View | null;
setPendingView: Dispatch<SetStateAction<View | null>>;
goPreviousView(): void;
currentView: View;
}
const AnimatedButton = animated.button as React.ComponentType<
React.ButtonHTMLAttributes<HTMLButtonElement> & { children?: React.ReactNode }
>;
export const MobileMenu = ({
menuTraversal,
setMenuTraversal,
pendingView,
setPendingView,
goPreviousView,
currentView,
}: MobileMenuProps) => {
const [menuOpen, setMenuOpen] = useState(false);
const goToView = useCallback(
(view: View) => {
setMenuTraversal((prev) => {
const next = prev.clone();
next.push(view);
return next;
});
setPendingView(view);
},
[setMenuTraversal, setPendingView]
);
useEffect(() => {
const onPop = () => {
goPreviousView();
};
window.addEventListener("popstate", onPop);
return () => window.removeEventListener("popstate", onPop);
}, [goPreviousView]);
useEffect(() => {
if (pendingView === null) return;
window.history.pushState(
{ depth: menuTraversal.size() },
"",
viewToHash[pendingView] ? `#${viewToHash[pendingView]}` : "/"
);
setPendingView(null);
}, [menuTraversal, pendingView, setPendingView]);
const slideProps = useSpring({
x: menuOpen ? 0 : -128,
config: { tension: 200, friction: 20 },
});
return (
<div className="w-min">
<AnimatedButton
className="m-2 pointer-events-auto"
onClick={() => setMenuOpen((prev) => !prev)}
>
<Icon
path={mdiMenu}
size={1.5}
color="white"
className="stroke-[0.75] stroke-black"
/>
</AnimatedButton>
{/* @ts-expect-error children not typed bug */}
<animated.div
style={{
transform: slideProps.x.to((x) => `translateX(${x}%)`),
}}
className="text-white flex flex-col w-min gap-2 text-stroke-2 text-stroke-black paint-sfm ml-2"
>
{[
{ label: "Home", view: View.MainView },
{ label: "Projects", view: View.PCView },
{ label: "Socials", view: View.CellphoneView },
{ label: "Resume", view: View.ResumeView },
{ label: "Hobbies", view: View.PrinterView },
{ label: "Credits", view: View.CreditsView },
].map(({ label, view }) => (
<button
key={view}
className={`${
currentView === view ? "bg-neutral-700/50" : "bg-neutral-500/50"
} px-2 py-1 transition-all duration-300 hover:bg-neutral-700/50 hover:scale-110 rounded pointer-events-auto`}
onClick={() => goToView(view)}
>
{label}
</button>
))}
</animated.div>
</div>
);
};
+13 -13
View File
@@ -308,11 +308,11 @@ const Window: FC<HTMLAttributes<HTMLDivElement>> = ({ children, ...props }) => {
<div className="flex h-full space-x-1">
<Icon
path={mdiFolder}
className="text-neutral-400 h-full w-fit rounded-tl-lg rounded-br-lg transition-all hover:bg-blue-800 hover:ring-blue-500 hover:ring-1 p-1.5"
className="text-neutral-400 size-8 rounded-tl-lg rounded-br-lg transition-all hover:bg-blue-800 hover:ring-blue-500 hover:ring-1 p-1.5"
/>
<Icon
path={mdiPin}
className="text-neutral-400 h-full w-fit rounded-b-lg transition-all hover:bg-blue-800 hover:ring-blue-500 hover:ring-1 p-1.5"
className="text-neutral-400 size-8 rounded-b-lg transition-all hover:bg-blue-800 hover:ring-blue-500 hover:ring-1 p-1.5"
/>
</div>
<p className="absolute left-1/2 transform -translate-x-1/2 text-white">
@@ -321,21 +321,21 @@ const Window: FC<HTMLAttributes<HTMLDivElement>> = ({ children, ...props }) => {
<div className="flex h-full space-x-1 ml-auto justify-end">
<Icon
path={mdiWindowMinimize}
className="text-neutral-100 h-full w-fit rounded-b-lg transition-all hover:bg-amber-800 hover:ring-amber-500 hover:ring-1 p-1.5"
className="text-neutral-100 size-8 rounded-b-lg transition-all hover:bg-amber-800 hover:ring-amber-500 hover:ring-1 p-1.5"
/>
<Icon
path={mdiWindowRestore}
className="text-neutral-100 h-full w-fit rounded-b-lg transition-all hover:bg-green-800 hover:ring-green-500 hover:ring-1 p-1.5"
className="text-neutral-100 size-8 rounded-b-lg transition-all hover:bg-green-800 hover:ring-green-500 hover:ring-1 p-1.5"
/>
<Icon
path={mdiWindowClose}
className="text-neutral-100 h-full w-fit rounded-tr-lg rounded-bl-lg transition-all hover:bg-red-800 hover:ring-red-500 hover:ring-1 p-1.5"
className="text-neutral-100 size-8 rounded-tr-lg rounded-bl-lg transition-all hover:bg-red-800 hover:ring-red-500 hover:ring-1 p-1.5"
/>
</div>
</div>
<div className="flex bg-neutral-950 size-full rounded-b-lg text-white">
<div className="h-full w-fit bg-neutral-950 flex flex-col rounded-bl-lg">
<div className="h-14 p-2 pr-1 flex justify-center w-fit space-x-1">
<div className="h-14 p-2 pr-1 flex justify-center w-36 space-x-1">
<Icon
path={mdiArrowLeft}
className="text-neutral-100 h-full w-fit rounded-md transition-all hover:ring-neutral-500 hover:ring-1 p-2"
@@ -402,7 +402,7 @@ const Window: FC<HTMLAttributes<HTMLDivElement>> = ({ children, ...props }) => {
>
<Icon
path={mdiHelp}
className="text-neutral-100 h-full w-fit rounded-md transition-all hover:ring-neutral-500 hover:ring-1 p-2"
className="text-neutral-100 size-11 rounded-md transition-all hover:ring-neutral-500 hover:ring-1 p-2"
/>
</button>
</div>
@@ -417,7 +417,7 @@ const Window: FC<HTMLAttributes<HTMLDivElement>> = ({ children, ...props }) => {
defaultValue="/home/ahmed/Desktop/projects"
/>
</div>
<div className="flex justify-center h-full w-fit space-x-1">
<div className="flex justify-center h-full w-36 space-x-1">
<Icon
path={mdiBackspace}
className="text-neutral-100 h-full w-fit rounded-md transition-all hover:ring-neutral-500 hover:ring-1 p-2"
@@ -446,7 +446,7 @@ const Taskbar = () => {
<div className="flex space-x-4 h-full w-fit items-center">
<SiArchlinux
color={SiArchlinuxHex}
className="h-full w-fit transition-transform hover:scale-110"
className="size-10 transition-transform hover:scale-110"
/>
<div className="w-8 h-full">
<div className="w-full h-1/2 bg-blue-900 outline-1 outline-blue-600 transition-all hover:bg-blue-700 hover:outline-blue-400" />
@@ -454,19 +454,19 @@ const Taskbar = () => {
</div>
<Icon
path={mdiFolder}
className="text-blue-500 h-full w-fit transition-transform hover:scale-110 border-b-2"
className="text-blue-500 size-10 transition-transform hover:scale-110 border-b-2"
/>
<SiVscodium
color={SiVscodiumHex}
className="h-full w-fit transition-transform hover:scale-110"
className="size-10 transition-transform hover:scale-110"
/>
<Icon
path={mdiConsole}
className="text-neutral-400 h-full w-fit transition-transform hover:scale-110"
className="text-neutral-400 size-10 transition-transform hover:scale-110"
/>
<SiFirefoxbrowser
color={SiFirefoxbrowserHex}
className="h-full w-fit transition-transform hover:scale-110"
className="size-10 transition-transform hover:scale-110"
/>
</div>
<div className="flex space-x-2 h-full w-fit items-center">
+5
View File
@@ -0,0 +1,5 @@
import { type HTMLAttributes } from "react";
export const Info = (props: HTMLAttributes<HTMLParagraphElement>) => (
<p {...props}>{process.env.NEXT_PUBLIC_APP_VERSION}</p>
);
+1
View File
@@ -5,6 +5,7 @@ import { prettify } from "awesome-ajv-errors";
export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
console.info(`Portfolio v2 ${process.env.NEXT_PUBLIC_APP_VERSION}`);
const phoneRegex = /^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}$/;
const ajv = new Ajv({ allErrors: true });
-8
View File
@@ -26,10 +26,6 @@ export type GLTFResult = GLTF & {
PenBody: Mesh;
PenTip: Mesh;
CellphoneMesh: Mesh;
Top: Mesh;
OfficePhoneBody: Mesh;
Screen: Mesh;
Buttons: Mesh;
Handle: Mesh;
HeadphonesBody: Mesh;
HeadphonesAccent: Mesh;
@@ -74,10 +70,6 @@ export type GLTFResult = GLTF & {
PenBodyMaterial: MeshStandardMaterial;
TipMaterial: MeshStandardMaterial;
CellphoneMaterial: MeshStandardMaterial;
TopMaterial: MeshStandardMaterial;
OfficePhoneBodyMaterial: MeshStandardMaterial;
ScreenMaterial: MeshStandardMaterial;
HandleAndButtonsMaterial: MeshStandardMaterial;
HeadphonesBodyMaterial: MeshStandardMaterial;
HeadphonesAccentMaterial: MeshStandardMaterial;
CoverMaterial: MeshStandardMaterial;
+5
View File
@@ -0,0 +1,5 @@
export default function isMobile(): boolean {
return typeof window === "undefined"
? false
: window.matchMedia?.("(pointer: coarse)").matches;
}
+438 -10
View File
@@ -403,6 +403,48 @@ __metadata:
languageName: node
linkType: hard
"@jridgewell/gen-mapping@npm:^0.3.2":
version: 0.3.8
resolution: "@jridgewell/gen-mapping@npm:0.3.8"
dependencies:
"@jridgewell/set-array": "npm:^1.2.1"
"@jridgewell/sourcemap-codec": "npm:^1.4.10"
"@jridgewell/trace-mapping": "npm:^0.3.24"
checksum: 10c0/c668feaf86c501d7c804904a61c23c67447b2137b813b9ce03eca82cb9d65ac7006d766c218685d76e3d72828279b6ee26c347aa1119dab23fbaf36aed51585a
languageName: node
linkType: hard
"@jridgewell/resolve-uri@npm:^3.1.0":
version: 3.1.2
resolution: "@jridgewell/resolve-uri@npm:3.1.2"
checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e
languageName: node
linkType: hard
"@jridgewell/set-array@npm:^1.2.1":
version: 1.2.1
resolution: "@jridgewell/set-array@npm:1.2.1"
checksum: 10c0/2a5aa7b4b5c3464c895c802d8ae3f3d2b92fcbe84ad12f8d0bfbb1f5ad006717e7577ee1fd2eac00c088abe486c7adb27976f45d2941ff6b0b92b2c3302c60f4
languageName: node
linkType: hard
"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14":
version: 1.5.0
resolution: "@jridgewell/sourcemap-codec@npm:1.5.0"
checksum: 10c0/2eb864f276eb1096c3c11da3e9bb518f6d9fc0023c78344cdc037abadc725172c70314bdb360f2d4b7bffec7f5d657ce006816bc5d4ecb35e61b66132db00c18
languageName: node
linkType: hard
"@jridgewell/trace-mapping@npm:^0.3.24":
version: 0.3.25
resolution: "@jridgewell/trace-mapping@npm:0.3.25"
dependencies:
"@jridgewell/resolve-uri": "npm:^3.1.0"
"@jridgewell/sourcemap-codec": "npm:^1.4.14"
checksum: 10c0/3d1ce6ebc69df9682a5a8896b414c6537e428a1d68b02fcc8363b04284a8ca0df04d0ee3013132252ab14f2527bc13bea6526a912ecb5658f0e39fd2860b4df4
languageName: node
linkType: hard
"@mdi/js@npm:^7.4.47":
version: 7.4.47
resolution: "@mdi/js@npm:7.4.47"
@@ -979,6 +1021,13 @@ __metadata:
languageName: node
linkType: hard
"@types/lodash@npm:^4":
version: 4.17.16
resolution: "@types/lodash@npm:4.17.16"
checksum: 10c0/cf017901b8ab1d7aabc86d5189d9288f4f99f19a75caf020c0e2c77b8d4cead4db0d0b842d009b029339f92399f49f34377dd7c2721053388f251778b4c23534
languageName: node
linkType: hard
"@types/node@npm:^20":
version: 20.17.30
resolution: "@types/node@npm:20.17.30"
@@ -1410,6 +1459,30 @@ __metadata:
languageName: node
linkType: hard
"any-promise@npm:^1.0.0":
version: 1.3.0
resolution: "any-promise@npm:1.3.0"
checksum: 10c0/60f0298ed34c74fef50daab88e8dab786036ed5a7fad02e012ab57e376e0a0b4b29e83b95ea9b5e7d89df762f5f25119b83e00706ecaccb22cfbacee98d74889
languageName: node
linkType: hard
"anymatch@npm:~3.1.2":
version: 3.1.3
resolution: "anymatch@npm:3.1.3"
dependencies:
normalize-path: "npm:^3.0.0"
picomatch: "npm:^2.0.4"
checksum: 10c0/57b06ae984bc32a0d22592c87384cd88fe4511b1dd7581497831c56d41939c8a001b28e7b853e1450f2bf61992dfcaa8ae2d0d161a0a90c4fb631ef07098fbac
languageName: node
linkType: hard
"arg@npm:^5.0.2":
version: 5.0.2
resolution: "arg@npm:5.0.2"
checksum: 10c0/ccaf86f4e05d342af6666c569f844bec426595c567d32a8289715087825c2ca7edd8a3d204e4d2fb2aa4602e09a57d0c13ea8c9eea75aac3dbb4af5514e6800e
languageName: node
linkType: hard
"argparse@npm:^2.0.1":
version: 2.0.1
resolution: "argparse@npm:2.0.1"
@@ -1617,6 +1690,13 @@ __metadata:
languageName: node
linkType: hard
"binary-extensions@npm:^2.0.0":
version: 2.3.0
resolution: "binary-extensions@npm:2.3.0"
checksum: 10c0/75a59cafc10fb12a11d510e77110c6c7ae3f4ca22463d52487709ca7f18f69d886aa387557cc9864fbdb10153d0bdb4caacabf11541f55e89ed6e18d12ece2b5
languageName: node
linkType: hard
"bl@npm:^4.0.3":
version: 4.1.0
resolution: "bl@npm:4.1.0"
@@ -1647,7 +1727,7 @@ __metadata:
languageName: node
linkType: hard
"braces@npm:^3.0.3":
"braces@npm:^3.0.3, braces@npm:~3.0.2":
version: 3.0.3
resolution: "braces@npm:3.0.3"
dependencies:
@@ -1744,6 +1824,13 @@ __metadata:
languageName: node
linkType: hard
"camelcase-css@npm:^2.0.1":
version: 2.0.1
resolution: "camelcase-css@npm:2.0.1"
checksum: 10c0/1a1a3137e8a781e6cbeaeab75634c60ffd8e27850de410c162cce222ea331cd1ba5364e8fb21c95e5ca76f52ac34b81a090925ca00a87221355746d049c6e273
languageName: node
linkType: hard
"camera-controls@npm:^2.9.0":
version: 2.10.1
resolution: "camera-controls@npm:2.10.1"
@@ -1795,6 +1882,25 @@ __metadata:
languageName: node
linkType: hard
"chokidar@npm:^3.6.0":
version: 3.6.0
resolution: "chokidar@npm:3.6.0"
dependencies:
anymatch: "npm:~3.1.2"
braces: "npm:~3.0.2"
fsevents: "npm:~2.3.2"
glob-parent: "npm:~5.1.2"
is-binary-path: "npm:~2.1.0"
is-glob: "npm:~4.0.1"
normalize-path: "npm:~3.0.0"
readdirp: "npm:~3.6.0"
dependenciesMeta:
fsevents:
optional: true
checksum: 10c0/8361dcd013f2ddbe260eacb1f3cb2f2c6f2b0ad118708a343a5ed8158941a39cb8fb1d272e0f389712e74ee90ce8ba864eece9e0e62b9705cb468a2f6d917462
languageName: node
linkType: hard
"chownr@npm:^1.1.1":
version: 1.1.4
resolution: "chownr@npm:1.1.4"
@@ -1859,6 +1965,13 @@ __metadata:
languageName: node
linkType: hard
"commander@npm:^4.0.0":
version: 4.1.1
resolution: "commander@npm:4.1.1"
checksum: 10c0/84a76c08fe6cc08c9c93f62ac573d2907d8e79138999312c92d4155bc2325d487d64d13f669b2000c9f8caf70493c1be2dac74fec3c51d5a04f8bc3ae1830bab
languageName: node
linkType: hard
"concat-map@npm:0.0.1":
version: 0.0.1
resolution: "concat-map@npm:0.0.1"
@@ -1889,6 +2002,15 @@ __metadata:
languageName: node
linkType: hard
"cssesc@npm:^3.0.0":
version: 3.0.0
resolution: "cssesc@npm:3.0.0"
bin:
cssesc: bin/cssesc
checksum: 10c0/6bcfd898662671be15ae7827120472c5667afb3d7429f1f917737f3bf84c4176003228131b643ae74543f17a394446247df090c597bb9a728cce298606ed0aa7
languageName: node
linkType: hard
"csstype@npm:^3.0.2":
version: 3.1.3
resolution: "csstype@npm:3.1.3"
@@ -2032,6 +2154,20 @@ __metadata:
languageName: node
linkType: hard
"didyoumean@npm:^1.2.2":
version: 1.2.2
resolution: "didyoumean@npm:1.2.2"
checksum: 10c0/95d0b53d23b851aacff56dfadb7ecfedce49da4232233baecfeecb7710248c4aa03f0aa8995062f0acafaf925adf8536bd7044a2e68316fd7d411477599bc27b
languageName: node
linkType: hard
"dlv@npm:^1.1.3":
version: 1.1.3
resolution: "dlv@npm:1.1.3"
checksum: 10c0/03eb4e769f19a027fd5b43b59e8a05e3fd2100ac239ebb0bf9a745de35d449e2f25cfaf3aa3934664551d72856f4ae8b7822016ce5c42c2d27c18ae79429ec42
languageName: node
linkType: hard
"doctrine@npm:^2.1.0":
version: 2.1.0
resolution: "doctrine@npm:2.1.0"
@@ -2687,6 +2823,15 @@ __metadata:
languageName: node
linkType: hard
"flat@npm:^6.0.1":
version: 6.0.1
resolution: "flat@npm:6.0.1"
bin:
flat: cli.js
checksum: 10c0/9dc0dbe6e2acc012512a53130d9ba1c82c1a596cdca91b23d11716348361c4a68928409bb4433c4493a17595c3efd0cab9f09e23dd3f9962a58af225c3efc23a
languageName: node
linkType: hard
"flatted@npm:^3.2.9":
version: 3.3.3
resolution: "flatted@npm:3.3.3"
@@ -2729,6 +2874,25 @@ __metadata:
languageName: node
linkType: hard
"fsevents@npm:~2.3.2":
version: 2.3.3
resolution: "fsevents@npm:2.3.3"
dependencies:
node-gyp: "npm:latest"
checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60
conditions: os=darwin
languageName: node
linkType: hard
"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin<compat/fsevents>":
version: 2.3.3
resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin<compat/fsevents>::version=2.3.3&hash=df0bf1"
dependencies:
node-gyp: "npm:latest"
conditions: os=darwin
languageName: node
linkType: hard
"function-bind@npm:^1.1.2":
version: 1.1.2
resolution: "function-bind@npm:1.1.2"
@@ -2812,7 +2976,7 @@ __metadata:
languageName: node
linkType: hard
"glob-parent@npm:^5.1.2":
"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2":
version: 5.1.2
resolution: "glob-parent@npm:5.1.2"
dependencies:
@@ -2830,7 +2994,7 @@ __metadata:
languageName: node
linkType: hard
"glob@npm:^10.2.2":
"glob@npm:^10.2.2, glob@npm:^10.3.10":
version: 10.4.5
resolution: "glob@npm:10.4.5"
dependencies:
@@ -3104,6 +3268,15 @@ __metadata:
languageName: node
linkType: hard
"is-binary-path@npm:~2.1.0":
version: 2.1.0
resolution: "is-binary-path@npm:2.1.0"
dependencies:
binary-extensions: "npm:^2.0.0"
checksum: 10c0/a16eaee59ae2b315ba36fad5c5dcaf8e49c3e27318f8ab8fa3cdb8772bf559c8d1ba750a589c2ccb096113bb64497084361a25960899cb6172a6925ab6123d38
languageName: node
linkType: hard
"is-boolean-object@npm:^1.2.1":
version: 1.2.2
resolution: "is-boolean-object@npm:1.2.2"
@@ -3195,7 +3368,7 @@ __metadata:
languageName: node
linkType: hard
"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3":
"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1":
version: 4.0.3
resolution: "is-glob@npm:4.0.3"
dependencies:
@@ -3378,6 +3551,15 @@ __metadata:
languageName: node
linkType: hard
"jiti@npm:^1.21.6":
version: 1.21.7
resolution: "jiti@npm:1.21.7"
bin:
jiti: bin/jiti.js
checksum: 10c0/77b61989c758ff32407cdae8ddc77f85e18e1a13fc4977110dbd2e05fc761842f5f71bce684d9a01316e1c4263971315a111385759951080bbfe17cbb5de8f7a
languageName: node
linkType: hard
"jiti@npm:^2.4.2":
version: 2.4.2
resolution: "jiti@npm:2.4.2"
@@ -3663,6 +3845,20 @@ __metadata:
languageName: node
linkType: hard
"lilconfig@npm:^3.0.0, lilconfig@npm:^3.1.3":
version: 3.1.3
resolution: "lilconfig@npm:3.1.3"
checksum: 10c0/f5604e7240c5c275743561442fbc5abf2a84ad94da0f5adc71d25e31fa8483048de3dcedcb7a44112a942fed305fd75841cdf6c9681c7f640c63f1049e9a5dcc
languageName: node
linkType: hard
"lines-and-columns@npm:^1.1.6":
version: 1.2.4
resolution: "lines-and-columns@npm:1.2.4"
checksum: 10c0/3da6ee62d4cd9f03f5dc90b4df2540fb85b352081bee77fe4bbcd12c9000ead7f35e0a38b8d09a9bb99b13223446dd8689ff3c4959807620726d788701a83d2d
languageName: node
linkType: hard
"locate-path@npm:^6.0.0":
version: 6.0.0
resolution: "locate-path@npm:6.0.0"
@@ -3679,6 +3875,13 @@ __metadata:
languageName: node
linkType: hard
"lodash@npm:^4.17.21":
version: 4.17.21
resolution: "lodash@npm:4.17.21"
checksum: 10c0/d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c
languageName: node
linkType: hard
"loose-envify@npm:^1.0.0, loose-envify@npm:^1.4.0":
version: 1.4.0
resolution: "loose-envify@npm:1.4.0"
@@ -3923,6 +4126,17 @@ __metadata:
languageName: node
linkType: hard
"mz@npm:^2.7.0":
version: 2.7.0
resolution: "mz@npm:2.7.0"
dependencies:
any-promise: "npm:^1.0.0"
object-assign: "npm:^4.0.1"
thenify-all: "npm:^1.0.0"
checksum: 10c0/103114e93f87362f0b56ab5b2e7245051ad0276b646e3902c98397d18bb8f4a77f2ea4a2c9d3ad516034ea3a56553b60d3f5f78220001ca4c404bd711bd0af39
languageName: node
linkType: hard
"nanoid@npm:^3.3.6, nanoid@npm:^3.3.8":
version: 3.3.11
resolution: "nanoid@npm:3.3.11"
@@ -4063,13 +4277,27 @@ __metadata:
languageName: node
linkType: hard
"object-assign@npm:^4.1.1":
"normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0":
version: 3.0.0
resolution: "normalize-path@npm:3.0.0"
checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046
languageName: node
linkType: hard
"object-assign@npm:^4.0.1, object-assign@npm:^4.1.1":
version: 4.1.1
resolution: "object-assign@npm:4.1.1"
checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414
languageName: node
linkType: hard
"object-hash@npm:^3.0.0":
version: 3.0.0
resolution: "object-hash@npm:3.0.0"
checksum: 10c0/a06844537107b960c1c8b96cd2ac8592a265186bfa0f6ccafe0d34eabdb526f6fa81da1f37c43df7ed13b12a4ae3457a16071603bcd39d8beddb5f08c37b0f47
languageName: node
linkType: hard
"object-inspect@npm:^1.13.3":
version: 1.13.4
resolution: "object-inspect@npm:1.13.4"
@@ -4280,7 +4508,7 @@ __metadata:
languageName: node
linkType: hard
"picomatch@npm:^2.3.1":
"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1":
version: 2.3.1
resolution: "picomatch@npm:2.3.1"
checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be
@@ -4294,6 +4522,20 @@ __metadata:
languageName: node
linkType: hard
"pify@npm:^2.3.0":
version: 2.3.0
resolution: "pify@npm:2.3.0"
checksum: 10c0/551ff8ab830b1052633f59cb8adc9ae8407a436e06b4a9718bcb27dc5844b83d535c3a8512b388b6062af65a98c49bdc0dd523d8b2617b188f7c8fee457158dc
languageName: node
linkType: hard
"pirates@npm:^4.0.1":
version: 4.0.7
resolution: "pirates@npm:4.0.7"
checksum: 10c0/a51f108dd811beb779d58a76864bbd49e239fa40c7984cd11596c75a121a8cc789f1c8971d8bb15f0dbf9d48b76c05bb62fcbce840f89b688c0fa64b37e8478a
languageName: node
linkType: hard
"portfolio-2@workspace:.":
version: 0.0.0-use.local
resolution: "portfolio-2@workspace:."
@@ -4307,6 +4549,7 @@ __metadata:
"@react-three/drei": "npm:^10.0.7"
"@react-three/fiber": "npm:^9.1.2"
"@tailwindcss/postcss": "npm:^4"
"@types/lodash": "npm:^4"
"@types/node": "npm:^20"
"@types/react": "npm:^19"
"@types/react-dom": "npm:^19"
@@ -4316,11 +4559,13 @@ __metadata:
awesome-ajv-errors: "npm:^5.1.0"
eslint: "npm:^9"
eslint-config-next: "npm:15.3.0"
lodash: "npm:^4.17.21"
next: "npm:15.3.0"
react: "npm:^19.1.0"
react-dom: "npm:^19.1.0"
react-pdf: "npm:^9.2.1"
tailwindcss: "npm:^4"
tailwindcss-text-fill-stroke: "npm:^2.0.0-beta.3"
three: "npm:^0.175.0"
typescript: "npm:^5"
languageName: unknown
@@ -4333,6 +4578,76 @@ __metadata:
languageName: node
linkType: hard
"postcss-import@npm:^15.1.0":
version: 15.1.0
resolution: "postcss-import@npm:15.1.0"
dependencies:
postcss-value-parser: "npm:^4.0.0"
read-cache: "npm:^1.0.0"
resolve: "npm:^1.1.7"
peerDependencies:
postcss: ^8.0.0
checksum: 10c0/518aee5c83ea6940e890b0be675a2588db68b2582319f48c3b4e06535a50ea6ee45f7e63e4309f8754473245c47a0372632378d1d73d901310f295a92f26f17b
languageName: node
linkType: hard
"postcss-js@npm:^4.0.1":
version: 4.0.1
resolution: "postcss-js@npm:4.0.1"
dependencies:
camelcase-css: "npm:^2.0.1"
peerDependencies:
postcss: ^8.4.21
checksum: 10c0/af35d55cb873b0797d3b42529514f5318f447b134541844285c9ac31a17497297eb72296902967911bb737a75163441695737300ce2794e3bd8c70c13a3b106e
languageName: node
linkType: hard
"postcss-load-config@npm:^4.0.2":
version: 4.0.2
resolution: "postcss-load-config@npm:4.0.2"
dependencies:
lilconfig: "npm:^3.0.0"
yaml: "npm:^2.3.4"
peerDependencies:
postcss: ">=8.0.9"
ts-node: ">=9.0.0"
peerDependenciesMeta:
postcss:
optional: true
ts-node:
optional: true
checksum: 10c0/3d7939acb3570b0e4b4740e483d6e555a3e2de815219cb8a3c8fc03f575a6bde667443aa93369c0be390af845cb84471bf623e24af833260de3a105b78d42519
languageName: node
linkType: hard
"postcss-nested@npm:^6.2.0":
version: 6.2.0
resolution: "postcss-nested@npm:6.2.0"
dependencies:
postcss-selector-parser: "npm:^6.1.1"
peerDependencies:
postcss: ^8.2.14
checksum: 10c0/7f9c3f2d764191a39364cbdcec350f26a312431a569c9ef17408021424726b0d67995ff5288405e3724bb7152a4c92f73c027e580ec91e798800ed3c52e2bc6e
languageName: node
linkType: hard
"postcss-selector-parser@npm:^6.1.1, postcss-selector-parser@npm:^6.1.2":
version: 6.1.2
resolution: "postcss-selector-parser@npm:6.1.2"
dependencies:
cssesc: "npm:^3.0.0"
util-deprecate: "npm:^1.0.2"
checksum: 10c0/523196a6bd8cf660bdf537ad95abd79e546d54180f9afb165a4ab3e651ac705d0f8b8ce6b3164fb9e3279ce482c5f751a69eb2d3a1e8eb0fd5e82294fb3ef13e
languageName: node
linkType: hard
"postcss-value-parser@npm:^4.0.0":
version: 4.2.0
resolution: "postcss-value-parser@npm:4.2.0"
checksum: 10c0/f4142a4f56565f77c1831168e04e3effd9ffcc5aebaf0f538eee4b2d465adfd4b85a44257bb48418202a63806a7da7fe9f56c330aebb3cac898e46b4cbf49161
languageName: node
linkType: hard
"postcss@npm:8.4.31":
version: 8.4.31
resolution: "postcss@npm:8.4.31"
@@ -4344,7 +4659,7 @@ __metadata:
languageName: node
linkType: hard
"postcss@npm:^8.4.41":
"postcss@npm:^8.4.41, postcss@npm:^8.4.47":
version: 8.5.3
resolution: "postcss@npm:8.5.3"
dependencies:
@@ -4539,6 +4854,15 @@ __metadata:
languageName: node
linkType: hard
"read-cache@npm:^1.0.0":
version: 1.0.0
resolution: "read-cache@npm:1.0.0"
dependencies:
pify: "npm:^2.3.0"
checksum: 10c0/90cb2750213c7dd7c80cb420654344a311fdec12944e81eb912cd82f1bc92aea21885fa6ce442e3336d9fccd663b8a7a19c46d9698e6ca55620848ab932da814
languageName: node
linkType: hard
"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0":
version: 3.6.2
resolution: "readable-stream@npm:3.6.2"
@@ -4550,6 +4874,15 @@ __metadata:
languageName: node
linkType: hard
"readdirp@npm:~3.6.0":
version: 3.6.0
resolution: "readdirp@npm:3.6.0"
dependencies:
picomatch: "npm:^2.2.1"
checksum: 10c0/6fa848cf63d1b82ab4e985f4cf72bd55b7dcfd8e0a376905804e48c3634b7e749170940ba77b32804d5fe93b3cc521aa95a8d7e7d725f830da6d93f3669ce66b
languageName: node
linkType: hard
"reflect.getprototypeof@npm:^1.0.6, reflect.getprototypeof@npm:^1.0.9":
version: 1.0.10
resolution: "reflect.getprototypeof@npm:1.0.10"
@@ -4608,7 +4941,7 @@ __metadata:
languageName: node
linkType: hard
"resolve@npm:^1.22.4":
"resolve@npm:^1.1.7, resolve@npm:^1.22.4, resolve@npm:^1.22.8":
version: 1.22.10
resolution: "resolve@npm:1.22.10"
dependencies:
@@ -4634,7 +4967,7 @@ __metadata:
languageName: node
linkType: hard
"resolve@patch:resolve@npm%3A^1.22.4#optional!builtin<compat/resolve>":
"resolve@patch:resolve@npm%3A^1.1.7#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin<compat/resolve>":
version: 1.22.10
resolution: "resolve@patch:resolve@npm%3A1.22.10#optional!builtin<compat/resolve>::version=1.22.10&hash=c3c19d"
dependencies:
@@ -5221,6 +5554,24 @@ __metadata:
languageName: node
linkType: hard
"sucrase@npm:^3.35.0":
version: 3.35.0
resolution: "sucrase@npm:3.35.0"
dependencies:
"@jridgewell/gen-mapping": "npm:^0.3.2"
commander: "npm:^4.0.0"
glob: "npm:^10.3.10"
lines-and-columns: "npm:^1.1.6"
mz: "npm:^2.7.0"
pirates: "npm:^4.0.1"
ts-interface-checker: "npm:^0.1.9"
bin:
sucrase: bin/sucrase
sucrase-node: bin/sucrase-node
checksum: 10c0/ac85f3359d2c2ecbf5febca6a24ae9bf96c931f05fde533c22a94f59c6a74895e5d5f0e871878dfd59c2697a75ebb04e4b2224ef0bfc24ca1210735c2ec191ef
languageName: node
linkType: hard
"supports-color@npm:^7.0.0, supports-color@npm:^7.1.0":
version: 7.2.0
resolution: "supports-color@npm:7.2.0"
@@ -5256,6 +5607,16 @@ __metadata:
languageName: node
linkType: hard
"tailwindcss-text-fill-stroke@npm:^2.0.0-beta.3":
version: 2.0.0-beta.3
resolution: "tailwindcss-text-fill-stroke@npm:2.0.0-beta.3"
dependencies:
flat: "npm:^6.0.1"
tailwindcss: "npm:^3.4.17"
checksum: 10c0/3f3510479fb7cbc5af28d6bd74edcd4e6d6ca354dca6c44a95bdb65a412f084913e2fb68774449171105298d7faffa744089810e9d7b3ff21a2478dde9581dc8
languageName: node
linkType: hard
"tailwindcss@npm:4.1.4, tailwindcss@npm:^4":
version: 4.1.4
resolution: "tailwindcss@npm:4.1.4"
@@ -5263,6 +5624,39 @@ __metadata:
languageName: node
linkType: hard
"tailwindcss@npm:^3.4.17":
version: 3.4.17
resolution: "tailwindcss@npm:3.4.17"
dependencies:
"@alloc/quick-lru": "npm:^5.2.0"
arg: "npm:^5.0.2"
chokidar: "npm:^3.6.0"
didyoumean: "npm:^1.2.2"
dlv: "npm:^1.1.3"
fast-glob: "npm:^3.3.2"
glob-parent: "npm:^6.0.2"
is-glob: "npm:^4.0.3"
jiti: "npm:^1.21.6"
lilconfig: "npm:^3.1.3"
micromatch: "npm:^4.0.8"
normalize-path: "npm:^3.0.0"
object-hash: "npm:^3.0.0"
picocolors: "npm:^1.1.1"
postcss: "npm:^8.4.47"
postcss-import: "npm:^15.1.0"
postcss-js: "npm:^4.0.1"
postcss-load-config: "npm:^4.0.2"
postcss-nested: "npm:^6.2.0"
postcss-selector-parser: "npm:^6.1.2"
resolve: "npm:^1.22.8"
sucrase: "npm:^3.35.0"
bin:
tailwind: lib/cli.js
tailwindcss: lib/cli.js
checksum: 10c0/cc42c6e7fdf88a5507a0d7fea37f1b4122bec158977f8c017b2ae6828741f9e6f8cb90282c6bf2bd5951fd1220a53e0a50ca58f5c1c00eb7f5d9f8b80dc4523c
languageName: node
linkType: hard
"tapable@npm:^2.2.0":
version: 2.2.1
resolution: "tapable@npm:2.2.1"
@@ -5319,6 +5713,24 @@ __metadata:
languageName: node
linkType: hard
"thenify-all@npm:^1.0.0":
version: 1.6.0
resolution: "thenify-all@npm:1.6.0"
dependencies:
thenify: "npm:>= 3.1.0 < 4"
checksum: 10c0/9b896a22735e8122754fe70f1d65f7ee691c1d70b1f116fda04fea103d0f9b356e3676cb789506e3909ae0486a79a476e4914b0f92472c2e093d206aed4b7d6b
languageName: node
linkType: hard
"thenify@npm:>= 3.1.0 < 4":
version: 3.3.1
resolution: "thenify@npm:3.3.1"
dependencies:
any-promise: "npm:^1.0.0"
checksum: 10c0/f375aeb2b05c100a456a30bc3ed07ef03a39cbdefe02e0403fb714b8c7e57eeaad1a2f5c4ecfb9ce554ce3db9c2b024eba144843cd9e344566d9fcee73b04767
languageName: node
linkType: hard
"three-mesh-bvh@npm:^0.8.3":
version: 0.8.3
resolution: "three-mesh-bvh@npm:0.8.3"
@@ -5423,6 +5835,13 @@ __metadata:
languageName: node
linkType: hard
"ts-interface-checker@npm:^0.1.9":
version: 0.1.13
resolution: "ts-interface-checker@npm:0.1.13"
checksum: 10c0/232509f1b84192d07b81d1e9b9677088e590ac1303436da1e92b296e9be8e31ea042e3e1fd3d29b1742ad2c959e95afe30f63117b8f1bc3a3850070a5142fea7
languageName: node
linkType: hard
"tsconfig-paths@npm:^3.15.0":
version: 3.15.0
resolution: "tsconfig-paths@npm:3.15.0"
@@ -5661,7 +6080,7 @@ __metadata:
languageName: node
linkType: hard
"util-deprecate@npm:^1.0.1":
"util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2":
version: 1.0.2
resolution: "util-deprecate@npm:1.0.2"
checksum: 10c0/41a5bdd214df2f6c3ecf8622745e4a366c4adced864bc3c833739791aeeeb1838119af7daed4ba36428114b5c67dcda034a79c882e97e43c03e66a4dd7389942
@@ -5831,6 +6250,15 @@ __metadata:
languageName: node
linkType: hard
"yaml@npm:^2.3.4":
version: 2.7.1
resolution: "yaml@npm:2.7.1"
bin:
yaml: bin.mjs
checksum: 10c0/ee2126398ab7d1fdde566b4013b68e36930b9e6d8e68b6db356875c99614c10d678b6f45597a145ff6d63814961221fc305bf9242af8bf7450177f8a68537590
languageName: node
linkType: hard
"yocto-queue@npm:^0.1.0":
version: 0.1.0
resolution: "yocto-queue@npm:0.1.0"