My personal site cherry.computer
htmx tailwind axum askama

feat: reinit website

Let's use Tailwind this time to quickly prototype styling. Might rip it
out later once things have settled down.

cherry.computer 6aa4ec8a 28a640a4

verified
+1663 -1999
+2 -1
.gitignore
··· 1 1 node_modules 2 2 /frontend/fonts 3 3 /frontend/build 4 + /frontend/src/css/tailwind-out.css 4 5 /frontend/styles 5 6 /frontend/css 6 7 /frontend/images 7 - /server/target 8 + /server/target
+5 -2
Dockerfile
··· 6 6 RUN npm install 7 7 8 8 COPY frontend . 9 + 10 + # tailwind classes are in the backend's HTML template files 11 + COPY server/templates templates 12 + RUN sed -i "s|../../../server/templates|../../templates|" src/css/tailwind.css 13 + 9 14 RUN npm run build:production 10 15 11 16 FROM rust:1.85 AS builder-rs ··· 26 31 27 32 WORKDIR /root 28 33 29 - COPY --from=build-js /usr/src/myivo/images ./images 30 - COPY --from=build-js /usr/src/myivo/fonts ./fonts 31 34 COPY --from=build-js /usr/src/myivo/build ./build 32 35 COPY --from=builder-rs /usr/local/cargo/bin/myivo-server /usr/local/bin/ 33 36
+25 -8
frontend/esbuild.js
··· 3 3 import esbuild from "esbuild"; 4 4 import { esbuildPluginBrowserslist } from "esbuild-plugin-browserslist"; 5 5 import minimist from "minimist"; 6 + 7 + import child_process from "node:child_process"; 6 8 import http from "node:http"; 7 9 8 10 const argv = minimist(process.argv.slice(2)); ··· 18 20 ], 19 21 }; 20 22 23 + const tailwindBin = "node_modules/.bin/tailwindcss"; 24 + const tailwindArgs = [ 25 + "-i", 26 + "src/css/tailwind.css", 27 + "-o", 28 + "src/css/tailwind-out.css", 29 + ]; 30 + 21 31 const devOptions = { 22 32 ...baseOptions, 23 33 sourcemap: true, 24 34 outfile: "build/app.js", 25 35 }; 26 - if (argv.prod) { 27 - await esbuild.build({ 28 - ...baseOptions, 29 - minify: true, 30 - outfile: "build/app.min.js", 31 - }); 32 - } else if (argv.serve) { 36 + if (argv.serve) { 37 + child_process.fork(tailwindBin, [...tailwindArgs, "-w"]); 38 + 33 39 const context = await esbuild.context(devOptions); 34 40 const { host, port } = await context.serve({ servedir: "." }); 35 41 ··· 62 68 }) 63 69 .listen(proxyPort); 64 70 } else { 65 - await esbuild.build(devOptions); 71 + const tailwindChild = child_process.fork(tailwindBin, tailwindArgs); 72 + await new Promise((resolve) => tailwindChild.on("exit", resolve)); 73 + 74 + if (argv.prod) { 75 + await esbuild.build({ 76 + ...baseOptions, 77 + minify: true, 78 + outfile: "build/app.min.js", 79 + }); 80 + } else { 81 + await esbuild.build(devOptions); 82 + } 66 83 }
+1606 -134
frontend/package-lock.json
··· 9 9 "version": "1.0.0", 10 10 "license": "MIT", 11 11 "dependencies": { 12 - "htmx-ext-sse": "^2.2.2", 13 12 "htmx.org": "^2.0.4", 14 - "modern-normalize": "^3.0.1", 15 - "three": "^0.172.0" 13 + "tailwindcss": "^4.1.12" 16 14 }, 17 15 "devDependencies": { 18 16 "@eslint/js": "^9.17.0", 17 + "@tailwindcss/cli": "^4.1.12", 19 18 "@types/node": "^22.10.5", 20 - "@types/three": "^0.172.0", 21 19 "browserslist": "^4.24.4", 22 20 "esbuild": "^0.24.2", 23 21 "esbuild-plugin-browserslist": "^0.15.0", ··· 720 718 "url": "https://github.com/sponsors/nzakas" 721 719 } 722 720 }, 721 + "node_modules/@isaacs/fs-minipass": { 722 + "version": "4.0.1", 723 + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", 724 + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", 725 + "dev": true, 726 + "license": "ISC", 727 + "dependencies": { 728 + "minipass": "^7.0.4" 729 + }, 730 + "engines": { 731 + "node": ">=18.0.0" 732 + } 733 + }, 734 + "node_modules/@jridgewell/gen-mapping": { 735 + "version": "0.3.13", 736 + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", 737 + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", 738 + "dev": true, 739 + "license": "MIT", 740 + "dependencies": { 741 + "@jridgewell/sourcemap-codec": "^1.5.0", 742 + "@jridgewell/trace-mapping": "^0.3.24" 743 + } 744 + }, 745 + "node_modules/@jridgewell/remapping": { 746 + "version": "2.3.5", 747 + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", 748 + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", 749 + "dev": true, 750 + "license": "MIT", 751 + "dependencies": { 752 + "@jridgewell/gen-mapping": "^0.3.5", 753 + "@jridgewell/trace-mapping": "^0.3.24" 754 + } 755 + }, 756 + "node_modules/@jridgewell/resolve-uri": { 757 + "version": "3.1.2", 758 + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 759 + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 760 + "dev": true, 761 + "license": "MIT", 762 + "engines": { 763 + "node": ">=6.0.0" 764 + } 765 + }, 766 + "node_modules/@jridgewell/sourcemap-codec": { 767 + "version": "1.5.5", 768 + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", 769 + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", 770 + "dev": true, 771 + "license": "MIT" 772 + }, 773 + "node_modules/@jridgewell/trace-mapping": { 774 + "version": "0.3.30", 775 + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", 776 + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", 777 + "dev": true, 778 + "license": "MIT", 779 + "dependencies": { 780 + "@jridgewell/resolve-uri": "^3.1.0", 781 + "@jridgewell/sourcemap-codec": "^1.4.14" 782 + } 783 + }, 723 784 "node_modules/@nodelib/fs.scandir": { 724 785 "version": "2.1.5", 725 786 "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", ··· 758 819 "node": ">= 8" 759 820 } 760 821 }, 761 - "node_modules/@tweenjs/tween.js": { 762 - "version": "23.1.3", 763 - "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", 764 - "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", 822 + "node_modules/@parcel/watcher": { 823 + "version": "2.5.1", 824 + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", 825 + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", 826 + "dev": true, 827 + "hasInstallScript": true, 828 + "license": "MIT", 829 + "dependencies": { 830 + "detect-libc": "^1.0.3", 831 + "is-glob": "^4.0.3", 832 + "micromatch": "^4.0.5", 833 + "node-addon-api": "^7.0.0" 834 + }, 835 + "engines": { 836 + "node": ">= 10.0.0" 837 + }, 838 + "funding": { 839 + "type": "opencollective", 840 + "url": "https://opencollective.com/parcel" 841 + }, 842 + "optionalDependencies": { 843 + "@parcel/watcher-android-arm64": "2.5.1", 844 + "@parcel/watcher-darwin-arm64": "2.5.1", 845 + "@parcel/watcher-darwin-x64": "2.5.1", 846 + "@parcel/watcher-freebsd-x64": "2.5.1", 847 + "@parcel/watcher-linux-arm-glibc": "2.5.1", 848 + "@parcel/watcher-linux-arm-musl": "2.5.1", 849 + "@parcel/watcher-linux-arm64-glibc": "2.5.1", 850 + "@parcel/watcher-linux-arm64-musl": "2.5.1", 851 + "@parcel/watcher-linux-x64-glibc": "2.5.1", 852 + "@parcel/watcher-linux-x64-musl": "2.5.1", 853 + "@parcel/watcher-win32-arm64": "2.5.1", 854 + "@parcel/watcher-win32-ia32": "2.5.1", 855 + "@parcel/watcher-win32-x64": "2.5.1" 856 + } 857 + }, 858 + "node_modules/@parcel/watcher-android-arm64": { 859 + "version": "2.5.1", 860 + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", 861 + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", 862 + "cpu": [ 863 + "arm64" 864 + ], 865 + "dev": true, 866 + "license": "MIT", 867 + "optional": true, 868 + "os": [ 869 + "android" 870 + ], 871 + "engines": { 872 + "node": ">= 10.0.0" 873 + }, 874 + "funding": { 875 + "type": "opencollective", 876 + "url": "https://opencollective.com/parcel" 877 + } 878 + }, 879 + "node_modules/@parcel/watcher-darwin-arm64": { 880 + "version": "2.5.1", 881 + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", 882 + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", 883 + "cpu": [ 884 + "arm64" 885 + ], 886 + "dev": true, 887 + "license": "MIT", 888 + "optional": true, 889 + "os": [ 890 + "darwin" 891 + ], 892 + "engines": { 893 + "node": ">= 10.0.0" 894 + }, 895 + "funding": { 896 + "type": "opencollective", 897 + "url": "https://opencollective.com/parcel" 898 + } 899 + }, 900 + "node_modules/@parcel/watcher-darwin-x64": { 901 + "version": "2.5.1", 902 + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", 903 + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", 904 + "cpu": [ 905 + "x64" 906 + ], 907 + "dev": true, 908 + "license": "MIT", 909 + "optional": true, 910 + "os": [ 911 + "darwin" 912 + ], 913 + "engines": { 914 + "node": ">= 10.0.0" 915 + }, 916 + "funding": { 917 + "type": "opencollective", 918 + "url": "https://opencollective.com/parcel" 919 + } 920 + }, 921 + "node_modules/@parcel/watcher-freebsd-x64": { 922 + "version": "2.5.1", 923 + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", 924 + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", 925 + "cpu": [ 926 + "x64" 927 + ], 928 + "dev": true, 929 + "license": "MIT", 930 + "optional": true, 931 + "os": [ 932 + "freebsd" 933 + ], 934 + "engines": { 935 + "node": ">= 10.0.0" 936 + }, 937 + "funding": { 938 + "type": "opencollective", 939 + "url": "https://opencollective.com/parcel" 940 + } 941 + }, 942 + "node_modules/@parcel/watcher-linux-arm-glibc": { 943 + "version": "2.5.1", 944 + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", 945 + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", 946 + "cpu": [ 947 + "arm" 948 + ], 949 + "dev": true, 950 + "license": "MIT", 951 + "optional": true, 952 + "os": [ 953 + "linux" 954 + ], 955 + "engines": { 956 + "node": ">= 10.0.0" 957 + }, 958 + "funding": { 959 + "type": "opencollective", 960 + "url": "https://opencollective.com/parcel" 961 + } 962 + }, 963 + "node_modules/@parcel/watcher-linux-arm-musl": { 964 + "version": "2.5.1", 965 + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", 966 + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", 967 + "cpu": [ 968 + "arm" 969 + ], 970 + "dev": true, 971 + "license": "MIT", 972 + "optional": true, 973 + "os": [ 974 + "linux" 975 + ], 976 + "engines": { 977 + "node": ">= 10.0.0" 978 + }, 979 + "funding": { 980 + "type": "opencollective", 981 + "url": "https://opencollective.com/parcel" 982 + } 983 + }, 984 + "node_modules/@parcel/watcher-linux-arm64-glibc": { 985 + "version": "2.5.1", 986 + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", 987 + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", 988 + "cpu": [ 989 + "arm64" 990 + ], 991 + "dev": true, 992 + "license": "MIT", 993 + "optional": true, 994 + "os": [ 995 + "linux" 996 + ], 997 + "engines": { 998 + "node": ">= 10.0.0" 999 + }, 1000 + "funding": { 1001 + "type": "opencollective", 1002 + "url": "https://opencollective.com/parcel" 1003 + } 1004 + }, 1005 + "node_modules/@parcel/watcher-linux-arm64-musl": { 1006 + "version": "2.5.1", 1007 + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", 1008 + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", 1009 + "cpu": [ 1010 + "arm64" 1011 + ], 1012 + "dev": true, 1013 + "license": "MIT", 1014 + "optional": true, 1015 + "os": [ 1016 + "linux" 1017 + ], 1018 + "engines": { 1019 + "node": ">= 10.0.0" 1020 + }, 1021 + "funding": { 1022 + "type": "opencollective", 1023 + "url": "https://opencollective.com/parcel" 1024 + } 1025 + }, 1026 + "node_modules/@parcel/watcher-linux-x64-glibc": { 1027 + "version": "2.5.1", 1028 + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", 1029 + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", 1030 + "cpu": [ 1031 + "x64" 1032 + ], 1033 + "dev": true, 1034 + "license": "MIT", 1035 + "optional": true, 1036 + "os": [ 1037 + "linux" 1038 + ], 1039 + "engines": { 1040 + "node": ">= 10.0.0" 1041 + }, 1042 + "funding": { 1043 + "type": "opencollective", 1044 + "url": "https://opencollective.com/parcel" 1045 + } 1046 + }, 1047 + "node_modules/@parcel/watcher-linux-x64-musl": { 1048 + "version": "2.5.1", 1049 + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", 1050 + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", 1051 + "cpu": [ 1052 + "x64" 1053 + ], 765 1054 "dev": true, 766 - "license": "MIT" 1055 + "license": "MIT", 1056 + "optional": true, 1057 + "os": [ 1058 + "linux" 1059 + ], 1060 + "engines": { 1061 + "node": ">= 10.0.0" 1062 + }, 1063 + "funding": { 1064 + "type": "opencollective", 1065 + "url": "https://opencollective.com/parcel" 1066 + } 1067 + }, 1068 + "node_modules/@parcel/watcher-win32-arm64": { 1069 + "version": "2.5.1", 1070 + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", 1071 + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", 1072 + "cpu": [ 1073 + "arm64" 1074 + ], 1075 + "dev": true, 1076 + "license": "MIT", 1077 + "optional": true, 1078 + "os": [ 1079 + "win32" 1080 + ], 1081 + "engines": { 1082 + "node": ">= 10.0.0" 1083 + }, 1084 + "funding": { 1085 + "type": "opencollective", 1086 + "url": "https://opencollective.com/parcel" 1087 + } 1088 + }, 1089 + "node_modules/@parcel/watcher-win32-ia32": { 1090 + "version": "2.5.1", 1091 + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", 1092 + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", 1093 + "cpu": [ 1094 + "ia32" 1095 + ], 1096 + "dev": true, 1097 + "license": "MIT", 1098 + "optional": true, 1099 + "os": [ 1100 + "win32" 1101 + ], 1102 + "engines": { 1103 + "node": ">= 10.0.0" 1104 + }, 1105 + "funding": { 1106 + "type": "opencollective", 1107 + "url": "https://opencollective.com/parcel" 1108 + } 1109 + }, 1110 + "node_modules/@parcel/watcher-win32-x64": { 1111 + "version": "2.5.1", 1112 + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", 1113 + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", 1114 + "cpu": [ 1115 + "x64" 1116 + ], 1117 + "dev": true, 1118 + "license": "MIT", 1119 + "optional": true, 1120 + "os": [ 1121 + "win32" 1122 + ], 1123 + "engines": { 1124 + "node": ">= 10.0.0" 1125 + }, 1126 + "funding": { 1127 + "type": "opencollective", 1128 + "url": "https://opencollective.com/parcel" 1129 + } 1130 + }, 1131 + "node_modules/@tailwindcss/cli": { 1132 + "version": "4.1.12", 1133 + "resolved": "https://registry.npmjs.org/@tailwindcss/cli/-/cli-4.1.12.tgz", 1134 + "integrity": "sha512-2PyJ5MGh/6JPS+cEaAq6MGDx3UemkX/mJt+/phm7/VOpycpecwNnHuFZbbgx6TNK/aIjvFOhhTVlappM7tmqvQ==", 1135 + "dev": true, 1136 + "license": "MIT", 1137 + "dependencies": { 1138 + "@parcel/watcher": "^2.5.1", 1139 + "@tailwindcss/node": "4.1.12", 1140 + "@tailwindcss/oxide": "4.1.12", 1141 + "enhanced-resolve": "^5.18.3", 1142 + "mri": "^1.2.0", 1143 + "picocolors": "^1.1.1", 1144 + "tailwindcss": "4.1.12" 1145 + }, 1146 + "bin": { 1147 + "tailwindcss": "dist/index.mjs" 1148 + } 1149 + }, 1150 + "node_modules/@tailwindcss/node": { 1151 + "version": "4.1.12", 1152 + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.12.tgz", 1153 + "integrity": "sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ==", 1154 + "dev": true, 1155 + "license": "MIT", 1156 + "dependencies": { 1157 + "@jridgewell/remapping": "^2.3.4", 1158 + "enhanced-resolve": "^5.18.3", 1159 + "jiti": "^2.5.1", 1160 + "lightningcss": "1.30.1", 1161 + "magic-string": "^0.30.17", 1162 + "source-map-js": "^1.2.1", 1163 + "tailwindcss": "4.1.12" 1164 + } 1165 + }, 1166 + "node_modules/@tailwindcss/oxide": { 1167 + "version": "4.1.12", 1168 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.12.tgz", 1169 + "integrity": "sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw==", 1170 + "dev": true, 1171 + "hasInstallScript": true, 1172 + "license": "MIT", 1173 + "dependencies": { 1174 + "detect-libc": "^2.0.4", 1175 + "tar": "^7.4.3" 1176 + }, 1177 + "engines": { 1178 + "node": ">= 10" 1179 + }, 1180 + "optionalDependencies": { 1181 + "@tailwindcss/oxide-android-arm64": "4.1.12", 1182 + "@tailwindcss/oxide-darwin-arm64": "4.1.12", 1183 + "@tailwindcss/oxide-darwin-x64": "4.1.12", 1184 + "@tailwindcss/oxide-freebsd-x64": "4.1.12", 1185 + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.12", 1186 + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.12", 1187 + "@tailwindcss/oxide-linux-arm64-musl": "4.1.12", 1188 + "@tailwindcss/oxide-linux-x64-gnu": "4.1.12", 1189 + "@tailwindcss/oxide-linux-x64-musl": "4.1.12", 1190 + "@tailwindcss/oxide-wasm32-wasi": "4.1.12", 1191 + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.12", 1192 + "@tailwindcss/oxide-win32-x64-msvc": "4.1.12" 1193 + } 1194 + }, 1195 + "node_modules/@tailwindcss/oxide-android-arm64": { 1196 + "version": "4.1.12", 1197 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.12.tgz", 1198 + "integrity": "sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ==", 1199 + "cpu": [ 1200 + "arm64" 1201 + ], 1202 + "dev": true, 1203 + "license": "MIT", 1204 + "optional": true, 1205 + "os": [ 1206 + "android" 1207 + ], 1208 + "engines": { 1209 + "node": ">= 10" 1210 + } 1211 + }, 1212 + "node_modules/@tailwindcss/oxide-darwin-arm64": { 1213 + "version": "4.1.12", 1214 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.12.tgz", 1215 + "integrity": "sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw==", 1216 + "cpu": [ 1217 + "arm64" 1218 + ], 1219 + "dev": true, 1220 + "license": "MIT", 1221 + "optional": true, 1222 + "os": [ 1223 + "darwin" 1224 + ], 1225 + "engines": { 1226 + "node": ">= 10" 1227 + } 1228 + }, 1229 + "node_modules/@tailwindcss/oxide-darwin-x64": { 1230 + "version": "4.1.12", 1231 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.12.tgz", 1232 + "integrity": "sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg==", 1233 + "cpu": [ 1234 + "x64" 1235 + ], 1236 + "dev": true, 1237 + "license": "MIT", 1238 + "optional": true, 1239 + "os": [ 1240 + "darwin" 1241 + ], 1242 + "engines": { 1243 + "node": ">= 10" 1244 + } 1245 + }, 1246 + "node_modules/@tailwindcss/oxide-freebsd-x64": { 1247 + "version": "4.1.12", 1248 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.12.tgz", 1249 + "integrity": "sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww==", 1250 + "cpu": [ 1251 + "x64" 1252 + ], 1253 + "dev": true, 1254 + "license": "MIT", 1255 + "optional": true, 1256 + "os": [ 1257 + "freebsd" 1258 + ], 1259 + "engines": { 1260 + "node": ">= 10" 1261 + } 1262 + }, 1263 + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { 1264 + "version": "4.1.12", 1265 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.12.tgz", 1266 + "integrity": "sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ==", 1267 + "cpu": [ 1268 + "arm" 1269 + ], 1270 + "dev": true, 1271 + "license": "MIT", 1272 + "optional": true, 1273 + "os": [ 1274 + "linux" 1275 + ], 1276 + "engines": { 1277 + "node": ">= 10" 1278 + } 1279 + }, 1280 + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { 1281 + "version": "4.1.12", 1282 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.12.tgz", 1283 + "integrity": "sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g==", 1284 + "cpu": [ 1285 + "arm64" 1286 + ], 1287 + "dev": true, 1288 + "license": "MIT", 1289 + "optional": true, 1290 + "os": [ 1291 + "linux" 1292 + ], 1293 + "engines": { 1294 + "node": ">= 10" 1295 + } 1296 + }, 1297 + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { 1298 + "version": "4.1.12", 1299 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.12.tgz", 1300 + "integrity": "sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==", 1301 + "cpu": [ 1302 + "arm64" 1303 + ], 1304 + "dev": true, 1305 + "license": "MIT", 1306 + "optional": true, 1307 + "os": [ 1308 + "linux" 1309 + ], 1310 + "engines": { 1311 + "node": ">= 10" 1312 + } 1313 + }, 1314 + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { 1315 + "version": "4.1.12", 1316 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.12.tgz", 1317 + "integrity": "sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==", 1318 + "cpu": [ 1319 + "x64" 1320 + ], 1321 + "dev": true, 1322 + "license": "MIT", 1323 + "optional": true, 1324 + "os": [ 1325 + "linux" 1326 + ], 1327 + "engines": { 1328 + "node": ">= 10" 1329 + } 1330 + }, 1331 + "node_modules/@tailwindcss/oxide-linux-x64-musl": { 1332 + "version": "4.1.12", 1333 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.12.tgz", 1334 + "integrity": "sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==", 1335 + "cpu": [ 1336 + "x64" 1337 + ], 1338 + "dev": true, 1339 + "license": "MIT", 1340 + "optional": true, 1341 + "os": [ 1342 + "linux" 1343 + ], 1344 + "engines": { 1345 + "node": ">= 10" 1346 + } 1347 + }, 1348 + "node_modules/@tailwindcss/oxide-wasm32-wasi": { 1349 + "version": "4.1.12", 1350 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.12.tgz", 1351 + "integrity": "sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==", 1352 + "bundleDependencies": [ 1353 + "@napi-rs/wasm-runtime", 1354 + "@emnapi/core", 1355 + "@emnapi/runtime", 1356 + "@tybys/wasm-util", 1357 + "@emnapi/wasi-threads", 1358 + "tslib" 1359 + ], 1360 + "cpu": [ 1361 + "wasm32" 1362 + ], 1363 + "dev": true, 1364 + "license": "MIT", 1365 + "optional": true, 1366 + "dependencies": { 1367 + "@emnapi/core": "^1.4.5", 1368 + "@emnapi/runtime": "^1.4.5", 1369 + "@emnapi/wasi-threads": "^1.0.4", 1370 + "@napi-rs/wasm-runtime": "^0.2.12", 1371 + "@tybys/wasm-util": "^0.10.0", 1372 + "tslib": "^2.8.0" 1373 + }, 1374 + "engines": { 1375 + "node": ">=14.0.0" 1376 + } 1377 + }, 1378 + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { 1379 + "version": "4.1.12", 1380 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.12.tgz", 1381 + "integrity": "sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg==", 1382 + "cpu": [ 1383 + "arm64" 1384 + ], 1385 + "dev": true, 1386 + "license": "MIT", 1387 + "optional": true, 1388 + "os": [ 1389 + "win32" 1390 + ], 1391 + "engines": { 1392 + "node": ">= 10" 1393 + } 1394 + }, 1395 + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { 1396 + "version": "4.1.12", 1397 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.12.tgz", 1398 + "integrity": "sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA==", 1399 + "cpu": [ 1400 + "x64" 1401 + ], 1402 + "dev": true, 1403 + "license": "MIT", 1404 + "optional": true, 1405 + "os": [ 1406 + "win32" 1407 + ], 1408 + "engines": { 1409 + "node": ">= 10" 1410 + } 1411 + }, 1412 + "node_modules/@tailwindcss/oxide/node_modules/detect-libc": { 1413 + "version": "2.0.4", 1414 + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", 1415 + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", 1416 + "dev": true, 1417 + "license": "Apache-2.0", 1418 + "engines": { 1419 + "node": ">=8" 1420 + } 767 1421 }, 768 1422 "node_modules/@types/estree": { 769 1423 "version": "1.0.6", ··· 788 1442 "dependencies": { 789 1443 "undici-types": "~6.20.0" 790 1444 } 791 - }, 792 - "node_modules/@types/stats.js": { 793 - "version": "0.17.3", 794 - "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.3.tgz", 795 - "integrity": "sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==", 796 - "dev": true, 797 - "license": "MIT" 798 - }, 799 - "node_modules/@types/three": { 800 - "version": "0.172.0", 801 - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.172.0.tgz", 802 - "integrity": "sha512-LrUtP3FEG26Zg5WiF0nbg8VoXiKokBLTcqM2iLvM9vzcfEiYmmBAPGdBgV0OYx9fvWlY3R/3ERTZcD9X5sc0NA==", 803 - "dev": true, 804 - "license": "MIT", 805 - "dependencies": { 806 - "@tweenjs/tween.js": "~23.1.3", 807 - "@types/stats.js": "*", 808 - "@types/webxr": "*", 809 - "@webgpu/types": "*", 810 - "fflate": "~0.8.2", 811 - "meshoptimizer": "~0.18.1" 812 - } 813 - }, 814 - "node_modules/@types/webxr": { 815 - "version": "0.5.20", 816 - "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.20.tgz", 817 - "integrity": "sha512-JGpU6qiIJQKUuVSKx1GtQnHJGxRjtfGIhzO2ilq43VZZS//f1h1Sgexbdk+Lq+7569a6EYhOWrUpIruR/1Enmg==", 818 - "dev": true, 819 - "license": "MIT" 820 1445 }, 821 1446 "node_modules/@typescript-eslint/eslint-plugin": { 822 1447 "version": "8.19.1", ··· 1010 1635 "funding": { 1011 1636 "url": "https://opencollective.com/eslint" 1012 1637 } 1013 - }, 1014 - "node_modules/@webgpu/types": { 1015 - "version": "0.1.52", 1016 - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.52.tgz", 1017 - "integrity": "sha512-eI883Nlag2hGIkhXxAnq8s4APpqXWuPL3Gbn2ghiU12UjLvfCbVqHK4XfXl3eLRTatqcMmeK7jws7IwWsGfbzw==", 1018 - "dev": true, 1019 - "license": "BSD-3-Clause" 1020 1638 }, 1021 1639 "node_modules/acorn": { 1022 1640 "version": "8.14.0", ··· 1190 1808 "url": "https://github.com/chalk/chalk?sponsor=1" 1191 1809 } 1192 1810 }, 1811 + "node_modules/chownr": { 1812 + "version": "3.0.0", 1813 + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", 1814 + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", 1815 + "dev": true, 1816 + "license": "BlueOak-1.0.0", 1817 + "engines": { 1818 + "node": ">=18" 1819 + } 1820 + }, 1193 1821 "node_modules/color-convert": { 1194 1822 "version": "2.0.1", 1195 1823 "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", ··· 1255 1883 "dev": true, 1256 1884 "license": "MIT" 1257 1885 }, 1886 + "node_modules/detect-libc": { 1887 + "version": "1.0.3", 1888 + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", 1889 + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", 1890 + "dev": true, 1891 + "license": "Apache-2.0", 1892 + "bin": { 1893 + "detect-libc": "bin/detect-libc.js" 1894 + }, 1895 + "engines": { 1896 + "node": ">=0.10" 1897 + } 1898 + }, 1258 1899 "node_modules/electron-to-chromium": { 1259 1900 "version": "1.5.79", 1260 1901 "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.79.tgz", 1261 1902 "integrity": "sha512-nYOxJNxQ9Om4EC88BE4pPoNI8xwSFf8pU/BAeOl4Hh/b/i6V4biTAzwV7pXi3ARKeoYO5JZKMIXTryXSVer5RA==", 1262 1903 "dev": true, 1263 1904 "license": "ISC" 1905 + }, 1906 + "node_modules/enhanced-resolve": { 1907 + "version": "5.18.3", 1908 + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", 1909 + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", 1910 + "dev": true, 1911 + "license": "MIT", 1912 + "dependencies": { 1913 + "graceful-fs": "^4.2.4", 1914 + "tapable": "^2.2.0" 1915 + }, 1916 + "engines": { 1917 + "node": ">=10.13.0" 1918 + } 1264 1919 }, 1265 1920 "node_modules/esbuild": { 1266 1921 "version": "0.24.2", ··· 1620 2275 "reusify": "^1.0.4" 1621 2276 } 1622 2277 }, 1623 - "node_modules/fflate": { 1624 - "version": "0.8.2", 1625 - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", 1626 - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", 1627 - "dev": true, 1628 - "license": "MIT" 1629 - }, 1630 2278 "node_modules/file-entry-cache": { 1631 2279 "version": "8.0.0", 1632 2280 "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", ··· 1717 2365 "url": "https://github.com/sponsors/sindresorhus" 1718 2366 } 1719 2367 }, 2368 + "node_modules/graceful-fs": { 2369 + "version": "4.2.11", 2370 + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 2371 + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", 2372 + "dev": true, 2373 + "license": "ISC" 2374 + }, 1720 2375 "node_modules/graphemer": { 1721 2376 "version": "1.4.0", 1722 2377 "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", ··· 1733 2388 "node": ">=8" 1734 2389 } 1735 2390 }, 1736 - "node_modules/htmx-ext-sse": { 1737 - "version": "2.2.2", 1738 - "resolved": "https://registry.npmjs.org/htmx-ext-sse/-/htmx-ext-sse-2.2.2.tgz", 1739 - "integrity": "sha512-MTnKkBzA2t4sI8gOXrRiPaceTlkUbrw3+3qOy1BfuBNIPBalsJiT4qxUGd6W48ggOkfe2akOnB8uxICJKw+Dsg==" 1740 - }, 1741 2391 "node_modules/htmx.org": { 1742 2392 "version": "2.0.4", 1743 2393 "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.4.tgz", ··· 1818 2468 "dev": true, 1819 2469 "license": "ISC" 1820 2470 }, 2471 + "node_modules/jiti": { 2472 + "version": "2.5.1", 2473 + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", 2474 + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", 2475 + "dev": true, 2476 + "license": "MIT", 2477 + "bin": { 2478 + "jiti": "lib/jiti-cli.mjs" 2479 + } 2480 + }, 1821 2481 "node_modules/js-yaml": { 1822 2482 "version": "4.1.0", 1823 2483 "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", ··· 1875 2535 "node": ">= 0.8.0" 1876 2536 } 1877 2537 }, 2538 + "node_modules/lightningcss": { 2539 + "version": "1.30.1", 2540 + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", 2541 + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", 2542 + "dev": true, 2543 + "license": "MPL-2.0", 2544 + "dependencies": { 2545 + "detect-libc": "^2.0.3" 2546 + }, 2547 + "engines": { 2548 + "node": ">= 12.0.0" 2549 + }, 2550 + "funding": { 2551 + "type": "opencollective", 2552 + "url": "https://opencollective.com/parcel" 2553 + }, 2554 + "optionalDependencies": { 2555 + "lightningcss-darwin-arm64": "1.30.1", 2556 + "lightningcss-darwin-x64": "1.30.1", 2557 + "lightningcss-freebsd-x64": "1.30.1", 2558 + "lightningcss-linux-arm-gnueabihf": "1.30.1", 2559 + "lightningcss-linux-arm64-gnu": "1.30.1", 2560 + "lightningcss-linux-arm64-musl": "1.30.1", 2561 + "lightningcss-linux-x64-gnu": "1.30.1", 2562 + "lightningcss-linux-x64-musl": "1.30.1", 2563 + "lightningcss-win32-arm64-msvc": "1.30.1", 2564 + "lightningcss-win32-x64-msvc": "1.30.1" 2565 + } 2566 + }, 2567 + "node_modules/lightningcss-darwin-arm64": { 2568 + "version": "1.30.1", 2569 + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", 2570 + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", 2571 + "cpu": [ 2572 + "arm64" 2573 + ], 2574 + "dev": true, 2575 + "license": "MPL-2.0", 2576 + "optional": true, 2577 + "os": [ 2578 + "darwin" 2579 + ], 2580 + "engines": { 2581 + "node": ">= 12.0.0" 2582 + }, 2583 + "funding": { 2584 + "type": "opencollective", 2585 + "url": "https://opencollective.com/parcel" 2586 + } 2587 + }, 2588 + "node_modules/lightningcss-darwin-x64": { 2589 + "version": "1.30.1", 2590 + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", 2591 + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", 2592 + "cpu": [ 2593 + "x64" 2594 + ], 2595 + "dev": true, 2596 + "license": "MPL-2.0", 2597 + "optional": true, 2598 + "os": [ 2599 + "darwin" 2600 + ], 2601 + "engines": { 2602 + "node": ">= 12.0.0" 2603 + }, 2604 + "funding": { 2605 + "type": "opencollective", 2606 + "url": "https://opencollective.com/parcel" 2607 + } 2608 + }, 2609 + "node_modules/lightningcss-freebsd-x64": { 2610 + "version": "1.30.1", 2611 + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", 2612 + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", 2613 + "cpu": [ 2614 + "x64" 2615 + ], 2616 + "dev": true, 2617 + "license": "MPL-2.0", 2618 + "optional": true, 2619 + "os": [ 2620 + "freebsd" 2621 + ], 2622 + "engines": { 2623 + "node": ">= 12.0.0" 2624 + }, 2625 + "funding": { 2626 + "type": "opencollective", 2627 + "url": "https://opencollective.com/parcel" 2628 + } 2629 + }, 2630 + "node_modules/lightningcss-linux-arm-gnueabihf": { 2631 + "version": "1.30.1", 2632 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", 2633 + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", 2634 + "cpu": [ 2635 + "arm" 2636 + ], 2637 + "dev": true, 2638 + "license": "MPL-2.0", 2639 + "optional": true, 2640 + "os": [ 2641 + "linux" 2642 + ], 2643 + "engines": { 2644 + "node": ">= 12.0.0" 2645 + }, 2646 + "funding": { 2647 + "type": "opencollective", 2648 + "url": "https://opencollective.com/parcel" 2649 + } 2650 + }, 2651 + "node_modules/lightningcss-linux-arm64-gnu": { 2652 + "version": "1.30.1", 2653 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", 2654 + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", 2655 + "cpu": [ 2656 + "arm64" 2657 + ], 2658 + "dev": true, 2659 + "license": "MPL-2.0", 2660 + "optional": true, 2661 + "os": [ 2662 + "linux" 2663 + ], 2664 + "engines": { 2665 + "node": ">= 12.0.0" 2666 + }, 2667 + "funding": { 2668 + "type": "opencollective", 2669 + "url": "https://opencollective.com/parcel" 2670 + } 2671 + }, 2672 + "node_modules/lightningcss-linux-arm64-musl": { 2673 + "version": "1.30.1", 2674 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", 2675 + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", 2676 + "cpu": [ 2677 + "arm64" 2678 + ], 2679 + "dev": true, 2680 + "license": "MPL-2.0", 2681 + "optional": true, 2682 + "os": [ 2683 + "linux" 2684 + ], 2685 + "engines": { 2686 + "node": ">= 12.0.0" 2687 + }, 2688 + "funding": { 2689 + "type": "opencollective", 2690 + "url": "https://opencollective.com/parcel" 2691 + } 2692 + }, 2693 + "node_modules/lightningcss-linux-x64-gnu": { 2694 + "version": "1.30.1", 2695 + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", 2696 + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", 2697 + "cpu": [ 2698 + "x64" 2699 + ], 2700 + "dev": true, 2701 + "license": "MPL-2.0", 2702 + "optional": true, 2703 + "os": [ 2704 + "linux" 2705 + ], 2706 + "engines": { 2707 + "node": ">= 12.0.0" 2708 + }, 2709 + "funding": { 2710 + "type": "opencollective", 2711 + "url": "https://opencollective.com/parcel" 2712 + } 2713 + }, 2714 + "node_modules/lightningcss-linux-x64-musl": { 2715 + "version": "1.30.1", 2716 + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", 2717 + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", 2718 + "cpu": [ 2719 + "x64" 2720 + ], 2721 + "dev": true, 2722 + "license": "MPL-2.0", 2723 + "optional": true, 2724 + "os": [ 2725 + "linux" 2726 + ], 2727 + "engines": { 2728 + "node": ">= 12.0.0" 2729 + }, 2730 + "funding": { 2731 + "type": "opencollective", 2732 + "url": "https://opencollective.com/parcel" 2733 + } 2734 + }, 2735 + "node_modules/lightningcss-win32-arm64-msvc": { 2736 + "version": "1.30.1", 2737 + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", 2738 + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", 2739 + "cpu": [ 2740 + "arm64" 2741 + ], 2742 + "dev": true, 2743 + "license": "MPL-2.0", 2744 + "optional": true, 2745 + "os": [ 2746 + "win32" 2747 + ], 2748 + "engines": { 2749 + "node": ">= 12.0.0" 2750 + }, 2751 + "funding": { 2752 + "type": "opencollective", 2753 + "url": "https://opencollective.com/parcel" 2754 + } 2755 + }, 2756 + "node_modules/lightningcss-win32-x64-msvc": { 2757 + "version": "1.30.1", 2758 + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", 2759 + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", 2760 + "cpu": [ 2761 + "x64" 2762 + ], 2763 + "dev": true, 2764 + "license": "MPL-2.0", 2765 + "optional": true, 2766 + "os": [ 2767 + "win32" 2768 + ], 2769 + "engines": { 2770 + "node": ">= 12.0.0" 2771 + }, 2772 + "funding": { 2773 + "type": "opencollective", 2774 + "url": "https://opencollective.com/parcel" 2775 + } 2776 + }, 2777 + "node_modules/lightningcss/node_modules/detect-libc": { 2778 + "version": "2.0.4", 2779 + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", 2780 + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", 2781 + "dev": true, 2782 + "license": "Apache-2.0", 2783 + "engines": { 2784 + "node": ">=8" 2785 + } 2786 + }, 1878 2787 "node_modules/locate-path": { 1879 2788 "version": "6.0.0", 1880 2789 "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", ··· 1897 2806 "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 1898 2807 "dev": true 1899 2808 }, 2809 + "node_modules/magic-string": { 2810 + "version": "0.30.18", 2811 + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", 2812 + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", 2813 + "dev": true, 2814 + "license": "MIT", 2815 + "dependencies": { 2816 + "@jridgewell/sourcemap-codec": "^1.5.5" 2817 + } 2818 + }, 1900 2819 "node_modules/merge2": { 1901 2820 "version": "1.4.1", 1902 2821 "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", ··· 1907 2826 "node": ">= 8" 1908 2827 } 1909 2828 }, 1910 - "node_modules/meshoptimizer": { 1911 - "version": "0.18.1", 1912 - "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", 1913 - "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", 1914 - "dev": true, 1915 - "license": "MIT" 1916 - }, 1917 2829 "node_modules/micromatch": { 1918 2830 "version": "4.0.8", 1919 2831 "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", ··· 1954 2866 "url": "https://github.com/sponsors/ljharb" 1955 2867 } 1956 2868 }, 1957 - "node_modules/modern-normalize": { 2869 + "node_modules/minipass": { 2870 + "version": "7.1.2", 2871 + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", 2872 + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", 2873 + "dev": true, 2874 + "license": "ISC", 2875 + "engines": { 2876 + "node": ">=16 || 14 >=14.17" 2877 + } 2878 + }, 2879 + "node_modules/minizlib": { 2880 + "version": "3.0.2", 2881 + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", 2882 + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", 2883 + "dev": true, 2884 + "license": "MIT", 2885 + "dependencies": { 2886 + "minipass": "^7.1.2" 2887 + }, 2888 + "engines": { 2889 + "node": ">= 18" 2890 + } 2891 + }, 2892 + "node_modules/mkdirp": { 1958 2893 "version": "3.0.1", 1959 - "resolved": "https://registry.npmjs.org/modern-normalize/-/modern-normalize-3.0.1.tgz", 1960 - "integrity": "sha512-VqlMdYi59Uch6fnUPxnpijWUQe+TW6zeWCvyr6Mb7JibheHzSuAAoJi2c71ZwIaWKpECpGpYHoaaBp6rBRr+/g==", 2894 + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", 2895 + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", 2896 + "dev": true, 1961 2897 "license": "MIT", 2898 + "bin": { 2899 + "mkdirp": "dist/cjs/src/bin.js" 2900 + }, 1962 2901 "engines": { 1963 - "node": ">=6" 2902 + "node": ">=10" 1964 2903 }, 1965 2904 "funding": { 1966 - "url": "https://github.com/sponsors/sindresorhus" 2905 + "url": "https://github.com/sponsors/isaacs" 2906 + } 2907 + }, 2908 + "node_modules/mri": { 2909 + "version": "1.2.0", 2910 + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", 2911 + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", 2912 + "dev": true, 2913 + "license": "MIT", 2914 + "engines": { 2915 + "node": ">=4" 1967 2916 } 1968 2917 }, 1969 2918 "node_modules/ms": { ··· 1979 2928 "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 1980 2929 "dev": true 1981 2930 }, 2931 + "node_modules/node-addon-api": { 2932 + "version": "7.1.1", 2933 + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", 2934 + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", 2935 + "dev": true, 2936 + "license": "MIT" 2937 + }, 1982 2938 "node_modules/node-releases": { 1983 2939 "version": "2.0.19", 1984 2940 "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", ··· 2227 3183 "node": ">=8" 2228 3184 } 2229 3185 }, 3186 + "node_modules/source-map-js": { 3187 + "version": "1.2.1", 3188 + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 3189 + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 3190 + "dev": true, 3191 + "license": "BSD-3-Clause", 3192 + "engines": { 3193 + "node": ">=0.10.0" 3194 + } 3195 + }, 2230 3196 "node_modules/strip-json-comments": { 2231 3197 "version": "3.1.1", 2232 3198 "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", ··· 2252 3218 "node": ">=8" 2253 3219 } 2254 3220 }, 2255 - "node_modules/three": { 2256 - "version": "0.172.0", 2257 - "resolved": "https://registry.npmjs.org/three/-/three-0.172.0.tgz", 2258 - "integrity": "sha512-6HMgMlzU97MsV7D/tY8Va38b83kz8YJX+BefKjspMNAv0Vx6dxMogHOrnRl/sbMIs3BPUKijPqDqJ/+UwJbIow==", 3221 + "node_modules/tailwindcss": { 3222 + "version": "4.1.12", 3223 + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.12.tgz", 3224 + "integrity": "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==", 2259 3225 "license": "MIT" 2260 3226 }, 3227 + "node_modules/tapable": { 3228 + "version": "2.2.3", 3229 + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", 3230 + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", 3231 + "dev": true, 3232 + "license": "MIT", 3233 + "engines": { 3234 + "node": ">=6" 3235 + }, 3236 + "funding": { 3237 + "type": "opencollective", 3238 + "url": "https://opencollective.com/webpack" 3239 + } 3240 + }, 3241 + "node_modules/tar": { 3242 + "version": "7.4.3", 3243 + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", 3244 + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", 3245 + "dev": true, 3246 + "license": "ISC", 3247 + "dependencies": { 3248 + "@isaacs/fs-minipass": "^4.0.0", 3249 + "chownr": "^3.0.0", 3250 + "minipass": "^7.1.2", 3251 + "minizlib": "^3.0.1", 3252 + "mkdirp": "^3.0.1", 3253 + "yallist": "^5.0.0" 3254 + }, 3255 + "engines": { 3256 + "node": ">=18" 3257 + } 3258 + }, 2261 3259 "node_modules/to-regex-range": { 2262 3260 "version": "5.0.1", 2263 3261 "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", ··· 2406 3404 "license": "MIT", 2407 3405 "engines": { 2408 3406 "node": ">=0.10.0" 3407 + } 3408 + }, 3409 + "node_modules/yallist": { 3410 + "version": "5.0.0", 3411 + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", 3412 + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", 3413 + "dev": true, 3414 + "license": "BlueOak-1.0.0", 3415 + "engines": { 3416 + "node": ">=18" 2409 3417 } 2410 3418 }, 2411 3419 "node_modules/yocto-queue": { ··· 2765 3773 "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", 2766 3774 "dev": true 2767 3775 }, 3776 + "@isaacs/fs-minipass": { 3777 + "version": "4.0.1", 3778 + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", 3779 + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", 3780 + "dev": true, 3781 + "requires": { 3782 + "minipass": "^7.0.4" 3783 + } 3784 + }, 3785 + "@jridgewell/gen-mapping": { 3786 + "version": "0.3.13", 3787 + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", 3788 + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", 3789 + "dev": true, 3790 + "requires": { 3791 + "@jridgewell/sourcemap-codec": "^1.5.0", 3792 + "@jridgewell/trace-mapping": "^0.3.24" 3793 + } 3794 + }, 3795 + "@jridgewell/remapping": { 3796 + "version": "2.3.5", 3797 + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", 3798 + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", 3799 + "dev": true, 3800 + "requires": { 3801 + "@jridgewell/gen-mapping": "^0.3.5", 3802 + "@jridgewell/trace-mapping": "^0.3.24" 3803 + } 3804 + }, 3805 + "@jridgewell/resolve-uri": { 3806 + "version": "3.1.2", 3807 + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 3808 + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 3809 + "dev": true 3810 + }, 3811 + "@jridgewell/sourcemap-codec": { 3812 + "version": "1.5.5", 3813 + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", 3814 + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", 3815 + "dev": true 3816 + }, 3817 + "@jridgewell/trace-mapping": { 3818 + "version": "0.3.30", 3819 + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", 3820 + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", 3821 + "dev": true, 3822 + "requires": { 3823 + "@jridgewell/resolve-uri": "^3.1.0", 3824 + "@jridgewell/sourcemap-codec": "^1.4.14" 3825 + } 3826 + }, 2768 3827 "@nodelib/fs.scandir": { 2769 3828 "version": "2.1.5", 2770 3829 "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", ··· 2791 3850 "fastq": "^1.6.0" 2792 3851 } 2793 3852 }, 2794 - "@tweenjs/tween.js": { 2795 - "version": "23.1.3", 2796 - "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", 2797 - "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", 2798 - "dev": true 3853 + "@parcel/watcher": { 3854 + "version": "2.5.1", 3855 + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", 3856 + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", 3857 + "dev": true, 3858 + "requires": { 3859 + "@parcel/watcher-android-arm64": "2.5.1", 3860 + "@parcel/watcher-darwin-arm64": "2.5.1", 3861 + "@parcel/watcher-darwin-x64": "2.5.1", 3862 + "@parcel/watcher-freebsd-x64": "2.5.1", 3863 + "@parcel/watcher-linux-arm-glibc": "2.5.1", 3864 + "@parcel/watcher-linux-arm-musl": "2.5.1", 3865 + "@parcel/watcher-linux-arm64-glibc": "2.5.1", 3866 + "@parcel/watcher-linux-arm64-musl": "2.5.1", 3867 + "@parcel/watcher-linux-x64-glibc": "2.5.1", 3868 + "@parcel/watcher-linux-x64-musl": "2.5.1", 3869 + "@parcel/watcher-win32-arm64": "2.5.1", 3870 + "@parcel/watcher-win32-ia32": "2.5.1", 3871 + "@parcel/watcher-win32-x64": "2.5.1", 3872 + "detect-libc": "^1.0.3", 3873 + "is-glob": "^4.0.3", 3874 + "micromatch": "^4.0.5", 3875 + "node-addon-api": "^7.0.0" 3876 + } 3877 + }, 3878 + "@parcel/watcher-android-arm64": { 3879 + "version": "2.5.1", 3880 + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", 3881 + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", 3882 + "dev": true, 3883 + "optional": true 3884 + }, 3885 + "@parcel/watcher-darwin-arm64": { 3886 + "version": "2.5.1", 3887 + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", 3888 + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", 3889 + "dev": true, 3890 + "optional": true 3891 + }, 3892 + "@parcel/watcher-darwin-x64": { 3893 + "version": "2.5.1", 3894 + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", 3895 + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", 3896 + "dev": true, 3897 + "optional": true 3898 + }, 3899 + "@parcel/watcher-freebsd-x64": { 3900 + "version": "2.5.1", 3901 + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", 3902 + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", 3903 + "dev": true, 3904 + "optional": true 3905 + }, 3906 + "@parcel/watcher-linux-arm-glibc": { 3907 + "version": "2.5.1", 3908 + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", 3909 + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", 3910 + "dev": true, 3911 + "optional": true 3912 + }, 3913 + "@parcel/watcher-linux-arm-musl": { 3914 + "version": "2.5.1", 3915 + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", 3916 + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", 3917 + "dev": true, 3918 + "optional": true 3919 + }, 3920 + "@parcel/watcher-linux-arm64-glibc": { 3921 + "version": "2.5.1", 3922 + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", 3923 + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", 3924 + "dev": true, 3925 + "optional": true 3926 + }, 3927 + "@parcel/watcher-linux-arm64-musl": { 3928 + "version": "2.5.1", 3929 + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", 3930 + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", 3931 + "dev": true, 3932 + "optional": true 3933 + }, 3934 + "@parcel/watcher-linux-x64-glibc": { 3935 + "version": "2.5.1", 3936 + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", 3937 + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", 3938 + "dev": true, 3939 + "optional": true 3940 + }, 3941 + "@parcel/watcher-linux-x64-musl": { 3942 + "version": "2.5.1", 3943 + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", 3944 + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", 3945 + "dev": true, 3946 + "optional": true 3947 + }, 3948 + "@parcel/watcher-win32-arm64": { 3949 + "version": "2.5.1", 3950 + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", 3951 + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", 3952 + "dev": true, 3953 + "optional": true 3954 + }, 3955 + "@parcel/watcher-win32-ia32": { 3956 + "version": "2.5.1", 3957 + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", 3958 + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", 3959 + "dev": true, 3960 + "optional": true 3961 + }, 3962 + "@parcel/watcher-win32-x64": { 3963 + "version": "2.5.1", 3964 + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", 3965 + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", 3966 + "dev": true, 3967 + "optional": true 3968 + }, 3969 + "@tailwindcss/cli": { 3970 + "version": "4.1.12", 3971 + "resolved": "https://registry.npmjs.org/@tailwindcss/cli/-/cli-4.1.12.tgz", 3972 + "integrity": "sha512-2PyJ5MGh/6JPS+cEaAq6MGDx3UemkX/mJt+/phm7/VOpycpecwNnHuFZbbgx6TNK/aIjvFOhhTVlappM7tmqvQ==", 3973 + "dev": true, 3974 + "requires": { 3975 + "@parcel/watcher": "^2.5.1", 3976 + "@tailwindcss/node": "4.1.12", 3977 + "@tailwindcss/oxide": "4.1.12", 3978 + "enhanced-resolve": "^5.18.3", 3979 + "mri": "^1.2.0", 3980 + "picocolors": "^1.1.1", 3981 + "tailwindcss": "4.1.12" 3982 + } 3983 + }, 3984 + "@tailwindcss/node": { 3985 + "version": "4.1.12", 3986 + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.12.tgz", 3987 + "integrity": "sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ==", 3988 + "dev": true, 3989 + "requires": { 3990 + "@jridgewell/remapping": "^2.3.4", 3991 + "enhanced-resolve": "^5.18.3", 3992 + "jiti": "^2.5.1", 3993 + "lightningcss": "1.30.1", 3994 + "magic-string": "^0.30.17", 3995 + "source-map-js": "^1.2.1", 3996 + "tailwindcss": "4.1.12" 3997 + } 3998 + }, 3999 + "@tailwindcss/oxide": { 4000 + "version": "4.1.12", 4001 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.12.tgz", 4002 + "integrity": "sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw==", 4003 + "dev": true, 4004 + "requires": { 4005 + "@tailwindcss/oxide-android-arm64": "4.1.12", 4006 + "@tailwindcss/oxide-darwin-arm64": "4.1.12", 4007 + "@tailwindcss/oxide-darwin-x64": "4.1.12", 4008 + "@tailwindcss/oxide-freebsd-x64": "4.1.12", 4009 + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.12", 4010 + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.12", 4011 + "@tailwindcss/oxide-linux-arm64-musl": "4.1.12", 4012 + "@tailwindcss/oxide-linux-x64-gnu": "4.1.12", 4013 + "@tailwindcss/oxide-linux-x64-musl": "4.1.12", 4014 + "@tailwindcss/oxide-wasm32-wasi": "4.1.12", 4015 + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.12", 4016 + "@tailwindcss/oxide-win32-x64-msvc": "4.1.12", 4017 + "detect-libc": "^2.0.4", 4018 + "tar": "^7.4.3" 4019 + }, 4020 + "dependencies": { 4021 + "detect-libc": { 4022 + "version": "2.0.4", 4023 + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", 4024 + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", 4025 + "dev": true 4026 + } 4027 + } 4028 + }, 4029 + "@tailwindcss/oxide-android-arm64": { 4030 + "version": "4.1.12", 4031 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.12.tgz", 4032 + "integrity": "sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ==", 4033 + "dev": true, 4034 + "optional": true 4035 + }, 4036 + "@tailwindcss/oxide-darwin-arm64": { 4037 + "version": "4.1.12", 4038 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.12.tgz", 4039 + "integrity": "sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw==", 4040 + "dev": true, 4041 + "optional": true 4042 + }, 4043 + "@tailwindcss/oxide-darwin-x64": { 4044 + "version": "4.1.12", 4045 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.12.tgz", 4046 + "integrity": "sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg==", 4047 + "dev": true, 4048 + "optional": true 4049 + }, 4050 + "@tailwindcss/oxide-freebsd-x64": { 4051 + "version": "4.1.12", 4052 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.12.tgz", 4053 + "integrity": "sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww==", 4054 + "dev": true, 4055 + "optional": true 4056 + }, 4057 + "@tailwindcss/oxide-linux-arm-gnueabihf": { 4058 + "version": "4.1.12", 4059 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.12.tgz", 4060 + "integrity": "sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ==", 4061 + "dev": true, 4062 + "optional": true 4063 + }, 4064 + "@tailwindcss/oxide-linux-arm64-gnu": { 4065 + "version": "4.1.12", 4066 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.12.tgz", 4067 + "integrity": "sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g==", 4068 + "dev": true, 4069 + "optional": true 4070 + }, 4071 + "@tailwindcss/oxide-linux-arm64-musl": { 4072 + "version": "4.1.12", 4073 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.12.tgz", 4074 + "integrity": "sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==", 4075 + "dev": true, 4076 + "optional": true 4077 + }, 4078 + "@tailwindcss/oxide-linux-x64-gnu": { 4079 + "version": "4.1.12", 4080 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.12.tgz", 4081 + "integrity": "sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==", 4082 + "dev": true, 4083 + "optional": true 4084 + }, 4085 + "@tailwindcss/oxide-linux-x64-musl": { 4086 + "version": "4.1.12", 4087 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.12.tgz", 4088 + "integrity": "sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==", 4089 + "dev": true, 4090 + "optional": true 4091 + }, 4092 + "@tailwindcss/oxide-wasm32-wasi": { 4093 + "version": "4.1.12", 4094 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.12.tgz", 4095 + "integrity": "sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==", 4096 + "dev": true, 4097 + "optional": true, 4098 + "requires": { 4099 + "@emnapi/core": "^1.4.5", 4100 + "@emnapi/runtime": "^1.4.5", 4101 + "@emnapi/wasi-threads": "^1.0.4", 4102 + "@napi-rs/wasm-runtime": "^0.2.12", 4103 + "@tybys/wasm-util": "^0.10.0", 4104 + "tslib": "^2.8.0" 4105 + } 4106 + }, 4107 + "@tailwindcss/oxide-win32-arm64-msvc": { 4108 + "version": "4.1.12", 4109 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.12.tgz", 4110 + "integrity": "sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg==", 4111 + "dev": true, 4112 + "optional": true 4113 + }, 4114 + "@tailwindcss/oxide-win32-x64-msvc": { 4115 + "version": "4.1.12", 4116 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.12.tgz", 4117 + "integrity": "sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA==", 4118 + "dev": true, 4119 + "optional": true 2799 4120 }, 2800 4121 "@types/estree": { 2801 4122 "version": "1.0.6", ··· 2818 4139 "undici-types": "~6.20.0" 2819 4140 } 2820 4141 }, 2821 - "@types/stats.js": { 2822 - "version": "0.17.3", 2823 - "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.3.tgz", 2824 - "integrity": "sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==", 2825 - "dev": true 2826 - }, 2827 - "@types/three": { 2828 - "version": "0.172.0", 2829 - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.172.0.tgz", 2830 - "integrity": "sha512-LrUtP3FEG26Zg5WiF0nbg8VoXiKokBLTcqM2iLvM9vzcfEiYmmBAPGdBgV0OYx9fvWlY3R/3ERTZcD9X5sc0NA==", 2831 - "dev": true, 2832 - "requires": { 2833 - "@tweenjs/tween.js": "~23.1.3", 2834 - "@types/stats.js": "*", 2835 - "@types/webxr": "*", 2836 - "@webgpu/types": "*", 2837 - "fflate": "~0.8.2", 2838 - "meshoptimizer": "~0.18.1" 2839 - } 2840 - }, 2841 - "@types/webxr": { 2842 - "version": "0.5.20", 2843 - "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.20.tgz", 2844 - "integrity": "sha512-JGpU6qiIJQKUuVSKx1GtQnHJGxRjtfGIhzO2ilq43VZZS//f1h1Sgexbdk+Lq+7569a6EYhOWrUpIruR/1Enmg==", 2845 - "dev": true 2846 - }, 2847 4142 "@typescript-eslint/eslint-plugin": { 2848 4143 "version": "8.19.1", 2849 4144 "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.1.tgz", ··· 2948 4243 } 2949 4244 } 2950 4245 }, 2951 - "@webgpu/types": { 2952 - "version": "0.1.52", 2953 - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.52.tgz", 2954 - "integrity": "sha512-eI883Nlag2hGIkhXxAnq8s4APpqXWuPL3Gbn2ghiU12UjLvfCbVqHK4XfXl3eLRTatqcMmeK7jws7IwWsGfbzw==", 2955 - "dev": true 2956 - }, 2957 4246 "acorn": { 2958 4247 "version": "8.14.0", 2959 4248 "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", ··· 3052 4341 "supports-color": "^7.1.0" 3053 4342 } 3054 4343 }, 4344 + "chownr": { 4345 + "version": "3.0.0", 4346 + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", 4347 + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", 4348 + "dev": true 4349 + }, 3055 4350 "color-convert": { 3056 4351 "version": "2.0.1", 3057 4352 "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", ··· 3099 4394 "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 3100 4395 "dev": true 3101 4396 }, 4397 + "detect-libc": { 4398 + "version": "1.0.3", 4399 + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", 4400 + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", 4401 + "dev": true 4402 + }, 3102 4403 "electron-to-chromium": { 3103 4404 "version": "1.5.79", 3104 4405 "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.79.tgz", 3105 4406 "integrity": "sha512-nYOxJNxQ9Om4EC88BE4pPoNI8xwSFf8pU/BAeOl4Hh/b/i6V4biTAzwV7pXi3ARKeoYO5JZKMIXTryXSVer5RA==", 3106 4407 "dev": true 3107 4408 }, 4409 + "enhanced-resolve": { 4410 + "version": "5.18.3", 4411 + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", 4412 + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", 4413 + "dev": true, 4414 + "requires": { 4415 + "graceful-fs": "^4.2.4", 4416 + "tapable": "^2.2.0" 4417 + } 4418 + }, 3108 4419 "esbuild": { 3109 4420 "version": "0.24.2", 3110 4421 "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", ··· 3350 4661 "reusify": "^1.0.4" 3351 4662 } 3352 4663 }, 3353 - "fflate": { 3354 - "version": "0.8.2", 3355 - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", 3356 - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", 3357 - "dev": true 3358 - }, 3359 4664 "file-entry-cache": { 3360 4665 "version": "8.0.0", 3361 4666 "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", ··· 3413 4718 "version": "15.14.0", 3414 4719 "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", 3415 4720 "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", 4721 + "dev": true 4722 + }, 4723 + "graceful-fs": { 4724 + "version": "4.2.11", 4725 + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 4726 + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", 3416 4727 "dev": true 3417 4728 }, 3418 4729 "graphemer": { ··· 3427 4738 "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 3428 4739 "dev": true 3429 4740 }, 3430 - "htmx-ext-sse": { 3431 - "version": "2.2.2", 3432 - "resolved": "https://registry.npmjs.org/htmx-ext-sse/-/htmx-ext-sse-2.2.2.tgz", 3433 - "integrity": "sha512-MTnKkBzA2t4sI8gOXrRiPaceTlkUbrw3+3qOy1BfuBNIPBalsJiT4qxUGd6W48ggOkfe2akOnB8uxICJKw+Dsg==" 3434 - }, 3435 4741 "htmx.org": { 3436 4742 "version": "2.0.4", 3437 4743 "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.4.tgz", ··· 3486 4792 "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 3487 4793 "dev": true 3488 4794 }, 4795 + "jiti": { 4796 + "version": "2.5.1", 4797 + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", 4798 + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", 4799 + "dev": true 4800 + }, 3489 4801 "js-yaml": { 3490 4802 "version": "4.1.0", 3491 4803 "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", ··· 3532 4844 "type-check": "~0.4.0" 3533 4845 } 3534 4846 }, 4847 + "lightningcss": { 4848 + "version": "1.30.1", 4849 + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", 4850 + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", 4851 + "dev": true, 4852 + "requires": { 4853 + "detect-libc": "^2.0.3", 4854 + "lightningcss-darwin-arm64": "1.30.1", 4855 + "lightningcss-darwin-x64": "1.30.1", 4856 + "lightningcss-freebsd-x64": "1.30.1", 4857 + "lightningcss-linux-arm-gnueabihf": "1.30.1", 4858 + "lightningcss-linux-arm64-gnu": "1.30.1", 4859 + "lightningcss-linux-arm64-musl": "1.30.1", 4860 + "lightningcss-linux-x64-gnu": "1.30.1", 4861 + "lightningcss-linux-x64-musl": "1.30.1", 4862 + "lightningcss-win32-arm64-msvc": "1.30.1", 4863 + "lightningcss-win32-x64-msvc": "1.30.1" 4864 + }, 4865 + "dependencies": { 4866 + "detect-libc": { 4867 + "version": "2.0.4", 4868 + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", 4869 + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", 4870 + "dev": true 4871 + } 4872 + } 4873 + }, 4874 + "lightningcss-darwin-arm64": { 4875 + "version": "1.30.1", 4876 + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", 4877 + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", 4878 + "dev": true, 4879 + "optional": true 4880 + }, 4881 + "lightningcss-darwin-x64": { 4882 + "version": "1.30.1", 4883 + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", 4884 + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", 4885 + "dev": true, 4886 + "optional": true 4887 + }, 4888 + "lightningcss-freebsd-x64": { 4889 + "version": "1.30.1", 4890 + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", 4891 + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", 4892 + "dev": true, 4893 + "optional": true 4894 + }, 4895 + "lightningcss-linux-arm-gnueabihf": { 4896 + "version": "1.30.1", 4897 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", 4898 + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", 4899 + "dev": true, 4900 + "optional": true 4901 + }, 4902 + "lightningcss-linux-arm64-gnu": { 4903 + "version": "1.30.1", 4904 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", 4905 + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", 4906 + "dev": true, 4907 + "optional": true 4908 + }, 4909 + "lightningcss-linux-arm64-musl": { 4910 + "version": "1.30.1", 4911 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", 4912 + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", 4913 + "dev": true, 4914 + "optional": true 4915 + }, 4916 + "lightningcss-linux-x64-gnu": { 4917 + "version": "1.30.1", 4918 + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", 4919 + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", 4920 + "dev": true, 4921 + "optional": true 4922 + }, 4923 + "lightningcss-linux-x64-musl": { 4924 + "version": "1.30.1", 4925 + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", 4926 + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", 4927 + "dev": true, 4928 + "optional": true 4929 + }, 4930 + "lightningcss-win32-arm64-msvc": { 4931 + "version": "1.30.1", 4932 + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", 4933 + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", 4934 + "dev": true, 4935 + "optional": true 4936 + }, 4937 + "lightningcss-win32-x64-msvc": { 4938 + "version": "1.30.1", 4939 + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", 4940 + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", 4941 + "dev": true, 4942 + "optional": true 4943 + }, 3535 4944 "locate-path": { 3536 4945 "version": "6.0.0", 3537 4946 "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", ··· 3547 4956 "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 3548 4957 "dev": true 3549 4958 }, 4959 + "magic-string": { 4960 + "version": "0.30.18", 4961 + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", 4962 + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", 4963 + "dev": true, 4964 + "requires": { 4965 + "@jridgewell/sourcemap-codec": "^1.5.5" 4966 + } 4967 + }, 3550 4968 "merge2": { 3551 4969 "version": "1.4.1", 3552 4970 "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 3553 4971 "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 3554 - "dev": true 3555 - }, 3556 - "meshoptimizer": { 3557 - "version": "0.18.1", 3558 - "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", 3559 - "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", 3560 4972 "dev": true 3561 4973 }, 3562 4974 "micromatch": { ··· 3584 4996 "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 3585 4997 "dev": true 3586 4998 }, 3587 - "modern-normalize": { 4999 + "minipass": { 5000 + "version": "7.1.2", 5001 + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", 5002 + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", 5003 + "dev": true 5004 + }, 5005 + "minizlib": { 5006 + "version": "3.0.2", 5007 + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", 5008 + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", 5009 + "dev": true, 5010 + "requires": { 5011 + "minipass": "^7.1.2" 5012 + } 5013 + }, 5014 + "mkdirp": { 3588 5015 "version": "3.0.1", 3589 - "resolved": "https://registry.npmjs.org/modern-normalize/-/modern-normalize-3.0.1.tgz", 3590 - "integrity": "sha512-VqlMdYi59Uch6fnUPxnpijWUQe+TW6zeWCvyr6Mb7JibheHzSuAAoJi2c71ZwIaWKpECpGpYHoaaBp6rBRr+/g==" 5016 + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", 5017 + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", 5018 + "dev": true 5019 + }, 5020 + "mri": { 5021 + "version": "1.2.0", 5022 + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", 5023 + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", 5024 + "dev": true 3591 5025 }, 3592 5026 "ms": { 3593 5027 "version": "2.1.3", ··· 3599 5033 "version": "1.4.0", 3600 5034 "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 3601 5035 "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 5036 + "dev": true 5037 + }, 5038 + "node-addon-api": { 5039 + "version": "7.1.1", 5040 + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", 5041 + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", 3602 5042 "dev": true 3603 5043 }, 3604 5044 "node-releases": { ··· 3738 5178 "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 3739 5179 "dev": true 3740 5180 }, 5181 + "source-map-js": { 5182 + "version": "1.2.1", 5183 + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 5184 + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 5185 + "dev": true 5186 + }, 3741 5187 "strip-json-comments": { 3742 5188 "version": "3.1.1", 3743 5189 "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", ··· 3753 5199 "has-flag": "^4.0.0" 3754 5200 } 3755 5201 }, 3756 - "three": { 3757 - "version": "0.172.0", 3758 - "resolved": "https://registry.npmjs.org/three/-/three-0.172.0.tgz", 3759 - "integrity": "sha512-6HMgMlzU97MsV7D/tY8Va38b83kz8YJX+BefKjspMNAv0Vx6dxMogHOrnRl/sbMIs3BPUKijPqDqJ/+UwJbIow==" 5202 + "tailwindcss": { 5203 + "version": "4.1.12", 5204 + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.12.tgz", 5205 + "integrity": "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==" 5206 + }, 5207 + "tapable": { 5208 + "version": "2.2.3", 5209 + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", 5210 + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", 5211 + "dev": true 5212 + }, 5213 + "tar": { 5214 + "version": "7.4.3", 5215 + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", 5216 + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", 5217 + "dev": true, 5218 + "requires": { 5219 + "@isaacs/fs-minipass": "^4.0.0", 5220 + "chownr": "^3.0.0", 5221 + "minipass": "^7.1.2", 5222 + "minizlib": "^3.0.1", 5223 + "mkdirp": "^3.0.1", 5224 + "yallist": "^5.0.0" 5225 + } 3760 5226 }, 3761 5227 "to-regex-range": { 3762 5228 "version": "5.0.1", ··· 3838 5304 "version": "1.2.5", 3839 5305 "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", 3840 5306 "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", 5307 + "dev": true 5308 + }, 5309 + "yallist": { 5310 + "version": "5.0.0", 5311 + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", 5312 + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", 3841 5313 "dev": true 3842 5314 }, 3843 5315 "yocto-queue": {
+2 -4
frontend/package.json
··· 6 6 "type": "module", 7 7 "devDependencies": { 8 8 "@eslint/js": "^9.17.0", 9 + "@tailwindcss/cli": "^4.1.12", 9 10 "@types/node": "^22.10.5", 10 - "@types/three": "^0.172.0", 11 11 "browserslist": "^4.24.4", 12 12 "esbuild": "^0.24.2", 13 13 "esbuild-plugin-browserslist": "^0.15.0", ··· 35 35 "url": "https://github.com/ivomurrell/myivo.git" 36 36 }, 37 37 "dependencies": { 38 - "htmx-ext-sse": "^2.2.2", 39 38 "htmx.org": "^2.0.4", 40 - "modern-normalize": "^3.0.1", 41 - "three": "^0.172.0" 39 + "tailwindcss": "^4.1.12" 42 40 }, 43 41 "volta": { 44 42 "node": "22.13.0"
-4
frontend/src/css/defaults.css
··· 1 - h1 { 2 - font-size: 3.6em; 3 - padding-left: 4%; 4 - }
-39
frontend/src/css/header.css
··· 1 - .header-container { 2 - --min-header-height: 50px; 3 - --header-viewport-proportion: 10vh; 4 - 5 - align-items: center; 6 - box-shadow: 0 2.4vh 1.2vh -2vh #808080; 7 - display: flex; 8 - flex-flow: row-reverse wrap-reverse; 9 - height: var(--header-viewport-proportion); 10 - justify-content: center; 11 - min-height: var(--min-header-height); 12 - overflow: hidden; 13 - position: relative; 14 - } 15 - 16 - .title { 17 - flex: 1; 18 - font-weight: bold; 19 - font-size: 6vh; 20 - margin: auto; 21 - padding-left: 3%; 22 - white-space: nowrap; 23 - 24 - @media screen and (max-height: 500px) { 25 - font-size: calc(var(--min-header-height) * 0.6); 26 - } 27 - } 28 - 29 - .logo-gl { 30 - --logo-aspect-ratio: 2.5; 31 - 32 - height: 100%; 33 - margin-right: 3%; 34 - width: calc(var(--header-viewport-proportion) * var(--logo-aspect-ratio)); 35 - 36 - @media screen and (max-height: 500px) { 37 - width: calc(var(--min-header-height) * var(--logo-aspect-ratio)); 38 - } 39 - }
+1 -5
frontend/src/css/main.css
··· 1 - @import "modern-normalize"; 2 - @import "defaults"; 3 - @import "header"; 4 - @import "projects"; 5 - @import "scrobblar"; 1 + @import "tailwind-out";
-89
frontend/src/css/projects.css
··· 1 - .projects { 2 - --animation-distance: calc(100% / 12); 3 - 4 - display: flex; 5 - flex-direction: column; 6 - align-items: center; 7 - } 8 - 9 - @keyframes project-fade-in-left { 10 - 0% { 11 - opacity: 0; 12 - transform: translateX(calc(var(--animation-distance) * -1)); 13 - } 14 - 15 - 100% { 16 - opacity: 100%; 17 - transform: 0; 18 - } 19 - } 20 - 21 - @keyframes project-fade-in-right { 22 - 0% { 23 - opacity: 0; 24 - transform: translateX(var(--animation-distance)); 25 - } 26 - 27 - 100% { 28 - opacity: 100%; 29 - transform: 0; 30 - } 31 - } 32 - 33 - .project-container { 34 - align-items: center; 35 - display: flex; 36 - margin: 0 var(--animation-distance) 6vh; 37 - max-width: 1000px; 38 - 39 - @media screen and (max-width: 900px) { 40 - flex-wrap: wrap-reverse; 41 - width: auto; 42 - } 43 - animation: var(--animation) 2s 0.1s backwards ease; 44 - flex-direction: var(--flex-direction); 45 - } 46 - 47 - .project-container:nth-child(odd) { 48 - --animation: project-fade-in-right; 49 - --flex-direction: row; 50 - 51 - .project-desc { 52 - margin-right: var(--margin); 53 - padding-left: var(--padding); 54 - text-align: right; 55 - } 56 - } 57 - .project-container:nth-child(even) { 58 - --animation: project-fade-in-left; 59 - --flex-direction: row-reverse; 60 - 61 - .project-desc { 62 - margin-left: var(--margin); 63 - padding-right: var(--padding); 64 - text-align: left; 65 - } 66 - } 67 - 68 - .project-desc { 69 - --margin: 2%; 70 - --padding: 4%; 71 - } 72 - 73 - .project-title { 74 - display: block; 75 - font-size: 3em; 76 - margin: auto; 77 - } 78 - 79 - .project-thumbnail { 80 - flex: 1 0 auto; 81 - height: inherit; 82 - margin: auto; 83 - object-fit: contain; 84 - width: 60%; 85 - 86 - @media screen and (max-width: 900px) { 87 - margin-bottom: 12px; 88 - } 89 - }
-48
frontend/src/css/scrobblar.css
··· 1 - .scrobble-bar { 2 - background: #f4f4f4; 3 - bottom: 0; 4 - opacity: 0.95; 5 - position: fixed; 6 - width: 100%; 7 - } 8 - 9 - .bar-container { 10 - display: flex; 11 - justify-content: flex-start; 12 - } 13 - 14 - @keyframes scrobblar-fade-in { 15 - 0% { 16 - opacity: 0; 17 - transform: translateX(9px); 18 - } 19 - 20 - 100% { 21 - opacity: 1; 22 - transform: 0; 23 - } 24 - } 25 - 26 - .bar-container { 27 - font-style: italic; 28 - margin: auto; 29 - overflow: hidden; 30 - } 31 - 32 - .bar-text-intro { 33 - display: inline; 34 - margin-left: 11px; 35 - white-space: pre; 36 - } 37 - 38 - .bar-text-music { 39 - animation: scrobblar-fade-in 0.4s 0.1s ease-in; 40 - display: inline; 41 - flex-grow: 1; 42 - white-space: nowrap; 43 - } 44 - 45 - .bar-cover { 46 - margin: auto 0 auto 0.5em; 47 - z-index: 1; 48 - }
+2
frontend/src/css/tailwind.css
··· 1 + @import "tailwindcss" source(none); 2 + @source "../../../server/templates";
-57
frontend/src/ts/logo.ts
··· 1 - import { PerspectiveCamera } from "three/src/cameras/PerspectiveCamera.js"; 2 - import { MeshBasicMaterial } from "three/src/materials/MeshBasicMaterial.js"; 3 - import { Mesh } from "three/src/objects/Mesh.js"; 4 - import { WebGLRenderer } from "three/src/renderers/WebGLRenderer.js"; 5 - import { Scene } from "three/src/scenes/Scene.js"; 6 - 7 - import { FontLoader } from "./three/FontLoader.js"; 8 - import { TextGeometry } from "./three/TextGeometry.js"; 9 - 10 - let previous: DOMHighResTimeStamp; 11 - 12 - const scene = new Scene(); 13 - const camera = new PerspectiveCamera(40, 25 / 10, 0.1, 1000); 14 - camera.zoom = 0.05; 15 - 16 - const canvas = document.getElementById("spinningCanvas") as HTMLCanvasElement; 17 - const renderer = new WebGLRenderer({ 18 - canvas, 19 - antialias: true, 20 - }); 21 - const ratio = window.devicePixelRatio ?? 1; 22 - renderer.setPixelRatio(ratio); 23 - renderer.setClearColor(0xffffff); 24 - 25 - const fLoader = new FontLoader(); 26 - let text: Mesh; 27 - fLoader.load("fonts/helvetiker_regular.typeface.json", (font) => { 28 - const geometry = new TextGeometry("Ivo", { 29 - size: 5, 30 - font, 31 - height: 2.5, 32 - }); 33 - const material = new MeshBasicMaterial({ 34 - color: 0xff0000, 35 - }); 36 - text = new Mesh(geometry, material); 37 - return scene.add(text); 38 - }); 39 - 40 - camera.position.y = 1.9; 41 - camera.position.z = 15; 42 - 43 - function render(now: DOMHighResTimeStamp) { 44 - const elapsed = now - (previous ?? now); 45 - previous = now; 46 - 47 - if (text) { 48 - text.rotation.y += elapsed * 0.006; 49 - text.rotation.y %= 2 * Math.PI; 50 - } 51 - 52 - renderer.render(scene, camera); 53 - 54 - requestAnimationFrame(render); 55 - } 56 - 57 - requestAnimationFrame(render);
-3
frontend/src/ts/main.ts
··· 2 2 3 3 // need to load htmx as a global variable before importing extensions 4 4 import "./htmx"; 5 - import "htmx-ext-sse"; 6 - 7 - import "./logo";
-182
frontend/src/ts/three/FontLoader.ts
··· 1 - /* eslint-disable */ 2 - import type { Shape } from "three/src/extras/core/Shape.js"; 3 - import { ShapePath } from "three/src/extras/core/ShapePath.js"; 4 - import { FileLoader } from "three/src/loaders/FileLoader.js"; 5 - import { Loader } from "three/src/loaders/Loader.js"; 6 - import type { LoadingManager } from "three/src/loaders/LoadingManager.js"; 7 - 8 - class FontLoader extends Loader { 9 - constructor(manager?: LoadingManager) { 10 - super(manager); 11 - } 12 - 13 - load( 14 - url: string, 15 - onLoad?: (responseFont: Font) => void, 16 - onProgress?: (event: ProgressEvent) => void, 17 - onError?: (event: unknown) => void, 18 - ) { 19 - const loader = new FileLoader(this.manager); 20 - loader.setPath(this.path); 21 - loader.setRequestHeader(this.requestHeader); 22 - loader.setWithCredentials(this.withCredentials); 23 - loader.load( 24 - url, 25 - (text) => { 26 - let json; 27 - 28 - text = text as string; 29 - try { 30 - json = JSON.parse(text); 31 - } catch { 32 - console.warn( 33 - "THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.", 34 - ); 35 - json = JSON.parse(text.substring(65, text.length - 2)); 36 - } 37 - 38 - const font = this.parse(json); 39 - 40 - if (onLoad) onLoad(font); 41 - }, 42 - onProgress, 43 - onError, 44 - ); 45 - } 46 - 47 - parse(json: any) { 48 - return new Font(json); 49 - } 50 - } 51 - 52 - // 53 - 54 - class Font { 55 - type: string; 56 - data: any; 57 - isFont = true; 58 - 59 - constructor(data: any) { 60 - this.type = "Font"; 61 - 62 - this.data = data; 63 - } 64 - 65 - generateShapes(text: string, size: number): Shape[] { 66 - const shapes: Shape[] = []; 67 - const paths = createPaths(text, size, this.data); 68 - 69 - for (let p = 0, pl = paths.length; p < pl; p++) { 70 - Array.prototype.push.apply(shapes, paths[p].toShapes(false)); 71 - } 72 - 73 - return shapes; 74 - } 75 - } 76 - 77 - function createPaths(text: string, size: number, data: any): ShapePath[] { 78 - const chars = Array.from(text); 79 - const scale = size / data.resolution; 80 - const line_height = 81 - (data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness) * 82 - scale; 83 - 84 - const paths = []; 85 - 86 - let offsetX = 0, 87 - offsetY = 0; 88 - 89 - for (let i = 0; i < chars.length; i++) { 90 - const char = chars[i]; 91 - 92 - if (char === "\n") { 93 - offsetX = 0; 94 - offsetY -= line_height; 95 - } else { 96 - const ret = createPath(char, scale, offsetX, offsetY, data); 97 - offsetX += ret.offsetX; 98 - paths.push(ret.path); 99 - } 100 - } 101 - 102 - return paths; 103 - } 104 - 105 - function createPath( 106 - char: string, 107 - scale: number, 108 - offsetX: number, 109 - offsetY: number, 110 - data: any, 111 - ) { 112 - const glyph = data.glyphs[char] || data.glyphs["?"]; 113 - 114 - if (!glyph) { 115 - throw new Error( 116 - 'THREE.Font: character "' + 117 - char + 118 - '" does not exists in font family ' + 119 - data.familyName + 120 - ".", 121 - ); 122 - } 123 - 124 - const path = new ShapePath(); 125 - 126 - let x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; 127 - 128 - if (glyph.o) { 129 - const outline = 130 - glyph._cachedOutline || (glyph._cachedOutline = glyph.o.split(" ")); 131 - 132 - for (let i = 0, l = outline.length; i < l; ) { 133 - const action = outline[i++]; 134 - 135 - switch (action) { 136 - case "m": // moveTo 137 - x = outline[i++] * scale + offsetX; 138 - y = outline[i++] * scale + offsetY; 139 - 140 - path.moveTo(x, y); 141 - 142 - break; 143 - 144 - case "l": // lineTo 145 - x = outline[i++] * scale + offsetX; 146 - y = outline[i++] * scale + offsetY; 147 - 148 - path.lineTo(x, y); 149 - 150 - break; 151 - 152 - case "q": // quadraticCurveTo 153 - cpx = outline[i++] * scale + offsetX; 154 - cpy = outline[i++] * scale + offsetY; 155 - cpx1 = outline[i++] * scale + offsetX; 156 - cpy1 = outline[i++] * scale + offsetY; 157 - 158 - path.quadraticCurveTo(cpx1, cpy1, cpx, cpy); 159 - 160 - break; 161 - 162 - case "b": // bezierCurveTo 163 - cpx = outline[i++] * scale + offsetX; 164 - cpy = outline[i++] * scale + offsetY; 165 - cpx1 = outline[i++] * scale + offsetX; 166 - cpy1 = outline[i++] * scale + offsetY; 167 - cpx2 = outline[i++] * scale + offsetX; 168 - cpy2 = outline[i++] * scale + offsetY; 169 - 170 - path.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, cpx, cpy); 171 - 172 - break; 173 - } 174 - } 175 - } 176 - 177 - return { offsetX: glyph.ha * scale, path: path }; 178 - } 179 - 180 - Font.prototype.isFont = true; 181 - 182 - export { FontLoader, Font };
-65
frontend/src/ts/three/TextGeometry.ts
··· 1 - /** 2 - * Text = 3D Text 3 - * 4 - * parameters = { 5 - * font: <THREE.Font>, // font 6 - * 7 - * size: <float>, // size of the text 8 - * height: <float>, // thickness to extrude text 9 - * curveSegments: <int>, // number of points on the curves 10 - * 11 - * bevelEnabled: <bool>, // turn on bevel 12 - * bevelThickness: <float>, // how deep into text bevel goes 13 - * bevelSize: <float>, // how far from text outline (including bevelOffset) is bevel 14 - * bevelOffset: <float> // how far from text outline does bevel start 15 - * } 16 - */ 17 - 18 - import { 19 - ExtrudeGeometry, 20 - ExtrudeGeometryOptions, 21 - } from "three/src/geometries/ExtrudeGeometry.js"; 22 - import { Font } from "./FontLoader.js"; 23 - 24 - export interface TextGeometryParameters { 25 - font?: Font; 26 - size: number; 27 - height?: number; 28 - curveSegments?: number; 29 - bevelEnabled?: boolean; 30 - bevelThickness?: number; 31 - bevelSize?: number; 32 - bevelOffset?: number; 33 - bevelSegments?: number; 34 - } 35 - 36 - class TextGeometry extends ExtrudeGeometry { 37 - override type = "TextGeometry"; 38 - 39 - constructor(text: string, parameters: TextGeometryParameters = { size: 1 }) { 40 - const font = parameters.font; 41 - 42 - if (font === undefined) { 43 - super(); // generate default extrude geometry 44 - } else { 45 - const shapes = font.generateShapes(text, parameters.size); 46 - 47 - // translate parameters to ExtrudeGeometry API 48 - 49 - (parameters as ExtrudeGeometryOptions).depth = 50 - parameters.height !== undefined ? parameters.height : 50; 51 - 52 - // defaults 53 - 54 - if (parameters.bevelThickness === undefined) 55 - parameters.bevelThickness = 10; 56 - if (parameters.bevelSize === undefined) parameters.bevelSize = 8; 57 - if (parameters.bevelEnabled === undefined) 58 - parameters.bevelEnabled = false; 59 - 60 - super(shapes, parameters); 61 - } 62 - } 63 - } 64 - 65 - export { TextGeometry };
+4 -971
server/Cargo.lock
··· 95 95 ] 96 96 97 97 [[package]] 98 - name = "async-stream" 99 - version = "0.3.6" 100 - source = "registry+https://github.com/rust-lang/crates.io-index" 101 - checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" 102 - dependencies = [ 103 - "async-stream-impl", 104 - "futures-core", 105 - "pin-project-lite", 106 - ] 107 - 108 - [[package]] 109 - name = "async-stream-impl" 110 - version = "0.3.6" 111 - source = "registry+https://github.com/rust-lang/crates.io-index" 112 - checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" 113 - dependencies = [ 114 - "proc-macro2", 115 - "quote", 116 - "syn", 117 - ] 118 - 119 - [[package]] 120 - name = "atomic-waker" 121 - version = "1.1.2" 122 - source = "registry+https://github.com/rust-lang/crates.io-index" 123 - checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 124 - 125 - [[package]] 126 98 name = "autocfg" 127 99 version = "1.4.0" 128 100 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 198 170 ] 199 171 200 172 [[package]] 201 - name = "base64" 202 - version = "0.22.1" 203 - source = "registry+https://github.com/rust-lang/crates.io-index" 204 - checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 205 - 206 - [[package]] 207 173 name = "basic-toml" 208 174 version = "0.1.9" 209 175 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 240 206 ] 241 207 242 208 [[package]] 243 - name = "bumpalo" 244 - version = "3.17.0" 245 - source = "registry+https://github.com/rust-lang/crates.io-index" 246 - checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 247 - 248 - [[package]] 249 209 name = "bytes" 250 210 version = "1.10.0" 251 211 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 269 229 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 270 230 271 231 [[package]] 272 - name = "core-foundation" 273 - version = "0.9.4" 274 - source = "registry+https://github.com/rust-lang/crates.io-index" 275 - checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 276 - dependencies = [ 277 - "core-foundation-sys", 278 - "libc", 279 - ] 280 - 281 - [[package]] 282 - name = "core-foundation-sys" 283 - version = "0.8.7" 284 - source = "registry+https://github.com/rust-lang/crates.io-index" 285 - checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 286 - 287 - [[package]] 288 232 name = "crc32fast" 289 233 version = "1.4.2" 290 234 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 294 238 ] 295 239 296 240 [[package]] 297 - name = "displaydoc" 298 - version = "0.2.5" 299 - source = "registry+https://github.com/rust-lang/crates.io-index" 300 - checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 301 - dependencies = [ 302 - "proc-macro2", 303 - "quote", 304 - "syn", 305 - ] 306 - 307 - [[package]] 308 - name = "encoding_rs" 309 - version = "0.8.35" 310 - source = "registry+https://github.com/rust-lang/crates.io-index" 311 - checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 312 - dependencies = [ 313 - "cfg-if", 314 - ] 315 - 316 - [[package]] 317 - name = "equivalent" 318 - version = "1.0.2" 319 - source = "registry+https://github.com/rust-lang/crates.io-index" 320 - checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 321 - 322 - [[package]] 323 - name = "errno" 324 - version = "0.3.10" 325 - source = "registry+https://github.com/rust-lang/crates.io-index" 326 - checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 327 - dependencies = [ 328 - "libc", 329 - "windows-sys 0.59.0", 330 - ] 331 - 332 - [[package]] 333 - name = "fastrand" 334 - version = "2.3.0" 335 - source = "registry+https://github.com/rust-lang/crates.io-index" 336 - checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 337 - 338 - [[package]] 339 241 name = "flate2" 340 242 version = "1.0.35" 341 243 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 350 252 version = "1.0.7" 351 253 source = "registry+https://github.com/rust-lang/crates.io-index" 352 254 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 353 - 354 - [[package]] 355 - name = "foreign-types" 356 - version = "0.3.2" 357 - source = "registry+https://github.com/rust-lang/crates.io-index" 358 - checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 359 - dependencies = [ 360 - "foreign-types-shared", 361 - ] 362 - 363 - [[package]] 364 - name = "foreign-types-shared" 365 - version = "0.1.1" 366 - source = "registry+https://github.com/rust-lang/crates.io-index" 367 - checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 368 255 369 256 [[package]] 370 257 name = "form_urlencoded" ··· 415 302 ] 416 303 417 304 [[package]] 418 - name = "getrandom" 419 - version = "0.2.15" 420 - source = "registry+https://github.com/rust-lang/crates.io-index" 421 - checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 422 - dependencies = [ 423 - "cfg-if", 424 - "libc", 425 - "wasi 0.11.0+wasi-snapshot-preview1", 426 - ] 427 - 428 - [[package]] 429 - name = "getrandom" 430 - version = "0.3.1" 431 - source = "registry+https://github.com/rust-lang/crates.io-index" 432 - checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" 433 - dependencies = [ 434 - "cfg-if", 435 - "libc", 436 - "wasi 0.13.3+wasi-0.2.2", 437 - "windows-targets", 438 - ] 439 - 440 - [[package]] 441 305 name = "gimli" 442 306 version = "0.31.1" 443 307 source = "registry+https://github.com/rust-lang/crates.io-index" 444 308 checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 445 309 446 310 [[package]] 447 - name = "h2" 448 - version = "0.4.8" 449 - source = "registry+https://github.com/rust-lang/crates.io-index" 450 - checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" 451 - dependencies = [ 452 - "atomic-waker", 453 - "bytes", 454 - "fnv", 455 - "futures-core", 456 - "futures-sink", 457 - "http", 458 - "indexmap", 459 - "slab", 460 - "tokio", 461 - "tokio-util", 462 - "tracing", 463 - ] 464 - 465 - [[package]] 466 - name = "hashbrown" 467 - version = "0.15.2" 468 - source = "registry+https://github.com/rust-lang/crates.io-index" 469 - checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 470 - 471 - [[package]] 472 311 name = "http" 473 312 version = "1.2.0" 474 313 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 538 377 "bytes", 539 378 "futures-channel", 540 379 "futures-util", 541 - "h2", 542 380 "http", 543 381 "http-body", 544 382 "httparse", ··· 547 385 "pin-project-lite", 548 386 "smallvec", 549 387 "tokio", 550 - "want", 551 - ] 552 - 553 - [[package]] 554 - name = "hyper-rustls" 555 - version = "0.27.5" 556 - source = "registry+https://github.com/rust-lang/crates.io-index" 557 - checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" 558 - dependencies = [ 559 - "futures-util", 560 - "http", 561 - "hyper", 562 - "hyper-util", 563 - "rustls", 564 - "rustls-pki-types", 565 - "tokio", 566 - "tokio-rustls", 567 - "tower-service", 568 - ] 569 - 570 - [[package]] 571 - name = "hyper-tls" 572 - version = "0.6.0" 573 - source = "registry+https://github.com/rust-lang/crates.io-index" 574 - checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 575 - dependencies = [ 576 - "bytes", 577 - "http-body-util", 578 - "hyper", 579 - "hyper-util", 580 - "native-tls", 581 - "tokio", 582 - "tokio-native-tls", 583 - "tower-service", 584 388 ] 585 389 586 390 [[package]] ··· 590 394 checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" 591 395 dependencies = [ 592 396 "bytes", 593 - "futures-channel", 594 397 "futures-util", 595 398 "http", 596 399 "http-body", 597 400 "hyper", 598 401 "pin-project-lite", 599 - "socket2", 600 402 "tokio", 601 403 "tower-service", 602 - "tracing", 603 404 ] 604 405 605 406 [[package]] 606 - name = "icu_collections" 607 - version = "1.5.0" 608 - source = "registry+https://github.com/rust-lang/crates.io-index" 609 - checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 610 - dependencies = [ 611 - "displaydoc", 612 - "yoke", 613 - "zerofrom", 614 - "zerovec", 615 - ] 616 - 617 - [[package]] 618 - name = "icu_locid" 619 - version = "1.5.0" 620 - source = "registry+https://github.com/rust-lang/crates.io-index" 621 - checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 622 - dependencies = [ 623 - "displaydoc", 624 - "litemap", 625 - "tinystr", 626 - "writeable", 627 - "zerovec", 628 - ] 629 - 630 - [[package]] 631 - name = "icu_locid_transform" 632 - version = "1.5.0" 633 - source = "registry+https://github.com/rust-lang/crates.io-index" 634 - checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 635 - dependencies = [ 636 - "displaydoc", 637 - "icu_locid", 638 - "icu_locid_transform_data", 639 - "icu_provider", 640 - "tinystr", 641 - "zerovec", 642 - ] 643 - 644 - [[package]] 645 - name = "icu_locid_transform_data" 646 - version = "1.5.0" 647 - source = "registry+https://github.com/rust-lang/crates.io-index" 648 - checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" 649 - 650 - [[package]] 651 - name = "icu_normalizer" 652 - version = "1.5.0" 653 - source = "registry+https://github.com/rust-lang/crates.io-index" 654 - checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 655 - dependencies = [ 656 - "displaydoc", 657 - "icu_collections", 658 - "icu_normalizer_data", 659 - "icu_properties", 660 - "icu_provider", 661 - "smallvec", 662 - "utf16_iter", 663 - "utf8_iter", 664 - "write16", 665 - "zerovec", 666 - ] 667 - 668 - [[package]] 669 - name = "icu_normalizer_data" 670 - version = "1.5.0" 671 - source = "registry+https://github.com/rust-lang/crates.io-index" 672 - checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" 673 - 674 - [[package]] 675 - name = "icu_properties" 676 - version = "1.5.1" 677 - source = "registry+https://github.com/rust-lang/crates.io-index" 678 - checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 679 - dependencies = [ 680 - "displaydoc", 681 - "icu_collections", 682 - "icu_locid_transform", 683 - "icu_properties_data", 684 - "icu_provider", 685 - "tinystr", 686 - "zerovec", 687 - ] 688 - 689 - [[package]] 690 - name = "icu_properties_data" 691 - version = "1.5.0" 692 - source = "registry+https://github.com/rust-lang/crates.io-index" 693 - checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" 694 - 695 - [[package]] 696 - name = "icu_provider" 697 - version = "1.5.0" 698 - source = "registry+https://github.com/rust-lang/crates.io-index" 699 - checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 700 - dependencies = [ 701 - "displaydoc", 702 - "icu_locid", 703 - "icu_provider_macros", 704 - "stable_deref_trait", 705 - "tinystr", 706 - "writeable", 707 - "yoke", 708 - "zerofrom", 709 - "zerovec", 710 - ] 711 - 712 - [[package]] 713 - name = "icu_provider_macros" 714 - version = "1.5.0" 715 - source = "registry+https://github.com/rust-lang/crates.io-index" 716 - checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 717 - dependencies = [ 718 - "proc-macro2", 719 - "quote", 720 - "syn", 721 - ] 722 - 723 - [[package]] 724 - name = "idna" 725 - version = "1.0.3" 726 - source = "registry+https://github.com/rust-lang/crates.io-index" 727 - checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 728 - dependencies = [ 729 - "idna_adapter", 730 - "smallvec", 731 - "utf8_iter", 732 - ] 733 - 734 - [[package]] 735 - name = "idna_adapter" 736 - version = "1.2.0" 737 - source = "registry+https://github.com/rust-lang/crates.io-index" 738 - checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 739 - dependencies = [ 740 - "icu_normalizer", 741 - "icu_properties", 742 - ] 743 - 744 - [[package]] 745 - name = "indexmap" 746 - version = "2.7.1" 747 - source = "registry+https://github.com/rust-lang/crates.io-index" 748 - checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" 749 - dependencies = [ 750 - "equivalent", 751 - "hashbrown", 752 - ] 753 - 754 - [[package]] 755 - name = "ipnet" 756 - version = "2.11.0" 757 - source = "registry+https://github.com/rust-lang/crates.io-index" 758 - checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 759 - 760 - [[package]] 761 407 name = "itoa" 762 408 version = "1.0.14" 763 409 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 773 419 ] 774 420 775 421 [[package]] 776 - name = "js-sys" 777 - version = "0.3.77" 778 - source = "registry+https://github.com/rust-lang/crates.io-index" 779 - checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 780 - dependencies = [ 781 - "once_cell", 782 - "wasm-bindgen", 783 - ] 784 - 785 - [[package]] 786 422 name = "lazy_static" 787 423 version = "1.5.0" 788 424 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 801 437 checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" 802 438 803 439 [[package]] 804 - name = "linux-raw-sys" 805 - version = "0.4.15" 806 - source = "registry+https://github.com/rust-lang/crates.io-index" 807 - checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 808 - 809 - [[package]] 810 - name = "litemap" 811 - version = "0.7.4" 812 - source = "registry+https://github.com/rust-lang/crates.io-index" 813 - checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" 814 - 815 - [[package]] 816 440 name = "lock_api" 817 441 version = "0.4.12" 818 442 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 878 502 checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 879 503 dependencies = [ 880 504 "libc", 881 - "wasi 0.11.0+wasi-snapshot-preview1", 882 - "windows-sys 0.52.0", 505 + "wasi", 506 + "windows-sys", 883 507 ] 884 508 885 509 [[package]] ··· 888 512 dependencies = [ 889 513 "anyhow", 890 514 "askama", 891 - "async-stream", 892 515 "axum", 893 - "reqwest", 894 - "serde", 895 516 "tokio", 896 - "tokio-stream", 897 517 "tower", 898 518 "tower-http", 899 519 "tracing", ··· 901 521 ] 902 522 903 523 [[package]] 904 - name = "native-tls" 905 - version = "0.2.14" 906 - source = "registry+https://github.com/rust-lang/crates.io-index" 907 - checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" 908 - dependencies = [ 909 - "libc", 910 - "log", 911 - "openssl", 912 - "openssl-probe", 913 - "openssl-sys", 914 - "schannel", 915 - "security-framework", 916 - "security-framework-sys", 917 - "tempfile", 918 - ] 919 - 920 - [[package]] 921 524 name = "nom" 922 525 version = "7.1.3" 923 526 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 962 565 checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" 963 566 964 567 [[package]] 965 - name = "openssl" 966 - version = "0.10.71" 967 - source = "registry+https://github.com/rust-lang/crates.io-index" 968 - checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" 969 - dependencies = [ 970 - "bitflags", 971 - "cfg-if", 972 - "foreign-types", 973 - "libc", 974 - "once_cell", 975 - "openssl-macros", 976 - "openssl-sys", 977 - ] 978 - 979 - [[package]] 980 - name = "openssl-macros" 981 - version = "0.1.1" 982 - source = "registry+https://github.com/rust-lang/crates.io-index" 983 - checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 984 - dependencies = [ 985 - "proc-macro2", 986 - "quote", 987 - "syn", 988 - ] 989 - 990 - [[package]] 991 - name = "openssl-probe" 992 - version = "0.1.6" 993 - source = "registry+https://github.com/rust-lang/crates.io-index" 994 - checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 995 - 996 - [[package]] 997 - name = "openssl-sys" 998 - version = "0.9.106" 999 - source = "registry+https://github.com/rust-lang/crates.io-index" 1000 - checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" 1001 - dependencies = [ 1002 - "cc", 1003 - "libc", 1004 - "pkg-config", 1005 - "vcpkg", 1006 - ] 1007 - 1008 - [[package]] 1009 568 name = "overload" 1010 569 version = "0.1.1" 1011 570 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1086 645 ] 1087 646 1088 647 [[package]] 1089 - name = "reqwest" 1090 - version = "0.12.12" 1091 - source = "registry+https://github.com/rust-lang/crates.io-index" 1092 - checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" 1093 - dependencies = [ 1094 - "base64", 1095 - "bytes", 1096 - "encoding_rs", 1097 - "futures-core", 1098 - "futures-util", 1099 - "h2", 1100 - "http", 1101 - "http-body", 1102 - "http-body-util", 1103 - "hyper", 1104 - "hyper-rustls", 1105 - "hyper-tls", 1106 - "hyper-util", 1107 - "ipnet", 1108 - "js-sys", 1109 - "log", 1110 - "mime", 1111 - "native-tls", 1112 - "once_cell", 1113 - "percent-encoding", 1114 - "pin-project-lite", 1115 - "rustls-pemfile", 1116 - "serde", 1117 - "serde_json", 1118 - "serde_urlencoded", 1119 - "sync_wrapper", 1120 - "system-configuration", 1121 - "tokio", 1122 - "tokio-native-tls", 1123 - "tower", 1124 - "tower-service", 1125 - "url", 1126 - "wasm-bindgen", 1127 - "wasm-bindgen-futures", 1128 - "web-sys", 1129 - "windows-registry", 1130 - ] 1131 - 1132 - [[package]] 1133 - name = "ring" 1134 - version = "0.17.9" 1135 - source = "registry+https://github.com/rust-lang/crates.io-index" 1136 - checksum = "e75ec5e92c4d8aede845126adc388046234541629e76029599ed35a003c7ed24" 1137 - dependencies = [ 1138 - "cc", 1139 - "cfg-if", 1140 - "getrandom 0.2.15", 1141 - "libc", 1142 - "untrusted", 1143 - "windows-sys 0.52.0", 1144 - ] 1145 - 1146 - [[package]] 1147 648 name = "rustc-demangle" 1148 649 version = "0.1.24" 1149 650 source = "registry+https://github.com/rust-lang/crates.io-index" 1150 651 checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1151 652 1152 653 [[package]] 1153 - name = "rustix" 1154 - version = "0.38.44" 1155 - source = "registry+https://github.com/rust-lang/crates.io-index" 1156 - checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 1157 - dependencies = [ 1158 - "bitflags", 1159 - "errno", 1160 - "libc", 1161 - "linux-raw-sys", 1162 - "windows-sys 0.59.0", 1163 - ] 1164 - 1165 - [[package]] 1166 - name = "rustls" 1167 - version = "0.23.23" 1168 - source = "registry+https://github.com/rust-lang/crates.io-index" 1169 - checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" 1170 - dependencies = [ 1171 - "once_cell", 1172 - "rustls-pki-types", 1173 - "rustls-webpki", 1174 - "subtle", 1175 - "zeroize", 1176 - ] 1177 - 1178 - [[package]] 1179 - name = "rustls-pemfile" 1180 - version = "2.2.0" 1181 - source = "registry+https://github.com/rust-lang/crates.io-index" 1182 - checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" 1183 - dependencies = [ 1184 - "rustls-pki-types", 1185 - ] 1186 - 1187 - [[package]] 1188 - name = "rustls-pki-types" 1189 - version = "1.11.0" 1190 - source = "registry+https://github.com/rust-lang/crates.io-index" 1191 - checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" 1192 - 1193 - [[package]] 1194 - name = "rustls-webpki" 1195 - version = "0.102.8" 1196 - source = "registry+https://github.com/rust-lang/crates.io-index" 1197 - checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" 1198 - dependencies = [ 1199 - "ring", 1200 - "rustls-pki-types", 1201 - "untrusted", 1202 - ] 1203 - 1204 - [[package]] 1205 654 name = "rustversion" 1206 655 version = "1.0.19" 1207 656 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1214 663 checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" 1215 664 1216 665 [[package]] 1217 - name = "schannel" 1218 - version = "0.1.27" 1219 - source = "registry+https://github.com/rust-lang/crates.io-index" 1220 - checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" 1221 - dependencies = [ 1222 - "windows-sys 0.59.0", 1223 - ] 1224 - 1225 - [[package]] 1226 666 name = "scopeguard" 1227 667 version = "1.2.0" 1228 668 source = "registry+https://github.com/rust-lang/crates.io-index" 1229 669 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1230 670 1231 671 [[package]] 1232 - name = "security-framework" 1233 - version = "2.11.1" 1234 - source = "registry+https://github.com/rust-lang/crates.io-index" 1235 - checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 1236 - dependencies = [ 1237 - "bitflags", 1238 - "core-foundation", 1239 - "core-foundation-sys", 1240 - "libc", 1241 - "security-framework-sys", 1242 - ] 1243 - 1244 - [[package]] 1245 - name = "security-framework-sys" 1246 - version = "2.14.0" 1247 - source = "registry+https://github.com/rust-lang/crates.io-index" 1248 - checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" 1249 - dependencies = [ 1250 - "core-foundation-sys", 1251 - "libc", 1252 - ] 1253 - 1254 - [[package]] 1255 672 name = "serde" 1256 673 version = "1.0.218" 1257 674 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1330 747 ] 1331 748 1332 749 [[package]] 1333 - name = "slab" 1334 - version = "0.4.9" 1335 - source = "registry+https://github.com/rust-lang/crates.io-index" 1336 - checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1337 - dependencies = [ 1338 - "autocfg", 1339 - ] 1340 - 1341 - [[package]] 1342 750 name = "smallvec" 1343 751 version = "1.14.0" 1344 752 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1351 759 checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" 1352 760 dependencies = [ 1353 761 "libc", 1354 - "windows-sys 0.52.0", 762 + "windows-sys", 1355 763 ] 1356 - 1357 - [[package]] 1358 - name = "stable_deref_trait" 1359 - version = "1.2.0" 1360 - source = "registry+https://github.com/rust-lang/crates.io-index" 1361 - checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1362 - 1363 - [[package]] 1364 - name = "subtle" 1365 - version = "2.6.1" 1366 - source = "registry+https://github.com/rust-lang/crates.io-index" 1367 - checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1368 764 1369 765 [[package]] 1370 766 name = "syn" ··· 1382 778 version = "1.0.2" 1383 779 source = "registry+https://github.com/rust-lang/crates.io-index" 1384 780 checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 1385 - dependencies = [ 1386 - "futures-core", 1387 - ] 1388 - 1389 - [[package]] 1390 - name = "synstructure" 1391 - version = "0.13.1" 1392 - source = "registry+https://github.com/rust-lang/crates.io-index" 1393 - checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 1394 - dependencies = [ 1395 - "proc-macro2", 1396 - "quote", 1397 - "syn", 1398 - ] 1399 - 1400 - [[package]] 1401 - name = "system-configuration" 1402 - version = "0.6.1" 1403 - source = "registry+https://github.com/rust-lang/crates.io-index" 1404 - checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" 1405 - dependencies = [ 1406 - "bitflags", 1407 - "core-foundation", 1408 - "system-configuration-sys", 1409 - ] 1410 - 1411 - [[package]] 1412 - name = "system-configuration-sys" 1413 - version = "0.6.0" 1414 - source = "registry+https://github.com/rust-lang/crates.io-index" 1415 - checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 1416 - dependencies = [ 1417 - "core-foundation-sys", 1418 - "libc", 1419 - ] 1420 - 1421 - [[package]] 1422 - name = "tempfile" 1423 - version = "3.17.1" 1424 - source = "registry+https://github.com/rust-lang/crates.io-index" 1425 - checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" 1426 - dependencies = [ 1427 - "cfg-if", 1428 - "fastrand", 1429 - "getrandom 0.3.1", 1430 - "once_cell", 1431 - "rustix", 1432 - "windows-sys 0.59.0", 1433 - ] 1434 781 1435 782 [[package]] 1436 783 name = "thread_local" ··· 1443 790 ] 1444 791 1445 792 [[package]] 1446 - name = "tinystr" 1447 - version = "0.7.6" 1448 - source = "registry+https://github.com/rust-lang/crates.io-index" 1449 - checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 1450 - dependencies = [ 1451 - "displaydoc", 1452 - "zerovec", 1453 - ] 1454 - 1455 - [[package]] 1456 793 name = "tokio" 1457 794 version = "1.43.0" 1458 795 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1467 804 "signal-hook-registry", 1468 805 "socket2", 1469 806 "tokio-macros", 1470 - "windows-sys 0.52.0", 807 + "windows-sys", 1471 808 ] 1472 809 1473 810 [[package]] ··· 1482 819 ] 1483 820 1484 821 [[package]] 1485 - name = "tokio-native-tls" 1486 - version = "0.3.1" 1487 - source = "registry+https://github.com/rust-lang/crates.io-index" 1488 - checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 1489 - dependencies = [ 1490 - "native-tls", 1491 - "tokio", 1492 - ] 1493 - 1494 - [[package]] 1495 - name = "tokio-rustls" 1496 - version = "0.26.1" 1497 - source = "registry+https://github.com/rust-lang/crates.io-index" 1498 - checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" 1499 - dependencies = [ 1500 - "rustls", 1501 - "tokio", 1502 - ] 1503 - 1504 - [[package]] 1505 - name = "tokio-stream" 1506 - version = "0.1.17" 1507 - source = "registry+https://github.com/rust-lang/crates.io-index" 1508 - checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" 1509 - dependencies = [ 1510 - "futures-core", 1511 - "pin-project-lite", 1512 - "tokio", 1513 - ] 1514 - 1515 - [[package]] 1516 822 name = "tokio-util" 1517 823 version = "0.7.13" 1518 824 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1639 945 ] 1640 946 1641 947 [[package]] 1642 - name = "try-lock" 1643 - version = "0.2.5" 1644 - source = "registry+https://github.com/rust-lang/crates.io-index" 1645 - checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1646 - 1647 - [[package]] 1648 948 name = "unicase" 1649 949 version = "2.8.1" 1650 950 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1657 957 checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" 1658 958 1659 959 [[package]] 1660 - name = "untrusted" 1661 - version = "0.9.0" 1662 - source = "registry+https://github.com/rust-lang/crates.io-index" 1663 - checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1664 - 1665 - [[package]] 1666 - name = "url" 1667 - version = "2.5.4" 1668 - source = "registry+https://github.com/rust-lang/crates.io-index" 1669 - checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 1670 - dependencies = [ 1671 - "form_urlencoded", 1672 - "idna", 1673 - "percent-encoding", 1674 - ] 1675 - 1676 - [[package]] 1677 - name = "utf16_iter" 1678 - version = "1.0.5" 1679 - source = "registry+https://github.com/rust-lang/crates.io-index" 1680 - checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 1681 - 1682 - [[package]] 1683 - name = "utf8_iter" 1684 - version = "1.0.4" 1685 - source = "registry+https://github.com/rust-lang/crates.io-index" 1686 - checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1687 - 1688 - [[package]] 1689 960 name = "valuable" 1690 961 version = "0.1.1" 1691 962 source = "registry+https://github.com/rust-lang/crates.io-index" 1692 963 checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 1693 964 1694 965 [[package]] 1695 - name = "vcpkg" 1696 - version = "0.2.15" 1697 - source = "registry+https://github.com/rust-lang/crates.io-index" 1698 - checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1699 - 1700 - [[package]] 1701 - name = "want" 1702 - version = "0.3.1" 1703 - source = "registry+https://github.com/rust-lang/crates.io-index" 1704 - checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1705 - dependencies = [ 1706 - "try-lock", 1707 - ] 1708 - 1709 - [[package]] 1710 966 name = "wasi" 1711 967 version = "0.11.0+wasi-snapshot-preview1" 1712 968 source = "registry+https://github.com/rust-lang/crates.io-index" 1713 969 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1714 970 1715 971 [[package]] 1716 - name = "wasi" 1717 - version = "0.13.3+wasi-0.2.2" 1718 - source = "registry+https://github.com/rust-lang/crates.io-index" 1719 - checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" 1720 - dependencies = [ 1721 - "wit-bindgen-rt", 1722 - ] 1723 - 1724 - [[package]] 1725 - name = "wasm-bindgen" 1726 - version = "0.2.100" 1727 - source = "registry+https://github.com/rust-lang/crates.io-index" 1728 - checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 1729 - dependencies = [ 1730 - "cfg-if", 1731 - "once_cell", 1732 - "rustversion", 1733 - "wasm-bindgen-macro", 1734 - ] 1735 - 1736 - [[package]] 1737 - name = "wasm-bindgen-backend" 1738 - version = "0.2.100" 1739 - source = "registry+https://github.com/rust-lang/crates.io-index" 1740 - checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 1741 - dependencies = [ 1742 - "bumpalo", 1743 - "log", 1744 - "proc-macro2", 1745 - "quote", 1746 - "syn", 1747 - "wasm-bindgen-shared", 1748 - ] 1749 - 1750 - [[package]] 1751 - name = "wasm-bindgen-futures" 1752 - version = "0.4.50" 1753 - source = "registry+https://github.com/rust-lang/crates.io-index" 1754 - checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 1755 - dependencies = [ 1756 - "cfg-if", 1757 - "js-sys", 1758 - "once_cell", 1759 - "wasm-bindgen", 1760 - "web-sys", 1761 - ] 1762 - 1763 - [[package]] 1764 - name = "wasm-bindgen-macro" 1765 - version = "0.2.100" 1766 - source = "registry+https://github.com/rust-lang/crates.io-index" 1767 - checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 1768 - dependencies = [ 1769 - "quote", 1770 - "wasm-bindgen-macro-support", 1771 - ] 1772 - 1773 - [[package]] 1774 - name = "wasm-bindgen-macro-support" 1775 - version = "0.2.100" 1776 - source = "registry+https://github.com/rust-lang/crates.io-index" 1777 - checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 1778 - dependencies = [ 1779 - "proc-macro2", 1780 - "quote", 1781 - "syn", 1782 - "wasm-bindgen-backend", 1783 - "wasm-bindgen-shared", 1784 - ] 1785 - 1786 - [[package]] 1787 - name = "wasm-bindgen-shared" 1788 - version = "0.2.100" 1789 - source = "registry+https://github.com/rust-lang/crates.io-index" 1790 - checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 1791 - dependencies = [ 1792 - "unicode-ident", 1793 - ] 1794 - 1795 - [[package]] 1796 - name = "web-sys" 1797 - version = "0.3.77" 1798 - source = "registry+https://github.com/rust-lang/crates.io-index" 1799 - checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 1800 - dependencies = [ 1801 - "js-sys", 1802 - "wasm-bindgen", 1803 - ] 1804 - 1805 - [[package]] 1806 972 name = "winapi" 1807 973 version = "0.3.9" 1808 974 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1825 991 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1826 992 1827 993 [[package]] 1828 - name = "windows-registry" 1829 - version = "0.2.0" 1830 - source = "registry+https://github.com/rust-lang/crates.io-index" 1831 - checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" 1832 - dependencies = [ 1833 - "windows-result", 1834 - "windows-strings", 1835 - "windows-targets", 1836 - ] 1837 - 1838 - [[package]] 1839 - name = "windows-result" 1840 - version = "0.2.0" 1841 - source = "registry+https://github.com/rust-lang/crates.io-index" 1842 - checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" 1843 - dependencies = [ 1844 - "windows-targets", 1845 - ] 1846 - 1847 - [[package]] 1848 - name = "windows-strings" 1849 - version = "0.1.0" 1850 - source = "registry+https://github.com/rust-lang/crates.io-index" 1851 - checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" 1852 - dependencies = [ 1853 - "windows-result", 1854 - "windows-targets", 1855 - ] 1856 - 1857 - [[package]] 1858 994 name = "windows-sys" 1859 995 version = "0.52.0" 1860 996 source = "registry+https://github.com/rust-lang/crates.io-index" 1861 997 checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1862 - dependencies = [ 1863 - "windows-targets", 1864 - ] 1865 - 1866 - [[package]] 1867 - name = "windows-sys" 1868 - version = "0.59.0" 1869 - source = "registry+https://github.com/rust-lang/crates.io-index" 1870 - checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1871 998 dependencies = [ 1872 999 "windows-targets", 1873 1000 ] ··· 1935 1062 version = "0.52.6" 1936 1063 source = "registry+https://github.com/rust-lang/crates.io-index" 1937 1064 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1938 - 1939 - [[package]] 1940 - name = "wit-bindgen-rt" 1941 - version = "0.33.0" 1942 - source = "registry+https://github.com/rust-lang/crates.io-index" 1943 - checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" 1944 - dependencies = [ 1945 - "bitflags", 1946 - ] 1947 - 1948 - [[package]] 1949 - name = "write16" 1950 - version = "1.0.0" 1951 - source = "registry+https://github.com/rust-lang/crates.io-index" 1952 - checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 1953 - 1954 - [[package]] 1955 - name = "writeable" 1956 - version = "0.5.5" 1957 - source = "registry+https://github.com/rust-lang/crates.io-index" 1958 - checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 1959 - 1960 - [[package]] 1961 - name = "yoke" 1962 - version = "0.7.5" 1963 - source = "registry+https://github.com/rust-lang/crates.io-index" 1964 - checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" 1965 - dependencies = [ 1966 - "serde", 1967 - "stable_deref_trait", 1968 - "yoke-derive", 1969 - "zerofrom", 1970 - ] 1971 - 1972 - [[package]] 1973 - name = "yoke-derive" 1974 - version = "0.7.5" 1975 - source = "registry+https://github.com/rust-lang/crates.io-index" 1976 - checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" 1977 - dependencies = [ 1978 - "proc-macro2", 1979 - "quote", 1980 - "syn", 1981 - "synstructure", 1982 - ] 1983 - 1984 - [[package]] 1985 - name = "zerofrom" 1986 - version = "0.1.5" 1987 - source = "registry+https://github.com/rust-lang/crates.io-index" 1988 - checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" 1989 - dependencies = [ 1990 - "zerofrom-derive", 1991 - ] 1992 - 1993 - [[package]] 1994 - name = "zerofrom-derive" 1995 - version = "0.1.5" 1996 - source = "registry+https://github.com/rust-lang/crates.io-index" 1997 - checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" 1998 - dependencies = [ 1999 - "proc-macro2", 2000 - "quote", 2001 - "syn", 2002 - "synstructure", 2003 - ] 2004 - 2005 - [[package]] 2006 - name = "zeroize" 2007 - version = "1.8.1" 2008 - source = "registry+https://github.com/rust-lang/crates.io-index" 2009 - checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 2010 - 2011 - [[package]] 2012 - name = "zerovec" 2013 - version = "0.10.4" 2014 - source = "registry+https://github.com/rust-lang/crates.io-index" 2015 - checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 2016 - dependencies = [ 2017 - "yoke", 2018 - "zerofrom", 2019 - "zerovec-derive", 2020 - ] 2021 - 2022 - [[package]] 2023 - name = "zerovec-derive" 2024 - version = "0.10.3" 2025 - source = "registry+https://github.com/rust-lang/crates.io-index" 2026 - checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 2027 - dependencies = [ 2028 - "proc-macro2", 2029 - "quote", 2030 - "syn", 2031 - ] 2032 1065 2033 1066 [[package]] 2034 1067 name = "zstd"
-4
server/Cargo.toml
··· 8 8 [dependencies] 9 9 anyhow = "1.0.57" 10 10 askama = { version = "0.13.0", git="https://github.com/rinja-rs/askama.git" } 11 - async-stream = "0.3.6" 12 11 axum = "0.8.1" 13 - reqwest = { version = "0.12.12", features = ["json"] } 14 - serde = { version = "1.0.217", features = ["derive"] } 15 12 tokio = { version = "1.18.2", features = ["full"] } 16 - tokio-stream = "0.1.17" 17 13 tower = "0.5.2" 18 14 tower-http = { version = "0.6.2", features = ["compression-full", "fs", "trace", "set-header"] } 19 15 tracing = "0.1.34"
+3 -15
server/src/index.rs
··· 1 - use crate::{scrobble::ScrobblesTemplate, scrobble_monitor::ScrobbleMonitor}; 2 - 3 1 use askama::Template; 4 2 5 3 #[derive(Template, Debug, Clone)] 6 4 #[template(path = "index.html")] 7 - pub struct RootTemplate { 8 - scrobble: Option<ScrobblesTemplate>, 9 - } 5 + pub struct RootTemplate {} 10 6 11 7 impl RootTemplate { 12 - pub fn new(monitor: ScrobbleMonitor) -> RootTemplate { 13 - let scrobbles_template = monitor.try_get_scrobble(); 14 - if scrobbles_template.is_none() { 15 - // start fetching scrobble so we can send a fresh response to the client ASAP 16 - tokio::spawn(async move { monitor.get_scrobble().await }); 17 - } 18 - 19 - RootTemplate { 20 - scrobble: scrobbles_template, 21 - } 8 + pub fn new() -> RootTemplate { 9 + RootTemplate {} 22 10 } 23 11 }
+4 -69
server/src/main.rs
··· 1 1 mod index; 2 - mod scrobble; 3 - mod scrobble_monitor; 4 2 5 - use std::{convert::Infallible, env, net::SocketAddr, time::Duration}; 3 + use std::net::SocketAddr; 6 4 7 5 use crate::index::RootTemplate; 8 - use crate::scrobble_monitor::ScrobbleMonitor; 9 6 10 7 use askama::Template; 11 - use async_stream::stream; 12 8 use axum::{ 13 9 Router, 14 - extract::{Query, State}, 15 10 http::{HeaderName, HeaderValue, StatusCode}, 16 - response::{Html, IntoResponse, Sse, sse}, 11 + response::{Html, IntoResponse}, 17 12 routing::{get, get_service}, 18 13 }; 19 - use serde::Deserialize; 20 - use tokio::time::{self, MissedTickBehavior}; 21 - use tokio_stream::Stream; 22 14 use tower::ServiceBuilder; 23 15 use tower_http::{ 24 16 compression::CompressionLayer, services::ServeDir, set_header::SetResponseHeaderLayer, ··· 29 21 async fn main() -> anyhow::Result<()> { 30 22 tracing_subscriber::fmt::init(); 31 23 32 - let monitor = ScrobbleMonitor::new(env::var("LAST_FM_API_KEY")?); 33 - 34 24 let app = Router::new() 35 25 .route("/", get(render_index_handler)) 36 - .route("/scrobbles", get(get_scrobble)) 37 26 .fallback(get_service(ServeDir::new("."))) 38 - .with_state(monitor) 39 27 .layer( 40 28 ServiceBuilder::new() 41 29 .layer(TraceLayer::new_for_http()) ··· 54 42 Ok(()) 55 43 } 56 44 57 - async fn render_index_handler(State(monitor): State<ScrobbleMonitor>) -> impl IntoResponse { 58 - let template = RootTemplate::new(monitor); 45 + async fn render_index_handler() -> impl IntoResponse { 46 + let template = RootTemplate::new(); 59 47 template.render().map(Html).map_err(|err| { 60 48 tracing::error!("failed to render index: {err:?}"); 61 49 StatusCode::INTERNAL_SERVER_ERROR 62 50 }) 63 51 } 64 - 65 - #[derive(Deserialize)] 66 - struct ScrobbleQuery { 67 - #[serde(default)] 68 - immediate: bool, 69 - } 70 - 71 - async fn get_scrobble( 72 - State(monitor): State<ScrobbleMonitor>, 73 - Query(ScrobbleQuery { immediate }): Query<ScrobbleQuery>, 74 - ) -> Sse<impl Stream<Item = Result<sse::Event, Infallible>>> { 75 - let stream = stream! { 76 - let mut last_template = None; 77 - 78 - let mut interval = time::interval(Duration::from_secs(30)); 79 - interval.set_missed_tick_behavior(MissedTickBehavior::Skip); 80 - if !immediate { 81 - interval.tick().await; 82 - last_template = monitor.get_scrobble().await.ok(); 83 - } 84 - 85 - loop { 86 - interval.tick().await; 87 - let new_template = match monitor.get_scrobble().await { 88 - Ok(template) => template, 89 - Err(error) => { 90 - tracing::error!(?error, "failed to get data from last.fm"); 91 - continue; 92 - } 93 - }; 94 - 95 - if last_template 96 - .as_ref() 97 - .is_some_and(|last_template| last_template == &new_template) 98 - { 99 - continue; 100 - } 101 - 102 - let data = match new_template.render() { 103 - Ok(data) => data, 104 - Err(error) => { 105 - tracing::error!(?error, "failed to render scrobble"); 106 - break; 107 - } 108 - }; 109 - yield Ok(sse::Event::default().event("scrobble").data(data)); 110 - 111 - last_template.replace(new_template); 112 - } 113 - }; 114 - 115 - Sse::new(stream) 116 - }
-90
server/src/scrobble.rs
··· 1 - use askama::Template; 2 - use serde::Deserialize; 3 - 4 - #[derive(Debug, Clone, Deserialize)] 5 - pub struct Artist { 6 - #[serde(rename = "#text")] 7 - pub text: String, 8 - } 9 - 10 - #[derive(Debug, Clone, Deserialize)] 11 - pub struct Image { 12 - #[serde(rename = "#text")] 13 - pub text: String, 14 - } 15 - 16 - #[derive(Debug, Clone, Deserialize)] 17 - pub struct Attributes { 18 - #[serde(rename = "nowplaying")] 19 - pub now_playing: String, 20 - } 21 - 22 - #[derive(Debug, Clone, Deserialize)] 23 - pub struct Track { 24 - pub artist: Artist, 25 - pub image: Vec<Image>, 26 - pub name: String, 27 - #[serde(rename = "@attr")] 28 - pub attributes: Option<Attributes>, 29 - } 30 - 31 - #[derive(Debug, Clone, Deserialize)] 32 - pub struct RecentTracks { 33 - pub track: Vec<Track>, 34 - } 35 - 36 - #[derive(Debug, Clone, Deserialize)] 37 - pub struct Scrobble { 38 - #[serde(rename = "recenttracks")] 39 - pub recent_tracks: RecentTracks, 40 - } 41 - 42 - #[derive(Debug, Clone, Deserialize)] 43 - pub struct Error { 44 - pub message: String, 45 - } 46 - 47 - #[derive(Debug, Clone, Deserialize)] 48 - #[serde(untagged)] 49 - pub enum Response { 50 - Scrobble(Scrobble), 51 - Error(Error), 52 - } 53 - 54 - #[derive(Template, Debug, Clone, PartialEq)] 55 - #[template(path = "scrobble.html")] 56 - pub struct ScrobblesTemplate { 57 - pub intro: &'static str, 58 - pub now_playing: String, 59 - pub image: Option<String>, 60 - pub srcset: Option<String>, 61 - } 62 - 63 - impl ScrobblesTemplate { 64 - pub fn new(scrobble: &Scrobble) -> ScrobblesTemplate { 65 - let latest_track = &scrobble.recent_tracks.track[0]; 66 - let srcset = latest_track.image.get(0..3).map(|images| { 67 - format!( 68 - "{}, {} 2x, {} 3x", 69 - images[0].text, images[1].text, images[2].text 70 - ) 71 - }); 72 - let text_intro = if latest_track 73 - .attributes 74 - .as_ref() 75 - .is_some_and(|attr| attr.now_playing == "true") 76 - { 77 - "Now playing: " 78 - } else { 79 - "Last played: " 80 - }; 81 - let now_playing = format!("{} - {}", latest_track.name, latest_track.artist.text); 82 - 83 - ScrobblesTemplate { 84 - intro: text_intro, 85 - now_playing, 86 - image: latest_track.image.first().map(|image| image.text.clone()), 87 - srcset, 88 - } 89 - } 90 - }
-98
server/src/scrobble_monitor.rs
··· 1 - use std::{ 2 - sync::Arc, 3 - time::{Duration, Instant}, 4 - }; 5 - 6 - use anyhow::anyhow; 7 - use reqwest::Client; 8 - use tokio::sync::RwLock; 9 - 10 - use crate::scrobble::{Response, Scrobble, ScrobblesTemplate}; 11 - 12 - #[derive(Debug, Clone)] 13 - struct CachedScrobble { 14 - data: ScrobblesTemplate, 15 - fetch_time: Instant, 16 - } 17 - 18 - impl CachedScrobble { 19 - fn is_fresh(&self) -> bool { 20 - self.fetch_time.elapsed() < Duration::from_secs(30) 21 - } 22 - } 23 - 24 - #[derive(Debug, Clone)] 25 - pub struct ScrobbleMonitor { 26 - client: Client, 27 - api_key: String, 28 - last_scrobble: Arc<RwLock<Option<CachedScrobble>>>, 29 - } 30 - 31 - impl ScrobbleMonitor { 32 - pub fn new(api_key: String) -> Self { 33 - Self { 34 - client: Client::new(), 35 - api_key, 36 - last_scrobble: Arc::new(RwLock::new(None)), 37 - } 38 - } 39 - 40 - pub fn try_get_scrobble(&self) -> Option<ScrobblesTemplate> { 41 - let scrobble = &*self.last_scrobble.try_read().ok()?; 42 - scrobble 43 - .as_ref() 44 - .filter(|scrobble| scrobble.is_fresh()) 45 - .map(|scrobble| scrobble.data.clone()) 46 - } 47 - 48 - pub async fn get_scrobble(&self) -> anyhow::Result<ScrobblesTemplate> { 49 - if let Some(scrobble) = &*self.last_scrobble.read().await { 50 - if scrobble.is_fresh() { 51 - tracing::debug!("returning recently fetched scrobble data"); 52 - return Ok(scrobble.data.clone()); 53 - } 54 - } 55 - 56 - let mut last_scrobble = self.last_scrobble.write().await; 57 - match &*last_scrobble { 58 - // make sure another task hasn't fetched the new data first after we 59 - // both waited for write access 60 - Some(scrobble) if scrobble.is_fresh() => { 61 - tracing::debug!("returning (very) recently fetched scrobble data"); 62 - Ok(scrobble.data.clone()) 63 - } 64 - _ => { 65 - tracing::debug!("fetching new scrobble data"); 66 - let scrobble = self.fetch_scrobble().await?; 67 - let scrobble_partial = ScrobblesTemplate::new(&scrobble); 68 - *last_scrobble = Some(CachedScrobble { 69 - data: scrobble_partial.clone(), 70 - fetch_time: Instant::now(), 71 - }); 72 - Ok(scrobble_partial) 73 - } 74 - } 75 - } 76 - 77 - async fn fetch_scrobble(&self) -> anyhow::Result<Scrobble> { 78 - let response = self 79 - .client 80 - .get("https://ws.audioscrobbler.com/2.0") 81 - .query(&[ 82 - ("method", "user.getRecentTracks"), 83 - ("api_key", &self.api_key), 84 - ("user", "Doomboy95"), 85 - ("limit", "1"), 86 - ("format", "json"), 87 - ]) 88 - .send() 89 - .await?; 90 - let response: Response = response.json().await?; 91 - match response { 92 - Response::Scrobble(scrobble) => Ok(scrobble), 93 - Response::Error(err) => { 94 - Err(anyhow!("last.fm responded with an error: {}", err.message)) 95 - } 96 - } 97 - } 98 - }
+9 -97
server/templates/index.html
··· 1 1 <!doctype html> 2 2 <html> 3 - <head> 4 - <title>Ivo's Bio</title> 5 - <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 - <link rel="stylesheet" href="build/app.css" /> 7 - <link rel="me" href="https://hachyderm.io/@cherry" /> 8 - <script defer src="build/app.js" type="module"></script> 9 - </head> 10 - <body> 11 - <div class="header-container"> 12 - <canvas class="logo-gl" id="spinningCanvas">spinning 'Ivo' text</canvas> 13 - <h1 class="title" id="title">Heya, gang!</h1> 14 - </div> 15 - <h1>My projects</h1> 16 - <div class="projects"> 17 - <div class="project-container"> 18 - <div class="project-desc"> 19 - <h2 class="project-title">Age of Ark</h2> 20 - <p> 21 - A game developed in the summer of '14. Coded in Unity by me and Cale 22 - Tilford. Combined the ideas of action-y spaceship navigation with 23 - strategic base building. Happy with the overall aesthetic though it 24 - never got to a point where the gameplay design was 'good'. 25 - </p> 26 - </div> 27 - <img 28 - class="project-thumbnail" 29 - src="images/arkcity.png" 30 - alt="Blender render for Age of Ark's player ship" 31 - srcset=" 32 - images/arkcity.png, 33 - images/arkcity@2x.png 2x, 34 - images/arkcity@3x.png 3x 35 - " 36 - /> 37 - </div> 38 - <div class="project-container"> 39 - <div class="project-desc"> 40 - <h2 class="project-title">Placement at Feral</h2> 41 - <p> 42 - From 2015 - 2016, I interned at Feral Interactive, where I was able 43 - to help port a number of big-time AAA games to macOS and other 44 - platforms. 45 - </p> 46 - </div> 47 - <img 48 - class="project-thumbnail" 49 - src="images/cheetah.jpg" 50 - alt="Cheetah referencing Feral's branding" 51 - srcset=" 52 - images/cheetah.jpg, 53 - images/cheetah@2x.jpg 2x, 54 - images/cheetah@3x.jpg 3x 55 - " 56 - /> 57 - </div> 58 - <div class="project-container"> 59 - <div class="project-desc-right"> 60 - <h2 class="project-title">cherry.computer</h2> 61 - <p> 62 - My own website. Really informative about how web technology works 63 - after a life of just using systems languages and low-level hacking. 64 - Maybe you've seen it somewhere before... 65 - </p> 66 - </div> 67 - <img 68 - class="project-thumbnail" 69 - src="images/screen.png" 70 - alt="Infinitely recursive screenshots of this website" 71 - /> 72 - </div> 73 - <div class="project-container"> 74 - <div class="project-desc"> 75 - <h2 class="project-title">Misguided</h2> 76 - <p> 77 - Coming soon! An experimental exploration of anxiety and obsession 78 - within the cockpit of a space capsule controlled by interfacing with 79 - a simulated Apollo computer guidance system. 80 - </p> 81 - </div> 82 - <img 83 - class="project-thumbnail" 84 - src="images/AGC.jpg" 85 - alt="A physical guidance computer which would be simulated" 86 - /> 87 - </div> 88 - </div> 89 - <div 90 - class="scrobble-bar" 91 - hx-ext="sse" 92 - sse-connect="/scrobbles{% if scrobble.is_none() %}?immediate=true{% endif %}" 93 - sse-swap="scrobble" 94 - > 95 - {%- if let Some(ScrobblesTemplate {intro, now_playing, image, srcset}) = scrobble %} 96 - {% include "scrobble.html" %} 97 - {%- endif %} 98 - </div> 99 - </body> 3 + <head> 4 + <title>Ivo's Bio</title> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 + <link rel="stylesheet" href="build/app.css" /> 7 + <link rel="me" href="https://hachyderm.io/@cherry" /> 8 + <script defer src="build/app.js" type="module"></script> 9 + </head> 10 + <body class="bg-gray-900"> 11 + </body> 100 12 </html>
-14
server/templates/scrobble.html
··· 1 - <div class="bar-container"> 2 - {%- if let Some(image) = image %} 3 - <img 4 - class="bar-cover" 5 - src="{{ image }}" 6 - alt="Cover art" 7 - {%- if let Some(srcset) = srcset %} 8 - srcset="{{ srcset }}" 9 - {%- endif %} 10 - /> 11 - {%- endif %} 12 - <p class="bar-text-intro">{{ intro }}</p> 13 - <p class="bar-text-music">{{ now_playing }}</p> 14 - </div>