Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
89f9194420
|
|||
|
53fa9c815f
|
|||
|
25e55272d9
|
|||
|
2a1258a820
|
|||
|
01ac4d61cf
|
|||
|
a6190c2694
|
|||
|
f4b7cef249
|
|||
|
4ff5e016b0
|
|||
|
84b1c36073
|
+14
-6
@@ -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
@@ -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"]
|
||||
|
||||
+5
-1
@@ -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;
|
||||
|
||||
@@ -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.
@@ -1 +1,2 @@
|
||||
@import "tailwindcss";
|
||||
@plugin "tailwindcss-text-fill-stroke";
|
||||
|
||||
+1
-5
@@ -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,
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
BackSide,
|
||||
} from "three";
|
||||
import { Billboard, Text } from "@react-three/drei";
|
||||
import { useConfig } from "../hooks/useConfig";
|
||||
|
||||
interface ButtonsProps {
|
||||
menuTraversal: Stack<View>;
|
||||
@@ -23,7 +22,6 @@ interface ButtonsProps {
|
||||
pendingView: View | null;
|
||||
setPendingView: Dispatch<SetStateAction<View | null>>;
|
||||
goPreviousView(): void;
|
||||
phoneHovered: boolean;
|
||||
currentView: View;
|
||||
}
|
||||
|
||||
@@ -35,11 +33,8 @@ export const Buttons = ({
|
||||
pendingView,
|
||||
setPendingView,
|
||||
goPreviousView,
|
||||
phoneHovered,
|
||||
currentView,
|
||||
}: ButtonsProps) => {
|
||||
const config = useConfig();
|
||||
|
||||
const [hovered, setHovered] = useState<View | null>(null);
|
||||
|
||||
const desktopRef = useDisableRaycast(currentView !== View.MainView);
|
||||
@@ -47,8 +42,6 @@ export const Buttons = ({
|
||||
const sideRef = useDisableRaycast(currentView !== View.MainView);
|
||||
const cellphoneRef = useDisableRaycast(currentView !== View.DesktopView);
|
||||
const pcRef = useDisableRaycast(currentView !== View.DesktopView);
|
||||
const resumeRef = useDisableRaycast(currentView !== View.SideView);
|
||||
const phoneRef = useDisableRaycast(currentView !== View.SideView);
|
||||
|
||||
const goToView = useCallback(
|
||||
(view: View) => {
|
||||
@@ -89,12 +82,12 @@ export const Buttons = ({
|
||||
}, [menuTraversal, pendingView, setPendingView]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hovered || phoneHovered) {
|
||||
if (hovered) {
|
||||
document.body.style.cursor = "pointer";
|
||||
} else {
|
||||
document.body.style.cursor = "auto";
|
||||
}
|
||||
}, [hovered, phoneHovered]);
|
||||
}, [hovered]);
|
||||
|
||||
const mainMenuSpring = useThreeSpring<MeshStandardMaterial>({
|
||||
opacity: currentView === View.MainView ? 1 : 0,
|
||||
@@ -116,14 +109,11 @@ export const Buttons = ({
|
||||
const printerMenuSpring = useThreeSpring<MeshStandardMaterial>({
|
||||
opacity: currentView === View.PrinterView ? 1 : 0,
|
||||
});
|
||||
const sideMenuSpring = useThreeSpring<MeshStandardMaterial>({
|
||||
opacity: currentView === View.SideView ? 1 : 0,
|
||||
});
|
||||
const desktopViewSpring = useThreeSpring<BufferGeometry>({
|
||||
scale: hovered === View.DesktopView ? 1.25 : 1,
|
||||
});
|
||||
const sideViewSpring = useThreeSpring<BufferGeometry>({
|
||||
scale: hovered === View.SideView ? 1.25 : 1,
|
||||
const resumeViewSpring = useThreeSpring<BufferGeometry>({
|
||||
scale: hovered === View.ResumeView ? 1.25 : 1,
|
||||
});
|
||||
const cellphoneViewSpring = useThreeSpring<BufferGeometry>({
|
||||
scale: hovered === View.CellphoneView ? 1.25 : 1,
|
||||
@@ -134,15 +124,9 @@ export const Buttons = ({
|
||||
const creditsViewSpring = useThreeSpring<BufferGeometry>({
|
||||
scale: hovered === View.CreditsView ? 1.25 : 1,
|
||||
});
|
||||
const resumeViewSpring = useThreeSpring<BufferGeometry>({
|
||||
scale: hovered === View.ResumeView ? 1.25 : 1,
|
||||
});
|
||||
const printerViewSpring = useThreeSpring<BufferGeometry>({
|
||||
scale: hovered === View.PrinterView ? 1.25 : 1,
|
||||
});
|
||||
const phoneViewSpring = useThreeSpring<BufferGeometry>({
|
||||
scale: hovered === View.PhoneView ? 1.25 : 1,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -242,16 +226,16 @@ export const Buttons = ({
|
||||
position={[2.525, 6, 6.75]}
|
||||
onClick={
|
||||
currentView === View.MainView
|
||||
? () => goToView(View.SideView)
|
||||
? () => goToView(View.ResumeView)
|
||||
: undefined
|
||||
}
|
||||
onPointerOver={
|
||||
currentView === View.MainView
|
||||
? () => setHovered(View.SideView)
|
||||
? () => setHovered(View.ResumeView)
|
||||
: undefined
|
||||
}
|
||||
onPointerOut={() => setHovered(null)}
|
||||
scale={sideViewSpring.scale}
|
||||
scale={resumeViewSpring.scale}
|
||||
>
|
||||
<sphereGeometry args={[0.6, 32, 32]} />
|
||||
<animated.meshStandardMaterial
|
||||
@@ -260,7 +244,10 @@ export const Buttons = ({
|
||||
opacity={mainMenuSpring.opacity}
|
||||
/>
|
||||
</animated.mesh>
|
||||
<animated.mesh position={[2.525, 6, 6.75]} scale={sideViewSpring.scale}>
|
||||
<animated.mesh
|
||||
position={[2.525, 6, 6.75]}
|
||||
scale={resumeViewSpring.scale}
|
||||
>
|
||||
<sphereGeometry args={[0.65, 32, 32]} />
|
||||
<animated.meshStandardMaterial
|
||||
transparent
|
||||
@@ -277,7 +264,7 @@ export const Buttons = ({
|
||||
fillOpacity={mainMenuSpring.opacity}
|
||||
outlineOpacity={mainMenuSpring.opacity}
|
||||
>
|
||||
Resume & Contact
|
||||
Resume
|
||||
</AnimatedText>
|
||||
</Billboard>
|
||||
</group>
|
||||
@@ -450,105 +437,6 @@ export const Buttons = ({
|
||||
</AnimatedText>
|
||||
</Billboard>
|
||||
</group>
|
||||
{/* Side Menu */}
|
||||
<group>
|
||||
<animated.mesh
|
||||
ref={resumeRef}
|
||||
position={[2.845, 4.75, 6.454]}
|
||||
onClick={
|
||||
currentView === View.SideView
|
||||
? () => goToView(View.ResumeView)
|
||||
: undefined
|
||||
}
|
||||
onPointerOver={
|
||||
currentView === View.SideView
|
||||
? () => setHovered(View.ResumeView)
|
||||
: undefined
|
||||
}
|
||||
onPointerOut={() => setHovered(null)}
|
||||
scale={resumeViewSpring.scale}
|
||||
>
|
||||
<sphereGeometry args={[0.175, 32, 32]} />
|
||||
<animated.meshStandardMaterial
|
||||
transparent
|
||||
color="white"
|
||||
opacity={sideMenuSpring.opacity}
|
||||
/>
|
||||
</animated.mesh>
|
||||
<animated.mesh
|
||||
position={[2.845, 4.75, 6.454]}
|
||||
scale={resumeViewSpring.scale}
|
||||
>
|
||||
<sphereGeometry args={[0.175 + 7 / 480, 32, 32]} />
|
||||
<animated.meshStandardMaterial
|
||||
transparent
|
||||
color="black"
|
||||
opacity={sideMenuSpring.opacity}
|
||||
side={BackSide}
|
||||
/>
|
||||
</animated.mesh>
|
||||
<Billboard position={[2.845, 5.1875, 6.454]}>
|
||||
<AnimatedText
|
||||
font="/assets/inter.ttf"
|
||||
fontSize={0.21875}
|
||||
outlineWidth={7 / 480}
|
||||
fillOpacity={sideMenuSpring.opacity}
|
||||
outlineOpacity={sideMenuSpring.opacity}
|
||||
>
|
||||
Resume
|
||||
</AnimatedText>
|
||||
</Billboard>
|
||||
{config.phoneNumber && (
|
||||
<>
|
||||
<animated.mesh
|
||||
ref={phoneRef}
|
||||
position={[3.75, 5.125, 7]}
|
||||
onClick={
|
||||
currentView === View.SideView
|
||||
? () => goToView(View.PhoneView)
|
||||
: undefined
|
||||
}
|
||||
onPointerOver={
|
||||
currentView === View.SideView
|
||||
? () => setHovered(View.PhoneView)
|
||||
: undefined
|
||||
}
|
||||
onPointerOut={() => setHovered(null)}
|
||||
scale={phoneViewSpring.scale}
|
||||
>
|
||||
<sphereGeometry args={[0.175, 32, 32]} />
|
||||
<animated.meshStandardMaterial
|
||||
transparent
|
||||
color="white"
|
||||
opacity={sideMenuSpring.opacity}
|
||||
/>
|
||||
</animated.mesh>
|
||||
<animated.mesh
|
||||
position={[3.75, 5.125, 7]}
|
||||
scale={phoneViewSpring.scale}
|
||||
>
|
||||
<sphereGeometry args={[0.175 + 7 / 480, 32, 32]} />
|
||||
<animated.meshStandardMaterial
|
||||
transparent
|
||||
color="black"
|
||||
opacity={sideMenuSpring.opacity}
|
||||
side={BackSide}
|
||||
/>
|
||||
</animated.mesh>
|
||||
<Billboard position={[3.75, 5.515, 7]}>
|
||||
<AnimatedText
|
||||
font="/assets/inter.ttf"
|
||||
fontSize={0.21875}
|
||||
outlineWidth={7 / 480}
|
||||
fillOpacity={sideMenuSpring.opacity}
|
||||
outlineOpacity={sideMenuSpring.opacity}
|
||||
>
|
||||
Contact
|
||||
</AnimatedText>
|
||||
</Billboard>
|
||||
</>
|
||||
)}
|
||||
</group>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
+201
-263
@@ -8,7 +8,7 @@ import {
|
||||
Text,
|
||||
Html,
|
||||
type PerspectiveCameraProps,
|
||||
useProgress,
|
||||
PerformanceMonitor,
|
||||
} from "@react-three/drei";
|
||||
import {
|
||||
animated as threeAnimated,
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
useCallback,
|
||||
type JSX,
|
||||
} from "react";
|
||||
import round from "lodash/round";
|
||||
import Stack from "@/util/stack";
|
||||
import { Canvas } from "@react-three/fiber";
|
||||
import { mdiArrowLeftBold } from "@mdi/js";
|
||||
@@ -42,6 +43,8 @@ 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";
|
||||
|
||||
const AnimatedCam = threeAnimated(PerspectiveCamera);
|
||||
const AnimatedButton = webAnimated.button as React.ComponentType<
|
||||
@@ -50,13 +53,13 @@ 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();
|
||||
|
||||
// States
|
||||
const [fps, setFps] = useState(0);
|
||||
const [dpr, setDpr] = useState(2);
|
||||
const [pendingView, setPendingView] = useState<View | null>(null);
|
||||
const [phoneHovered, setPhoneHovered] = useState(false);
|
||||
const [backHovered, setBackHovered] = useState(false);
|
||||
const [backClicked, setBackClicked] = useState(false);
|
||||
const [menuTraversal, setMenuTraversal] = useState<Stack<View>>(() => {
|
||||
@@ -110,7 +113,6 @@ export function Scene(props: JSX.IntrinsicElements["group"]) {
|
||||
precision: 0.0001,
|
||||
friction: 80,
|
||||
mass: 10,
|
||||
clamp: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -126,7 +128,8 @@ export function Scene(props: JSX.IntrinsicElements["group"]) {
|
||||
{isWebGL2Available() ? (
|
||||
width >= 1.5 * height ? (
|
||||
<>
|
||||
<div className="pointer-events-none fixed z-[999999999]">
|
||||
<Loader />
|
||||
<div className="pointer-events-none fixed z-[999999999] size-full">
|
||||
<AnimatedButton
|
||||
style={{
|
||||
transform: backScale.to(
|
||||
@@ -151,277 +154,212 @@ export function Scene(props: JSX.IntrinsicElements["group"]) {
|
||||
className="stroke-[0.75] stroke-black"
|
||||
/>
|
||||
</AnimatedButton>
|
||||
<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 && (
|
||||
<>
|
||||
{fps.toFixed(0)} fps | {dpr}x
|
||||
</>
|
||||
)}
|
||||
</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}
|
||||
phoneHovered={phoneHovered}
|
||||
currentView={currentView}
|
||||
/>
|
||||
<group>
|
||||
<primitive
|
||||
object={spotlight}
|
||||
position={[-1.915, 20, 1.0925]}
|
||||
<PerformanceMonitor
|
||||
ms={100}
|
||||
onChange={(api) => {
|
||||
setFps(api.fps);
|
||||
setDpr(round(1.5 * api.factor + 0.5, 1));
|
||||
}}
|
||||
>
|
||||
<Environment preset="apartment" />
|
||||
<AnimatedCam makeDefault {...cameraSpring} />
|
||||
<Buttons
|
||||
menuTraversal={menuTraversal}
|
||||
setMenuTraversal={setMenuTraversal}
|
||||
pendingView={pendingView}
|
||||
setPendingView={setPendingView}
|
||||
goPreviousView={goPreviousView}
|
||||
currentView={currentView}
|
||||
/>
|
||||
<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}
|
||||
>
|
||||
<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}
|
||||
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.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}
|
||||
>
|
||||
<CellphoneUI />
|
||||
</Html>
|
||||
</mesh>
|
||||
</group>
|
||||
<group
|
||||
position={[4, 4.74056, 7.25]}
|
||||
rotation={[-Math.PI, -Math.PI / 4, -Math.PI]}
|
||||
scale={0.3}
|
||||
>
|
||||
<group
|
||||
position={[-0.45957, -1.57735, -0.97302]}
|
||||
rotation={[-Math.PI / 2, 0, 0]}
|
||||
scale={100}
|
||||
onClick={
|
||||
config.phoneNumber && currentView === View.PhoneView
|
||||
? () => {
|
||||
window.open(
|
||||
`tel:${config.phoneNumber}`,
|
||||
"_blank",
|
||||
"noopener,noreferrer"
|
||||
);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
onPointerOver={
|
||||
config.phoneNumber && currentView === View.PhoneView
|
||||
? () => setPhoneHovered(true)
|
||||
: undefined
|
||||
}
|
||||
onPointerOut={() => setPhoneHovered(false)}
|
||||
>
|
||||
<mesh
|
||||
castShadow
|
||||
receiveShadow
|
||||
geometry={nodes.Top.geometry}
|
||||
material={materials.TopMaterial}
|
||||
<group>
|
||||
<primitive
|
||||
object={spotlight}
|
||||
position={[-1.915, 20, 1.0925]}
|
||||
/>
|
||||
<mesh
|
||||
castShadow
|
||||
receiveShadow
|
||||
geometry={nodes.OfficePhoneBody.geometry}
|
||||
material={materials.OfficePhoneBodyMaterial}
|
||||
/>
|
||||
<mesh
|
||||
castShadow
|
||||
receiveShadow
|
||||
geometry={nodes.Screen.geometry}
|
||||
material={materials.ScreenMaterial}
|
||||
/>
|
||||
<mesh
|
||||
castShadow
|
||||
receiveShadow
|
||||
geometry={nodes.Buttons.geometry}
|
||||
material={materials.HandleAndButtonsMaterial}
|
||||
/>
|
||||
<mesh
|
||||
castShadow
|
||||
receiveShadow
|
||||
geometry={nodes.Handle.geometry}
|
||||
material={materials.HandleAndButtonsMaterial}
|
||||
position={[-0.01639, -0.0078, 0.02006]}
|
||||
rotation={[-0.37437, 0, Math.PI / 2]}
|
||||
<primitive
|
||||
object={spotlight.target}
|
||||
position={[-1.915, 0, 1.0925]}
|
||||
/>
|
||||
</group>
|
||||
</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 && (
|
||||
<group
|
||||
position={[2.50415, 0.12973, 3.47808]}
|
||||
rotation={[0, -Math.PI / 2, 0]}
|
||||
scale={0.01}
|
||||
>
|
||||
<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-[#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}
|
||||
>
|
||||
📍 {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}
|
||||
>
|
||||
📍 {config.location}
|
||||
</Text>
|
||||
)}
|
||||
<StaticMeshes nodes={nodes} materials={materials} />
|
||||
</PerformanceMonitor>
|
||||
</group>
|
||||
</Suspense>
|
||||
</Canvas>
|
||||
@@ -432,9 +370,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}
|
||||
|
||||
@@ -36,11 +36,6 @@ export const views: PerspectiveCameraProps[] = [
|
||||
position: [3.0143, 5.5, 6.0997],
|
||||
rotation: [-2.55743, -0.69621, -2.74104],
|
||||
},
|
||||
{
|
||||
// SideView
|
||||
position: [0.5, 7.75, 4.15],
|
||||
rotation: [-2.1005, -0.68155, -2.35619],
|
||||
},
|
||||
{
|
||||
// CreditsView
|
||||
position: [1.25, 5.9822, -2.075],
|
||||
@@ -56,8 +51,7 @@ export enum View {
|
||||
CellphoneView = 4,
|
||||
DesktopView = 5,
|
||||
PhoneView = 6,
|
||||
SideView = 7,
|
||||
CreditsView = 8,
|
||||
CreditsView = 7,
|
||||
}
|
||||
|
||||
export const hashtoView: Record<string, View> = {
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
mdiWifi,
|
||||
mdiBattery80,
|
||||
mdiHelp,
|
||||
mdiEmail,
|
||||
} from "@mdi/js";
|
||||
import Icon from "@mdi/react";
|
||||
import { useRef, useState } from "react";
|
||||
@@ -88,6 +89,17 @@ export const CellphoneUI = () => {
|
||||
<div className={`${appClass} ${fillerClass}`}>
|
||||
<Icon path={mdiCalculator} className="text-neutral-300" />
|
||||
</div>
|
||||
{config.email && (
|
||||
<a
|
||||
href={`mailto:${config.email}`}
|
||||
target="_blank"
|
||||
className={appClass}
|
||||
onMouseOver={handleEnter}
|
||||
onMouseOut={handleLeave}
|
||||
>
|
||||
<Icon path={mdiEmail} className="text-neutral-300" />
|
||||
</a>
|
||||
)}
|
||||
{config.socials?.matrix && (
|
||||
<a
|
||||
href={`https://matrix.to/#/${config.socials.matrix}`}
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
"use client";
|
||||
|
||||
import { useProgress } from "@react-three/drei";
|
||||
import { animated, useSpring, useTransition } from "@react-spring/web";
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
export const Loader = ({ minDuration = 750, fadeMs = 600 }) => {
|
||||
const { progress } = useProgress();
|
||||
const [loadedAt, setLoadedAt] = useState<number | null>(null);
|
||||
|
||||
const maxSeen = useRef(0);
|
||||
if (progress > maxSeen.current) maxSeen.current = progress;
|
||||
|
||||
useEffect(() => {
|
||||
if (maxSeen.current === 100 && loadedAt === null) setLoadedAt(Date.now());
|
||||
}, [loadedAt]);
|
||||
|
||||
const visible =
|
||||
loadedAt === null || Date.now() - loadedAt < minDuration + fadeMs;
|
||||
|
||||
const barSpring = useSpring({
|
||||
from: { width: "0%" },
|
||||
to: { width: "100%" },
|
||||
config: { duration: minDuration },
|
||||
});
|
||||
|
||||
const overlay = useTransition(visible, {
|
||||
from: { opacity: 1 },
|
||||
enter: { opacity: 1 },
|
||||
leave: { opacity: 0 },
|
||||
config: { duration: fadeMs, easing: (t) => Math.pow(t, 2) },
|
||||
});
|
||||
|
||||
return createPortal(
|
||||
overlay(
|
||||
(styles, show) =>
|
||||
show && (
|
||||
// @ts-expect-error children not typed bug
|
||||
<animated.div
|
||||
style={{
|
||||
...styles,
|
||||
position: "fixed",
|
||||
inset: 0,
|
||||
background: "#1b1b1b",
|
||||
zIndex: 999999999,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: "1.5rem",
|
||||
fontSize: "2.25rem",
|
||||
color: "white",
|
||||
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={{
|
||||
...barSpring,
|
||||
height: "100%",
|
||||
background: "#ffffff",
|
||||
}}
|
||||
>
|
||||
{/* @ts-expect-error children not typed bug */}
|
||||
<animated.div className="text-[#1b1b1b] text-xs justify-center flex items-center h-full">
|
||||
{barSpring.width.to((w) => {
|
||||
const p = parseInt(w) || 0;
|
||||
return `${p > 5 ? p : ""}`;
|
||||
})}
|
||||
</animated.div>
|
||||
</animated.div>
|
||||
</div>
|
||||
</animated.div>
|
||||
)
|
||||
),
|
||||
document.body
|
||||
);
|
||||
};
|
||||
+13
-13
@@ -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">
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { type HTMLAttributes } from "react";
|
||||
|
||||
export const Info = (props: HTMLAttributes<HTMLParagraphElement>) => (
|
||||
<p {...props}>{process.env.NEXT_PUBLIC_APP_VERSION}</p>
|
||||
);
|
||||
@@ -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 });
|
||||
@@ -66,6 +67,11 @@ export async function register() {
|
||||
format: "phone",
|
||||
nullable: true,
|
||||
},
|
||||
email: {
|
||||
type: "string",
|
||||
format: "email",
|
||||
nullable: true,
|
||||
},
|
||||
fallbackUrl: {
|
||||
type: "string",
|
||||
format: "uri",
|
||||
|
||||
Vendored
+1
@@ -16,6 +16,7 @@ export interface Config {
|
||||
tags?: string[];
|
||||
}[];
|
||||
phoneNumber?: string;
|
||||
email?: string;
|
||||
fallbackUrl?: string;
|
||||
name: [string, string];
|
||||
status?: string;
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user