Compare commits

..

2 commits

Author SHA1 Message Date
Mihkel Martin Kasterpalu
7de8638796 Add projects page with enhanced images 2025-01-31 16:44:38 +02:00
Mihkel Martin Kasterpalu
06e09c804d Add shadcn component to .prettierignore 2025-01-31 16:44:07 +02:00
19 changed files with 712 additions and 25 deletions

View file

@ -2,3 +2,4 @@
package-lock.json
pnpm-lock.yaml
yarn.lock
src/lib/components/ui

View file

@ -16,13 +16,14 @@
"@eslint/compat": "^1.2.5",
"@eslint/js": "^9.18.0",
"@sveltejs/adapter-node": "^5.2.12",
"@sveltejs/enhanced-img": "^0.4.4",
"@sveltejs/kit": "^2.16.1",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@tailwindcss/container-queries": "^0.1.1",
"@tailwindcss/forms": "^0.5.10",
"@types/spotify-web-api-node": "^5.0.11",
"autoprefixer": "^10.4.20",
"bits-ui": "1.0.0-next.78",
"bits-ui": "1.0.0-next.80",
"clsx": "^2.1.1",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",

319
pnpm-lock.yaml generated
View file

@ -36,6 +36,9 @@ importers:
'@sveltejs/adapter-node':
specifier: ^5.2.12
version: 5.2.12(@sveltejs/kit@2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.1)(vite@6.0.11(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.1)(vite@6.0.11(jiti@1.21.7)(yaml@2.7.0)))
'@sveltejs/enhanced-img':
specifier: ^0.4.4
version: 0.4.4(rollup@4.31.0)(svelte@5.19.1)(vite@6.0.11(jiti@1.21.7)(yaml@2.7.0))
'@sveltejs/kit':
specifier: ^2.16.1
version: 2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.1)(vite@6.0.11(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.1)(vite@6.0.11(jiti@1.21.7)(yaml@2.7.0))
@ -55,8 +58,8 @@ importers:
specifier: ^10.4.20
version: 10.4.20(postcss@8.5.1)
bits-ui:
specifier: 1.0.0-next.78
version: 1.0.0-next.78(svelte@5.19.1)
specifier: 1.0.0-next.80
version: 1.0.0-next.80(svelte@5.19.1)
clsx:
specifier: ^2.1.1
version: 2.1.1
@ -125,6 +128,9 @@ packages:
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
'@emnapi/runtime@1.3.1':
resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==}
'@esbuild/aix-ppc64@0.24.2':
resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==}
engines: {node: '>=18'}
@ -353,6 +359,111 @@ packages:
resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==}
engines: {node: '>=18.18'}
'@img/sharp-darwin-arm64@0.33.5':
resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [darwin]
'@img/sharp-darwin-x64@0.33.5':
resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [darwin]
'@img/sharp-libvips-darwin-arm64@1.0.4':
resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==}
cpu: [arm64]
os: [darwin]
'@img/sharp-libvips-darwin-x64@1.0.4':
resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==}
cpu: [x64]
os: [darwin]
'@img/sharp-libvips-linux-arm64@1.0.4':
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
cpu: [arm64]
os: [linux]
'@img/sharp-libvips-linux-arm@1.0.5':
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
cpu: [arm]
os: [linux]
'@img/sharp-libvips-linux-s390x@1.0.4':
resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
cpu: [s390x]
os: [linux]
'@img/sharp-libvips-linux-x64@1.0.4':
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
cpu: [x64]
os: [linux]
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
cpu: [arm64]
os: [linux]
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
cpu: [x64]
os: [linux]
'@img/sharp-linux-arm64@0.33.5':
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
'@img/sharp-linux-arm@0.33.5':
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
'@img/sharp-linux-s390x@0.33.5':
resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
'@img/sharp-linux-x64@0.33.5':
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
'@img/sharp-linuxmusl-arm64@0.33.5':
resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
'@img/sharp-linuxmusl-x64@0.33.5':
resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
'@img/sharp-wasm32@0.33.5':
resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [wasm32]
'@img/sharp-win32-ia32@0.33.5':
resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ia32]
os: [win32]
'@img/sharp-win32-x64@0.33.5':
resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [win32]
'@internationalized/date@3.7.0':
resolution: {integrity: sha512-VJ5WS3fcVx0bejE/YHfbDKR/yawZgKqn/if+oEeLqNwBtPzVB06olkfcnojTmEMX+gTpH+FlQ69SHNitJ8/erQ==}
@ -537,6 +648,12 @@ packages:
peerDependencies:
'@sveltejs/kit': ^2.4.0
'@sveltejs/enhanced-img@0.4.4':
resolution: {integrity: sha512-BlBTGfbLUgHa+zSVrsGLOd+noCKWfipoOjoxE26bAAX97v7zh5eiCAp1KEdpkluL05Tl3+nR14gQdPsATyZqoA==}
peerDependencies:
svelte: ^5.0.0
vite: '>= 5.0.0'
'@sveltejs/kit@2.16.1':
resolution: {integrity: sha512-2pF5sgGJx9brYZ/9nNDYnh5KX0JguPF14dnvvtf/MqrvlWrDj/e7Rk3LBJPecFLLK1GRs6ZniD24gFPqZm/NFw==}
engines: {node: '>=18.13'}
@ -711,8 +828,8 @@ packages:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
bits-ui@1.0.0-next.78:
resolution: {integrity: sha512-jZjG2ObZ/CNyCNaXecpItC7hRXqJAgEfMhr06/eNrf3wHiiPyhdcy4OkzLcJyxeOrDyj+xma8cZTd3JRWqJdAw==}
bits-ui@1.0.0-next.80:
resolution: {integrity: sha512-b6r5QgUwi/feFuZZfvTI+HuSCT1/5NoFTWcuqGTTSMjBAtsnS4NI34dzOpB7enKWR5Cb/UxuGhzS/lXUgoTV8Q==}
engines: {node: '>=18', pnpm: '>=8.7.0'}
peerDependencies:
svelte: ^5.11.0
@ -774,6 +891,13 @@ packages:
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
color-string@1.9.1:
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
color@4.2.3:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
@ -827,6 +951,10 @@ packages:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
detect-libc@2.0.3:
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
engines: {node: '>=8'}
devalue@5.1.1:
resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==}
@ -1078,6 +1206,10 @@ packages:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
imagetools-core@7.0.2:
resolution: {integrity: sha512-nrLdKLJHHXd8MitwlXK6/h1TSwGaH3X1DZ3z6yMv/tX7dJ12ecLxZ6P5jgKetfIFh8IJwH9fCWMoTA8ixg0VVA==}
engines: {node: '>=18.0.0'}
import-fresh@3.3.0:
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
engines: {node: '>=6'}
@ -1095,6 +1227,9 @@ packages:
inline-style-parser@0.2.4:
resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==}
is-arrayish@0.3.2:
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
@ -1536,8 +1671,8 @@ packages:
peerDependencies:
svelte: ^5.7.0
runed@0.22.0:
resolution: {integrity: sha512-ZWVXWhOr0P5xdNgtviz6D1ivLUDWKLCbeC5SUEJ3zBkqLReVqWHenFxMNFeFaiC5bfxhFxyxzyzB+98uYFtwdA==}
runed@0.23.1:
resolution: {integrity: sha512-h1ZDmin0LBoSMEZxvHOJbCWsUBz4099cjI+/rQ4FZystgOq294s5Rh+OEeu9HIObc8XQQEya23eAhJeal0VBuA==}
peerDependencies:
svelte: ^5.7.0
@ -1556,6 +1691,10 @@ packages:
set-cookie-parser@2.7.1:
resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
sharp@0.33.5:
resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@ -1584,6 +1723,9 @@ packages:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
simple-swizzle@0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
sirv@3.0.0:
resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==}
engines: {node: '>=18'}
@ -1667,6 +1809,11 @@ packages:
'@sveltejs/kit': ^1.0.0 || ^2.0.0
svelte: ^5.1.13
svelte-parse-markup@0.1.5:
resolution: {integrity: sha512-T6mqZrySltPCDwfKXWQ6zehipVLk4GWfH1zCMGgRtLlOIFPuw58ZxVYxVvotMJgJaurKi1i14viB2GIRKXeJTQ==}
peerDependencies:
svelte: ^3.0.0 || ^4.0.0 || ^5.0.0-next.1
svelte-toolbelt@0.7.0:
resolution: {integrity: sha512-i/Tv4NwAWWqJnK5H0F8y/ubDnogDYlwwyzKhrspTUFzrFuGnYshqd2g4/R43ds841wmaFiSW/HsdsdWhPOlrAA==}
engines: {node: '>=18', pnpm: '>=8.7.0'}
@ -1754,6 +1901,10 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
vite-imagetools@7.0.5:
resolution: {integrity: sha512-OOvVnaBTqJJ2J7X1cM1qpH4pj9jsfTxia1VSuWeyXtf+OnP8d0YI1LHpv8y2NT47wg+n7XiTgh3BvcSffuBWrw==}
engines: {node: '>=18.0.0'}
vite@6.0.11:
resolution: {integrity: sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@ -1844,6 +1995,11 @@ snapshots:
'@jridgewell/gen-mapping': 0.3.8
'@jridgewell/trace-mapping': 0.3.25
'@emnapi/runtime@1.3.1':
dependencies:
tslib: 2.8.1
optional: true
'@esbuild/aix-ppc64@0.24.2':
optional: true
@ -1993,6 +2149,81 @@ snapshots:
'@humanwhocodes/retry@0.4.1': {}
'@img/sharp-darwin-arm64@0.33.5':
optionalDependencies:
'@img/sharp-libvips-darwin-arm64': 1.0.4
optional: true
'@img/sharp-darwin-x64@0.33.5':
optionalDependencies:
'@img/sharp-libvips-darwin-x64': 1.0.4
optional: true
'@img/sharp-libvips-darwin-arm64@1.0.4':
optional: true
'@img/sharp-libvips-darwin-x64@1.0.4':
optional: true
'@img/sharp-libvips-linux-arm64@1.0.4':
optional: true
'@img/sharp-libvips-linux-arm@1.0.5':
optional: true
'@img/sharp-libvips-linux-s390x@1.0.4':
optional: true
'@img/sharp-libvips-linux-x64@1.0.4':
optional: true
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
optional: true
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
optional: true
'@img/sharp-linux-arm64@0.33.5':
optionalDependencies:
'@img/sharp-libvips-linux-arm64': 1.0.4
optional: true
'@img/sharp-linux-arm@0.33.5':
optionalDependencies:
'@img/sharp-libvips-linux-arm': 1.0.5
optional: true
'@img/sharp-linux-s390x@0.33.5':
optionalDependencies:
'@img/sharp-libvips-linux-s390x': 1.0.4
optional: true
'@img/sharp-linux-x64@0.33.5':
optionalDependencies:
'@img/sharp-libvips-linux-x64': 1.0.4
optional: true
'@img/sharp-linuxmusl-arm64@0.33.5':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-arm64': 1.0.4
optional: true
'@img/sharp-linuxmusl-x64@0.33.5':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.0.4
optional: true
'@img/sharp-wasm32@0.33.5':
dependencies:
'@emnapi/runtime': 1.3.1
optional: true
'@img/sharp-win32-ia32@0.33.5':
optional: true
'@img/sharp-win32-x64@0.33.5':
optional: true
'@internationalized/date@3.7.0':
dependencies:
'@swc/helpers': 0.5.15
@ -2143,6 +2374,18 @@ snapshots:
'@sveltejs/kit': 2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.1)(vite@6.0.11(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.1)(vite@6.0.11(jiti@1.21.7)(yaml@2.7.0))
rollup: 4.31.0
'@sveltejs/enhanced-img@0.4.4(rollup@4.31.0)(svelte@5.19.1)(vite@6.0.11(jiti@1.21.7)(yaml@2.7.0))':
dependencies:
magic-string: 0.30.17
sharp: 0.33.5
svelte: 5.19.1
svelte-parse-markup: 0.1.5(svelte@5.19.1)
vite: 6.0.11(jiti@1.21.7)(yaml@2.7.0)
vite-imagetools: 7.0.5(rollup@4.31.0)
zimmerframe: 1.1.2
transitivePeerDependencies:
- rollup
'@sveltejs/kit@2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.1)(vite@6.0.11(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.1)(vite@6.0.11(jiti@1.21.7)(yaml@2.7.0))':
dependencies:
'@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.19.1)(vite@6.0.11(jiti@1.21.7)(yaml@2.7.0))
@ -2344,13 +2587,13 @@ snapshots:
binary-extensions@2.3.0: {}
bits-ui@1.0.0-next.78(svelte@5.19.1):
bits-ui@1.0.0-next.80(svelte@5.19.1):
dependencies:
'@floating-ui/core': 1.6.9
'@floating-ui/dom': 1.6.13
'@internationalized/date': 3.7.0
esm-env: 1.2.2
runed: 0.22.0(svelte@5.19.1)
runed: 0.23.1(svelte@5.19.1)
svelte: 5.19.1
svelte-toolbelt: 0.7.0(svelte@5.19.1)
@ -2419,6 +2662,16 @@ snapshots:
color-name@1.1.4: {}
color-string@1.9.1:
dependencies:
color-name: 1.1.4
simple-swizzle: 0.2.2
color@4.2.3:
dependencies:
color-convert: 2.0.1
color-string: 1.9.1
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
@ -2453,6 +2706,8 @@ snapshots:
delayed-stream@1.0.0: {}
detect-libc@2.0.3: {}
devalue@5.1.1: {}
didyoumean@1.2.2: {}
@ -2745,6 +3000,8 @@ snapshots:
ignore@5.3.2: {}
imagetools-core@7.0.2: {}
import-fresh@3.3.0:
dependencies:
parent-module: 1.0.1
@ -2758,6 +3015,8 @@ snapshots:
inline-style-parser@0.2.4: {}
is-arrayish@0.3.2: {}
is-binary-path@2.1.0:
dependencies:
binary-extensions: 2.3.0
@ -3087,7 +3346,7 @@ snapshots:
esm-env: 1.2.2
svelte: 5.19.1
runed@0.22.0(svelte@5.19.1):
runed@0.23.1(svelte@5.19.1):
dependencies:
esm-env: 1.2.2
svelte: 5.19.1
@ -3102,6 +3361,32 @@ snapshots:
set-cookie-parser@2.7.1: {}
sharp@0.33.5:
dependencies:
color: 4.2.3
detect-libc: 2.0.3
semver: 7.6.3
optionalDependencies:
'@img/sharp-darwin-arm64': 0.33.5
'@img/sharp-darwin-x64': 0.33.5
'@img/sharp-libvips-darwin-arm64': 1.0.4
'@img/sharp-libvips-darwin-x64': 1.0.4
'@img/sharp-libvips-linux-arm': 1.0.5
'@img/sharp-libvips-linux-arm64': 1.0.4
'@img/sharp-libvips-linux-s390x': 1.0.4
'@img/sharp-libvips-linux-x64': 1.0.4
'@img/sharp-libvips-linuxmusl-arm64': 1.0.4
'@img/sharp-libvips-linuxmusl-x64': 1.0.4
'@img/sharp-linux-arm': 0.33.5
'@img/sharp-linux-arm64': 0.33.5
'@img/sharp-linux-s390x': 0.33.5
'@img/sharp-linux-x64': 0.33.5
'@img/sharp-linuxmusl-arm64': 0.33.5
'@img/sharp-linuxmusl-x64': 0.33.5
'@img/sharp-wasm32': 0.33.5
'@img/sharp-win32-ia32': 0.33.5
'@img/sharp-win32-x64': 0.33.5
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
@ -3138,6 +3423,10 @@ snapshots:
signal-exit@4.1.0: {}
simple-swizzle@0.2.2:
dependencies:
is-arrayish: 0.3.2
sirv@3.0.0:
dependencies:
'@polka/url': 1.0.0-next.28
@ -3246,6 +3535,10 @@ snapshots:
'@sveltejs/kit': 2.16.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.1)(vite@6.0.11(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.1)(vite@6.0.11(jiti@1.21.7)(yaml@2.7.0))
svelte: 5.19.1
svelte-parse-markup@0.1.5(svelte@5.19.1):
dependencies:
svelte: 5.19.1
svelte-toolbelt@0.7.0(svelte@5.19.1):
dependencies:
clsx: 2.1.1
@ -3360,6 +3653,14 @@ snapshots:
util-deprecate@1.0.2: {}
vite-imagetools@7.0.5(rollup@4.31.0):
dependencies:
'@rollup/pluginutils': 5.1.4(rollup@4.31.0)
imagetools-core: 7.0.2
sharp: 0.33.5
transitivePeerDependencies:
- rollup
vite@6.0.11(jiti@1.21.7)(yaml@2.7.0):
dependencies:
esbuild: 0.24.2

BIN
src/lib/assets/dysaster.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
src/lib/assets/saueaugu.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 773 KiB

BIN
src/lib/assets/skp.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

View file

@ -0,0 +1,87 @@
<script lang="ts">
import { mode } from 'mode-watcher';
import { Badge, type BadgeVariant } from '$lib/components/ui/badge/index.js';
import * as HoverCard from '$lib/components/ui/hover-card/index.js';
import Instagram from 'lucide-svelte/icons/instagram';
import Facebook from 'lucide-svelte/icons/facebook';
import Globe from 'lucide-svelte/icons/globe';
import Link from 'lucide-svelte/icons/link';
import type { EnhancedImage, ImageCreditType, Tag } from '$lib/types';
let {
image,
tags = [],
class: className
}: { image: EnhancedImage; tags: Tag[]; class: string } = $props();
let badgeVariant: BadgeVariant = $derived($mode == 'dark' ? 'secondary' : 'default');
</script>
{#snippet creditText(author: string, type: ImageCreditType, className: string)}
{#if type === 'instagram'}
<Instagram class={className} />
{:else if type === 'facebook'}
<Facebook class={className} />
{:else if type === 'web'}
<Globe class={className} />
{:else}
<Link class={className} />
{/if}
<span>
{author}
</span>
{/snippet}
<div
class="projectCardImage grid justify-items-center overflow-hidden rounded-md bg-primary shadow-lg"
>
{#if typeof image.src === 'string'}
<img src={image.src} alt={image.alt} class={className} />
{:else}
<enhanced:img src={image.src} alt={image.alt} class={className} />
{/if}
{#if tags}
<div class="space-x-4 self-start p-4">
{#each tags as { name, description }}
<HoverCard.Root>
<HoverCard.Trigger>
<Badge class="transition-none" variant={badgeVariant}>{name}</Badge>
</HoverCard.Trigger>
<HoverCard.Content>
{description}
</HoverCard.Content>
</HoverCard.Root>
{/each}
</div>
{/if}
{#if image.credit}
<div class="w-full self-end bg-black/50 text-center shadow-lg backdrop-blur-md">
<p
class="flex items-center justify-center px-4 py-2 font-mono text-xs font-semibold text-secondary/80 dark:text-primary/80"
>
{#if image.credit.href}
<a class="w-full transition-colors hover:text-white" href={image.credit.href}>
{@render creditText(
image.credit.author,
image.credit.type,
'w-4 inline align-[-0.65em]'
)}
</a>
{:else}
{@render creditText(image.credit.author, image.credit.type, 'w-4 inline align-[-0.65em]')}
{/if}
</p>
</div>
{/if}
</div>
<style>
.projectCardImage > * {
grid-area: 1/1/2/2;
}
</style>

View file

@ -0,0 +1,48 @@
<script lang="ts" module>
import { type VariantProps, tv } from 'tailwind-variants';
export const badgeVariants = tv({
base: 'focus:ring-ring inline-flex select-none items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2',
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/80 border-transparent shadow',
secondary:
'bg-secondary text-secondary-foreground hover:bg-secondary/80 border-transparent',
destructive:
'bg-destructive text-destructive-foreground hover:bg-destructive/80 border-transparent shadow',
outline: 'text-foreground'
}
},
defaultVariants: {
variant: 'default'
}
});
export type BadgeVariant = VariantProps<typeof badgeVariants>['variant'];
</script>
<script lang="ts">
import type { WithElementRef } from 'bits-ui';
import type { HTMLAnchorAttributes } from 'svelte/elements';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
href,
class: className,
variant = 'default',
children,
...restProps
}: WithElementRef<HTMLAnchorAttributes> & {
variant?: BadgeVariant;
} = $props();
</script>
<svelte:element
this={href ? 'a' : 'span'}
bind:this={ref}
{href}
class={cn(badgeVariants({ variant }), className)}
{...restProps}
>
{@render children?.()}
</svelte:element>

View file

@ -0,0 +1,2 @@
export { default as Badge } from "./badge.svelte";
export { badgeVariants, type BadgeVariant } from "./badge.svelte";

View file

@ -0,0 +1,28 @@
<script lang="ts">
import { LinkPreview as HoverCardPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
align = "center",
sideOffset = 4,
portalProps,
...restProps
}: HoverCardPrimitive.ContentProps & {
portalProps?: HoverCardPrimitive.PortalProps;
} = $props();
</script>
<HoverCardPrimitive.Portal {...portalProps}>
<HoverCardPrimitive.Content
bind:ref
{sideOffset}
{align}
class={cn(
"bg-popover text-popover-foreground z-50 w-64 rounded-md border p-4 shadow-md outline-none",
className
)}
{...restProps}
/>
</HoverCardPrimitive.Portal>

View file

@ -0,0 +1,14 @@
import { LinkPreview as HoverCardPrimitive } from "bits-ui";
import Content from "./hover-card-content.svelte";
const Root = HoverCardPrimitive.Root;
const Trigger = HoverCardPrimitive.Trigger;
export {
Root,
Content,
Trigger,
Root as HoverCard,
Content as HoverCardContent,
Trigger as HoverCardTrigger,
};

View file

@ -1,4 +1,4 @@
import type { GamesObj } from './types';
import type { GamesObj, TagsObj } from './types';
export const site = {
name: 'Stuff.Kasterpalu',
@ -26,3 +26,9 @@ export const games: GamesObj = {
description: ''
}
};
export const badges: TagsObj = {
muusika: { name: 'Muusika', description: 'Tegelen siin muusikaga' },
veeb: { name: 'Veebileht', description: 'Disainisin/kirjutasin veebilehe' },
asutaja: { name: 'Asutaja', description: 'Olin osa selle loomisest' }
};

View file

@ -1,3 +1,23 @@
import type { Picture } from 'vite-imagetools';
export type EnhancedImage = {
src: string | Picture;
alt: string;
credit?: ImageCredit;
};
export type ImageCredit = {
author: string;
type?: ImageCreditType;
href?: string;
};
export enum ImageCreditType {
instagram = 'instagram',
facebook = 'facebook',
web = 'web'
}
export type AlbumData = {
names: AlbumDataField[];
artists: AlbumDataField[];
@ -22,14 +42,6 @@ export type Player = {
playing: boolean;
};
export type Game = {
name: string;
image: string;
description: string;
};
export type GamesObj = Record<string, Game>;
export interface TimeRemaining {
years: number;
months: number;
@ -38,3 +50,27 @@ export interface TimeRemaining {
minutes: number;
seconds: number;
}
export type Game = {
name: string;
image: string;
description: string;
};
// the key is the link href for the game
export type GamesObj = Record<string, Game>;
export type Project = {
name: string;
description: string;
link: string;
image: EnhancedImage;
tags: Tag[];
};
export type Tag = {
name: string;
description: string;
};
export type TagsObj = Record<string, Tag>;

View file

@ -53,7 +53,7 @@
</Button>
</header>
<div class="container flex flex-col items-center">
<div class="container relative flex flex-col items-center">
{@render children()}
</div>

View file

@ -1,5 +1,5 @@
<script lang="ts">
import { games, site, baseURL } from '$lib/config';
import { site, baseURL, games } from '$lib/config';
</script>
<svelte:head>
@ -14,9 +14,18 @@
<header class="mb-24 flex flex-col items-center text-center font-title">
<h1 class="mb-1 scroll-m-20 text-5xl font-extrabold tracking-tight lg:text-6xl">
stuff.kasterpalu.ee
Hei! Mina olen Mihkel
</h1>
<p class="text-xl font-semibold text-muted-foreground">Minimängud ja muud huvitavat</p>
<p class="text-xl font-semibold text-muted-foreground">
Siin saidil on mu loodud minimängud ja muud huvitavat. Aitäh <a
href="https://neal.fun"
class="font-medium underline underline-offset-4">neal.fun</a
> inspo eest :]
</p>
<p class="text-xl font-semibold text-muted-foreground">
Vaata ka mu
<a href="/projektid" class="text-primary underline underline-offset-4">teisi projekte</a>.
</p>
</header>
<main class="grid w-full max-w-4xl justify-items-center gap-y-8 lg:grid-cols-2">
{#each Object.entries(games) as [href, { image, name }]}

View file

@ -0,0 +1,139 @@
<script lang="ts">
import type { EnhancedImage, Project, Tag } from '$lib/types';
import { site, baseURL, badges } from '$lib/config';
import SquareArrowOutUpRight from 'lucide-svelte/icons/square-arrow-out-up-right';
import { Button } from '$lib/components/ui/button/index.js';
import Image from '$lib/components/Image.svelte';
import skpImg from '$lib/assets/skp.jpg?enhanced';
import dysasterImg from '$lib/assets/dysaster.jpg?enhanced';
import monospaceeImg from '$lib/assets/monospacee.jpg?enhanced';
import saueauguImg from '$lib/assets/saueaugu.jpg?enhanced';
export const projects: Project[] = [
{
name: 'SUPIKÖÖGIPOSID',
image: {
src: skpImg,
credit: {
type: 'instagram',
author: 'Mimmu',
href: 'https://www.instagram.com/musamimmu/'
},
alt: 'Pilt neljaliikmeliseslt räpibändist SUPIKÖÖGIPOISID. Pilt on tehtud pilves ilmaga õues. Pildil on 4 mees valgete triiksärkide, mustade lipsude ja mustade tagidega.'
},
description: 'Räpikollektiiv. Alustasime 2019.',
link: 'https://skpoisid.bandcamp.com/',
tags: [badges['muusika']]
},
{
name: 'Dysaster Collective',
image: {
src: dysasterImg,
credit: {
type: 'instagram',
author: 'Mattias Mägi',
href: 'https://www.instagram.com/mattias.mix/'
},
alt: 'Sinakas digitaalne kollaž Dysaster Collective liigetest.'
},
description: '2022 asutatud mitmekülgne loomekollektiiv.',
link: 'https://dysaster.ee',
tags: [badges['asutaja'], badges['veeb']]
},
{
name: 'monospac.ee',
image: {
src: monospaceeImg,
credit: {
type: 'instagram',
author: 'Liisa Jõhvik',
href: 'https://www.instagram.com/liisajohvik.photo/'
},
alt: 'DJ duo monospacee nende Silent Disco setil Tartu Uus Teater saalis aastal 2024. Pilt on külje pealt, esiplaanil DJ Mimmu ja ta tagant paistab DJ Rx. Nad on valgustatud punasesega, taustal häguselt näha siniselt valgustatud kolmandat DJd.'
},
description: 'DJ duo kaasliikmega Rx.',
link: 'https://monospac.ee',
tags: [badges['muusika'], badges['veeb']]
},
{
name: 'Saueagu Teatritalu',
image: {
src: saueauguImg,
alt: 'Suvine Saueaugu teatrihoone. All paistab roheline muru ja katuse tagant paistab paari pilvega sinine taevas.'
},
description: 'Taluteater Läänemaal. Tegin veebilehe.',
link: 'https://saueaugu.ee',
tags: [badges['veeb']]
},
{
name: 'Tartu Häkkerikoda',
image: {
src: '/assets/hakkerikoda.svg',
credit: {
type: 'web',
author: 'treierxyz',
href: 'https://treier.xyz'
},
alt: 'Tartu Häkkerikoda logo'
},
description: 'Makerspace Tartus. Liige ning osa veebilehe loojatest.',
link: 'https://hakkerikoda.ee',
tags: [badges['veeb']]
}
];
</script>
{#snippet projectCard(
name: string,
description: string,
image: EnhancedImage,
tags: Tag[],
link: string
)}
<div class="mb-16 w-80 space-y-3 md:w-64">
<Image {image} {tags} class="aspect-[4/5] object-cover" />
<div class="grid grid-cols-[1fr_auto] items-center text-sm">
<div class="mt-2 pr-4">
<h3 class="font-medium leading-none">{name}</h3>
<p class="mt-2 text-xs leading-5 text-muted-foreground">{description}</p>
</div>
<Button target="_blank" href={link} variant="secondary" size="icon">
<SquareArrowOutUpRight />
</Button>
</div>
</div>
{/snippet}
<svelte:head>
<title>Projektid | {site.name}</title>
<meta property="og:title" content={'Projektid | ' + site.name} />
<meta
name="description"
content="Projektid millega tegelenud, asjad mida teinud - tule uudista."
/>
<meta
property="og:description"
content="Projektid millega tegelenud, asjad mida teinud - tule uudista."
/>
<meta property="og:image" content={baseURL + site.image} />
</svelte:head>
<header class="mb-24 flex flex-col items-center text-center font-title">
<h1 class="mb-1 scroll-m-20 text-5xl font-extrabold tracking-tight lg:text-6xl">
Muud projektid
</h1>
<p class="text-xl font-semibold text-muted-foreground">
Peale selle toreda saidi on mul veel palju hobisid
</p>
</header>
<main class="flex w-full max-w-4xl flex-wrap justify-center gap-x-8">
{#each projects as { name, description, image, tags, link }}
{@render projectCard(name, description, image, tags, link)}
{/each}
</main>

View file

@ -0,0 +1,14 @@
<svg width="346.78" height="346.78" viewBox="0 0 91.752 91.752" xml:space="preserve"
xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath clipPathUnits="userSpaceOnUse" id="a">
<path style="fill:#fff;fill-opacity:1;stroke:none;stroke-width:3.65559" class="powerclip" d="M88.155 24.485h47.08v38.311h-47.08zm23.94 10.547a1.921 1.862 0 0 0-1.92-1.862 1.921 1.862 0 0 0-1.922 1.862 1.921 1.862 0 0 0 1.921 1.863 1.921 1.862 0 0 0 1.921-1.863z"/>
</clipPath>
</defs>
<path style="font-variation-settings:'wght' 400;fill:#fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:.953998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" d="M93.155 35.764s.411-.483 3.384-2.885c10.366-8.375 20.53 1.35 21.315 1.642.785.29 5.138 1.673 7.97 3.026 3.141 1.499 4.176 3.065 4.412 4.11-5.624.32-11.536.193-13.252.615-5.289 1.302-8.731 4.666-10.785 6.854-2.053 2.189-7.124 8.67-7.124 8.67-5.28-7.017-5.92-13.823-5.92-22.032z" clip-path="url(#a)" transform="matrix(2.163 0 0 2.163 -195.72 -54.569)"/>
<path style="font-variation-settings:'wght' 679,'YOPQ' 276;fill:#fff;fill-opacity:1;fill-rule:evenodd;stroke-width:1.95121;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill" d="M121.886 44.093a.825.825 0 0 0-1.022.569l-3.447 12.088a.826.826 0 0 0 .568 1.022l.535.152c-.003.007-.006.014-.007.02l-.999 3.502a.825.825 0 0 0 .568 1.022l3.123.89a.825.825 0 0 0 1.022-.568l.999-3.501c.003-.007.002-.014.006-.021l.534.152a.825.825 0 0 0 1.022-.568l3.448-12.088a.825.825 0 0 0-.569-1.023zm2.417 2.484a1.003 1.003 0 1 1-.55 1.93 1.003 1.003 0 0 1 .55-1.93zm-5.327 12.287.705.201-.844 2.96-.705-.202zm2.666.76.705.202-.844 2.959-.704-.201zm-1.816-.37.705.201-.766 2.686-.705-.201zm.889.254.704.2-.766 2.686-.704-.2z" transform="matrix(2.163 0 0 2.163 -195.72 -54.569)"/>
<path style="font-variation-settings:'wght' 400;fill:none;fill-rule:evenodd;stroke:#fff;stroke-width:1.058;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" d="M124.614 38.947s-3.263 7.383-.306 8.011c2.957.628 2.17-8.088 2.17-8.088" transform="matrix(2.163 0 0 2.163 -195.72 -54.569)"/>
<path style="fill:none;stroke-width:.264583" d="M20.79-4.773h91.752v91.752H20.79z" transform="translate(-20.79 4.773)"/>
<style>@media (prefers-color-scheme: light) { :root { filter: none; } }
@media (prefers-color-scheme: dark) { :root { filter: invert(100%); } }</style>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -1,6 +1,7 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { enhancedImages } from '@sveltejs/enhanced-img';
import { sveltekit } from '@sveltejs/kit/vite';
export default defineConfig({
plugins: [sveltekit()]
plugins: [enhancedImages(), sveltekit()]
});