diff --git a/package-lock.json b/package-lock.json
index ede8db8..d84fe5f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,9 +17,9 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
- "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
+ "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
"cpu": [
"ppc64"
],
@@ -34,9 +34,9 @@
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz",
- "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
+ "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
"cpu": [
"arm"
],
@@ -51,9 +51,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz",
- "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
+ "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
"cpu": [
"arm64"
],
@@ -68,9 +68,9 @@
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz",
- "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
+ "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
"cpu": [
"x64"
],
@@ -85,9 +85,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz",
- "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
+ "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
"cpu": [
"arm64"
],
@@ -102,9 +102,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz",
- "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
+ "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
"cpu": [
"x64"
],
@@ -119,9 +119,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz",
- "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
"cpu": [
"arm64"
],
@@ -136,9 +136,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz",
- "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
+ "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
"cpu": [
"x64"
],
@@ -153,9 +153,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz",
- "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
+ "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
"cpu": [
"arm"
],
@@ -170,9 +170,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz",
- "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
+ "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
"cpu": [
"arm64"
],
@@ -187,9 +187,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz",
- "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
+ "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
"cpu": [
"ia32"
],
@@ -204,9 +204,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz",
- "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
+ "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
"cpu": [
"loong64"
],
@@ -221,9 +221,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz",
- "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
+ "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
"cpu": [
"mips64el"
],
@@ -238,9 +238,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz",
- "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
+ "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
"cpu": [
"ppc64"
],
@@ -255,9 +255,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz",
- "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
+ "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
"cpu": [
"riscv64"
],
@@ -272,9 +272,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz",
- "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
+ "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
"cpu": [
"s390x"
],
@@ -289,9 +289,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz",
- "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
+ "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
"cpu": [
"x64"
],
@@ -306,9 +306,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz",
- "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
"cpu": [
"arm64"
],
@@ -323,9 +323,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz",
- "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
+ "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
"cpu": [
"x64"
],
@@ -340,9 +340,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz",
- "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
"cpu": [
"arm64"
],
@@ -357,9 +357,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz",
- "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
+ "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
"cpu": [
"x64"
],
@@ -373,10 +373,27 @@
"node": ">=18"
}
},
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
+ "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@esbuild/sunos-x64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz",
- "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
+ "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
"cpu": [
"x64"
],
@@ -391,9 +408,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz",
- "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
+ "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
"cpu": [
"arm64"
],
@@ -408,9 +425,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz",
- "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
+ "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
"cpu": [
"ia32"
],
@@ -425,9 +442,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz",
- "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
+ "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
"cpu": [
"x64"
],
@@ -442,9 +459,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz",
- "integrity": "sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.48.0.tgz",
+ "integrity": "sha512-aVzKH922ogVAWkKiyKXorjYymz2084zrhrZRXtLrA5eEx5SO8Dj0c/4FpCHZyn7MKzhW2pW4tK28vVr+5oQ2xw==",
"cpu": [
"arm"
],
@@ -456,9 +473,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.1.tgz",
- "integrity": "sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.48.0.tgz",
+ "integrity": "sha512-diOdQuw43xTa1RddAFbhIA8toirSzFMcnIg8kvlzRbK26xqEnKJ/vqQnghTAajy2Dcy42v+GMPMo6jq67od+Dw==",
"cpu": [
"arm64"
],
@@ -470,9 +487,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.1.tgz",
- "integrity": "sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.48.0.tgz",
+ "integrity": "sha512-QhR2KA18fPlJWFefySJPDYZELaVqIUVnYgAOdtJ+B/uH96CFg2l1TQpX19XpUMWUqMyIiyY45wje8K6F4w4/CA==",
"cpu": [
"arm64"
],
@@ -484,9 +501,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.1.tgz",
- "integrity": "sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.48.0.tgz",
+ "integrity": "sha512-Q9RMXnQVJ5S1SYpNSTwXDpoQLgJ/fbInWOyjbCnnqTElEyeNvLAB3QvG5xmMQMhFN74bB5ZZJYkKaFPcOG8sGg==",
"cpu": [
"x64"
],
@@ -498,9 +515,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.1.tgz",
- "integrity": "sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.48.0.tgz",
+ "integrity": "sha512-3jzOhHWM8O8PSfyft+ghXZfBkZawQA0PUGtadKYxFqpcYlOYjTi06WsnYBsbMHLawr+4uWirLlbhcYLHDXR16w==",
"cpu": [
"arm64"
],
@@ -512,9 +529,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.1.tgz",
- "integrity": "sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.48.0.tgz",
+ "integrity": "sha512-NcD5uVUmE73C/TPJqf78hInZmiSBsDpz3iD5MF/BuB+qzm4ooF2S1HfeTChj5K4AV3y19FFPgxonsxiEpy8v/A==",
"cpu": [
"x64"
],
@@ -526,9 +543,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.1.tgz",
- "integrity": "sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.48.0.tgz",
+ "integrity": "sha512-JWnrj8qZgLWRNHr7NbpdnrQ8kcg09EBBq8jVOjmtlB3c8C6IrynAJSMhMVGME4YfTJzIkJqvSUSVJRqkDnu/aA==",
"cpu": [
"arm"
],
@@ -540,9 +557,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.1.tgz",
- "integrity": "sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.48.0.tgz",
+ "integrity": "sha512-9xu92F0TxuMH0tD6tG3+GtngwdgSf8Bnz+YcsPG91/r5Vgh5LNofO48jV55priA95p3c92FLmPM7CvsVlnSbGQ==",
"cpu": [
"arm"
],
@@ -554,9 +571,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.1.tgz",
- "integrity": "sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.48.0.tgz",
+ "integrity": "sha512-NLtvJB5YpWn7jlp1rJiY0s+G1Z1IVmkDuiywiqUhh96MIraC0n7XQc2SZ1CZz14shqkM+XN2UrfIo7JB6UufOA==",
"cpu": [
"arm64"
],
@@ -568,9 +585,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.1.tgz",
- "integrity": "sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.48.0.tgz",
+ "integrity": "sha512-QJ4hCOnz2SXgCh+HmpvZkM+0NSGcZACyYS8DGbWn2PbmA0e5xUk4bIP8eqJyNXLtyB4gZ3/XyvKtQ1IFH671vQ==",
"cpu": [
"arm64"
],
@@ -582,9 +599,9 @@
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.1.tgz",
- "integrity": "sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.48.0.tgz",
+ "integrity": "sha512-Pk0qlGJnhILdIC5zSKQnprFjrGmjfDM7TPZ0FKJxRkoo+kgMRAg4ps1VlTZf8u2vohSicLg7NP+cA5qE96PaFg==",
"cpu": [
"loong64"
],
@@ -595,10 +612,10 @@
"linux"
]
},
- "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.1.tgz",
- "integrity": "sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==",
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.48.0.tgz",
+ "integrity": "sha512-/dNFc6rTpoOzgp5GKoYjT6uLo8okR/Chi2ECOmCZiS4oqh3mc95pThWma7Bgyk6/WTEvjDINpiBCuecPLOgBLQ==",
"cpu": [
"ppc64"
],
@@ -610,9 +627,23 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.1.tgz",
- "integrity": "sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.48.0.tgz",
+ "integrity": "sha512-YBwXsvsFI8CVA4ej+bJF2d9uAeIiSkqKSPQNn0Wyh4eMDY4wxuSp71BauPjQNCKK2tD2/ksJ7uhJ8X/PVY9bHQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.48.0.tgz",
+ "integrity": "sha512-FI3Rr2aGAtl1aHzbkBIamsQyuauYtTF9SDUJ8n2wMXuuxwchC3QkumZa1TEXYIv/1AUp1a25Kwy6ONArvnyeVQ==",
"cpu": [
"riscv64"
],
@@ -624,9 +655,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.1.tgz",
- "integrity": "sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.48.0.tgz",
+ "integrity": "sha512-Dx7qH0/rvNNFmCcIRe1pyQ9/H0XO4v/f0SDoafwRYwc2J7bJZ5N4CHL/cdjamISZ5Cgnon6iazAVRFlxSoHQnQ==",
"cpu": [
"s390x"
],
@@ -638,9 +669,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.1.tgz",
- "integrity": "sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.48.0.tgz",
+ "integrity": "sha512-GUdZKTeKBq9WmEBzvFYuC88yk26vT66lQV8D5+9TgkfbewhLaTHRNATyzpQwwbHIfJvDJ3N9WJ90wK/uR3cy3Q==",
"cpu": [
"x64"
],
@@ -652,9 +683,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.1.tgz",
- "integrity": "sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.48.0.tgz",
+ "integrity": "sha512-ao58Adz/v14MWpQgYAb4a4h3fdw73DrDGtaiF7Opds5wNyEQwtO6M9dBh89nke0yoZzzaegq6J/EXs7eBebG8A==",
"cpu": [
"x64"
],
@@ -666,9 +697,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.1.tgz",
- "integrity": "sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.48.0.tgz",
+ "integrity": "sha512-kpFno46bHtjZVdRIOxqaGeiABiToo2J+st7Yce+aiAoo1H0xPi2keyQIP04n2JjDVuxBN6bSz9R6RdTK5hIppw==",
"cpu": [
"arm64"
],
@@ -680,9 +711,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.1.tgz",
- "integrity": "sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.48.0.tgz",
+ "integrity": "sha512-rFYrk4lLk9YUTIeihnQMiwMr6gDhGGSbWThPEDfBoU/HdAtOzPXeexKi7yU8jO+LWRKnmqPN9NviHQf6GDwBcQ==",
"cpu": [
"ia32"
],
@@ -694,9 +725,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.1.tgz",
- "integrity": "sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.48.0.tgz",
+ "integrity": "sha512-sq0hHLTgdtwOPDB5SJOuaoHyiP1qSwg+71TQWk8iDS04bW1wIE0oQ6otPiRj2ZvLYNASLMaTp8QRGUVZ+5OL5A==",
"cpu": [
"x64"
],
@@ -714,9 +745,9 @@
"license": "MIT"
},
"node_modules/@types/estree": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
- "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
@@ -753,9 +784,9 @@
"license": "BSD-3-Clause"
},
"node_modules/esbuild": {
- "version": "0.24.2",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
- "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
+ "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -766,31 +797,50 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.24.2",
- "@esbuild/android-arm": "0.24.2",
- "@esbuild/android-arm64": "0.24.2",
- "@esbuild/android-x64": "0.24.2",
- "@esbuild/darwin-arm64": "0.24.2",
- "@esbuild/darwin-x64": "0.24.2",
- "@esbuild/freebsd-arm64": "0.24.2",
- "@esbuild/freebsd-x64": "0.24.2",
- "@esbuild/linux-arm": "0.24.2",
- "@esbuild/linux-arm64": "0.24.2",
- "@esbuild/linux-ia32": "0.24.2",
- "@esbuild/linux-loong64": "0.24.2",
- "@esbuild/linux-mips64el": "0.24.2",
- "@esbuild/linux-ppc64": "0.24.2",
- "@esbuild/linux-riscv64": "0.24.2",
- "@esbuild/linux-s390x": "0.24.2",
- "@esbuild/linux-x64": "0.24.2",
- "@esbuild/netbsd-arm64": "0.24.2",
- "@esbuild/netbsd-x64": "0.24.2",
- "@esbuild/openbsd-arm64": "0.24.2",
- "@esbuild/openbsd-x64": "0.24.2",
- "@esbuild/sunos-x64": "0.24.2",
- "@esbuild/win32-arm64": "0.24.2",
- "@esbuild/win32-ia32": "0.24.2",
- "@esbuild/win32-x64": "0.24.2"
+ "@esbuild/aix-ppc64": "0.25.9",
+ "@esbuild/android-arm": "0.25.9",
+ "@esbuild/android-arm64": "0.25.9",
+ "@esbuild/android-x64": "0.25.9",
+ "@esbuild/darwin-arm64": "0.25.9",
+ "@esbuild/darwin-x64": "0.25.9",
+ "@esbuild/freebsd-arm64": "0.25.9",
+ "@esbuild/freebsd-x64": "0.25.9",
+ "@esbuild/linux-arm": "0.25.9",
+ "@esbuild/linux-arm64": "0.25.9",
+ "@esbuild/linux-ia32": "0.25.9",
+ "@esbuild/linux-loong64": "0.25.9",
+ "@esbuild/linux-mips64el": "0.25.9",
+ "@esbuild/linux-ppc64": "0.25.9",
+ "@esbuild/linux-riscv64": "0.25.9",
+ "@esbuild/linux-s390x": "0.25.9",
+ "@esbuild/linux-x64": "0.25.9",
+ "@esbuild/netbsd-arm64": "0.25.9",
+ "@esbuild/netbsd-x64": "0.25.9",
+ "@esbuild/openbsd-arm64": "0.25.9",
+ "@esbuild/openbsd-x64": "0.25.9",
+ "@esbuild/openharmony-arm64": "0.25.9",
+ "@esbuild/sunos-x64": "0.25.9",
+ "@esbuild/win32-arm64": "0.25.9",
+ "@esbuild/win32-ia32": "0.25.9",
+ "@esbuild/win32-x64": "0.25.9"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
}
},
"node_modules/fflate": {
@@ -821,9 +871,9 @@
"license": "MIT"
},
"node_modules/nanoid": {
- "version": "3.3.8",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
- "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
@@ -846,10 +896,23 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/postcss": {
- "version": "8.4.49",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
- "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true,
"funding": [
{
@@ -867,7 +930,7 @@
],
"license": "MIT",
"dependencies": {
- "nanoid": "^3.3.7",
+ "nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
@@ -876,13 +939,13 @@
}
},
"node_modules/rollup": {
- "version": "4.30.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.1.tgz",
- "integrity": "sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==",
+ "version": "4.48.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.48.0.tgz",
+ "integrity": "sha512-BXHRqK1vyt9XVSEHZ9y7xdYtuYbwVod2mLwOMFP7t/Eqoc1pHRlG/WdV2qNeNvZHRQdLedaFycljaYYM96RqJQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@types/estree": "1.0.6"
+ "@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
@@ -892,25 +955,26 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.30.1",
- "@rollup/rollup-android-arm64": "4.30.1",
- "@rollup/rollup-darwin-arm64": "4.30.1",
- "@rollup/rollup-darwin-x64": "4.30.1",
- "@rollup/rollup-freebsd-arm64": "4.30.1",
- "@rollup/rollup-freebsd-x64": "4.30.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.30.1",
- "@rollup/rollup-linux-arm-musleabihf": "4.30.1",
- "@rollup/rollup-linux-arm64-gnu": "4.30.1",
- "@rollup/rollup-linux-arm64-musl": "4.30.1",
- "@rollup/rollup-linux-loongarch64-gnu": "4.30.1",
- "@rollup/rollup-linux-powerpc64le-gnu": "4.30.1",
- "@rollup/rollup-linux-riscv64-gnu": "4.30.1",
- "@rollup/rollup-linux-s390x-gnu": "4.30.1",
- "@rollup/rollup-linux-x64-gnu": "4.30.1",
- "@rollup/rollup-linux-x64-musl": "4.30.1",
- "@rollup/rollup-win32-arm64-msvc": "4.30.1",
- "@rollup/rollup-win32-ia32-msvc": "4.30.1",
- "@rollup/rollup-win32-x64-msvc": "4.30.1",
+ "@rollup/rollup-android-arm-eabi": "4.48.0",
+ "@rollup/rollup-android-arm64": "4.48.0",
+ "@rollup/rollup-darwin-arm64": "4.48.0",
+ "@rollup/rollup-darwin-x64": "4.48.0",
+ "@rollup/rollup-freebsd-arm64": "4.48.0",
+ "@rollup/rollup-freebsd-x64": "4.48.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.48.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.48.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.48.0",
+ "@rollup/rollup-linux-arm64-musl": "4.48.0",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.48.0",
+ "@rollup/rollup-linux-ppc64-gnu": "4.48.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.48.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.48.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.48.0",
+ "@rollup/rollup-linux-x64-gnu": "4.48.0",
+ "@rollup/rollup-linux-x64-musl": "4.48.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.48.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.48.0",
+ "@rollup/rollup-win32-x64-msvc": "4.48.0",
"fsevents": "~2.3.2"
}
},
@@ -930,6 +994,23 @@
"integrity": "sha512-AUwVmViIEUgBwxJJ7stnF0NkPpZxx1aZ6WiAbQ/Qq61h6I9UR4grXtZDmO8mnlaNORhHnIBlXJ1uBxILEKuVyw==",
"license": "MIT"
},
+ "node_modules/tinyglobby": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
+ "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
"node_modules/typescript": {
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
@@ -945,15 +1026,18 @@
}
},
"node_modules/vite": {
- "version": "6.0.11",
- "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.11.tgz",
- "integrity": "sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==",
+ "version": "6.3.5",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
+ "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "esbuild": "^0.24.2",
- "postcss": "^8.4.49",
- "rollup": "^4.23.0"
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
},
"bin": {
"vite": "bin/vite.js"
diff --git a/src/components/cd-cover/index.ts b/src/components/cd-cover/index.ts
deleted file mode 100644
index 01604bc..0000000
--- a/src/components/cd-cover/index.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import './styles.css';
-
-export type cdCoverContent = {
- readonly coverTitle: string;
- readonly coverDescription: string;
- readonly coverImage: string;
-}
-
-// --------------------------------------------------------------------------------
-
-export function cdCover() {
- const cdItem = document.createElement('div');
- const cover = document.createElement('div');
- const textBlurb = document.createElement('div');
- const title = document.createElement('p');
- const description = document.createElement('p');
-
- cdItem.className = 'cd-cover';
- cover.className = 'cover-image';
- textBlurb.className = 'cover-text-blurb';
- title.className = 'cover-title';
- description.className = 'cover-description';
-
- title.textContent = "test";
- description.textContent = 'This is a test description for the CD cover.';
-
- textBlurb.appendChild(title);
- textBlurb.appendChild(description);
- cdItem.appendChild(cover);
- cdItem.appendChild(textBlurb);
-
- return cdItem;
-}
\ No newline at end of file
diff --git a/src/components/cd-cover/styles.css b/src/components/cd-cover/styles.css
deleted file mode 100644
index 98f59ff..0000000
--- a/src/components/cd-cover/styles.css
+++ /dev/null
@@ -1,11 +0,0 @@
-.cd-cover {
- max-width: 100%;
- text-align: center;
-}
-
-.cover-image {
- width: 100%;
- height: 250px;
- background-color: #3BFFC5;
-}
-
diff --git a/src/components/logo/assets/cd-labs-logo.png b/src/components/logo/assets/cd-labs-logo.png
new file mode 100755
index 0000000..d176a68
Binary files /dev/null and b/src/components/logo/assets/cd-labs-logo.png differ
diff --git a/src/components/logo/index.ts b/src/components/logo/index.ts
new file mode 100644
index 0000000..7e80041
--- /dev/null
+++ b/src/components/logo/index.ts
@@ -0,0 +1,35 @@
+import './styles.css';
+import LOGO from './assets/cd-labs-logo.png';
+
+// --------------------------------------------------------------------------------
+
+export function buildMainLogo() {
+ const logoContainer = document.createElement('div');
+ const logoImg = document.createElement('img');
+ const logoText = document.createElement('p');
+
+ logoContainer.className = 'main-logo';
+
+ logoImg.src = LOGO;
+ logoImg.alt = "CD-Labs Logo";
+
+ logoText.textContent = "CD-Labs";
+
+ logoContainer.appendChild(logoImg);
+ logoContainer.appendChild(logoText);
+
+ return logoContainer;
+}
+
+// --------------------------------------------------------------------------------
+
+export function changeLogoOnScroll() {
+ const logoContainer = document.querySelector('.main-logo') as HTMLElement;
+ if (!logoContainer) return;
+
+ if (window.scrollY > 50) {
+ logoContainer.classList.add('scrolled');
+ } else {
+ logoContainer.classList.remove('scrolled');
+ }
+}
\ No newline at end of file
diff --git a/src/components/logo/styles.css b/src/components/logo/styles.css
new file mode 100644
index 0000000..825aca4
--- /dev/null
+++ b/src/components/logo/styles.css
@@ -0,0 +1,46 @@
+.main-logo {
+ position: fixed;
+ top: 40px;
+ left: 55px;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ z-index: 10;
+
+ img {
+ width: 95px;
+ height: 95px;
+ image-rendering: pixelated;
+ transition: width 0.3s, height 0.3s;
+ }
+
+ img:hover {
+ animation: rotate 0.5s steps(4) infinite;
+ }
+
+ p {
+ margin-top: 9px;
+ text-align: center;
+ text-transform: uppercase;
+ font-family: "Handjet", sans-serif;
+ font-size: 2em;
+ font-weight: 900;
+ transition: opacity 0.3s;
+ }
+}
+
+.main-logo.scrolled img {
+ width: 60px;
+ height: 60px;
+}
+
+.main-logo.scrolled p {
+ opacity: 0;
+ pointer-events: none;
+}
+
+
+@keyframes rotate {
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
diff --git a/src/components/pixel-grid/index.ts b/src/components/pixel-grid/index.ts
new file mode 100644
index 0000000..ef685ca
--- /dev/null
+++ b/src/components/pixel-grid/index.ts
@@ -0,0 +1,96 @@
+import './styles.css';
+import { createPixelPattern } from './pixel.ts';
+
+export type GRID_CONFIG = {
+ rows: number;
+ colors: string[];
+};
+
+const EMPTY_RATIO = 0.5; // Fixed constant for empty bias
+
+// --------------------------------------------------------------------------------
+
+export function createPixelGrid(config: GRID_CONFIG, alignment: 'left' | 'right'): HTMLCanvasElement {
+ const canvas = createCanvasElement();
+ const ctx = canvas.getContext('2d');
+
+ if (!ctx) return canvas;
+
+ // Use ResizeObserver for responsive updates
+ const resizeObserver = new ResizeObserver(() => {
+ const parent = canvas.parentElement;
+ if (parent) {
+ renderPixelGrid(canvas, ctx, config, alignment, parent.clientWidth, parent.clientHeight);
+ }
+ });
+
+ // Initial render
+ setTimeout(() => {
+ const parent = canvas.parentElement;
+ if (parent) {
+ resizeObserver.observe(parent);
+ renderPixelGrid(canvas, ctx, config, alignment, parent.clientWidth, parent.clientHeight);
+ }
+ }, 0);
+
+ return canvas;
+}
+
+// --------------------------------------------------------------------------------
+
+function createCanvasElement(): HTMLCanvasElement {
+ const canvas = document.createElement('canvas');
+ canvas.className = 'pixel-grid';
+ canvas.style.width = '100%';
+ canvas.style.height = '100%';
+ canvas.style.display = 'block';
+ return canvas;
+}
+
+function renderPixelGrid(
+ canvas: HTMLCanvasElement,
+ ctx: CanvasRenderingContext2D,
+ config: GRID_CONFIG,
+ alignment: 'left' | 'right',
+ width: number,
+ height: number
+) {
+ // Setup canvas
+ canvas.width = width;
+ canvas.height = height;
+ ctx.clearRect(0, 0, width, height);
+
+ // Calculate pixel size based on rows (perfect squares)
+ const pixelSize = height / config.rows;
+
+ // Calculate number of columns dynamically based on width and pixel size
+ const cols = Math.ceil(width / pixelSize);
+
+ const canvasRect = canvas.getBoundingClientRect();
+
+ // Render each pixel
+ for (let row = 0; row < config.rows; row++) {
+ for (let col = 0; col < cols; col++) {
+ const screenX = canvasRect.left + (col * pixelSize);
+ const color = createPixelPattern({
+ row,
+ col,
+ totalRows: config.rows,
+ totalCols: cols,
+ screenX,
+ screenWidth: window.innerWidth,
+ alignment,
+ emptyRatio: EMPTY_RATIO,
+ colors: config.colors
+ });
+
+ if (color) {
+ ctx.fillStyle = color;
+ const x = Math.floor(col * pixelSize);
+ const y = Math.floor(row * pixelSize);
+ const size = Math.ceil(pixelSize) + 1;
+ ctx.fillRect(x, y, size, size);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/components/pixel-grid/pixel.ts b/src/components/pixel-grid/pixel.ts
new file mode 100644
index 0000000..3017856
--- /dev/null
+++ b/src/components/pixel-grid/pixel.ts
@@ -0,0 +1,70 @@
+
+type PixelInput = {
+ row: number;
+ col: number;
+ totalRows: number;
+ totalCols: number;
+ screenX: number;
+ screenWidth: number;
+ alignment: 'left' | 'right';
+ emptyRatio: number;
+ colors: readonly string[];
+};
+
+// --------------------------------------------------------------------------------
+
+export function createPixelPattern(input: PixelInput): string | null {
+ const normalizedX = input.col / (input.totalCols - 1);
+ const normalizedY = input.row / (input.totalRows - 1);
+ const normalizedScreenX = input.screenX / input.screenWidth;
+
+ const isEmpty = shouldPixelBeEmpty(
+ normalizedX,
+ normalizedScreenX,
+ normalizedY,
+ input.alignment,
+ input.emptyRatio
+ );
+
+ return isEmpty ? null : getRandomColor(input.colors);
+}
+
+// --------------------------------------------------------------------------------
+
+function shouldPixelBeEmpty(
+ _normalizedX: number, // Container position (unused, kept for future use)
+ normalizedScreenX: number,
+ normalizedY: number,
+ alignment: 'left' | 'right',
+ emptyRatio: number
+): boolean {
+ const boundary = calculateBoundary(normalizedY, emptyRatio);
+
+ if (alignment === 'right') {
+ return normalizedScreenX > boundary ? hasGhostPixel() : hasScatterEffect(normalizedScreenX, boundary, normalizedY);
+ } else {
+ return normalizedScreenX < boundary ? hasGhostPixel() : hasScatterEffect(normalizedScreenX, boundary, normalizedY);
+ }
+}
+
+function calculateBoundary(normalizedY: number, emptyRatio: number): number {
+ const waveOffset = Math.sin(normalizedY * Math.PI * 3) * 0.1;
+ const randomOffset = (Math.random() - 0.5) * 0.15;
+ return emptyRatio + waveOffset + randomOffset;
+}
+
+function hasScatterEffect(screenX: number, boundary: number, normalizedY: number): boolean {
+ const distance = Math.abs(screenX - boundary);
+ const scatterChance = Math.pow(1 - distance / boundary, 2) * 0.25;
+ const rowVariation = Math.sin(normalizedY * Math.PI * 2) * 0.1;
+
+ return Math.random() < (scatterChance + rowVariation);
+}
+
+function hasGhostPixel(): boolean {
+ return Math.random() >= 0.3;
+}
+
+function getRandomColor(colors: readonly string[]): string {
+ return colors[Math.floor(Math.random() * colors.length)];
+}
\ No newline at end of file
diff --git a/src/components/pixel-grid/styles.css b/src/components/pixel-grid/styles.css
new file mode 100644
index 0000000..70ed05f
--- /dev/null
+++ b/src/components/pixel-grid/styles.css
@@ -0,0 +1,20 @@
+canvas.pixel-grid {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ z-index: -1;
+ overflow: hidden;
+ background: transparent;
+}
+
+.pixel {
+ transition: background 0.2s;
+}
+
+.pixel-empty {
+ background: transparent !important;
+ opacity: 0;
+}
\ No newline at end of file
diff --git a/src/components/top-nav/index.ts b/src/components/top-nav/index.ts
new file mode 100644
index 0000000..82cf75e
--- /dev/null
+++ b/src/components/top-nav/index.ts
@@ -0,0 +1,37 @@
+import './styles.css';
+
+type LinkItem = [
+ name: string,
+ path: string,
+]
+
+const navLinks: LinkItem[] = [
+ ['Side Quests', '/projects'],
+ ['About', '/about'],
+ ['Logs', '/logs'],
+ ['Contact', '/contact'],
+]
+
+//-----------------------------------------------------------------------
+
+export function buildTopNav() {
+ const navBox = document.createElement("div");
+ const ul = document.createElement("ul");
+
+ navBox.id = "top-nav";
+
+ navLinks.forEach(link => {
+ const [name, path] = link;
+ const li = document.createElement("li");
+ const a = document.createElement("a");
+
+ a.href = path;
+ a.textContent = name;
+
+ li.appendChild(a);
+ ul.appendChild(li);
+ })
+
+ navBox.appendChild(ul);
+ return navBox
+}
\ No newline at end of file
diff --git a/src/components/top-nav/styles.css b/src/components/top-nav/styles.css
new file mode 100644
index 0000000..ec7732c
--- /dev/null
+++ b/src/components/top-nav/styles.css
@@ -0,0 +1,44 @@
+#top-nav {
+ position: fixed;
+ top: 40px;
+ left: 50%;
+ transform: translateX(-50%);
+ display: flex;
+ text-align: center;
+ padding: 0 50px;
+ z-index: 10;
+
+ ul {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 50px;
+ height: 60px;
+ margin: 0;
+ padding: 0 35px;
+ list-style: none;
+
+ border: 2px solid rgba(255, 255, 255, 0.1);
+ border-radius: 30px;
+ background-color: rgba(255, 255, 255, 0.05);
+
+ backdrop-filter: blur(30px);
+ -webkit-backdrop-filter: blur(30px);
+ overflow: hidden;
+ }
+
+ li a {
+ font-family: "Handjet", sans-serif;
+ font-size: 1.5em;
+ font-weight: 500;
+ text-transform: lowercase;
+ text-decoration: none;
+ color: #ffffff;
+ cursor: pointer;
+ transition: color 0.3s ease;
+
+ &:hover {
+ color: #3BFFC5;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/components/vertical-nav/assets/github-icon.svg b/src/components/vertical-nav/assets/github-icon.svg
deleted file mode 100644
index c8ccfd2..0000000
--- a/src/components/vertical-nav/assets/github-icon.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/src/components/vertical-nav/assets/instagram-icon.svg b/src/components/vertical-nav/assets/instagram-icon.svg
deleted file mode 100644
index d53e54b..0000000
--- a/src/components/vertical-nav/assets/instagram-icon.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/src/components/vertical-nav/assets/linkedin-icon.svg b/src/components/vertical-nav/assets/linkedin-icon.svg
deleted file mode 100644
index 3b20022..0000000
--- a/src/components/vertical-nav/assets/linkedin-icon.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/src/components/vertical-nav/index.ts b/src/components/vertical-nav/index.ts
deleted file mode 100644
index 1ba81d7..0000000
--- a/src/components/vertical-nav/index.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-import './styles.css';
-
-import cdIcon from "/img/common/cd_icon_green.png";
-import githubIcon from "./assets/github-icon.svg";
-import linkedIcon from "./assets/linkedin-icon.svg";
-import instagramIcon from "./assets/instagram-icon.svg";
-import {navLinks} from "./nav-links.ts";
-
-const navTitle = "CD-BASH";
-const githubProfile: string = "https://github.com/CD-BASH"
-const linkedinProfile: string = "https://www.linkedin.com/in/charlesdouc/"
-const instagramProfile: string = "https://www.instagram.com/charlesdouc/"
-const footerCopyrights: string = "© 2025 Charles Doucet - All Rights Reserved";
-
-type SocialLink = [
- path: string,
- image: string,
-]
-
-const FOO_SOCIALS: SocialLink[] = [
- [githubProfile, githubIcon],
- [linkedinProfile, linkedIcon],
- [instagramProfile, instagramIcon]
-]
-
-//-----------------------------------------------------------------------
-
-export function buildVerticalNav() {
- const navBox = document.createElement("div");
- const navHeader = header();
- const navLinksSection = navLinks();
- const navInfo = document.createElement("div");
- const navFooter = footer();
-
- navBox.id = "vertical-nav";
- navInfo.id = "nav-info";
-
- navBox.appendChild(navHeader);
- navHeader.appendChild(navLinksSection);
- navBox.appendChild(navInfo);
- navBox.appendChild(navFooter);
-
- return navBox;
-}
-
-
-export function renderNavInfo(info: HTMLElement) {
- const navInfo = clearInfo();
- navInfo?.appendChild(info);
-}
-
-//-----------------------------------------------------------------------
-
-const header = () => {
- const container = document.createElement("div");
- const topTriangle = document.createElement("div");
- const logo = document.createElement("div");
- const icon = document.createElement("img");
- const title = document.createElement("h5");
-
- container.className = "header";
- topTriangle.className = "top-triangle";
-
- logo.className = "info";
- icon.className = "icon";
- icon.src = cdIcon;
-
- title.textContent = navTitle;
-
- container.appendChild(topTriangle);
- container.appendChild(logo);
- logo.appendChild(icon);
- logo.appendChild(title);
-
- return container;
-}
-
-
-const footer = () => {
- const container = document.createElement("div");
- const socials = document.createElement("ul");
- const copyrights = document.createElement("p");
-
- container.className = "nav-footer";
- socials.className = "socials";
- copyrights.className = "copyrights";
- copyrights.textContent = footerCopyrights;
-
- container.appendChild(socials);
- container.appendChild(copyrights);
-
- FOO_SOCIALS.forEach(social => {
- const [path, image] = social;
- const socialLink = document.createElement("li");
- const link = document.createElement("a");
- const icon = document.createElement("img");
-
- link.href = path;
- link.target = "_blank";
- icon.src = image;
-
- socials.appendChild(socialLink);
- socialLink.appendChild(link);
- link.appendChild(icon);
- })
-
- return container;
-}
-
-
-function clearInfo() {
- const cleanInfo = document.getElementById('nav-info');
- cleanInfo?.childNodes.forEach((childNode) => {
- cleanInfo.removeChild(childNode);
- });
-
- return cleanInfo;
-}
\ No newline at end of file
diff --git a/src/components/vertical-nav/info-project.ts b/src/components/vertical-nav/info-project.ts
deleted file mode 100644
index 0f6d00d..0000000
--- a/src/components/vertical-nav/info-project.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import {threeDataViewer} from "../three-radar-chart";
-import {TechUsage} from "../three-radar-chart/radar.ts";
-
-
-export type ButtonLink = [
- label: string,
- path: string,
- highlight: boolean,
-]
-
-//-----------------------------------------------------------------------
-
-export function projectInfo(buttons: ButtonLink[], techs: TechUsage[]) {
- const container = document.createElement('div');
- const buttonList = document.createElement('ul');
-
- container.className = "project-info";
- buttonList.className = "button-list";
- container.appendChild(dataSection(techs));
- container.appendChild(buttonList);
-
- buttons.forEach(button => {
- buttonList.appendChild(createButton(button));
- })
-
- return container;
-}
-
-//-----------------------------------------------------------------------
-
-
-function createButton(newButton: ButtonLink) {
- const btn = document.createElement('li');
- const btnLine = document.createElement('a');
- const [label, path, highlight] = newButton;
-
- if (highlight) {
- btn.className = "btn-highlight";
- }
-
- btnLine.type = "button";
- btnLine.textContent = label;
- btnLine.href = path;
- btnLine.target = "_blank";
-
- btn.appendChild(btnLine);
- return btn;
-}
-
-function dataSection(newTechRadar: TechUsage[]) {
- const detailSection = document.createElement("section");
- const detailTitle = document.createElement("h4");
- detailTitle.textContent = "Made with";
-
- detailSection.appendChild(detailTitle);
- detailSection.appendChild(threeDataViewer(newTechRadar));
-
- return detailSection;
-}
-
-
-
diff --git a/src/components/vertical-nav/nav-links.ts b/src/components/vertical-nav/nav-links.ts
deleted file mode 100644
index 96d0fa9..0000000
--- a/src/components/vertical-nav/nav-links.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-type NavLinkItem = [
- name: string,
- path: string,
- color: string,
- textColor: string,
-]
-
-const navLinkItems: NavLinkItem[] = [
- ['Home', '/', '#3BFFC5', 'black'],
- ['Projects', '/projects', '#3B62FF', 'black'],
- ['Logs', '/logs', '#1E1E1E', 'white'],
- ['About', '/about', '#1E1E1E', 'white'],
- ['Contact', '/contact', '#1E1E1E', 'white'],
-]
-
-//-----------------------------------------------------------------------
-
-export function navLinks() {
- const nav = document.createElement('nav');
- const ul = document.createElement('ul');
-
- nav.className = 'nav-links';
- nav.appendChild(ul);
-
- navLinkItems.forEach(LinkItem => {
- const [name, path, color, textColor] = LinkItem;
- const li = document.createElement('li');
- const link = document.createElement('a');
-
- link.href = path;
- link.textContent = name;
- link.style.backgroundColor = color;
- link.style.color = textColor;
-
- ul.appendChild(li);
- li.appendChild(link);
- });
-
- return nav;
-}
\ No newline at end of file
diff --git a/src/components/vertical-nav/styles.css b/src/components/vertical-nav/styles.css
deleted file mode 100644
index 3775620..0000000
--- a/src/components/vertical-nav/styles.css
+++ /dev/null
@@ -1,188 +0,0 @@
-#vertical-nav {
- position: fixed;
- top: 60px;
- left: 30px;
- width: 215px;
-
- .header {
- background-color: #101010;
- border-radius: 15px;
- padding: 0 0 5px 0;
- box-shadow: #030303 0px 0px 56px 0px;
-
-
- .top-triangle {
- position: relative;
- width: 100%;
- height: 100px;
- clip-path: polygon(0 0, 100% 0, 0 100%);
- background-color: #3BFFC5;
- z-index: 1;
- border-radius: 15px 15px 0 0;
- }
-
- .info {
- text-align: center;
- margin-top: -85px;
- }
-
- .icon {
- position: relative;
- width: 96px;
- image-rendering: pixelated;
- z-index: 2;
- }
-
- h5 {
- font-family: "Handjet", serif;
- color: white;
- margin: 0;
- font-weight: 300;
- font-size: 3em;
- text-transform: uppercase;
- }
- }
-
- .nav-links {
- list-style-type: none;
- margin: 10px 0 30px 0;
- padding: 0;
- text-transform: lowercase;
-
- ul {
- list-style-type: none;
- text-align: center;
- margin: 0;
- padding: 0;
- }
-
- li {
- display: inline-block;
- margin: 0 5px;
- line-height: 210%;
- color: white;
- font-family: "handjet", serif;
- }
-
- li a {
- font-family: "handjet", serif;
- font-weight: 700;
- text-decoration: none;
- color: black;
- padding: 4px 6px;
- margin-right: 5px;
-
- }
- }
-
- #nav-info {
- margin-top: 20px;
- width: 100%;
- background-color: #101010;
- box-shadow: #030303 0px 0px 56px 0px;
- border-radius: 15px;
-
- .project-info {
- padding: 20px 15px;
- }
-
- canvas {
- width: 100%;
- height: 100%;
- border-radius: 5px;
- }
-
- canvas:hover {
- cursor: pointer;
- }
-
- canvas:active {
- cursor: grabbing;
- }
-
-
- section {
- padding: 10px 0;
-
- h4 {
- font-family: "Cabin", sans-serif;
- color: white;
- text-transform: uppercase;
- font-weight: bold;
- font-size: 1em;
- margin: 0 0 15px 0;
- padding: 0;
- }
- }
-
- .button-list {
- list-style-type: none;
- padding: 0;
-
- li {
- margin: 5px 0;
-
- }
-
- a {
- display: block;
- width: 100%;
- font-family: "Handjet", serif;
- font-size: 1.2em;
- padding: 10px 0;
- border-radius: 5px;
- border: none;
- color: white;
- text-decoration: none;
- font-weight: bold;
- text-transform: uppercase;
- text-align: center;
- background-color: #252525;
- }
-
- .btn-highlight a {
- background-color: #FF523B;
- }
- }
- }
-
-
- .nav-footer {
- margin-top: 20px;
- width: 100%;
- text-align: center;
- color: white;
- font-family: "Cabin", sans-serif;
-
- .socials {
- list-style-type: none;
- margin: 0;
- padding: 0;
- }
-
- .socials li {
- display: inline-block;
- width: 17px;
- margin: 0 5px;
- }
-
- .socials li:hover {
- opacity: 0.5;
- }
-
- .socials img {
- color: white;
- z-index: 500;
-
- }
-
- .copyrights {
- font-size: 0.6em;
- color: #a1a1a1;
- }
-
- .button-container {
- width: 100%;
- }
- }
-}
\ No newline at end of file
diff --git a/src/content/home/assets/cd-labs-home-page-hook-animation.mp4 b/src/content/home/assets/cd-labs-home-page-hook-animation.mp4
new file mode 100644
index 0000000..7bd060c
Binary files /dev/null and b/src/content/home/assets/cd-labs-home-page-hook-animation.mp4 differ
diff --git a/src/content/home/assets/cd-labs-home-page-hook-animation.webm b/src/content/home/assets/cd-labs-home-page-hook-animation.webm
new file mode 100644
index 0000000..5fc0121
Binary files /dev/null and b/src/content/home/assets/cd-labs-home-page-hook-animation.webm differ
diff --git a/src/content/home/assets/philosophy-background-temp.png b/src/content/home/assets/philosophy-background-temp.png
new file mode 100644
index 0000000..e310a24
Binary files /dev/null and b/src/content/home/assets/philosophy-background-temp.png differ
diff --git a/src/content/home/index.ts b/src/content/home/index.ts
new file mode 100644
index 0000000..bfad40d
--- /dev/null
+++ b/src/content/home/index.ts
@@ -0,0 +1,112 @@
+import {HomePageContentStructure} from "../../views";
+
+import HOOK_VIDEO_WEBM from './assets/cd-labs-home-page-hook-animation.webm';
+import HOOK_VIDEO_MP4 from './assets/cd-labs-home-page-hook-animation.mp4';
+
+import PHILOSOPHY_BG from './assets/philosophy-background-temp.png';
+
+export const homePageContent: HomePageContentStructure = {
+ hook: {
+ header: "When was the last time you finished a game?",
+ body: "CD-Labs’ initiative is to design complete gaming experiences for busy lives.",
+ videoWebem: HOOK_VIDEO_WEBM,
+ videoMp4: HOOK_VIDEO_MP4,
+ },
+
+ // ------------------------------------------------------------------------
+ philosophy: {
+ sectionBG: PHILOSOPHY_BG,
+ header: "Games that respect your time.",
+
+ paragraphs: [
+ "I believe gaming should be an escape, not a second job. But as games have gotten bigger and more demanding, it's become harder to find the time to even finish one.",
+ "That's why I started cd-labs. My goal is to deliver compact, complete gaming experiences that honor your schedule. Each game is a focused, handcrafted quest meant to be enjoyed in a single sitting or over a few evenings.",
+ ],
+ buttons: [
+ { text: "Read More", path: "#", styleType: "secondary", contrastMode: 'dark' },
+ { text: "First Production", path: "#", styleType: "primary", contrastMode: 'dark' }
+ ],
+
+ alignment: 'left'
+ },
+
+ // ------------------------------------------------------------------------
+ teaser: {
+ sectionBG: PHILOSOPHY_BG,
+
+ introTitle: "The First Experience is Taking Shape",
+ header: "Project Aqua",
+
+ paragraphs: [
+ "Inspired by the frustrations of everyday bad design, Aqua places you in a sleek testing facility. Your mission: master a series of deceptively simple shower prototypes.",
+ "In this fast-paced puzzler, can you solve one of design's most infuriating problems—the shower interface—and calibrate each prototype?",
+ "A sharp puzzle and a fresh perspective distilled into a single, focused game.CD?",
+
+ paragraphs: [
+ "Hi, I'm CD. After more than a decade as a professional designer and developer in marketing, web, and the AAA games industry, I decided to take a different path. I left the world of blockbuster recipes behind to focus on what I love most: crafting truly unique gameplay ideas.",
+ "My goal with cd-labs is simple: to bring back that classic feeling of seeing a story through to its conclusion. I design my games to leave you feeling satisfied and inspired, not drained by an endless time commitment.",
+ ],
+
+ buttons: [
+ { text: "Let's connect", path: "#", styleType: "primary", contrastMode: 'dark' },
+ { text: "Read my story", path: "#", styleType: "secondary", contrastMode: 'dark' },
+ ],
+
+ alignment: 'left'
+ },
+
+ // ------------------------------------------------------------------------
+ sideQuests: {
+ sectionBG: PHILOSOPHY_BG,
+
+ introTitle: "Collaborating on New Interactive Adventures",
+ header: "Side Quests",
+
+ paragraphs: [
+ "While my main quest is crafting original games for cd-labs, I also take on special contract work. These 'side quests' allow me to collaborate with innovative clients on their own interactive adventures.",
+ "I apply the same philosophy of focused, user-centric design to these projects, helping build everything from polished prototypes to unique web applications. It's a chance to tackle new challenges and help bring other exciting visions to life.",
+ ],
+ buttons: [
+ { text: "Start a side quest", path: "#", styleType: "primary", contrastMode: 'dark' },
+ { text: "See our main quest", path: "#", styleType: "secondary", contrastMode: 'dark' }
+ ],
+
+ alignment: 'right'
+ },
+
+ // ------------------------------------------------------------------------
+ ctaBannerB: {
+ header: "Ready to press start?",
+ body: "Follow the development of Aqua, chat with other players, and get behind-the-scenes updates in the official cd-labs Discord server.",
+ buttons: [
+ { text: "Join the Discord", path: "#", styleType: "primary", contrastMode: 'light' },
+ { text: "Learn More", path: "#", styleType: "secondary", contrastMode: 'light' }
+ ],
+ alignment: 'left'
+ },
+}
\ No newline at end of file
diff --git a/src/content/projects/index.ts b/src/content/projects/index.ts
index 50e5f3b..6258d11 100644
--- a/src/content/projects/index.ts
+++ b/src/content/projects/index.ts
@@ -1,6 +1,4 @@
import {projectView, renderView} from "../../views";
-import {projectInfo} from "../../components/vertical-nav/info-project.ts";
-import {renderNavInfo} from "../../components/vertical-nav";
import {projectThumbnail} from "../../components/thumbnail-project";
import {archivePageReferences} from "./archives";
@@ -26,9 +24,7 @@ export function buildProjectPage(pageReference: string) {
}
const { content, techs, buttons } = page;
const viewContent = projectView(content);
- const navInfo = projectInfo(buttons, techs);
-
- renderNavInfo(navInfo);
+
renderView(viewContent);
}
diff --git a/src/heads/default.ts b/src/heads/default.ts
index 5ea62fc..866a73c 100644
--- a/src/heads/default.ts
+++ b/src/heads/default.ts
@@ -5,5 +5,5 @@
-
+
`
\ No newline at end of file
diff --git a/src/index.ts b/src/index.ts
index 89d2086..7c2ae7c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,15 +1,11 @@
+import {buildMainLogo, changeLogoOnScroll} from "./components/logo";
import { router } from "./core/router";
-import {buildVerticalNav} from "./components/vertical-nav";
-import {buildProjectPage} from "./content/projects";
-import {homeView, aboutView, contactView, buildViewBase, projectCollectionView, renderView} from "./views";
+import {homeView, buildViewBase, renderView} from "./views";
+import {buildTopNav} from "./components/top-nav";
const routes = [
{ path: '', handler: () => renderView(homeView()) },
- { path: '/projects', handler: () => renderView(projectCollectionView()) },
- { path: '/projects/:id', handler: (params) => buildProjectPage(params?.id) },
- { path: '/about', handler: () => renderView(aboutView()) },
- { path: '/contact', handler: () => renderView(contactView()) },
];
routes.forEach(route => router.registerRoute(route.path, route.handler));
@@ -19,10 +15,15 @@ routes.forEach(route => router.registerRoute(route.path, route.handler));
function init() {
const body = document.getElementsByTagName("body")[0];
const contentPage = buildViewBase();
- const verticalNav = buildVerticalNav();
+ const mainLogo = buildMainLogo();
+ const nav = buildTopNav();
+
+ body.appendChild(mainLogo);
+ body.appendChild(nav);
+ window.addEventListener('scroll', changeLogoOnScroll);
body.appendChild(contentPage);
- contentPage.appendChild(verticalNav);
+
router.handleRoute(window.location.pathname);
}
diff --git a/src/views/common/call-to-action/index.ts b/src/views/common/call-to-action/index.ts
new file mode 100644
index 0000000..ae20e7d
--- /dev/null
+++ b/src/views/common/call-to-action/index.ts
@@ -0,0 +1,66 @@
+import "./styles.css";
+import { createPixelGridBackground } from "../../utils/backgrounds-utils";
+import { writeTitle, writeParagraph, createMainButton, MainButtonOptions, createWrapper } from "../../utils";
+
+export type CallToActionOptions = {
+ header: string;
+ body: string;
+ buttons: MainButtonOptions[];
+ alignment: 'left' | 'right';
+}
+
+// --------------------------------------------------------------------------------
+
+export function createPixelBannerCTA(content: CallToActionOptions) {
+ const banner = createBanner();
+ banner.appendChild(createBackground(content.alignment));
+ banner.appendChild(createContent(content));
+ return banner;
+}
+
+// --------------------------------------------------------------------------------
+
+function createBanner() {
+ const banner = document.createElement('div');
+ banner.className = 'cta-banner pixel-banner';
+ return banner;
+}
+
+function createBackground(alignment: 'left' | 'right') {
+ const background = document.createElement('div');
+ background.className = 'cta-background';
+ const emptyAlignment = alignment === 'right' ? 'left' : 'right';
+ background.appendChild(createPixelGridBackground(emptyAlignment, {
+ rows: 4,
+ colors: ['#0f0f0f', '#2a2a2a', '#181818']
+ }));
+ return background;
+}
+
+function createContent(content: CallToActionOptions) {
+ const wrapper = createWrapper();
+ const article = document.createElement('article');
+ article.className = `${content.alignment}`;
+
+ article.appendChild(createTextSection(content));
+ article.appendChild(createButtonsSection(content.buttons));
+
+ wrapper.appendChild(article);
+ return wrapper;
+}
+
+function createTextSection(content: CallToActionOptions) {
+ const container = document.createElement('div');
+ container.className = 'text-container';
+ container.appendChild(writeTitle("h4", content.header));
+ container.appendChild(writeParagraph(content.body));
+ return container;
+}
+
+function createButtonsSection(buttons: MainButtonOptions[]) {
+ const container = document.createElement('ul');
+ buttons.forEach(button => {
+ container.appendChild(createMainButton(button));
+ });
+ return container;
+}
\ No newline at end of file
diff --git a/src/views/common/call-to-action/styles.css b/src/views/common/call-to-action/styles.css
new file mode 100644
index 0000000..626bb0a
--- /dev/null
+++ b/src/views/common/call-to-action/styles.css
@@ -0,0 +1,142 @@
+.cta-banner.pixel-banner {
+ position: relative;
+ width: 100%;
+ text-align: left;
+ overflow: hidden;
+}
+
+.cta-banner.pixel-banner .cta-background {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ z-index: -5;
+ width: 100%;
+ height: 100%;
+ opacity: 0.6;
+}
+
+.cta-banner.pixel-banner article {
+ z-index: 2;
+ padding: clamp(30px, 8vw, 50px);
+ background-color: white;
+ width: fit-content;
+
+ display: grid;
+ grid-template-columns: auto auto;
+ gap: clamp(50px, 15vw, 150px);
+ align-items: center;
+
+ p {
+ color: black;
+ }
+
+ .text-container {
+ display: flex;
+ flex-direction: column;
+ gap: clamp(15px, 4vw, 20px);
+ max-width: clamp(400px, 80vw, 600px);
+ }
+
+ ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ display: flex;
+ flex-direction: row;
+ gap: clamp(15px, 4vw, 20px);
+ flex-wrap: wrap;
+ }
+}
+
+.cta-banner.pixel-banner article.right {
+ margin-left: auto;
+ justify-content: end;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: clamp(15vw, 20vw, 25vw);
+ height: 100%;
+ background-color: white;
+ z-index: -1;
+ }
+}
+
+.cta-banner.pixel-banner article.left {
+ margin-right: auto;
+ justify-content: start;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: clamp(15vw, 20vw, 25vw);
+ height: 100%;
+ background-color: white;
+ z-index: -1;
+ }
+}
+
+/* Media Queries for enhanced mobile experience */
+@media (max-width: 1500px) {
+ .cta-banner.pixel-banner {
+ background-color: white;
+ }
+ .cta-banner.pixel-banner .cta-background{
+ display: none;
+ }
+}
+
+@media (max-width: 768px) {
+ .cta-banner.pixel-banner article {
+ grid-template-columns: 1fr;
+ gap: clamp(30px, 8vw, 40px);
+ padding: clamp(25px, 6vw, 40px);
+ width: 90%;
+ margin: 0 auto;
+ text-align: center;
+
+ .text-container {
+ max-width: 100%;
+ }
+
+ ul {
+ justify-content: center;
+ flex-direction: column;
+ align-items: center;
+ gap: clamp(12px, 3vw, 15px);
+ }
+ }
+
+ .cta-banner.pixel-banner article.right,
+ .cta-banner.pixel-banner article.left {
+ margin: 0 auto;
+ justify-content: center;
+
+ &::before {
+ width: 100%;
+ left: 0;
+ right: 0;
+ }
+ }
+}
+
+@media (max-width: 480px) {
+ .cta-banner.pixel-banner article {
+ width: 95%;
+ padding: clamp(20px, 5vw, 30px);
+ gap: clamp(20px, 6vw, 30px);
+
+ ul {
+ width: 100%;
+
+ li {
+ width: 100%;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/views/home.ts b/src/views/home.ts
deleted file mode 100644
index e234a44..0000000
--- a/src/views/home.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import {createThreeBackground, writeParagraph, writeTitle} from "./utils";
-import {BackgroundChoice} from "../components/three-background";
-import {cdCover} from "../components/cd-cover";
-
-export type HomePageContent = {
- readonly title: string;
- readonly introText: string;
-}
-
-// --------------------------------------------------------------------------------
-
-export function homeView() {
- const article = document.createElement('article');
- const introBlock = document.createElement('div');
- const pageTitle = writeTitle("h1", "pick a track");
- const pageText = writeParagraph("Welcome to my hub! I’m CD-BASH, a creative developer based in Montreal.");
-
- article.id = 'home-page';
- introBlock.className = 'intro-block';
-
- createThreeBackground(BackgroundChoice.Home);
- introBlock.appendChild(pageTitle);
- introBlock.appendChild(pageText);
- article.appendChild(introBlock);
- article.appendChild(cdCoverSelection());
-
- return article;
-}
-
-// --------------------------------------------------------------------------------
-
-function cdCoverSelection() {
- const cdCovers = document.createElement('div');
- cdCovers.className = 'cd-covers-selection';
-
- const projectCover = cdCover();
- const aboutCover = cdCover();
- const logsCover = cdCover();
-
- cdCovers.appendChild(projectCover);
- cdCovers.appendChild(aboutCover);
- cdCovers.appendChild(logsCover);
-
- return cdCovers;
-}
-
diff --git a/src/views/home/contentSection.ts b/src/views/home/contentSection.ts
new file mode 100644
index 0000000..b1fecd0
--- /dev/null
+++ b/src/views/home/contentSection.ts
@@ -0,0 +1,75 @@
+import { GRID_CONFIG } from '../../components/pixel-grid';
+import { createMainButton, MainButtonOptions } from '../utils/';
+import {createWrapper, writeParagraph, writeTitle} from "../utils";
+import { createPixelGridBackground } from '../utils/backgrounds-utils';
+
+const pixelGridConfigs: GRID_CONFIG = {
+ rows: 6,
+ colors: ['#0f0f0f', '#2a2a2a', '#181818']
+}
+
+export type SectionContent = {
+ readonly sectionBG: string;
+ readonly introTitle?: string;
+ readonly header: string;
+ readonly paragraphs: string[];
+ readonly buttons: MainButtonOptions[];
+ readonly alignment: 'left' | 'right';
+}
+
+
+// --------------------------------------------------------------------------------
+
+export function createContentSection(content: SectionContent) {
+ const section = document.createElement('section');
+ const wrapper = createWrapper();
+ const article = document.createElement('article');
+
+ section.className = `content-section ${content.alignment}`;
+ section.style.backgroundImage = `url('${content.sectionBG}')`;
+ section.appendChild(createPixelGridBackground(content.alignment, pixelGridConfigs));
+
+ if (content.introTitle) {
+ article.appendChild(introTitle(content.introTitle));
+ }
+
+ article.appendChild(writeTitle("h2", content.header));
+ article.appendChild(sectionParagraphs(content.paragraphs));
+ article.appendChild(sectionButtons(content.buttons));
+
+ wrapper.appendChild(article);
+ section.appendChild(wrapper);
+
+ return section;
+}
+
+// --------------------------------------------------------------------------------
+
+function sectionParagraphs(paragraphs: string[]) {
+ const paragraphsContainer = document.createElement('div');
+ paragraphsContainer.className = 'paragraphs-container';
+
+ paragraphs.forEach(text => {
+ const p = writeParagraph(text);
+ paragraphsContainer.appendChild(p);
+ });
+ return paragraphsContainer;
+}
+
+function sectionButtons(contentButtons: MainButtonOptions[]) {
+ const buttonsContainer = document.createElement('ul');
+ buttonsContainer.className = 'buttons-container';
+
+ contentButtons.forEach(buttonOptions => {
+ const button = createMainButton(buttonOptions);
+ buttonsContainer.appendChild(button);
+ });
+ return buttonsContainer;
+}
+
+function introTitle(text: string) {
+ const intro = document.createElement('h3');
+ intro.className = 'intro-title';
+ intro.innerHTML = text;
+ return intro;
+}
\ No newline at end of file
diff --git a/src/views/home/index.ts b/src/views/home/index.ts
new file mode 100644
index 0000000..52169c6
--- /dev/null
+++ b/src/views/home/index.ts
@@ -0,0 +1,59 @@
+import './styles.css';
+import { arrowButton, createVideoBackground, createWrapper, writeParagraph, writeTitle } from "../utils";
+import { CallToActionOptions, createPixelBannerCTA } from '../common/call-to-action';
+import {SectionContent, createContentSection} from "./contentSection";
+import {homePageContent} from "../../content/home";
+
+export type HomePageContentStructure = {
+ readonly hook: HookContent;
+ readonly philosophy: SectionContent;
+ readonly teaser: SectionContent;
+ readonly ctaBannerA: CallToActionOptions;
+ readonly founder: SectionContent;
+ readonly sideQuests: SectionContent
+ readonly ctaBannerB: CallToActionOptions;
+}
+
+type HookContent = {
+ readonly header: string;
+ readonly body: string;
+ readonly videoWebem: string;
+ readonly videoMp4: string;
+}
+
+// --------------------------------------------------------------------------------
+
+export function homeView() {
+ const page = document.createElement('div');
+ page.id = 'home-page';
+
+ page.appendChild(hookSection(homePageContent.hook));
+ page.appendChild(createContentSection(homePageContent.philosophy));
+ page.appendChild(createContentSection(homePageContent.teaser));
+ page.appendChild(createPixelBannerCTA(homePageContent.ctaBannerA));
+ page.appendChild(createContentSection(homePageContent.founder));
+ page.appendChild(createContentSection(homePageContent.sideQuests));
+ page.appendChild(createPixelBannerCTA(homePageContent.ctaBannerB));
+
+ return page;
+}
+
+// --------------------------------------------------------------------------------
+
+function hookSection(content: HookContent) {
+ const hook = document.createElement('section');
+ const videoBg = createVideoBackground(content.videoWebem, content.videoMp4);
+ const wrapper = createWrapper();
+ const article = document.createElement('article');
+
+ hook.className = 'hook';
+
+ article.appendChild(writeTitle("h1", content.header));
+ article.appendChild(writeParagraph(content.body));
+ article.appendChild(arrowButton());
+ wrapper.appendChild(article);
+ hook.appendChild(videoBg);
+ hook.appendChild(wrapper);
+
+ return hook;
+}
diff --git a/src/views/home/styles.css b/src/views/home/styles.css
new file mode 100644
index 0000000..0c3c6cb
--- /dev/null
+++ b/src/views/home/styles.css
@@ -0,0 +1,154 @@
+#home-page {
+
+ #wrapper {
+ z-index: 2;
+ }
+
+ h2 {
+ margin-bottom: clamp(30px, 5vw, 50px);
+ }
+
+ section {
+ width: 100%;
+ min-height: 100vh;
+ height: 100vh;
+ margin: 0;
+ padding: 0 clamp(1rem, 5vw, 2rem);
+ box-sizing: border-box;
+ background-size: cover;
+ background-position: center;
+ }
+
+ .highlight {
+ text-decoration: underline;
+ text-decoration-color: #3BFFC5;
+ text-underline-offset: clamp(5px, 3vw, 20px);
+ }
+
+ .hook {
+ h1, p {
+ text-shadow: 0 4px 55px rgba(0, 0, 0, 0.65);
+ max-width: min(1160px, 90vw);
+ }
+
+ article {
+ margin: 0 clamp(5%, 15vw, 15%);
+ display: flex;
+ text-align: center;
+ flex-direction: column; /* Stack children vertically */
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ padding: clamp(1rem, 5vw, 2rem);
+
+ p {
+ font-size: clamp(1.1em, 3vw, 1.5em);
+ margin-top: clamp(30px, 8vw, 75px);
+ margin-bottom: clamp(15px, 3vw, 20px);
+ max-width: min(630px, 80vw);
+ }
+ }
+ }
+
+ .content-section {
+ position: relative; /* Ensure the section is the positioning context */
+ overflow: hidden; /* Hide the overflowing pixels */
+
+ canvas.pixel-grid {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 1; /* Below the article */
+ pointer-events: none;
+ }
+
+ article {
+ position: relative;
+ z-index: 2; /* Above the pixel grid */
+ display: flex;
+ width: clamp(600px, 50%, 900px);
+ flex-direction: column;
+ justify-content: center;
+ align-items: flex-start;
+ height: 100vh;
+ padding: clamp(1rem, 5vw, 2rem);
+ }
+
+ &.right article {
+ margin-left: auto;
+ }
+
+ p {
+ max-width: clamp(500px, 90%, 750px);
+ margin-bottom: clamp(20px, 4vw, 30px);
+ text-align: left;
+ font-size: clamp(0.9rem, 2.5vw, 1.1rem);
+ }
+
+ .buttons-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: clamp(10px, 3vw, 20px);
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ }
+ }
+
+ /* Media Queries for better mobile experience */
+ @media (max-width: 768px) {
+ section {
+ height: auto;
+ min-height: 100vh;
+ padding: 0;
+ }
+
+ .hook article {
+ margin: 0 5%;
+ padding: 0;
+ }
+
+ .content-section {
+ article {
+ width: 90%;
+ padding: 0;
+ text-align: center;
+ align-items: center;
+ }
+
+ &.right article {
+ margin-left: auto;
+ margin-right: auto;
+ }
+
+ p {
+ max-width: 100%;
+ text-align: center;
+ }
+
+ .buttons-container {
+ justify-content: center;
+ width: 100%;
+ }
+ }
+ }
+
+ @media (max-width: 480px) {
+ .hook article {
+ margin: 0;
+ padding: 0;
+ }
+
+ .content-section article {
+ width: 100%;
+ padding: 0;
+ }
+
+ .buttons-container {
+ flex-direction: column;
+ align-items: center;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/views/index.ts b/src/views/index.ts
index ad2998b..0447e33 100644
--- a/src/views/index.ts
+++ b/src/views/index.ts
@@ -1,6 +1,6 @@
import "./styles.css";
-export * from "./home";
+export * from "./home/";
export * from "./projectCollection.ts";
export * from "./project.ts";
export * from "./about.ts";
@@ -10,27 +10,23 @@ export * from "./contact.ts";
// --------------------------------------------------------------------------------
export function buildViewBase() {
- const viewBox = document.createElement('div');
- const viewWrapper = document.createElement('div');
- viewBox.id = 'view-box';
- viewWrapper.id = 'view-wrapper';
-
- viewBox.appendChild(viewWrapper);
- return viewBox;
+ const view = document.createElement('div');
+ view.id = 'view';
+ return view;
}
-export function renderView(view: HTMLElement) {
- const viewWrapper = clearView();
- viewWrapper.appendChild(view);
+export function renderView(newView: HTMLElement) {
+ const cleanView = clearView();
+ cleanView.appendChild(newView);
}
// --------------------------------------------------------------------------------
function clearView() {
- const cleanViewWrapper = document.getElementById('view-wrapper')!;
- cleanViewWrapper?.childNodes.forEach((childNode) => {
- cleanViewWrapper.removeChild(childNode);
- });
+ const view = document.getElementById('view')!;
+ while (view.firstChild) {
+ view.removeChild(view.firstChild);
+ }
- return cleanViewWrapper;
+ return view;
}
diff --git a/src/views/styles.css b/src/views/styles.css
index d877835..235db25 100644
--- a/src/views/styles.css
+++ b/src/views/styles.css
@@ -1,238 +1,16 @@
-body {
- background-color: black;
-}
-
-h1 {
- margin: 0;
- max-width: 75%;
- font-size: 6em;
- line-height: 75%;
- color: white;
- text-transform: lowercase;
-}
-
-h2 {
- margin: 0 0 20px 0;
- font-size: 1.2em;
- color: white;
- text-transform: uppercase;
-}
-
-h3 {
- font-family: "Handjet", serif;
- font-size: 2.8em;
- font-weight: 600;
- color: #828282;
-}
-
-h5 {
- font-family: "Handjet", serif;
- font-size: 1.5em;
- font-weight: 600;
- text-transform: lowercase;
- color: #828282;
-}
-
-h6 {
- margin: 0 0 20px 0;
- font-family: "Handjet", serif;
- font-size: 2em;
- font-weight: 600;
- text-transform: lowercase;
- color: #3BFFC5;
-}
-
-p {
- font-size: 2em;
- font-weight: normal;
- color: #828282;
- line-height: 150%;
- margin: 0;
-}
-
-p.intro-text {
- margin-bottom: 50px;
-}
-
-p.description {
- font-family: "Cabin", sans-serif;
- color: #A1A1A1;
- font-size: 0.9em;
-}
-
-#view-box {
- padding-left: 280px;
+
+#view {
height: 100%;
- font-family: "Cabin", sans-serif;
-
+ font-family: 'Inter', sans-serif;
- #view-wrapper {
- width: 720px;
+ #wrapper {
+ width: 85%;
margin: 0 auto;
- padding-top: 60px;
-
- section {
- margin:0;
- padding: 58px 0;
- clear: both;
- }
-
- .showcase {
- margin: 50px 0;
- width: 100%;
- border: #080808 solid 3px;
- }
-
- .video-showcase::before, .video-showcase::after {
- content: "";
- display: block;
- width: 100%;
- padding-top: 65px;
- z-index: -2;
- background-image: url("utils/assets/video-showcase-decoration.png");
- background-blend-mode: multiply;
- background-repeat: repeat-x;
- filter: invert(100%) sepia(100%) hue-rotate(240deg) saturate(100%);
- }
-
- .video-showcase::before {
- margin-bottom: -15px;
- }
-
- .video-showcase::after {
- margin-top: -15px;
- transform: scaleY(-1);
- }
-
- .video-showcase video {
- width: 100%;
- border-radius: 15px;
- z-index: 5;
- position: relative;
- }
-
- .gallery-content {
- padding-top: 20px;
- }
-
- .gallery-content img {
- object-fit: cover;
- width: 45%;
- aspect-ratio:1/1;
- margin: 10px 0;
- margin-right: 4%;
- border: 2px solid #505050;
- border-radius: 10px;
- }
-
- .link {
- font-family: "Handjet", serif;
- display: inline-block;
- color: #828282;;
- font-size: 2.5em;
- font-weight: normal;
- padding: 0 30px 15px 0;
- text-align: center;
- text-decoration-color: #3BFFC5;
- }
-
- #home-page {
- .cd-covers-selection {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 40px;
- justify-items: center;
- align-items: center;
- width: 170%; /* wider than wrapper */
- max-width: none;
- margin-left: -35%; /* center and overflow */
- position: relative;
- z-index: 1;
- padding: 40px 0;
-
- }
- }
-
- #about-page {
-
- .intro {
- text-align: center;
- padding-bottom: 50px;
- display: block;
- }
-
- .title {
- font-family: "Handjet", serif;
- font-size: 2.5em;
- font-weight: 600;
- color: #3BFFC5;
- text-transform: lowercase;
- }
-
- .grid-category {
- display: grid;
- padding: 50px 0 0 0;
- margin: 0 0 25px 0;
- border-top: 2px dashed #505050;
- grid-template-columns: repeat(6, 1fr);
- gap: 2%;
-
- h2 {
- grid-column: span 6;
- }
-
- .grid-item {
- margin: 0 0 50px 0;
- grid-column: span 3;
-
-
- .subtitle {
- color: white;
- }
-
- .text {
- font-size: 1em;
- }
- }
- }
-
- .achievements, .profile-links {
- border-top: 2px dashed #505050;
- padding: 50px 0 0 0;
- margin: 0 0 25px 0;
-
- .item {
- margin: 0 0 50px 0;
- }
- }
-
- }
-
- #home-page {
-
- h1 {
- margin: 0 auto 50px auto;
-
- }
-
- .intro-block {
- text-align: center;
- }
-
- }
- }
-
- .three-background {
- position: fixed;
- background-color: #000000;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: -15;
}
}
+
+
diff --git a/src/views/utils/assets/icon_arrow_down.svg b/src/views/utils/assets/icon_arrow_down.svg
new file mode 100644
index 0000000..4933aba
--- /dev/null
+++ b/src/views/utils/assets/icon_arrow_down.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/views/utils/backgrounds-utils.ts b/src/views/utils/backgrounds-utils.ts
index be055b4..32a5cee 100644
--- a/src/views/utils/backgrounds-utils.ts
+++ b/src/views/utils/backgrounds-utils.ts
@@ -1,5 +1,5 @@
import {BackgroundChoice, threeBackground} from "../../components/three-background";
-
+import { createPixelGrid, GRID_CONFIG } from "../../components/pixel-grid";
export function createThreeBackground(choice: BackgroundChoice) {
const viewBox = document.getElementById('view-box')!;
@@ -11,4 +11,30 @@ export function createThreeBackground(choice: BackgroundChoice) {
viewBox.appendChild(background);
}
+}
+
+export function createVideoBackground(videoWebm: string, videoMp4: string) {
+ const background = document.createElement('video');
+ background.className = 'video-background';
+ background.autoplay = true;
+ background.loop = true;
+ background.muted = true;
+
+ const sourceWebm = document.createElement('source');
+ sourceWebm.src = videoWebm;
+ sourceWebm.type = 'video/webm';
+
+ const sourceMp4 = document.createElement('source');
+ sourceMp4.src = videoMp4;
+ sourceMp4.type = 'video/mp4';
+
+ background.appendChild(sourceWebm);
+ background.appendChild(sourceMp4);
+
+ return background;
+}
+
+export function createPixelGridBackground(contentAlignment: 'left' | 'right', configs: GRID_CONFIG) {
+ const alignment = contentAlignment === 'right' ? 'left' : 'right';
+ return createPixelGrid(configs, alignment);
}
\ No newline at end of file
diff --git a/src/views/utils/buttons-utils.ts b/src/views/utils/buttons-utils.ts
new file mode 100644
index 0000000..c222188
--- /dev/null
+++ b/src/views/utils/buttons-utils.ts
@@ -0,0 +1,30 @@
+import ARROW_ICON from './assets/icon_arrow_down.svg';
+
+export type MainButtonOptions = {
+ text: string;
+ path: string;
+ styleType: 'primary' | 'secondary';
+ contrastMode: 'light' | 'dark';
+};
+
+// ------------------------------------------------------------------------
+
+export function arrowButton() {
+ const button = document.createElement('button');
+ button.className = 'arrow-button';
+ button.innerHTML = `
`;
+
+ return button;
+}
+
+export function createMainButton(options: MainButtonOptions) {
+ const { text, path, styleType, contrastMode } = options;
+
+ const button = document.createElement('a');
+ button.className = `main-button ${styleType} ${contrastMode}`;
+ button.innerHTML = text;
+ button.href = path;
+ button.role = 'button';
+
+ return button;
+}
\ No newline at end of file
diff --git a/src/views/utils/index.ts b/src/views/utils/index.ts
index dfd3651..065c5c4 100644
--- a/src/views/utils/index.ts
+++ b/src/views/utils/index.ts
@@ -1,7 +1,18 @@
-export * from "./text-utils.ts";
+import "./styles.css";
+
+export * from "./text-utils.ts";
export * from "./visual-utils.ts";
export * from "./backgrounds-utils.ts";
+export * from "./buttons-utils.ts";
+
+// ------------------------------------------------------------------------
export function scrollTop() {
window.scrollTo(0, 0);
+}
+
+export function createWrapper() {
+ const wrapper = document.createElement('div');
+ wrapper.id = 'wrapper';
+ return wrapper;
}
\ No newline at end of file
diff --git a/src/views/utils/styles.css b/src/views/utils/styles.css
new file mode 100644
index 0000000..e6a922a
--- /dev/null
+++ b/src/views/utils/styles.css
@@ -0,0 +1,204 @@
+body {
+ background-color: black;
+ margin: 0;
+}
+
+h1, h2 {
+ margin: 0;
+ font-family: "Inter", sans-serif;
+ font-size: clamp(2.5rem, 8vw, 6rem);
+ font-weight: 900;
+ line-height: 100%;
+ color: white;
+}
+
+h3 {
+ font-family: "Inter", sans-serif;
+ font-size: clamp(1.25rem, 4vw, 2rem);
+ font-weight: 700;
+ color: #ffffff;
+}
+
+h4 {
+ margin: 0;
+ font-family: "Inter", sans-serif;
+ font-size: clamp(1.5rem, 5vw, 3rem);
+ font-weight: 900;
+ color: #000000;
+}
+
+h5 {
+ font-family: "Handjet", serif;
+ font-size: clamp(1rem, 3vw, 1.5rem);
+ font-weight: 600;
+ text-transform: lowercase;
+ color: #828282;
+}
+
+h6 {
+ margin: 0 0 clamp(15px, 3vw, 20px) 0;
+ font-family: "Handjet", serif;
+ font-size: clamp(1.2rem, 4vw, 2rem);
+ font-weight: 600;
+ text-transform: lowercase;
+ color: #3BFFC5;
+}
+
+p {
+ font-size: clamp(0.875rem, 2.5vw, 1rem);
+ font-weight: normal;
+ color: white;
+ line-height: 130%;
+ margin: 0;
+}
+
+/* -------------------------------------------------------------------------------- */
+.arrow-button {
+ background: none;
+ border: none;
+ cursor: pointer;
+ width: clamp(60px, 15vw, 75px);
+ height: clamp(60px, 15vw, 75px);
+ padding: clamp(15px, 4vw, 20px);
+ background-color: rgba(255, 255, 255, 0.05);
+ border-radius: 50%;
+ border: 2px solid rgba(255, 255, 255, 0.1);
+ margin: clamp(15px, 4vw, 20px) auto 0 auto;
+ display: block;
+ backdrop-filter: blur(8px);
+ -webkit-backdrop-filter: blur(8px);
+}
+
+
+/* -------------------------------------------------------------------------------- */
+.main-button {
+ display: inline-block;
+ min-width: clamp(180px, 45vw, 220px);
+ height: clamp(36px, 10vw, 40px);
+
+ text-align: center;
+ line-height: clamp(32px, 8vw, 36px);
+ font-family: 'Handjet', sans-serif;
+ font-size: clamp(1.1rem, 3.5vw, 1.4rem);
+ text-transform: uppercase;
+ text-decoration: none;
+
+ border-radius: clamp(18px, 5vw, 20px);
+ border: 2px solid;
+
+ backdrop-filter: blur(30px);
+ -webkit-backdrop-filter: blur(30px);
+ overflow: hidden;
+
+ transition: background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease;
+ cursor: pointer;
+}
+
+.main-button.primary.dark {
+ background-color: rgba(59, 255, 197, 0.05);
+ border-color: rgba(59, 255, 197, 1);
+ color: #ffffff;
+
+ &:hover {
+ background-color: #34e6c0;
+ border-color: #34e6c0;
+ }
+}
+
+.main-button.primary.light {
+ background-color: rgba(59, 255, 197, 0.15);
+ border-color: rgba(59, 255, 197, 1);
+ color: #000000;
+
+ &:hover {
+ background-color: #34e6c0;
+ border-color: #34e6c0;
+ color: #ffffff;
+ }
+}
+
+.main-button.secondary.dark {
+ background-color: rgba(255, 255, 255, 0.05);
+ border-color: rgba(255, 255, 255, 0.1);
+ color: #ffffff;
+
+ &:hover {
+ background-color: rgba(255, 255, 255, 1);
+ border-color: #ffffff;
+ color: #000000;
+ }
+}
+
+.main-button.secondary.light {
+ background-color: rgba(0, 0, 0, 0.05);
+ border-color: rgba(0, 0, 0, 0.1);
+ color: #000000;
+
+ &:hover {
+ background-color: #555555;
+ border-color: #555555;
+ color: #ffffff;
+ }
+}
+
+
+
+/* -------------------------------------------------------------------------------- */
+.video-background {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ transform: translate(-50%, -50%);
+ z-index: -1;
+ min-width: 100%;
+ min-height: 100%;
+}
+
+/* Media Queries for enhanced mobile typography */
+@media (max-width: 768px) {
+ h1, h2 {
+ line-height: 110%;
+ }
+
+ h3, h4, h5, h6 {
+ line-height: 120%;
+ }
+
+ p {
+ line-height: 140%;
+ }
+
+ .main-button {
+ min-width: 160px;
+ padding: 0 1rem;
+ width: 100%;
+ max-width: 280px;
+ }
+
+ .arrow-button {
+ margin: 15px auto 0 auto;
+ }
+}
+
+@media (max-width: 480px) {
+ h1, h2 {
+ font-size: clamp(2rem, 12vw, 3.5rem);
+ line-height: 105%;
+ }
+
+ .main-button {
+ width: 100%;
+ max-width: 240px;
+ font-size: 1rem;
+ line-height: 32px;
+ height: 36px;
+ }
+}
+
+
+
+
+
diff --git a/src/views/utils/text-utils.ts b/src/views/utils/text-utils.ts
index f428a0c..b3ef7c2 100644
--- a/src/views/utils/text-utils.ts
+++ b/src/views/utils/text-utils.ts
@@ -6,14 +6,14 @@
export function writeTitle(importance: string,text: string) {
const title = document.createElement(importance);
- title.textContent = text;
+ title.innerHTML = text;
return title;
}
export function writeParagraph(text: string) {
const paragraph = document.createElement("p");
- paragraph.textContent = text;
+ paragraph.innerHTML = text;
return paragraph;
}
@@ -22,7 +22,7 @@ export function writeLink(text: string, href: string) {
const link = document.createElement('a');
link.className = "link";
link.href = href;
- link.textContent = text;
+ link.innerHTML = text;
link.target = "_blank";
return link;
diff --git a/webhub.code-workspace b/webhub.code-workspace
new file mode 100644
index 0000000..876a149
--- /dev/null
+++ b/webhub.code-workspace
@@ -0,0 +1,8 @@
+{
+ "folders": [
+ {
+ "path": "."
+ }
+ ],
+ "settings": {}
+}
\ No newline at end of file