mirror of
https://github.com/hyprwm/hyprland-website.git
synced 2026-05-08 00:58:20 +02:00
Merge 67f388ff75 into 5ba1043b05
This commit is contained in:
commit
970fa6c160
67 changed files with 2610 additions and 1664 deletions
|
|
@ -1,6 +1,10 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
extends: ['eslint:recommended', 'plugin:svelte/recommended', 'prettier'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:svelte/recommended',
|
||||
'prettier'
|
||||
],
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 'latest',
|
||||
|
|
|
|||
5
.vscode/extensions.json
vendored
5
.vscode/extensions.json
vendored
|
|
@ -1,3 +1,6 @@
|
|||
{
|
||||
"recommendations": ["bradlc.vscode-tailwindcss", "svelte.svelte-vscode"]
|
||||
"recommendations": [
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"svelte.svelte-vscode"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
20
package.json
20
package.json
|
|
@ -3,6 +3,7 @@
|
|||
"version": "1.1.0",
|
||||
"description": "Website for Hyprland - Hyprland - A wayland compositor with the looks.",
|
||||
"repository": "github:hyprwm/hyprland-website",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
|
|
@ -13,7 +14,7 @@
|
|||
"keywords": [
|
||||
"hyprland"
|
||||
],
|
||||
"author": "",
|
||||
"author": "VDawg",
|
||||
"license": "BSD-3-Clause",
|
||||
"private": "true",
|
||||
"os": [
|
||||
|
|
@ -27,26 +28,25 @@
|
|||
"@iconify/json": "^2.2.220",
|
||||
"@interactjs/types": "^1.10.27",
|
||||
"@sveltejs/adapter-static": "^3.0.2",
|
||||
"@sveltejs/kit": "^2.5.17",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
||||
"@sveltejs/kit": "^2.39.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^4.0.4",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"eslint": "^9.5.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-svelte": "^2.40.0",
|
||||
"eslint-plugin-svelte": "^2.46.1",
|
||||
"mdsvex": "^0.11.2",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.3.2",
|
||||
"prettier-plugin-svelte": "^3.2.4",
|
||||
"prettier-plugin-svelte": "^3.4.0",
|
||||
"simplex-noise": "^4.0.1",
|
||||
"svelte": "^4.2.18",
|
||||
"svelte-check": "^3.8.1",
|
||||
"svelte": "^5.38.10",
|
||||
"svelte-check": "^4.3.1",
|
||||
"tailwindcss": "^3.4.4",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"typescript": "^5.4.5",
|
||||
"typescript": "^5.9.2",
|
||||
"unplugin-icons": "^0.19.0",
|
||||
"vite": "^5.3.1"
|
||||
"vite": "^5.4.20"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@fontsource-variable/inter": "^5.2.5",
|
||||
"@fontsource/ibm-plex-mono": "^5.0.13",
|
||||
|
|
|
|||
906
pnpm-lock.yaml
generated
906
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -3,7 +3,7 @@ module.exports = {
|
|||
singleQuote: true,
|
||||
semi: false,
|
||||
trailingComma: 'none',
|
||||
printWidth: 100,
|
||||
printWidth: 70,
|
||||
plugins: ['prettier-plugin-svelte', 'prettier-plugin-tailwindcss'],
|
||||
tailwindFunctions: ['clsx']
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,10 @@ import { spawnSync } from 'node:child_process'
|
|||
// This script should be run from the root of the application
|
||||
const root = new URL('..', import.meta.url)
|
||||
|
||||
const imageDirectories = ['static/ricing_competitions', 'static/plugins-data/logos']
|
||||
const imageDirectories = [
|
||||
'static/ricing_competitions',
|
||||
'static/plugins-data/logos'
|
||||
]
|
||||
const generatedPrefix = 'generated_'
|
||||
// This value seems to work well
|
||||
const maxBrightness = 65535
|
||||
|
|
@ -23,12 +26,16 @@ const filePaths = await globby(imageDirectories, {
|
|||
}).then((filePaths) =>
|
||||
filePaths
|
||||
.filter((filePath) => {
|
||||
const isGenerated = getFileNameWithoutExtension(filePath).startsWith(generatedPrefix)
|
||||
const isGenerated =
|
||||
getFileNameWithoutExtension(filePath).startsWith(
|
||||
generatedPrefix
|
||||
)
|
||||
|
||||
if (isGenerated) return false
|
||||
|
||||
const fileName = getFileNameWithoutExtension(filePath)
|
||||
const fileDirectory = filePath.split('/').slice(0, -2).join('/') + '/'
|
||||
const fileDirectory =
|
||||
filePath.split('/').slice(0, -2).join('/') + '/'
|
||||
const generatedFilePath = `${fileDirectory}${generatedPrefix}${fileName}.webp`
|
||||
|
||||
const isAlreadyBlurred = filePaths.includes(generatedFilePath)
|
||||
|
|
@ -41,19 +48,29 @@ const filePaths = await globby(imageDirectories, {
|
|||
for (const filePathUrl of filePaths) {
|
||||
const extension = filePathUrl.pathname.split('.').at(-1)
|
||||
const generatedFileName =
|
||||
generatedPrefix + getFileNameWithoutExtension(filePathUrl.href) + '.webp'
|
||||
const outPath = decodeURIComponent(new URL('.', filePathUrl).pathname + generatedFileName)
|
||||
generatedPrefix +
|
||||
getFileNameWithoutExtension(filePathUrl.href) +
|
||||
'.webp'
|
||||
const outPath = decodeURIComponent(
|
||||
new URL('.', filePathUrl).pathname + generatedFileName
|
||||
)
|
||||
const filePath = decodeURIComponent(filePathUrl.pathname)
|
||||
|
||||
const currentBrightness = Number(
|
||||
exec(`convert "${filePath}" -colorspace Gray -format "%[mean]" info: `)
|
||||
exec(
|
||||
`convert "${filePath}" -colorspace Gray -format "%[mean]" info: `
|
||||
)
|
||||
)
|
||||
|
||||
// Boost the brightness if the image is very dark
|
||||
const brightnessIncrease = Math.max(1 - (currentBrightness / brightnessThreshold) * 50, 0)
|
||||
const brightnessIncrease = Math.max(
|
||||
1 - (currentBrightness / brightnessThreshold) * 50,
|
||||
0
|
||||
)
|
||||
|
||||
// Nessecary to do it like that for Zx
|
||||
const svgCommands = extension === 'svg' ? '-background none -resize 2500x2500' : ''
|
||||
const svgCommands =
|
||||
extension === 'svg' ? '-background none -resize 2500x2500' : ''
|
||||
|
||||
exec(
|
||||
`magick convert ${svgCommands} -brightness-contrast ${brightnessIncrease}x40 -modulate 100,1000,100 "${filePath}" "${outPath}"`
|
||||
|
|
@ -81,7 +98,8 @@ function exec(command) {
|
|||
|
||||
const stdError = stderr.toString().trim()
|
||||
|
||||
if (stdError && !stdError.includes('WARNING')) throw new Error(stdError)
|
||||
if (stdError && !stdError.includes('WARNING'))
|
||||
throw new Error(stdError)
|
||||
|
||||
return stdout
|
||||
} catch (error) {
|
||||
|
|
|
|||
23
src/app.html
23
src/app.html
|
|
@ -1,15 +1,30 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.ico" />
|
||||
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
|
||||
<meta property="og:image" content="/imgs/og-img.png" />
|
||||
<link rel="alternate" type="application/rss+xml" title="Hyprland News" href="/rss.xml" />
|
||||
|
||||
<link
|
||||
rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title="Hyprland News"
|
||||
href="/rss.xml"
|
||||
/>
|
||||
|
||||
<style></style>
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
%sveltekit.head% </head
|
||||
>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ If you want to support the development for 5€ + tax a month, and also get a fe
|
|||
please check out [the pricing page](https://account.hypr.land/pricing)
|
||||
|
||||
You will get:
|
||||
|
||||
- Member-only forum access, with dev Q&A, support from me, and more
|
||||
- Premium desktop experience, which is a set of preconfigured dotfiles with a one-click install and update
|
||||
- And of course, support the continued development.
|
||||
|
|
@ -28,4 +29,4 @@ where you can find answers, ask questions, and interact with the community.
|
|||
Also, thank you for all the support, guys. You are awesome!
|
||||
|
||||
Cheers,
|
||||
vax.
|
||||
vax.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
title: Hyprland 0.46.0 is upon us!
|
||||
title: Hyprland 0.46.0 is upon us!
|
||||
date: 1734392927
|
||||
author:
|
||||
name: Vaxry
|
||||
|
|
@ -22,26 +22,28 @@ Some stuff has changed:
|
|||
Window/layer rule regexes now require a full match (not any match) to trigger.
|
||||
|
||||
For example, in the case of `jeremy`:
|
||||
- `jeremy`: OK
|
||||
- `^(jeremy)$`: OK
|
||||
- `jer`: Used to match, now won't. You'll need to do `.*jer.*` to make it act like before. (Consider _not_ doing that though, make a better regex.)
|
||||
|
||||
- `jeremy`: OK
|
||||
- `^(jeremy)$`: OK
|
||||
- `jer`: Used to match, now won't. You'll need to do `.*jer.*` to make it act like before. (Consider _not_ doing that though, make a better regex.)
|
||||
|
||||
## New stuff
|
||||
|
||||
Tons of new stuff, you can think of all of these as Christmas presents.
|
||||
- Color handling has been moved to OkLab from sRGB. This means gradients and color transitions will now look more natural.
|
||||
- XWayland Drag and Drop is back! You can now drag stuff from your Wayland clients to X11 clients.
|
||||
- New update screen! Whenever you update, you'll get a small popup reassuring everything went well and reminding you to check the release notes.
|
||||
- Window and layer rule handling has been improved and optimized, and regex handling is now done via RE2 from Google, which is faster and generally better.
|
||||
- `cursor:warp_on_change_workspace` now accepts `force` to bypass `cursor:no_warps`.
|
||||
- hyprctl: `clients` got `inhibitingIdle`, `monitors` got `directScanout`, `plugins list` got `-j` support.
|
||||
- `cursor:warp_back_after_non_mouse_input` added, allowing you to keep touch / tablet input from messing with your mouse input.
|
||||
- `lockdead_screen_delay` has been added if your lockscreen can't appear fast enough to avoid the "lockscreen dead" from flashing for a moment.
|
||||
- You can now blur IME popups with `decoration:blur:input_methods`
|
||||
- Version requests now also show linked versions of hypr* deps.
|
||||
- New windowrules for mouse and touchpad scroll factors.
|
||||
- Added some new festive splashes for xmas and new years :)
|
||||
- And more!
|
||||
|
||||
- Color handling has been moved to OkLab from sRGB. This means gradients and color transitions will now look more natural.
|
||||
- XWayland Drag and Drop is back! You can now drag stuff from your Wayland clients to X11 clients.
|
||||
- New update screen! Whenever you update, you'll get a small popup reassuring everything went well and reminding you to check the release notes.
|
||||
- Window and layer rule handling has been improved and optimized, and regex handling is now done via RE2 from Google, which is faster and generally better.
|
||||
- `cursor:warp_on_change_workspace` now accepts `force` to bypass `cursor:no_warps`.
|
||||
- hyprctl: `clients` got `inhibitingIdle`, `monitors` got `directScanout`, `plugins list` got `-j` support.
|
||||
- `cursor:warp_back_after_non_mouse_input` added, allowing you to keep touch / tablet input from messing with your mouse input.
|
||||
- `lockdead_screen_delay` has been added if your lockscreen can't appear fast enough to avoid the "lockscreen dead" from flashing for a moment.
|
||||
- You can now blur IME popups with `decoration:blur:input_methods`
|
||||
- Version requests now also show linked versions of hypr\* deps.
|
||||
- New windowrules for mouse and touchpad scroll factors.
|
||||
- Added some new festive splashes for xmas and new years :)
|
||||
- And more!
|
||||
|
||||
## Fixes
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ a bunch of things, and we hope to get more and more tests there as time goes on.
|
|||
### Other
|
||||
|
||||
Other new stuff include amongst others:
|
||||
|
||||
- New `monitorv2` syntax for monitor configs that are less cluttered
|
||||
- tons of small optimizations (thanks Tom!)
|
||||
- new `ext_workspace_v1` support
|
||||
|
|
@ -52,10 +53,10 @@ Other new stuff include amongst others:
|
|||
- new permission management for keyboards
|
||||
- and more...
|
||||
|
||||
|
||||
### Fixes
|
||||
|
||||
Tons of fixes, including:
|
||||
|
||||
- fixes for some `hyprpm` and hyprland crashes.
|
||||
- fixed some minor blur artifacts on popups
|
||||
- snap now respects outer gaps
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Hey hey people, vaxry here. It's time for a new Hyprland update, after 2 months
|
|||
## Breaking changes
|
||||
|
||||
- The gesture system has been reworked and is now way more flexible. Thus, the old `gestures:workspace_swipe`,
|
||||
`gestures:workspace_swipe_fingers` and `gestures:workspace_swipe_min_fingers` are gone.
|
||||
`gestures:workspace_swipe_fingers` and `gestures:workspace_swipe_min_fingers` are gone.
|
||||
|
||||
- `animations:first_launch_animation` is gone, use the new `monitorAdded` animation leaf.
|
||||
|
||||
|
|
@ -40,7 +40,6 @@ Check the new [gestures wiki page](https://wiki.hypr.land/Configuring/Gestures/)
|
|||
- Screensharing now forces 8-bit by default (fixes chromium / firefox screensharing on wide color gamut displays)
|
||||
- New `novrr` windowrule.
|
||||
|
||||
|
||||
## Fixes
|
||||
|
||||
Tons of fixes as usual:
|
||||
|
|
@ -60,4 +59,4 @@ Tons of fixes as usual:
|
|||
If you are one of these people that enjoy reading, check the release on [Github](https://github.com/hyprwm/Hyprland/releases/tag/v0.51.0).
|
||||
|
||||
Cheers,
|
||||
vax
|
||||
vax
|
||||
|
|
|
|||
|
|
@ -1,418 +1,280 @@
|
|||
[
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/372809091208445953/a_33fd25e26c0ba17c05566bc3179c6476.gif",
|
||||
"class": "outline-red-500",
|
||||
"coordinates": [
|
||||
187,
|
||||
296
|
||||
],
|
||||
"size": 172
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/444952344308744203/9ee39cd422568dad9e70319df27b2560.webp",
|
||||
"class": "outline-yellow-500",
|
||||
"coordinates": [
|
||||
735,
|
||||
441
|
||||
],
|
||||
"size": 164
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/419880181101232129/d5ec2616075dc0fddb3de528c9113628.webp",
|
||||
"class": "outline-orange-500",
|
||||
"coordinates": [
|
||||
391,
|
||||
615
|
||||
],
|
||||
"size": 149
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/378704069726044170/415dcb2ef8d1ef635e35e1d04d523cba.webp",
|
||||
"class": "outline-amber-500",
|
||||
"coordinates": [
|
||||
568,
|
||||
594
|
||||
],
|
||||
"size": 120
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/862314649307054080/bce0b10ae900daa2da139f9e089c8e56.webp",
|
||||
"class": "outline-cyan-400",
|
||||
"coordinates": [
|
||||
648,
|
||||
709
|
||||
],
|
||||
"size": 128
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/163678036401586177/0930f3f6f0839b4f9c59846e185aa8ad.webp",
|
||||
"class": "outline-yellow-500",
|
||||
"coordinates": [
|
||||
24,
|
||||
341
|
||||
],
|
||||
"size": 49
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/223160360461402122/c6552a1c768613bffb83bf88e4ce2632.webp",
|
||||
"class": "outline-red-500",
|
||||
"coordinates": [
|
||||
47,
|
||||
86
|
||||
],
|
||||
"size": 48
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/979473046404476998/3d12629a52071fabff4493c1362d59fa.webp",
|
||||
"class": "outline-amber-500",
|
||||
"coordinates": [
|
||||
1038,
|
||||
446
|
||||
],
|
||||
"size": 52
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/486802226577276929/2a6355319a116ba3e2aff9acf4163e95.webp",
|
||||
"class": "outline-cyan-500",
|
||||
"coordinates": [
|
||||
273,
|
||||
760
|
||||
],
|
||||
"size": 52,
|
||||
"quote": "\"meds\""
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/837425748435796060/248cd938377647404e3d8d1c53b639cf.webp",
|
||||
"class": "outline-orange-500",
|
||||
"coordinates": [
|
||||
648,
|
||||
364
|
||||
],
|
||||
"size": 105
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/480024733535174668/5453c57e69ff16f495d8dcbd597070e9.webp",
|
||||
"class": "outline-purple-500",
|
||||
"coordinates": [
|
||||
187,
|
||||
764
|
||||
],
|
||||
"size": 62
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/544368824842190861/1fbf29ea7ca9c2109b97af2ede3806fa.webp",
|
||||
"class": "outline-lime-500",
|
||||
"coordinates": [
|
||||
736,
|
||||
277
|
||||
],
|
||||
"size": 105
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/344854021166727180/ace83ad13015f29bc33a3f858e15b4d7.webp",
|
||||
"class": "outline-yellow-500",
|
||||
"coordinates": [
|
||||
898,
|
||||
364
|
||||
],
|
||||
"size": 68
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/579120037034721281/c7b4341806d515f5d92bb53e322a4728.webp",
|
||||
"class": "outline-rose-500",
|
||||
"coordinates": [
|
||||
887,
|
||||
159
|
||||
],
|
||||
"size": 39
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/246125351464337418/10c8bb5456d1ca1cebf2edff62b7001f.webp",
|
||||
"class": "outline-amber-500",
|
||||
"coordinates": [
|
||||
1023,
|
||||
552
|
||||
],
|
||||
"size": 48
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/143031299152674816/4a143f00b0e014e9c3da37cd8599b106.webp",
|
||||
"class": "outline-pink-500",
|
||||
"coordinates": [
|
||||
147,
|
||||
553
|
||||
],
|
||||
"size": 87
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/194157962498015232/ab11cc35bd9d057769254d1d3e29e468.webp",
|
||||
"class": "outline-amber-500 ",
|
||||
"coordinates": [
|
||||
65,
|
||||
643
|
||||
],
|
||||
"size": 74
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/482139697796349953/0a588671aaff04b64788b86bfa8e15f9.webp",
|
||||
"class": "outline-stone-500 ",
|
||||
"coordinates": [
|
||||
263,
|
||||
653
|
||||
],
|
||||
"size": 65
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/231040215085481984/7f378337240110b76e6e9baa31f83670.webp",
|
||||
"class": "outline-amber-500",
|
||||
"coordinates": [
|
||||
354,
|
||||
798
|
||||
],
|
||||
"size": 80
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/784153590595321876/27df3e463a7111b75805baecaff4cdc9.webp",
|
||||
"class": "outline-white",
|
||||
"coordinates": [
|
||||
583,
|
||||
824
|
||||
],
|
||||
"size": 69,
|
||||
"quote": "Perfect being"
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/408513270916579338/bac966d3bae8ad01546ee32584917be9.webp",
|
||||
"class": "outline-rose-300",
|
||||
"coordinates": [
|
||||
275,
|
||||
844
|
||||
],
|
||||
"size": 40
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/860670651026767942/7122b752fa6097472f8c76cc4458e33b.webp",
|
||||
"class": "outline-orange-500",
|
||||
"coordinates": [
|
||||
939,
|
||||
552
|
||||
],
|
||||
"size": 48
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/520860407720837131/81abed4e03a9a7fc9d973b969c9d2ddd.webp",
|
||||
"class": "outline-orange-500 ",
|
||||
"coordinates": [
|
||||
458,
|
||||
913
|
||||
],
|
||||
"size": 35
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/390226958241366016/aca819f6f27ff5b33f1d327b4d2f7049.webp",
|
||||
"class": "outline-blue-500 ",
|
||||
"coordinates": [
|
||||
858,
|
||||
707
|
||||
],
|
||||
"size": 45
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/594256188422488069/1e532709dbd756c172c54add8536e558.webp",
|
||||
"class": "outline-blue-500 bg-black ",
|
||||
"coordinates": [
|
||||
8,
|
||||
477
|
||||
],
|
||||
"size": 54
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/335725027377020929/2ead48f16d6119d01a01d1c17e2e97e5.webp",
|
||||
"class": "outline-pink-500",
|
||||
"coordinates": [
|
||||
69,
|
||||
561
|
||||
],
|
||||
"size": 54
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/561863734264594452/1b3bb5e0c68b0a53d05898f8456fa5d5.webp",
|
||||
"class": "outline-orange-500 bg-black ",
|
||||
"coordinates": [
|
||||
908,
|
||||
463
|
||||
],
|
||||
"size": 54
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/426690378830184448/09181b1b476f221b7850702158778c1a.webp",
|
||||
"class": "outline-blue-500 bg-black ",
|
||||
"coordinates": [
|
||||
823,
|
||||
184
|
||||
],
|
||||
"size": 44
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/219573494713810945/5202607a6cf5e34ed705308459d0115c.webp",
|
||||
"class": "outline-stone-900 bg-black ",
|
||||
"coordinates": [
|
||||
133,
|
||||
493
|
||||
],
|
||||
"size": 49
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/317785409763541002/38ba01511d57eb11c96dc74a9c4c149d.webp",
|
||||
"class": "outline-blue-500 bg-black ",
|
||||
"coordinates": [
|
||||
119,
|
||||
202
|
||||
],
|
||||
"size": 49
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/465960044094160908/db149a0d49fd80cf7caf7cc2a637e084.webp",
|
||||
"class": "outline-yellow-500 bg-black ",
|
||||
"coordinates": [
|
||||
179,
|
||||
671
|
||||
],
|
||||
"size": 69
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/706672850907430942/0cbb7c41b54155cee739542461755e65.webp",
|
||||
"class": "outline-yellow-500 bg-black ",
|
||||
"coordinates": [
|
||||
771,
|
||||
818
|
||||
],
|
||||
"size": 49
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/708440359587414067/2bcdf703f69ee35c4cf39b2a89c429e8.webp",
|
||||
"class": "outline-lime-500 bg-black ",
|
||||
"coordinates": [
|
||||
1018,
|
||||
123
|
||||
],
|
||||
"size": 28
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/142688838127583232/cd8a2857cfd1b4090e3cd2934d0bcd4e.webp",
|
||||
"class": "outline-red-500 bg-black ",
|
||||
"coordinates": [
|
||||
57,
|
||||
313
|
||||
],
|
||||
"size": 57
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/389202953862512641/1ec9240abbae61b1f1f8f4a154859890.webp",
|
||||
"class": "outline-stone-500 bg-black ",
|
||||
"coordinates": [
|
||||
819,
|
||||
738
|
||||
],
|
||||
"size": 48
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/1249703149057343549/4e5ddf63c78dab083a5cc3b8800b99f9.webp",
|
||||
"class": "outline-stone-500 bg-black ",
|
||||
"coordinates": [
|
||||
474,
|
||||
850
|
||||
],
|
||||
"size": 63
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/419571665832378369/0c5806bb19fa2d06b25045a91369e950.webp",
|
||||
"class": "outline-orange-500",
|
||||
"coordinates": [
|
||||
945,
|
||||
697
|
||||
],
|
||||
"size": 42
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/993044302957662228/6ee715b893be571ca2ff5760e7292f59.webp",
|
||||
"class": "outline-purple-500",
|
||||
"coordinates": [
|
||||
113,
|
||||
736
|
||||
],
|
||||
"size": 40
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/284239193813680129/e81d7a3f73dd0a9d71f383ef813a50c8.webp",
|
||||
"class": "outline-red-500",
|
||||
"coordinates": [
|
||||
143,
|
||||
274
|
||||
],
|
||||
"size": 44
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/194584980922433536/9040f8f62191553a3ab4a212ab989d41.webp",
|
||||
"class": "outline-red-500",
|
||||
"coordinates": [
|
||||
53,
|
||||
399
|
||||
],
|
||||
"size": 75
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/1016936898670903336/c376d8fb319f8aa57c98142ad2ec101c.webp",
|
||||
"class": "outline-blue-500",
|
||||
"coordinates": [
|
||||
50,
|
||||
250
|
||||
],
|
||||
"size": 40
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/773595387150860328/2828efc505a0cc83544a9437d089e54e.webp",
|
||||
"class": "outline-cyan-500",
|
||||
"coordinates": [
|
||||
211,
|
||||
846
|
||||
],
|
||||
"size": 44
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/531392146767347712/0b5e5671e7e8eb0e4f6ec547c686667b.webp",
|
||||
"class": "outline-orange-500 bg-stone-800",
|
||||
"coordinates": [
|
||||
525,
|
||||
764
|
||||
],
|
||||
"size": 80
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/623781003382751243/6a950611b395b0fbf2e8835ba9af1e84.webp",
|
||||
"class": "outline-orange-500",
|
||||
"coordinates": [
|
||||
950,
|
||||
277
|
||||
],
|
||||
"size": 47
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/633313654283960330/edbd0bc89f1d17a90870c25958816671.webp",
|
||||
"class": "outline-orange-500",
|
||||
"coordinates": [
|
||||
868,
|
||||
606
|
||||
],
|
||||
"size": 85
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/321614457623019520/abd926b4f0c2ba37d6ee9359ad0599d2.webp",
|
||||
"class": "outline-orange-500",
|
||||
"coordinates": [
|
||||
962,
|
||||
626
|
||||
],
|
||||
"size": 45
|
||||
}
|
||||
]
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/372809091208445953/a_33fd25e26c0ba17c05566bc3179c6476.gif",
|
||||
"class": "outline-red-500",
|
||||
"coordinates": [187, 296],
|
||||
"size": 172
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/444952344308744203/9ee39cd422568dad9e70319df27b2560.webp",
|
||||
"class": "outline-yellow-500",
|
||||
"coordinates": [735, 441],
|
||||
"size": 164
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/419880181101232129/d5ec2616075dc0fddb3de528c9113628.webp",
|
||||
"class": "outline-orange-500",
|
||||
"coordinates": [391, 615],
|
||||
"size": 149
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/378704069726044170/415dcb2ef8d1ef635e35e1d04d523cba.webp",
|
||||
"class": "outline-amber-500",
|
||||
"coordinates": [568, 594],
|
||||
"size": 120
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/862314649307054080/bce0b10ae900daa2da139f9e089c8e56.webp",
|
||||
"class": "outline-cyan-400",
|
||||
"coordinates": [648, 709],
|
||||
"size": 128
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/163678036401586177/0930f3f6f0839b4f9c59846e185aa8ad.webp",
|
||||
"class": "outline-yellow-500",
|
||||
"coordinates": [24, 341],
|
||||
"size": 49
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/223160360461402122/c6552a1c768613bffb83bf88e4ce2632.webp",
|
||||
"class": "outline-red-500",
|
||||
"coordinates": [47, 86],
|
||||
"size": 48
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/979473046404476998/3d12629a52071fabff4493c1362d59fa.webp",
|
||||
"class": "outline-amber-500",
|
||||
"coordinates": [1038, 446],
|
||||
"size": 52
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/486802226577276929/2a6355319a116ba3e2aff9acf4163e95.webp",
|
||||
"class": "outline-cyan-500",
|
||||
"coordinates": [273, 760],
|
||||
"size": 52,
|
||||
"quote": "\"meds\""
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/837425748435796060/248cd938377647404e3d8d1c53b639cf.webp",
|
||||
"class": "outline-orange-500",
|
||||
"coordinates": [648, 364],
|
||||
"size": 105
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/480024733535174668/5453c57e69ff16f495d8dcbd597070e9.webp",
|
||||
"class": "outline-purple-500",
|
||||
"coordinates": [187, 764],
|
||||
"size": 62
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/544368824842190861/1fbf29ea7ca9c2109b97af2ede3806fa.webp",
|
||||
"class": "outline-lime-500",
|
||||
"coordinates": [736, 277],
|
||||
"size": 105
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/344854021166727180/ace83ad13015f29bc33a3f858e15b4d7.webp",
|
||||
"class": "outline-yellow-500",
|
||||
"coordinates": [898, 364],
|
||||
"size": 68
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/579120037034721281/c7b4341806d515f5d92bb53e322a4728.webp",
|
||||
"class": "outline-rose-500",
|
||||
"coordinates": [887, 159],
|
||||
"size": 39
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/246125351464337418/10c8bb5456d1ca1cebf2edff62b7001f.webp",
|
||||
"class": "outline-amber-500",
|
||||
"coordinates": [1023, 552],
|
||||
"size": 48
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/143031299152674816/4a143f00b0e014e9c3da37cd8599b106.webp",
|
||||
"class": "outline-pink-500",
|
||||
"coordinates": [147, 553],
|
||||
"size": 87
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/194157962498015232/ab11cc35bd9d057769254d1d3e29e468.webp",
|
||||
"class": "outline-amber-500 ",
|
||||
"coordinates": [65, 643],
|
||||
"size": 74
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/482139697796349953/0a588671aaff04b64788b86bfa8e15f9.webp",
|
||||
"class": "outline-stone-500 ",
|
||||
"coordinates": [263, 653],
|
||||
"size": 65
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/231040215085481984/7f378337240110b76e6e9baa31f83670.webp",
|
||||
"class": "outline-amber-500",
|
||||
"coordinates": [354, 798],
|
||||
"size": 80
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/784153590595321876/27df3e463a7111b75805baecaff4cdc9.webp",
|
||||
"class": "outline-white",
|
||||
"coordinates": [583, 824],
|
||||
"size": 69,
|
||||
"quote": "Perfect being"
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/408513270916579338/bac966d3bae8ad01546ee32584917be9.webp",
|
||||
"class": "outline-rose-300",
|
||||
"coordinates": [275, 844],
|
||||
"size": 40
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/860670651026767942/7122b752fa6097472f8c76cc4458e33b.webp",
|
||||
"class": "outline-orange-500",
|
||||
"coordinates": [939, 552],
|
||||
"size": 48
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/520860407720837131/81abed4e03a9a7fc9d973b969c9d2ddd.webp",
|
||||
"class": "outline-orange-500 ",
|
||||
"coordinates": [458, 913],
|
||||
"size": 35
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/390226958241366016/aca819f6f27ff5b33f1d327b4d2f7049.webp",
|
||||
"class": "outline-blue-500 ",
|
||||
"coordinates": [858, 707],
|
||||
"size": 45
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/594256188422488069/1e532709dbd756c172c54add8536e558.webp",
|
||||
"class": "outline-blue-500 bg-black ",
|
||||
"coordinates": [8, 477],
|
||||
"size": 54
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/335725027377020929/2ead48f16d6119d01a01d1c17e2e97e5.webp",
|
||||
"class": "outline-pink-500",
|
||||
"coordinates": [69, 561],
|
||||
"size": 54
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/561863734264594452/1b3bb5e0c68b0a53d05898f8456fa5d5.webp",
|
||||
"class": "outline-orange-500 bg-black ",
|
||||
"coordinates": [908, 463],
|
||||
"size": 54
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/426690378830184448/09181b1b476f221b7850702158778c1a.webp",
|
||||
"class": "outline-blue-500 bg-black ",
|
||||
"coordinates": [823, 184],
|
||||
"size": 44
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/219573494713810945/5202607a6cf5e34ed705308459d0115c.webp",
|
||||
"class": "outline-stone-900 bg-black ",
|
||||
"coordinates": [133, 493],
|
||||
"size": 49
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/317785409763541002/38ba01511d57eb11c96dc74a9c4c149d.webp",
|
||||
"class": "outline-blue-500 bg-black ",
|
||||
"coordinates": [119, 202],
|
||||
"size": 49
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/465960044094160908/db149a0d49fd80cf7caf7cc2a637e084.webp",
|
||||
"class": "outline-yellow-500 bg-black ",
|
||||
"coordinates": [179, 671],
|
||||
"size": 69
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/706672850907430942/0cbb7c41b54155cee739542461755e65.webp",
|
||||
"class": "outline-yellow-500 bg-black ",
|
||||
"coordinates": [771, 818],
|
||||
"size": 49
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/708440359587414067/2bcdf703f69ee35c4cf39b2a89c429e8.webp",
|
||||
"class": "outline-lime-500 bg-black ",
|
||||
"coordinates": [1018, 123],
|
||||
"size": 28
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/142688838127583232/cd8a2857cfd1b4090e3cd2934d0bcd4e.webp",
|
||||
"class": "outline-red-500 bg-black ",
|
||||
"coordinates": [57, 313],
|
||||
"size": 57
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/389202953862512641/1ec9240abbae61b1f1f8f4a154859890.webp",
|
||||
"class": "outline-stone-500 bg-black ",
|
||||
"coordinates": [819, 738],
|
||||
"size": 48
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/1249703149057343549/4e5ddf63c78dab083a5cc3b8800b99f9.webp",
|
||||
"class": "outline-stone-500 bg-black ",
|
||||
"coordinates": [474, 850],
|
||||
"size": 63
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/419571665832378369/0c5806bb19fa2d06b25045a91369e950.webp",
|
||||
"class": "outline-orange-500",
|
||||
"coordinates": [945, 697],
|
||||
"size": 42
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/993044302957662228/6ee715b893be571ca2ff5760e7292f59.webp",
|
||||
"class": "outline-purple-500",
|
||||
"coordinates": [113, 736],
|
||||
"size": 40
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/284239193813680129/e81d7a3f73dd0a9d71f383ef813a50c8.webp",
|
||||
"class": "outline-red-500",
|
||||
"coordinates": [143, 274],
|
||||
"size": 44
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/194584980922433536/9040f8f62191553a3ab4a212ab989d41.webp",
|
||||
"class": "outline-red-500",
|
||||
"coordinates": [53, 399],
|
||||
"size": 75
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/1016936898670903336/c376d8fb319f8aa57c98142ad2ec101c.webp",
|
||||
"class": "outline-blue-500",
|
||||
"coordinates": [50, 250],
|
||||
"size": 40
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/773595387150860328/2828efc505a0cc83544a9437d089e54e.webp",
|
||||
"class": "outline-cyan-500",
|
||||
"coordinates": [211, 846],
|
||||
"size": 44
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/531392146767347712/0b5e5671e7e8eb0e4f6ec547c686667b.webp",
|
||||
"class": "outline-orange-500 bg-stone-800",
|
||||
"coordinates": [525, 764],
|
||||
"size": 80
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/623781003382751243/6a950611b395b0fbf2e8835ba9af1e84.webp",
|
||||
"class": "outline-orange-500",
|
||||
"coordinates": [950, 277],
|
||||
"size": 47
|
||||
},
|
||||
{
|
||||
"image": "https://cdn.discordapp.com/avatars/633313654283960330/edbd0bc89f1d17a90870c25958816671.webp",
|
||||
"class": "outline-orange-500",
|
||||
"coordinates": [868, 606],
|
||||
"size": 85
|
||||
},
|
||||
{
|
||||
"image": "https://xcdn.discordapp.com/avatars/321614457623019520/abd926b4f0c2ba37d6ee9359ad0599d2.webp",
|
||||
"class": "outline-orange-500",
|
||||
"coordinates": [962, 626],
|
||||
"size": 45
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -42,11 +42,16 @@ export function animateIn(
|
|||
// Do nothing on mobile
|
||||
if (getIsMobile()) return { destroy: () => undefined }
|
||||
|
||||
const observer = inview(node, { unobserveOnEnter: true, threshold: options.threshold ?? 0.4 })
|
||||
const observer = inview(node, {
|
||||
unobserveOnEnter: true,
|
||||
threshold: options.threshold ?? 0.4
|
||||
})
|
||||
|
||||
options.duration ??= 840
|
||||
|
||||
const effects = Object.entries(pick(options, ['fade', 'zoom', 'slide', 'duration']))
|
||||
const effects = Object.entries(
|
||||
pick(options, ['fade', 'zoom', 'slide', 'duration'])
|
||||
)
|
||||
|
||||
const style = effects
|
||||
.map(([effect, value]) => {
|
||||
|
|
@ -73,9 +78,12 @@ export function animateIn(
|
|||
timeoutId = setTimeout(
|
||||
() =>
|
||||
effects.forEach(([effect]) => {
|
||||
if (effect === 'slide') node.style.removeProperty('translate')
|
||||
else if (effect === 'fade') node.style.removeProperty('opacity')
|
||||
else if (effect === 'zoom') node.style.removeProperty('scale')
|
||||
if (effect === 'slide')
|
||||
node.style.removeProperty('translate')
|
||||
else if (effect === 'fade')
|
||||
node.style.removeProperty('opacity')
|
||||
else if (effect === 'zoom')
|
||||
node.style.removeProperty('scale')
|
||||
}),
|
||||
options.delay ?? 0
|
||||
)
|
||||
|
|
@ -120,7 +128,10 @@ export function getIsMobile(): boolean {
|
|||
}
|
||||
|
||||
/** Get the `generated_<filename>` for the provided path **/
|
||||
export function getGeneratedPath(path: string, extension: string = 'webp') {
|
||||
export function getGeneratedPath(
|
||||
path: string,
|
||||
extension: string = 'webp'
|
||||
) {
|
||||
const directory = path.substring(0, path.lastIndexOf('/'))
|
||||
const filename = getFileNameWithoutExtension(path)
|
||||
return `${directory}/generated_${filename}.${extension}`
|
||||
|
|
@ -138,7 +149,9 @@ export function formatDate(
|
|||
) {
|
||||
const dateToFormat = new Date(date)
|
||||
|
||||
const dateFormatter = new Intl.DateTimeFormat(locales, { dateStyle })
|
||||
const dateFormatter = new Intl.DateTimeFormat(locales, {
|
||||
dateStyle
|
||||
})
|
||||
|
||||
return dateFormatter.format(dateToFormat)
|
||||
}
|
||||
|
|
@ -164,7 +177,11 @@ export function getFileNameWithoutExtension(filePath: string) {
|
|||
*
|
||||
* Used here to do fancy stuff with clicks
|
||||
*/
|
||||
export function createThresholdStream({ clicksTarget = 69, clicksEachMs = 400, fallof = 20 }) {
|
||||
export function createThresholdStream({
|
||||
clicksTarget = 69,
|
||||
clicksEachMs = 400,
|
||||
fallof = 20
|
||||
}) {
|
||||
const FALLOF = -clicksTarget / fallof
|
||||
|
||||
return rxpipe(
|
||||
|
|
@ -182,7 +199,9 @@ export function createThresholdStream({ clicksTarget = 69, clicksEachMs = 400, f
|
|||
)
|
||||
)
|
||||
),
|
||||
scan((level, value) => Math.min(clicksTarget, Math.max(level + value, 0))),
|
||||
scan((level, value) =>
|
||||
Math.min(clicksTarget, Math.max(level + value, 0))
|
||||
),
|
||||
startWith(0)
|
||||
)
|
||||
}
|
||||
|
|
@ -232,8 +251,14 @@ export function convertStoreToObservable<T>(
|
|||
* Checks if two rectangles are intersecting
|
||||
*/
|
||||
export function isIntersecting(
|
||||
rect1: { size: number; coordinates: readonly [x: number, y: number] },
|
||||
rect2: { size: number; coordinates: readonly [x: number, y: number] }
|
||||
rect1: {
|
||||
size: number
|
||||
coordinates: readonly [x: number, y: number]
|
||||
},
|
||||
rect2: {
|
||||
size: number
|
||||
coordinates: readonly [x: number, y: number]
|
||||
}
|
||||
) {
|
||||
return !(
|
||||
rect1.coordinates[0] + rect1.size < rect2.coordinates[0] ||
|
||||
|
|
|
|||
|
|
@ -1,53 +1,77 @@
|
|||
<script>
|
||||
<script lang="ts">
|
||||
import {
|
||||
BehaviorSubject,
|
||||
Subject,
|
||||
auditTime,
|
||||
combineLatest,
|
||||
debounceTime,
|
||||
delay,
|
||||
distinctUntilChanged,
|
||||
filter,
|
||||
map,
|
||||
of,
|
||||
startWith,
|
||||
switchMap,
|
||||
timer
|
||||
timer,
|
||||
type Observable
|
||||
} from 'rxjs'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import { spring } from 'svelte/motion'
|
||||
import { Spring } from 'svelte/motion'
|
||||
|
||||
/** The start position of the gradient. */
|
||||
export let startPosition = [-1000, -1000]
|
||||
type Props = {
|
||||
/** The start position of the gradient. */
|
||||
startPosition?: [number, number]
|
||||
class?: string
|
||||
}
|
||||
|
||||
let { class: className, startPosition = [-1000, -1000] }: Props =
|
||||
$props()
|
||||
|
||||
let wrapperElement: HTMLDivElement | undefined = $state(undefined)
|
||||
|
||||
/** @type {HTMLDivElement}*/
|
||||
let wrapperElement = undefined
|
||||
/** @type {import('rxjs').BehaviorSubject<boolean>}*/
|
||||
const isMouseOver$ = new BehaviorSubject(false).pipe(
|
||||
// Do not harshly stop updating the gradient when the mouse leaves, but wait a bit
|
||||
switchMap((isTrue) => (isTrue ? of(isTrue) : timer(1500).pipe(map(() => false)))),
|
||||
switchMap((isTrue) =>
|
||||
isTrue ? of(isTrue) : timer(1500).pipe(map(() => false))
|
||||
),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
) as BehaviorSubject<boolean>
|
||||
|
||||
/** @type {import('rxjs').BehaviorSubject<number>}*/
|
||||
const gradientSize$ = new BehaviorSubject().pipe(
|
||||
const gradientSize$: BehaviorSubject<number> = new BehaviorSubject(
|
||||
undefined
|
||||
).pipe(
|
||||
// Debounce resize events with some high number for performance
|
||||
debounceTime(1),
|
||||
map(() => wrapperElement.getBoundingClientRect().width * 3),
|
||||
auditTime(16),
|
||||
map(
|
||||
() => (wrapperElement?.getBoundingClientRect().width ?? 0) * 3
|
||||
),
|
||||
startWith(800)
|
||||
)
|
||||
/** @type {import('rxjs').Subject< {clientX: number, clientY: number} >}*/
|
||||
const mousePosition$ = new Subject()
|
||||
) as BehaviorSubject<number>
|
||||
const mousePosition$: Subject<{
|
||||
clientX: number
|
||||
clientY: number
|
||||
}> = new Subject()
|
||||
|
||||
const gradientPosition$ = combineLatest([mousePosition$, gradientSize$, isMouseOver$]).pipe(
|
||||
filter(([_, __, isMouseOver]) => isMouseOver),
|
||||
map(([{ clientX, clientY }, gradientSize]) => {
|
||||
const { x, y } = wrapperElement?.getBoundingClientRect() ?? { x: 0, y: 0 }
|
||||
return [clientX - x - gradientSize * 0.5, clientY - y - gradientSize * 0.5]
|
||||
}),
|
||||
startWith(startPosition)
|
||||
const gradientPosition$: Observable<[number, number]> =
|
||||
combineLatest([mousePosition$, gradientSize$, isMouseOver$]).pipe(
|
||||
filter(([_, __, isMouseOver]) => isMouseOver),
|
||||
map(([{ clientX, clientY }, gradientSize]) => {
|
||||
const { x, y } = wrapperElement?.getBoundingClientRect() ?? {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
return [
|
||||
clientX - x - gradientSize * 0.5,
|
||||
clientY - y - gradientSize * 0.5
|
||||
] as [number, number]
|
||||
}),
|
||||
startWith(startPosition)
|
||||
)
|
||||
const gradientWiggle = new Spring(startPosition, {
|
||||
damping: 0.95,
|
||||
stiffness: 0.1
|
||||
})
|
||||
const subscription = gradientPosition$.subscribe((data) =>
|
||||
gradientWiggle.set(data)
|
||||
)
|
||||
const gradientWiggle = spring(startPosition, { damping: 0.95, stiffness: 0.1 })
|
||||
const subscription = gradientPosition$.subscribe((data) => gradientWiggle.set(data))
|
||||
|
||||
onDestroy(() => {
|
||||
subscription.unsubscribe()
|
||||
|
|
@ -60,16 +84,16 @@
|
|||
hasJustMounted = false
|
||||
})
|
||||
|
||||
$: {
|
||||
$effect(() => {
|
||||
if (!$isMouseOver$) {
|
||||
globalThis.document?.removeEventListener('mousemove', track)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function resizeGradient() {
|
||||
if (hasJustMounted || !isMouseOver$) return
|
||||
|
||||
gradientSize$.next()
|
||||
gradientSize$.next(0)
|
||||
}
|
||||
|
||||
function startTrackingMouse() {
|
||||
|
|
@ -78,7 +102,13 @@
|
|||
globalThis.document?.addEventListener('mousemove', track)
|
||||
}
|
||||
|
||||
function track({ clientX, clientY }) {
|
||||
function track({
|
||||
clientX,
|
||||
clientY
|
||||
}: {
|
||||
clientX: number
|
||||
clientY: number
|
||||
}) {
|
||||
mousePosition$.next({ clientX, clientY })
|
||||
}
|
||||
|
||||
|
|
@ -90,12 +120,16 @@
|
|||
<svelte:window on:resize={resizeGradient} />
|
||||
|
||||
<div
|
||||
class={$$props.class + ' wrapper'}
|
||||
on:mouseenter={startTrackingMouse}
|
||||
on:mouseleave={({ clientX, clientY, currentTarget }) => {
|
||||
const { x, width, y, height } = currentTarget.getBoundingClientRect()
|
||||
class={className + ' wrapper'}
|
||||
onmouseenter={startTrackingMouse}
|
||||
onmouseleave={({ clientX, clientY, currentTarget }) => {
|
||||
const { x, width, y, height } =
|
||||
currentTarget.getBoundingClientRect()
|
||||
const isMouseStillOver =
|
||||
x <= clientX && y <= clientY && x + width > clientX && y + height > clientY
|
||||
x <= clientX &&
|
||||
y <= clientY &&
|
||||
x + width > clientX &&
|
||||
y + height > clientY
|
||||
isMouseOver$.next(isMouseStillOver)
|
||||
}}
|
||||
aria-hidden="true"
|
||||
|
|
@ -103,12 +137,17 @@
|
|||
>
|
||||
<div
|
||||
class="gradient"
|
||||
style:--x={$gradientWiggle.at(0) + 'px'}
|
||||
style:--y={$gradientWiggle.at(1) + 'px'}
|
||||
style:--x={gradientWiggle.current.at(0) + 'px'}
|
||||
style:--y={gradientWiggle.current.at(1) + 'px'}
|
||||
style:--size={$gradientSize$ + 'px'}
|
||||
></div>
|
||||
|
||||
<svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
width="100%"
|
||||
height="100%"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<pattern
|
||||
id="background-pattern-id"
|
||||
x="0"
|
||||
|
|
@ -117,7 +156,14 @@
|
|||
height="30"
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<rect x="0.5" y="0.5" width="30" height="30" rx="0" stroke="currentColor" />
|
||||
<rect
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
width="30"
|
||||
height="30"
|
||||
rx="0"
|
||||
stroke="currentColor"
|
||||
/>
|
||||
</pattern>
|
||||
|
||||
<rect
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export interface CommunityProfile {
|
|||
containerClass?: string
|
||||
size: number
|
||||
quote?: string
|
||||
class?: string,
|
||||
class?: string
|
||||
}
|
||||
|
||||
export interface CommunityContext {
|
||||
|
|
@ -30,7 +30,10 @@ interface ProfilesState {
|
|||
*/
|
||||
intersections: string[]
|
||||
/** All tagged profiles */
|
||||
profiles: Record<string, { size: number; coordinates: readonly [x: number, y: number] }>
|
||||
profiles: Record<
|
||||
string,
|
||||
{ size: number; coordinates: readonly [x: number, y: number] }
|
||||
>
|
||||
/** Current active events. Currently not used, but maybe in the future */
|
||||
events: string[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,38 @@
|
|||
<script lang="ts">
|
||||
import { cn } from '$lib/Helper'
|
||||
|
||||
export let size: 'md' | 'lg' | 'xl' = 'md'
|
||||
export let type: 'primary' | 'outline' | 'fancyOutline' = 'primary'
|
||||
interface Props {
|
||||
size?: 'md' | 'lg' | 'xl'
|
||||
type?: 'primary' | 'outline' | 'fancyOutline'
|
||||
href?: string | undefined
|
||||
newTab?: boolean
|
||||
children?: import('svelte').Snippet
|
||||
onClick?: () => void
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export let href: string | undefined = undefined
|
||||
export let newTab = false
|
||||
let {
|
||||
size = 'md',
|
||||
type = 'primary',
|
||||
href = undefined,
|
||||
newTab = false,
|
||||
children,
|
||||
onClick,
|
||||
...rest
|
||||
}: Props = $props()
|
||||
|
||||
$: classes = cn(
|
||||
'animate rounded text-sm font-bold hover:scale-[1.01] active:scale-100',
|
||||
'primary' == type && 'bg-slate-200 text-black',
|
||||
'outline' == type && 'bg-transparent text-white outline outline-2 outline-slate-200',
|
||||
'fancyOutline' == type && 'fancy',
|
||||
'md' == size && 'min-w-[5.5rem] px-4 py-2.5',
|
||||
'lg' == size && 'min-w-[5.5rem] px-6 py-3',
|
||||
'xl' == size && 'min-w-[5.5rem] px-8 py-3.5',
|
||||
$$restProps.class
|
||||
let classes = $derived(
|
||||
cn(
|
||||
'animate rounded text-sm font-bold hover:scale-[1.01] active:scale-100',
|
||||
'primary' == type && 'bg-slate-200 text-black',
|
||||
'outline' == type &&
|
||||
'bg-transparent text-white outline outline-2 outline-slate-200',
|
||||
'fancyOutline' == type && 'fancy',
|
||||
'md' == size && 'min-w-[5.5rem] px-4 py-2.5',
|
||||
'lg' == size && 'min-w-[5.5rem] px-6 py-3',
|
||||
'xl' == size && 'min-w-[5.5rem] px-8 py-3.5',
|
||||
rest.class
|
||||
)
|
||||
)
|
||||
</script>
|
||||
|
||||
|
|
@ -23,38 +40,39 @@
|
|||
<div class="relative max-w-max">
|
||||
<svelte:element
|
||||
this={href ? 'a' : 'button'}
|
||||
{...$$restProps}
|
||||
{...rest}
|
||||
{href}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
target={newTab ? '_blank' : undefined}
|
||||
class={classes}
|
||||
on:click
|
||||
onclick={onClick}
|
||||
>
|
||||
<slot>NO LABEL PROVIDED</slot>
|
||||
{#if children}{@render children()}{:else}NO LABEL PROVIDED{/if}
|
||||
</svelte:element>
|
||||
<span
|
||||
class="fancy-bg absolute inset-0 -z-10 h-full w-[110%] min-w-[5rem] scale-y-75 bg-cyan-500/90 px-4 py-2 blur-xl"
|
||||
style="--easing: x; --duration: 8s;"
|
||||
/>
|
||||
></span>
|
||||
<span
|
||||
class="fancy-bg absolute inset-0 -z-10 h-full w-[110%] min-w-[5rem] scale-y-75 bg-secondary/90 px-4 py-2 blur-xl"
|
||||
style="--easing: y; --duration: 8s;"
|
||||
/>
|
||||
></span>
|
||||
<span
|
||||
class="fancy-bg absolute inset-0 -z-10 h-full w-[110%] min-w-[5rem] scale-y-75 bg-purple-500/90 px-4 py-2 blur-xl"
|
||||
style="--easing: z;--duration: 8s;"
|
||||
/>
|
||||
></span>
|
||||
</div>
|
||||
{:else}
|
||||
<svelte:element
|
||||
this={href ? 'a' : 'button'}
|
||||
{...$$restProps}
|
||||
{...rest}
|
||||
{href}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
class={classes}
|
||||
on:click><slot>NO LABEL PROVIDED</slot></svelte:element
|
||||
onclick={onClick}
|
||||
>{#if children}{@render children()}{:else}NO LABEL PROVIDED{/if}</svelte:element
|
||||
>
|
||||
{/if}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,40 +1,68 @@
|
|||
<script>
|
||||
import { run, createBubbler, handlers } from 'svelte/legacy'
|
||||
|
||||
const bubble = createBubbler()
|
||||
import clsx from 'clsx'
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { cardsContext } from '$lib/components/CardsContainer.svelte'
|
||||
import { spring } from 'svelte/motion'
|
||||
import { getIsMobile } from '$lib/Helper.ts'
|
||||
/** @type {'cyan' | 'purple'}*/
|
||||
export let color = 'cyan'
|
||||
/** @type {number | number}*/
|
||||
export let gradientOpacity = undefined
|
||||
|
||||
const { mouseCoordinates$, isHoverCards, enableBorders = true } = getContext(cardsContext)
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {'cyan' | 'purple'} [color]
|
||||
* @property {number | number} [gradientOpacity]
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
/** @type {Props & { [key: string]: any }} */
|
||||
let {
|
||||
color = 'cyan',
|
||||
gradientOpacity = undefined,
|
||||
children,
|
||||
...rest
|
||||
} = $props()
|
||||
|
||||
const {
|
||||
mouseCoordinates$,
|
||||
isHoverCards,
|
||||
enableBorders = true
|
||||
} = getContext(cardsContext)
|
||||
|
||||
/** @type HTMLDivElement */
|
||||
let container
|
||||
let container = $state()
|
||||
let isMobile = false
|
||||
|
||||
const damping = 0.2
|
||||
|
||||
const fillX = spring(0, { damping, stiffness: 0.021, precision: 0.3 })
|
||||
const fillY = spring(0, { damping, stiffness: 0.021, precision: 0.3 })
|
||||
const borderX = spring(0, { damping, stiffness: 0.03, precision: 0.3 })
|
||||
const borderY = spring(0, { damping, stiffness: 0.03, precision: 0.3 })
|
||||
const fillX = spring(0, {
|
||||
damping,
|
||||
stiffness: 0.021,
|
||||
precision: 0.3
|
||||
})
|
||||
const fillY = spring(0, {
|
||||
damping,
|
||||
stiffness: 0.021,
|
||||
precision: 0.3
|
||||
})
|
||||
const borderX = spring(0, {
|
||||
damping,
|
||||
stiffness: 0.03,
|
||||
precision: 0.3
|
||||
})
|
||||
const borderY = spring(0, {
|
||||
damping,
|
||||
stiffness: 0.03,
|
||||
precision: 0.3
|
||||
})
|
||||
|
||||
const bounceBack = 2
|
||||
const soft = 0.8
|
||||
|
||||
let isMouseOver = false
|
||||
let isMouseOver = $state(false)
|
||||
/** Has the mouse entered and not left*/
|
||||
let hasMouseEntered = false
|
||||
|
||||
$: {
|
||||
if (container && $mouseCoordinates$?.x !== undefined) {
|
||||
updateGradient()
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
isMobile = getIsMobile()
|
||||
})
|
||||
|
|
@ -42,7 +70,12 @@
|
|||
function updateGradient() {
|
||||
if (isMobile) return
|
||||
|
||||
const { x: rectX, y: rectY, width, height } = container.getBoundingClientRect()
|
||||
const {
|
||||
x: rectX,
|
||||
y: rectY,
|
||||
width,
|
||||
height
|
||||
} = container.getBoundingClientRect()
|
||||
|
||||
const normX = $mouseCoordinates$.x - rectX
|
||||
const normY = $mouseCoordinates$.y - rectY
|
||||
|
|
@ -67,43 +100,56 @@
|
|||
}
|
||||
*/
|
||||
|
||||
if ($mouseCoordinates$.x < rectX) fillX.set(rectX + bounceBack, { soft })
|
||||
else if ($mouseCoordinates$.x > rectX + width) fillX.set(rectX + width - bounceBack, { soft })
|
||||
if ($mouseCoordinates$.x < rectX)
|
||||
fillX.set(rectX + bounceBack, { soft })
|
||||
else if ($mouseCoordinates$.x > rectX + width)
|
||||
fillX.set(rectX + width - bounceBack, { soft })
|
||||
else fillX.set(normX)
|
||||
|
||||
if ($mouseCoordinates$.y < rectY) fillY.set(rectY + bounceBack, { soft: 1 })
|
||||
if ($mouseCoordinates$.y > rectY + height) fillX.set(rectY - height - bounceBack, { soft })
|
||||
if ($mouseCoordinates$.y < rectY)
|
||||
fillY.set(rectY + bounceBack, { soft: 1 })
|
||||
if ($mouseCoordinates$.y > rectY + height)
|
||||
fillX.set(rectY - height - bounceBack, { soft })
|
||||
else fillY.set(normY)
|
||||
}
|
||||
run(() => {
|
||||
if (container && $mouseCoordinates$?.x !== undefined) {
|
||||
updateGradient()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={clsx('card group ', $$restProps.class)}
|
||||
class={clsx('card group ', rest.class)}
|
||||
style:--x={$fillX}
|
||||
style:--y={$fillY}
|
||||
style:--borderX={enableBorders && $borderX}
|
||||
style:--borderY={enableBorders && $borderY}
|
||||
class:isHoverCards={$isHoverCards}
|
||||
bind:this={container}
|
||||
on:mouseenter={() => (isMouseOver = true)}
|
||||
on:mouseleave={() => {
|
||||
onmouseenter={handlers(
|
||||
() => (isMouseOver = true),
|
||||
bubble('mouseenter')
|
||||
)}
|
||||
onmouseleave={handlers(() => {
|
||||
isMouseOver = false
|
||||
updateGradient()
|
||||
}}
|
||||
}, bubble('mouseleave'))}
|
||||
class:purpleGradient={color === 'purple'}
|
||||
role="contentinfo"
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
>
|
||||
<div
|
||||
class="absolute inset-0 z-10 m-0.5 size-full min-h-0 min-w-0 grow overflow-hidden rounded-3xl"
|
||||
>
|
||||
<slot>Nothing in the slot here</slot>
|
||||
{#if children}{@render children()}{:else}Nothing in the slot here{/if}
|
||||
</div>
|
||||
<div class="gradient max-sm:hidden" style:opacity={gradientOpacity} />
|
||||
<div class="gradient_black max-sm:hidden" />
|
||||
<div
|
||||
class="gradient max-sm:hidden"
|
||||
style:opacity={gradientOpacity}
|
||||
></div>
|
||||
<div class="gradient_black max-sm:hidden"></div>
|
||||
{#if enableBorders}
|
||||
<div class="border-gradient max-sm:hidden" />
|
||||
<div class="border-gradient max-sm:hidden"></div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
@ -156,8 +202,13 @@
|
|||
pointer-events: none;
|
||||
contain: strict;
|
||||
background: radial-gradient(
|
||||
620px circle at calc(var(--borderX) * 1px) calc(var(--borderY) * 1px),
|
||||
color-mix(in srgb, var(--color1, theme(colors.cyan.500)), transparent 50%),
|
||||
620px circle at calc(var(--borderX) * 1px)
|
||||
calc(var(--borderY) * 1px),
|
||||
color-mix(
|
||||
in srgb,
|
||||
var(--color1, theme(colors.cyan.500)),
|
||||
transparent 50%
|
||||
),
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
|
@ -191,7 +242,8 @@
|
|||
opacity: 0%;
|
||||
contain: strict;
|
||||
|
||||
background: url('/imgs/grain.webp'),
|
||||
background:
|
||||
url('/imgs/grain.webp'),
|
||||
radial-gradient(
|
||||
ellipse at calc(var(--x) * 1px) calc(var(--y) * 1px),
|
||||
var(--color1, theme(colors.cyan.500 / 100%)),
|
||||
|
|
|
|||
|
|
@ -1,24 +1,38 @@
|
|||
<script context="module">
|
||||
<script module>
|
||||
export const cardsContext = Symbol('mouseContext')
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { getIsMobile } from '$lib/Helper.ts'
|
||||
import { BehaviorSubject, Subject, throttle, throttleTime } from 'rxjs'
|
||||
import {
|
||||
BehaviorSubject,
|
||||
Subject,
|
||||
throttle,
|
||||
throttleTime
|
||||
} from 'rxjs'
|
||||
|
||||
import { onMount, setContext } from 'svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
export let enableBorders = true
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {boolean} [enableBorders]
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
/** @type {Props & { [key: string]: any }} */
|
||||
let { enableBorders = true, children, ...rest } = $props()
|
||||
|
||||
const fps = 1000 / 60
|
||||
|
||||
/** @type {HTMLElement}*/
|
||||
let containerElement
|
||||
let isMobile = false
|
||||
let containerElement = $state()
|
||||
let isMobile = $state(false)
|
||||
|
||||
const context = setContext(cardsContext, {
|
||||
mouseCoordinates$: new BehaviorSubject({ x: 0, y: 0 }).pipe(throttleTime(fps)),
|
||||
mouseCoordinates$: new BehaviorSubject({ x: 0, y: 0 }).pipe(
|
||||
throttleTime(fps)
|
||||
),
|
||||
isHoverCards: writable(false),
|
||||
enableBorders
|
||||
})
|
||||
|
|
@ -38,16 +52,17 @@
|
|||
onMount(() => {
|
||||
isMobile = getIsMobile()
|
||||
|
||||
return () => containerElement.removeEventListener('mousemove', trackMouse)
|
||||
return () =>
|
||||
containerElement.removeEventListener('mousemove', trackMouse)
|
||||
})
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={$$restProps.class}
|
||||
class={rest.class}
|
||||
bind:this={containerElement}
|
||||
role="contentinfo"
|
||||
on:mouseenter={!isMobile && onMouseEnter}
|
||||
on:mouseleave={!isMobile && onMouseLeave}
|
||||
onmouseenter={!isMobile && onMouseEnter}
|
||||
onmouseleave={!isMobile && onMouseLeave}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,29 @@
|
|||
<script lang="ts">
|
||||
export let href: string | undefined = undefined
|
||||
export let isExternal: boolean = true
|
||||
interface Props {
|
||||
href?: string | undefined
|
||||
isExternal?: boolean
|
||||
children?: import('svelte').Snippet
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
let isExternalProp = isExternal ? { target: '_blank', rel: 'noopener noreferrer' } : {}
|
||||
let {
|
||||
href = undefined,
|
||||
isExternal = true,
|
||||
children,
|
||||
...rest
|
||||
}: Props = $props()
|
||||
|
||||
let isExternalProp = isExternal
|
||||
? { target: '_blank', rel: 'noopener noreferrer' }
|
||||
: {}
|
||||
</script>
|
||||
|
||||
{#if href}
|
||||
<a {href} {...isExternalProp} class="cursor-pointer {$$restProps.class}">
|
||||
<slot />
|
||||
<a {href} {...isExternalProp} class="cursor-pointer {rest.class}">
|
||||
{@render children?.()}
|
||||
</a>
|
||||
{:else}
|
||||
<div class={$$restProps.class}>
|
||||
<slot />
|
||||
<div class={rest.class}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
<!-- @migration-task Error while migrating Svelte code: Cannot split a chunk that has already been edited (28:12 – "on:click={$$slots.default ? undefined : copyCommand}") -->
|
||||
<script>
|
||||
import { onDestroy } from 'svelte'
|
||||
import ClipboardIcon from '~icons/mingcute/copy-2-line'
|
||||
|
|
@ -13,7 +14,9 @@
|
|||
let timeoutId
|
||||
|
||||
async function copyCommand() {
|
||||
await navigator.clipboard.writeText(command).then(() => (isShowingCopied = true))
|
||||
await navigator.clipboard
|
||||
.writeText(command)
|
||||
.then(() => (isShowingCopied = true))
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = setTimeout(() => (isShowingCopied = false), 1400)
|
||||
}
|
||||
|
|
@ -23,7 +26,9 @@
|
|||
})
|
||||
</script>
|
||||
|
||||
<div class="relative flex grow flex-col font-mono {containerClass ?? ''}">
|
||||
<div
|
||||
class="relative flex grow flex-col font-mono {containerClass ?? ''}"
|
||||
>
|
||||
<button
|
||||
class="group flex min-w-[18rem] items-center justify-center gap-4 rounded-lg border border-primary py-3 pl-6 pr-6 text-base font-medium transition-transform active:scale-[1.01] sm:rounded-full"
|
||||
on:click={$$slots.default ? undefined : copyCommand}
|
||||
|
|
|
|||
|
|
@ -1,56 +1,89 @@
|
|||
<script lang="ts">
|
||||
import clsx from 'clsx'
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import { spring, type Spring } from 'svelte/motion'
|
||||
import { convertStoreToObservable, lerp } from '$lib/Helper'
|
||||
import { inview } from 'svelte-inview'
|
||||
import { throttleTime } from 'rxjs'
|
||||
import type { Interactable } from '@interactjs/types/index'
|
||||
import { lerp } from '$lib/Helper'
|
||||
|
||||
export let image: string
|
||||
export let containerClass: string = ''
|
||||
export let size: number
|
||||
export let coordinates: readonly [number, number]
|
||||
type Props = {
|
||||
image: string
|
||||
containerClass?: string
|
||||
size: number
|
||||
coordinates: readonly [number, number]
|
||||
class?: string
|
||||
|
||||
export let quote: string | undefined = undefined
|
||||
quote?: string | undefined
|
||||
|
||||
export let isAnimating = true
|
||||
isAnimating?: boolean
|
||||
|
||||
export let element: HTMLElement | undefined = undefined
|
||||
export let imageWrapper: HTMLDivElement | undefined = undefined
|
||||
export let imageElement: HTMLImageElement | undefined = undefined
|
||||
export let style: string | undefined = undefined
|
||||
export let spawnDelay = 0
|
||||
export let spawnInstanly = false
|
||||
/**
|
||||
* Usually just the size / biggestSize
|
||||
* Goes from 0 - 1
|
||||
*/
|
||||
export let weight: number
|
||||
export let getRestrictionElement: (() => HTMLElement) | undefined = undefined
|
||||
element?: HTMLElement | undefined
|
||||
imageWrapper?: HTMLDivElement | undefined
|
||||
imageElement?: HTMLImageElement | undefined
|
||||
style?: string | undefined
|
||||
spawnDelay?: number
|
||||
spawnInstanly?: boolean
|
||||
/**
|
||||
* Usually just the size / biggestSize
|
||||
* Goes from 0 - 1
|
||||
*/
|
||||
weight: number
|
||||
getRestrictionElement?: (() => HTMLElement) | undefined
|
||||
onClick?: () => void
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
enteredView: {
|
||||
enteredView?: (data: {
|
||||
dragCoordinates: Spring<readonly [number, number]>
|
||||
imageElement: HTMLImageElement
|
||||
element: HTMLElement
|
||||
delay: number
|
||||
}) => void
|
||||
dragged?: (data: readonly [number, number]) => void
|
||||
dragStart?: (data: any) => void
|
||||
dragEnd?: (data: any) => void
|
||||
hover?: (data: any) => void
|
||||
|
||||
children?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
let {
|
||||
image,
|
||||
class: className,
|
||||
containerClass = '',
|
||||
size,
|
||||
coordinates,
|
||||
quote = undefined,
|
||||
isAnimating = true,
|
||||
element = undefined,
|
||||
imageWrapper = undefined,
|
||||
imageElement = undefined,
|
||||
style = undefined,
|
||||
spawnDelay = 0,
|
||||
spawnInstanly = false,
|
||||
/**
|
||||
* Usually just the size / biggestSize
|
||||
* Goes from 0 - 1
|
||||
*/
|
||||
weight,
|
||||
getRestrictionElement = undefined,
|
||||
onClick,
|
||||
dragEnd,
|
||||
dragStart,
|
||||
enteredView,
|
||||
hover,
|
||||
children
|
||||
}: Props = $props()
|
||||
|
||||
const dragCoordinates = spring(
|
||||
[0, 0] as readonly [number, number],
|
||||
{
|
||||
damping: lerp(0.2, 0.03, weight),
|
||||
stiffness: lerp(0.2, 0.01, weight),
|
||||
precision: 0.001
|
||||
}
|
||||
dragged: readonly [number, number]
|
||||
dragStart: any
|
||||
dragEnd: any
|
||||
hover: any
|
||||
}>()
|
||||
)
|
||||
|
||||
const dragCoordinates = spring([0, 0] as readonly [number, number], {
|
||||
damping: lerp(0.2, 0.03, weight),
|
||||
stiffness: lerp(0.2, 0.01, weight),
|
||||
// stiffness: lerp(0.81, 0.9, relativeSize),
|
||||
precision: 0.001
|
||||
})
|
||||
|
||||
let hasEnteredView = false
|
||||
let hasImageLoaded = false
|
||||
let hasEnteredView = $state(false)
|
||||
let hasImageLoaded = $state(false)
|
||||
|
||||
let interactionjs: Interactable
|
||||
|
||||
|
|
@ -63,7 +96,7 @@
|
|||
() => {
|
||||
hasEnteredView = true
|
||||
|
||||
dispatch('enteredView', {
|
||||
enteredView?.({
|
||||
dragCoordinates,
|
||||
imageElement: imageElement!,
|
||||
element: element!,
|
||||
|
|
@ -79,18 +112,14 @@
|
|||
inertia: { resistance: lerp(5, 200, weight) },
|
||||
listeners: {
|
||||
move({ dx, dy }) {
|
||||
dragCoordinates.update(([x, y]) => {
|
||||
x += dx
|
||||
y += dy
|
||||
return [x, y]
|
||||
})
|
||||
dragCoordinates.update(([x, y]) => [x + dx, y + dy])
|
||||
},
|
||||
|
||||
start(event) {
|
||||
dispatch('dragStart', event)
|
||||
dragStart?.(event)
|
||||
},
|
||||
end(event) {
|
||||
dispatch('dragEnd', event)
|
||||
dragEnd?.(event)
|
||||
}
|
||||
},
|
||||
modifiers: getRestrictionElement
|
||||
|
|
@ -105,29 +134,21 @@
|
|||
})
|
||||
}
|
||||
|
||||
const draggedSubscription = convertStoreToObservable(dragCoordinates)
|
||||
.pipe(throttleTime(80))
|
||||
.subscribe((drag) => {
|
||||
const displayedPosition = getDisplayedPosition(coordinates, drag)
|
||||
dispatch('dragged', displayedPosition)
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
// Nesecarry as the load image event might not get fired when its already loaded ( for example after a page reload )
|
||||
hasImageLoaded = hasImageLoaded || !!imageElement?.complete
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
draggedSubscription.unsubscribe()
|
||||
interactionjs?.unset()
|
||||
})
|
||||
|
||||
function getDisplayedPosition(
|
||||
origin: readonly [x: number, y: number],
|
||||
dragCoordinates: readonly [x: number, y: number]
|
||||
) {
|
||||
return [origin[0] + dragCoordinates[0], origin[1] + dragCoordinates[1]] as const
|
||||
}
|
||||
// function getDisplayedPosition(
|
||||
// origin: readonly [x: number, y: number],
|
||||
// dragCoordinates: readonly [x: number, y: number]
|
||||
// ) {
|
||||
// return [origin[0] + dragCoordinates[0], origin[1] + dragCoordinates[1]] as const
|
||||
// }
|
||||
</script>
|
||||
|
||||
<div
|
||||
|
|
@ -149,18 +170,18 @@
|
|||
style:translate={`calc( ${$dragCoordinates[0]}px ) ${$dragCoordinates[1]}px`}
|
||||
use:inview={{ unobserveOnEnter: true, threshold: 0.2 }}
|
||||
class:_animate={hasImageLoaded && isAnimating && hasEnteredView}
|
||||
on:inview_enter={onViewEnter}
|
||||
on:click
|
||||
oninview_enter={onViewEnter}
|
||||
onclick={onClick}
|
||||
>
|
||||
<div class="" bind:this={imageWrapper}>
|
||||
<img
|
||||
class="group aspect-square h-full w-full touch-none select-none rounded-[50%] object-cover outline outline-4 {$$restProps.class}"
|
||||
class="group aspect-square h-full w-full touch-none select-none rounded-[50%] object-cover outline outline-4 {className}"
|
||||
bind:this={imageElement}
|
||||
on:load={() => (hasImageLoaded = true)}
|
||||
onload={() => (hasImageLoaded = true)}
|
||||
src={image}
|
||||
alt="community profile picture"
|
||||
aria-hidden="true"
|
||||
on:mouseenter={(event) => dispatch('hover', event)}
|
||||
onmouseenter={(event) => hover?.(event)}
|
||||
class:hover:scale-125={!!quote}
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer"
|
||||
|
|
@ -170,10 +191,12 @@
|
|||
{...{
|
||||
/* @ts-ignore */
|
||||
}}
|
||||
onerror="this.__error = true"
|
||||
onerror={() => (this.__error = true)}
|
||||
{style}
|
||||
/>
|
||||
<slot />
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if quote}
|
||||
|
|
@ -186,7 +209,8 @@
|
|||
|
||||
<style lang="postcss">
|
||||
._animate {
|
||||
animation: reveal 440ms 1 var(--delay) both cubic-bezier(0, 1, 0.765, 3.8);
|
||||
animation: reveal 440ms 1 var(--delay) both
|
||||
cubic-bezier(0, 1, 0.765, 3.8);
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,35 @@
|
|||
<script>
|
||||
import DiscordIcon from '~icons/prime/discord'
|
||||
import GithubIcon from '~icons/ri/github-fill'
|
||||
import { accountsLink, discordLink, forumLink } from '$lib/constants'
|
||||
import {
|
||||
accountsLink,
|
||||
discordLink,
|
||||
forumLink
|
||||
} from '$lib/constants'
|
||||
import { forgejoLink } from '$lib/constants'
|
||||
import RssIcon from '~icons/mingcute/rss-fill'
|
||||
import ForgejoIcon from '~icons/fe/git'
|
||||
|
||||
/** @type {[string, string, string, string]} */
|
||||
const team = [
|
||||
['Fufexan', 'Supporting Developer', 'cyan', 'https://github.com/fufexan'],
|
||||
['NotAShelf', 'Community Manager', 'teal', 'https://github.com/NotAShelf'],
|
||||
['VDawg', 'Webdesign and dev', 'emerald', 'https://github.com/vdawg-git']
|
||||
[
|
||||
'Fufexan',
|
||||
'Supporting Developer',
|
||||
'cyan',
|
||||
'https://github.com/fufexan'
|
||||
],
|
||||
[
|
||||
'NotAShelf',
|
||||
'Community Manager',
|
||||
'teal',
|
||||
'https://github.com/NotAShelf'
|
||||
],
|
||||
[
|
||||
'VDawg',
|
||||
'Webdesign and dev',
|
||||
'emerald',
|
||||
'https://github.com/vdawg-git'
|
||||
]
|
||||
]
|
||||
function createRole(role, color) {
|
||||
return `<span class='text-${color}-500'><span class='text-${color}-600'>[ </span>${role}<span class='text-${color}-600'> ]</span></span>`
|
||||
|
|
@ -34,12 +53,18 @@
|
|||
</li>
|
||||
{#each team as [name, role, color, href]}
|
||||
<li>
|
||||
<a {href} target="_blank">{name} {@html createRole(role, color)}</a>
|
||||
<a {href} target="_blank"
|
||||
>{name} {@html createRole(role, color)}</a
|
||||
>
|
||||
</li>
|
||||
{/each}
|
||||
<li>
|
||||
<a href="https://github.com/hyprwm/Hyprland/graphs/contributors" target="_blank"
|
||||
>and our <span class="text-indigo-500">fellow contributors</span></a
|
||||
<a
|
||||
href="https://github.com/hyprwm/Hyprland/graphs/contributors"
|
||||
target="_blank"
|
||||
>and our <span class="text-indigo-500"
|
||||
>fellow contributors</span
|
||||
></a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
@ -48,10 +73,13 @@
|
|||
<div class="flex flex-col gap-4">
|
||||
<div class="pretitle">Links</div>
|
||||
<ul class="flex flex-col gap-3 font-medium">
|
||||
<li><a href="https://wiki.hypr.land/" target="_blank">Wiki</a></li>
|
||||
<li>
|
||||
<a href="https://wiki.hypr.land/Getting-Started/Master-Tutorial/" target="_blank"
|
||||
>Get started</a
|
||||
<a href="https://wiki.hypr.land/" target="_blank">Wiki</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://wiki.hypr.land/Getting-Started/Master-Tutorial/"
|
||||
target="_blank">Get started</a
|
||||
>
|
||||
</li>
|
||||
<li><a href="/hall_of_fame">Hall of fame</a></li>
|
||||
|
|
@ -76,7 +104,8 @@
|
|||
href="https://github.com/hyprwm/Hyprland"
|
||||
class="text-slate-400 hover:text-slate-200"
|
||||
target="_blank"
|
||||
aria-label="Go to our Github"><GithubIcon class="h-12 w-12 " /></a
|
||||
aria-label="Go to our Github"
|
||||
><GithubIcon class="h-12 w-12 " /></a
|
||||
>
|
||||
</li>
|
||||
<li class="">
|
||||
|
|
@ -92,20 +121,26 @@
|
|||
href={forgejoLink}
|
||||
class="text-slate-400 hover:text-slate-200"
|
||||
target="_blank"
|
||||
aria-label="Rss Feed"><ForgejoIcon class="h-12 w-12 " /></a
|
||||
aria-label="Rss Feed"
|
||||
><ForgejoIcon class="h-12 w-12 " /></a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full flex-wrap gap-4 text-sm font-medium text-slate-400">
|
||||
<p>Hyprland is licensed under the BSD 3-Clause "New" or "Revised" License.</p>
|
||||
<div
|
||||
class="flex w-full flex-wrap gap-4 text-sm font-medium text-slate-400"
|
||||
>
|
||||
<p>
|
||||
Hyprland is licensed under the BSD 3-Clause "New" or "Revised"
|
||||
License.
|
||||
</p>
|
||||
<p>© Hyprland Development {new Date().getFullYear()}.</p>
|
||||
<p>Stay hydrated</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gradient" aria-hidden="true" />
|
||||
<div class="gradient" aria-hidden="true"></div>
|
||||
</footer>
|
||||
|
||||
<style lang="postcss">
|
||||
|
|
@ -127,8 +162,17 @@
|
|||
width: 100%;
|
||||
height: 900px;
|
||||
z-index: -10;
|
||||
mask-image: radial-gradient(105vw 450px at 50% 50%, rgba(0, 0, 0, 1) 80%, transparent);
|
||||
background: url('/imgs/grain.webp'),
|
||||
radial-gradient(105vw 450px at 50% 50%, theme(colors.blue.600 / 80%), transparent);
|
||||
mask-image: radial-gradient(
|
||||
105vw 450px at 50% 50%,
|
||||
rgba(0, 0, 0, 1) 80%,
|
||||
transparent
|
||||
);
|
||||
background:
|
||||
url('/imgs/grain.webp'),
|
||||
radial-gradient(
|
||||
105vw 450px at 50% 50%,
|
||||
theme(colors.blue.600 / 80%),
|
||||
transparent
|
||||
);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,25 +1,35 @@
|
|||
<script>
|
||||
import { lerp } from '$lib/Helper.ts'
|
||||
<script lang="ts">
|
||||
import { lerp } from '$lib/Helper'
|
||||
import { createNoise2D } from 'simplex-noise'
|
||||
import { onMount } from 'svelte'
|
||||
import { expoIn } from 'svelte/easing'
|
||||
|
||||
/** Lifespan in milliseconds */
|
||||
export let lifeSpan = 1500
|
||||
export let maxSpeed = 20
|
||||
export let minSpeed = 4
|
||||
export let maxOpacity = 1
|
||||
export let scale = 1
|
||||
type Props = {
|
||||
/** Lifespan in milliseconds */
|
||||
lifeSpan: number
|
||||
maxSpeed: number
|
||||
minSpeed: number
|
||||
maxOpacity: number
|
||||
scale: number
|
||||
}
|
||||
|
||||
let {
|
||||
lifeSpan = 1500,
|
||||
maxSpeed = 20,
|
||||
minSpeed = 4,
|
||||
maxOpacity = 1,
|
||||
scale = 1
|
||||
}: Props = $props()
|
||||
|
||||
const isDescending = Math.random() > 0.8
|
||||
const wobbliness = lerp(0.0001, 0.004, Math.random())
|
||||
|
||||
const speed = Math.random() * (maxSpeed - minSpeed) + minSpeed
|
||||
|
||||
let x = Math.random() * 30 - 30
|
||||
let y = Math.random() * 2
|
||||
let lifeRemaining = lifeSpan
|
||||
$: lifePercentage = lifeRemaining / lifeSpan
|
||||
let x = $state(Math.random() * 30 - 30)
|
||||
let y = $state(Math.random() * 2)
|
||||
let lifeRemaining = $state(lifeSpan)
|
||||
let lifePercentage = $derived(lifeRemaining / lifeSpan)
|
||||
let timestamp = Date.now()
|
||||
|
||||
const noiseY = createNoise2D()
|
||||
|
|
@ -29,13 +39,14 @@
|
|||
const color = colors.at(Math.ceil(colors.length * Math.random()))
|
||||
|
||||
onMount(() => {
|
||||
let animationId
|
||||
let animationId: number
|
||||
let i = 0
|
||||
|
||||
animate()
|
||||
|
||||
async function animate() {
|
||||
const deltaTime = (timestamp - Date.now()) / 17 // One frame should last roughly 17ms for 60fps
|
||||
// One frame should last roughly 17ms for 60fps
|
||||
const deltaTime = (timestamp - Date.now()) / 17
|
||||
|
||||
x += noiseX(i, 1) * speed * deltaTime * expoIn(lifePercentage)
|
||||
y += noiseY(i, 1) * speed * deltaTime * expoIn(lifePercentage)
|
||||
|
|
@ -59,7 +70,9 @@
|
|||
style:translate={`${x}px ${y}px`}
|
||||
style:background={color}
|
||||
style:opacity={(lifeRemaining / lifeSpan - (1 - maxOpacity)) ** 5}
|
||||
style:scale={isDescending ? (lifeRemaining / lifeSpan - (1 - scale)) ** 2 : undefined}
|
||||
style:scale={isDescending
|
||||
? (lifeRemaining / lifeSpan - (1 - scale)) ** 2
|
||||
: undefined}
|
||||
style:top={50 + (Math.random() * 5 - 5) + '%'}
|
||||
style:left={50 + (Math.random() * 5 - 5) + '%'}
|
||||
></div>
|
||||
|
|
|
|||
|
|
@ -3,19 +3,33 @@
|
|||
import type { Sponsor } from '../../routes/api/sponsors/+server'
|
||||
import Clickable from './Clickable.svelte'
|
||||
|
||||
export let sponsor: Sponsor
|
||||
export let showImage = false
|
||||
export let showSlogan = false
|
||||
export let sloganClass = ''
|
||||
interface Props {
|
||||
sponsor: Sponsor
|
||||
showImage?: boolean
|
||||
showSlogan?: boolean
|
||||
sloganClass?: string
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
let {
|
||||
sponsor,
|
||||
showImage = false,
|
||||
showSlogan = false,
|
||||
sloganClass = '',
|
||||
...rest
|
||||
}: Props = $props()
|
||||
</script>
|
||||
|
||||
<Clickable href={sponsor.link} class="flex flex-col items-center gap-4">
|
||||
<Clickable
|
||||
href={sponsor.link}
|
||||
class="flex flex-col items-center gap-4"
|
||||
>
|
||||
{#if showImage && sponsor.image}
|
||||
<img
|
||||
title={sponsor.name}
|
||||
class={cn(
|
||||
'size-full self-center justify-self-start rounded-md object-contain',
|
||||
$$restProps.class
|
||||
rest.class
|
||||
)}
|
||||
src={sponsor.image}
|
||||
alt={sponsor.name}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,15 @@
|
|||
default: 'text-5xl md:text-7xl lg:text-8xl',
|
||||
small: 'text-3xl md:text-5xl lg:text-6xl'
|
||||
}
|
||||
export let size: keyof typeof sizes = 'default'
|
||||
interface Props {
|
||||
size?: keyof typeof sizes
|
||||
children?: import('svelte').Snippet
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
let { size = 'default', children, ...rest }: Props = $props()
|
||||
</script>
|
||||
|
||||
<h1 class={cn('mb-12 font-bold', sizes[size], $$restProps.class)}>
|
||||
<slot>No title given!!!</slot>
|
||||
<h1 class={cn('mb-12 font-bold', sizes[size], rest.class)}>
|
||||
{#if children}{@render children()}{:else}No title given!!!{/if}
|
||||
</h1>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,13 @@
|
|||
<script>
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
/** @type {Props} */
|
||||
let { children } = $props()
|
||||
</script>
|
||||
|
||||
<p class="mb-3 font-extrabold text-slate-300">
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,15 @@
|
|||
<p class="-mt-4 mb-7 font-extrabold text-slate-300 sm:text-lg {$$restProps.class}">
|
||||
<slot />
|
||||
<script>
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
/** @type {Props & { [key: string]: any }} */
|
||||
let { children, ...rest } = $props()
|
||||
</script>
|
||||
|
||||
<p
|
||||
class="-mt-4 mb-7 font-extrabold text-slate-300 sm:text-lg {rest.class}"
|
||||
>
|
||||
{@render children?.()}
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -1,26 +1,43 @@
|
|||
<script>
|
||||
import { animateIn, cn } from '$lib/Helper.ts'
|
||||
|
||||
/** @type { 'left' | 'right' | 'center'} */
|
||||
export let align = 'center'
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property { 'left' | 'right' | 'center'} [align]
|
||||
* @property {import('svelte').Snippet} [pre]
|
||||
* @property {import('svelte').Snippet} [title]
|
||||
* @property {import('svelte').Snippet} [subtitle]
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
$: alignClass =
|
||||
/** @type {Props & { [key: string]: any }} */
|
||||
let {
|
||||
align = 'center',
|
||||
pre,
|
||||
title,
|
||||
subtitle,
|
||||
children,
|
||||
...rest
|
||||
} = $props()
|
||||
|
||||
let alignClass = $derived(
|
||||
align === 'center'
|
||||
? 'text-center items-center'
|
||||
: align === 'left'
|
||||
? 'text-left items-start'
|
||||
: 'text-right items-end'
|
||||
)
|
||||
</script>
|
||||
|
||||
<hgroup
|
||||
use:animateIn={{ slide: 24, fade: 0 }}
|
||||
class={cn('z-10 flex flex-col px-3 ', alignClass, $$restProps.class)}
|
||||
class={cn('z-10 flex flex-col px-3 ', alignClass, rest.class)}
|
||||
>
|
||||
<slot name="pre" />
|
||||
{@render pre?.()}
|
||||
|
||||
<slot name="title">No title given!!!</slot>
|
||||
{#if title}{@render title()}{:else}No title given!!!{/if}
|
||||
|
||||
<slot name="subtitle" />
|
||||
{@render subtitle?.()}
|
||||
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</hgroup>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
<!-- @migration-task Error while migrating Svelte code: $$props is used together with named props in a way that cannot be automatically migrated. -->
|
||||
<script>
|
||||
import clsx from 'clsx'
|
||||
import PlayIcon from '~icons/mingcute/play-circle-line'
|
||||
|
|
|
|||
|
|
@ -2,21 +2,28 @@
|
|||
import ArrowRight from '~icons/mingcute/arrow-right-circle-line'
|
||||
|
||||
import { animateIn, formatDate } from '$lib/Helper.ts'
|
||||
export let entry
|
||||
let { entry } = $props()
|
||||
|
||||
$: link = `/news/${entry.slug}`
|
||||
let link = $derived(`/news/${entry.slug}`)
|
||||
</script>
|
||||
|
||||
<li class="flex" use:animateIn={{ fade: 0, slide: 24 }}>
|
||||
<a href={link} class="w-full transition-transform hover:-translate-y-0.5">
|
||||
<a
|
||||
href={link}
|
||||
class="w-full transition-transform hover:-translate-y-0.5"
|
||||
>
|
||||
<article
|
||||
class="flex h-[100%] flex-col justify-between gap-3 rounded hover:outline-sky-500/80 md:flex-row md:rounded-3xl md:bg-gradient-to-tr md:from-cyan-500/10 md:to-transparent md:p-8 md:shadow-xl md:outline md:outline-1 md:outline-sky-500/30"
|
||||
>
|
||||
<div>
|
||||
<div class="flex flex-col gap-4 font-medium text-slate-400">
|
||||
<p class="font-bold text-slate-400">{formatDate(entry.date)}</p>
|
||||
<p class="font-bold text-slate-400">
|
||||
{formatDate(entry.date)}
|
||||
</p>
|
||||
</div>
|
||||
<h2 class="title text-xl font-bold hover:text-slate-200 md:text-2xl lg:text-3xl">
|
||||
<h2
|
||||
class="title text-xl font-bold hover:text-slate-200 md:text-2xl lg:text-3xl"
|
||||
>
|
||||
{entry.title}
|
||||
</h2>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,64 +1,184 @@
|
|||
<svg viewBox="0 330 1006.49 347.4685344827586" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" style="width: 6rem; height: 2rem;" class={$$props.class} width="1006.49" height="347.4685344827586">
|
||||
<defs>
|
||||
<style>
|
||||
.st0 {
|
||||
fill: url(#a);
|
||||
}
|
||||
<script>
|
||||
/** @type {{ [key: string]: any }} */
|
||||
let { ...props } = $props()
|
||||
</script>
|
||||
|
||||
.st1 {
|
||||
fill: url(#b);
|
||||
}
|
||||
<svg
|
||||
viewBox="0 330 1006.49 347.4685344827586"
|
||||
version="1.1"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style="width: 6rem; height: 2rem;"
|
||||
class={props.class}
|
||||
width="1006.49"
|
||||
height="347.4685344827586"
|
||||
>
|
||||
<defs>
|
||||
<style>
|
||||
.st0 {
|
||||
fill: url(#a);
|
||||
}
|
||||
|
||||
.st2 {
|
||||
fill: url(#c);
|
||||
}
|
||||
.st1 {
|
||||
fill: url(#b);
|
||||
}
|
||||
|
||||
.st3 {
|
||||
fill: url(#d);
|
||||
}
|
||||
.st2 {
|
||||
fill: url(#c);
|
||||
}
|
||||
|
||||
.st4 {
|
||||
fill: url(#e);
|
||||
}
|
||||
.st3 {
|
||||
fill: url(#d);
|
||||
}
|
||||
|
||||
.st5 {
|
||||
fill: url(#f);
|
||||
}
|
||||
.st4 {
|
||||
fill: url(#e);
|
||||
}
|
||||
|
||||
.st6 {
|
||||
fill: url(#g);
|
||||
}
|
||||
.st5 {
|
||||
fill: url(#f);
|
||||
}
|
||||
|
||||
.st7 {
|
||||
fill: url(#h);
|
||||
}
|
||||
.st6 {
|
||||
fill: url(#g);
|
||||
}
|
||||
|
||||
.st8 {
|
||||
fill: url(#i);
|
||||
}
|
||||
</style><style class="darkreader darkreader--sync" media="screen"/>
|
||||
<linearGradient gradientUnits="userSpaceOnUse" y2="491.29" x2="561.93" y1="593.85" x1="561.93" id="i">
|
||||
<stop stop-color="#00a8f4" offset="0" style="--darkreader-inline-stopcolor: var(--darkreader-background-00a8f4, #0086c3);" data-darkreader-inline-stopcolor=""/>
|
||||
<stop stop-color="#00e5d0" offset="1" style="--darkreader-inline-stopcolor: var(--darkreader-background-00e5d0, #00b7a6);" data-darkreader-inline-stopcolor=""/>
|
||||
</linearGradient>
|
||||
<linearGradient xlink:href="#i" y2="491.58" x2="630.69" y1="567.48" x1="630.69" id="c"/>
|
||||
<linearGradient xlink:href="#i" y2="493.77" x2="480.34" y1="596.04" x1="480.34" id="a"/>
|
||||
<linearGradient xlink:href="#i" y2="470.31" x2="392.86" y1="567.48" x1="392.86" id="b"/>
|
||||
<linearGradient xlink:href="#i" y2="491.73" x2="804.79" y1="567.48" x1="804.79" id="f"/>
|
||||
<linearGradient xlink:href="#i" y2="463.46" x2="883.97" y1="569.67" x1="883.97" id="g"/>
|
||||
<linearGradient xlink:href="#i" y2="465.94" x2="671.56" y1="567.48" x1="671.56" id="d"/>
|
||||
<linearGradient xlink:href="#i" y2="491.58" x2="724.23" y1="569.67" x1="724.23" id="e"/>
|
||||
<linearGradient xlink:href="#i" y2="346.78" x2="202.09" y1="659.7" x1="202.09" id="h"/>
|
||||
</defs>
|
||||
<g>
|
||||
<path d="M580.29,495c-5.59-2.48-12.07-3.71-19.45-3.71-4.86,0-9.4.36-13.62,1.09-4.22.73-7.94,1.82-11.15,3.28-3.2,1.46-5.73,3.3-7.58,5.54-1.85,2.24-2.77,4.95-2.77,8.16v84.5h21.27v-28.09c1.05.58,2.16,1.1,3.35,1.5,3.01,1.02,5.92,1.67,8.74,1.97,2.82.29,4.95.44,6.41.44,6.9,0,12.75-1.6,17.56-4.81,4.81-3.21,8.52-7.7,11.15-13.48,2.62-5.78,3.93-12.46,3.93-20.03,0-10.1-1.58-18.07-4.73-23.89-3.16-5.83-7.53-9.98-13.11-12.46ZM574.46,543.37c-1.31,3.16-3.23,5.42-5.75,6.77-2.53,1.36-5.73,2.04-9.62,2.04-1.75,0-3.74-.24-5.97-.73-2.24-.48-4.28-1.26-6.12-2.33v-34.67c0-1.46.99-2.64,2.99-3.57,1.99-.92,5.32-1.38,9.98-1.38,4.27,0,7.6.85,9.98,2.55,2.38,1.7,4.05,4.1,5.03,7.21.97,3.11,1.46,6.9,1.46,11.36,0,5.34-.66,9.59-1.97,12.75Z" class="st8"/>
|
||||
<path d="M638.78,491.58c-4.57,0-9.11.61-13.62,1.82-4.52,1.22-8.52,2.57-12.02,4.08-3.5,1.51-5.93,2.84-7.28,4.01v66h21.71v-56.96c1.65-.29,3.69-.48,6.12-.58,2.43-.1,5-.12,7.72-.07,2.72.05,5.34.17,7.87.36,2.52.2,4.61.49,6.26.87v-15.88c-1.36-1.07-3.35-1.94-5.97-2.62-2.62-.68-6.22-1.02-10.78-1.02Z" class="st2"/>
|
||||
<path d="M481.21,542.1l-15.66-48.33h-24.91l29.61,72.88c-.37,1.26-.77,2.39-1.21,3.39-1.17,2.67-2.65,4.56-4.44,5.68-1.8,1.12-3.91,1.68-6.34,1.68-1.46,0-2.87-.12-4.22-.36-1.36-.24-2.82-.66-4.37-1.24v17.63c1.65.78,3.64,1.41,5.97,1.89,2.33.48,4.71.73,7.14.73,3.5,0,6.9-.87,10.2-2.62,3.3-1.75,6.48-4.61,9.54-8.6,3.06-3.98,5.9-9.37,8.52-16.17l28.99-74.89h-23.31l-15.52,48.33Z" class="st0"/>
|
||||
<polygon points="410.78 509.65 375.08 509.65 375.08 470.31 351.48 470.31 351.48 567.48 375.08 567.48 375.08 528.88 410.78 528.88 410.78 567.48 434.23 567.48 434.23 470.31 410.78 470.31 410.78 509.65" class="st1"/>
|
||||
<path d="M825.12,494.28c-4.66-1.7-10.3-2.55-16.9-2.55-3.11,0-6.48.24-10.13.73-3.64.49-7.26,1.12-10.85,1.89-3.59.78-6.9,1.68-9.91,2.7-3.01,1.02-5.44,2.02-7.28,2.99v67.45h21.71v-55.36c1.17-.58,2.86-1.14,5.1-1.67,2.23-.53,4.47-.8,6.7-.8,2.62,0,4.86.32,6.7.95,1.84.63,3.3,1.51,4.37,2.62,1.07,1.12,1.87,2.45,2.4,4.01.53,1.56.8,3.26.8,5.1v45.16h21.71v-49.54c0-6.51-1.24-11.65-3.71-15.44-2.48-3.79-6.05-6.53-10.71-8.23Z" class="st5"/>
|
||||
<path d="M899.27,463.46v31.53c-.93-.55-1.9-1.01-2.91-1.37-2.72-.97-5.34-1.58-7.87-1.82-2.53-.24-4.57-.36-6.12-.36-11.75,0-20.57,3.45-26.44,10.34-5.88,6.9-8.81,16.32-8.81,28.26,0,7.67.99,14.08,2.99,19.23,1.99,5.15,4.73,9.2,8.23,12.17,3.5,2.96,7.65,5.08,12.46,6.34,4.81,1.26,10.08,1.89,15.81,1.89,4.27,0,8.45-.46,12.53-1.38,4.08-.92,7.74-2.26,11-4.01,3.25-1.75,5.85-3.96,7.79-6.63,1.94-2.67,2.91-5.8,2.91-9.4v-84.79h-21.56ZM899.27,545.92c0,1.85-1.09,3.23-3.28,4.15-2.19.92-5.03,1.38-8.52,1.38-4.57,0-8.18-.85-10.85-2.55-2.67-1.7-4.59-4.08-5.75-7.14-1.17-3.06-1.75-6.72-1.75-11,0-5.44.7-9.76,2.11-12.97,1.41-3.2,3.52-5.49,6.34-6.85,2.82-1.36,6.26-2.04,10.34-2.04,1.84,0,3.76.27,5.75.8,1.99.54,3.86,1.29,5.61,2.26v33.95Z" class="st6"/>
|
||||
<rect height="101.55" width="21.56" y="465.94" x="660.78" class="st3"/>
|
||||
<path d="M750.3,498.72c-2.38-1.94-4.98-3.42-7.79-4.44-2.82-1.02-5.63-1.72-8.45-2.11-2.82-.39-5.39-.58-7.72-.58-6.61,0-12.55.68-17.85,2.04-5.3,1.36-9.2,2.82-11.73,4.37v19.23c2.72-2.14,6.24-3.96,10.56-5.46,4.32-1.5,8.62-2.26,12.89-2.26,5.24,0,9.3,1,12.17,2.99,2.86,1.99,4.3,5.22,4.3,9.69v4.68c-.7-.4-1.44-.8-2.26-1.19-2.48-1.17-5.27-2.09-8.38-2.77-3.11-.68-6.32-1.02-9.62-1.02-5.93,0-10.83.92-14.71,2.77-3.89,1.85-6.77,4.47-8.67,7.87-1.89,3.4-2.84,7.38-2.84,11.95,0,4.86.82,8.91,2.48,12.16,1.65,3.26,3.96,5.83,6.92,7.72,2.96,1.89,6.41,3.25,10.34,4.08,3.93.82,8.13,1.24,12.6,1.24,7.48,0,13.86-.58,19.16-1.75,5.29-1.17,9.35-3.03,12.16-5.61,2.82-2.57,4.23-5.95,4.23-10.13l.15-34.82c0-4.47-.73-8.21-2.19-11.22-1.46-3.01-3.38-5.49-5.75-7.43ZM733.7,553.35c-1.99.68-5.46,1.02-10.42,1.02-1.94,0-3.81-.39-5.61-1.17-1.8-.78-3.23-1.89-4.3-3.35-1.07-1.46-1.6-3.11-1.6-4.95,0-3.01,1.09-5.22,3.28-6.63,2.19-1.41,5.66-2.11,10.42-2.11,3.11,0,6.02.34,8.74,1.02.87.22,1.69.47,2.48.74v11.36c0,2.04-1,3.4-2.99,4.08Z" class="st4"/>
|
||||
</g>
|
||||
<path d="M311.03,491.55c-9.09-20.56-22.42-39.71-35.29-58.22-2.38-3.41-4.62-6.64-6.84-9.87-3.15-4.6-7.42-10.49-12.36-17.31-11.29-15.59-28.92-39.62-40.84-59.36v49.42c12.28,17.62,24.2,33.49,30.57,42.78,13.94,20.33,30.09,42,39.67,63.66,28.78,65.13-11.7,128.85-82.05,129.61h-1.26c-.18,0-.35,0-.53,0-.18,0-.35,0-.53,0h-1.26c-70.35-.76-110.84-64.48-82.05-129.61,9.58-21.67,25.72-43.33,39.67-63.66,6.36-9.28,18.28-25.16,30.57-42.78v-49.42c-11.92,19.75-29.55,43.78-40.84,59.36-4.94,6.81-9.21,12.7-12.36,17.31-2.22,3.23-4.46,6.45-6.84,9.87-12.88,18.52-26.21,37.67-35.29,58.22-8.83,19.97-12.7,40.38-11.49,60.65,1.16,19.65,7.28,38.57,17.68,54.73,10.28,15.97,24.74,29.21,41.81,38.29,17.64,9.37,37.44,14.25,58.87,14.48.52,0,1.03,0,1.56,0,.18,0,.35,0,.53,0,.18,0,.35,0,.53,0,.52,0,1.03,0,1.56,0,21.43-.23,41.23-5.1,58.87-14.48,17.07-9.08,31.52-22.32,41.81-38.29,10.4-16.16,16.52-35.08,17.68-54.73,1.2-20.27-2.67-40.68-11.49-60.65Z" class="st7"/>
|
||||
.st7 {
|
||||
fill: url(#h);
|
||||
}
|
||||
|
||||
.st8 {
|
||||
fill: url(#i);
|
||||
}
|
||||
</style><style
|
||||
class="darkreader darkreader--sync"
|
||||
media="screen"
|
||||
></style>
|
||||
<linearGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="491.29"
|
||||
x2="561.93"
|
||||
y1="593.85"
|
||||
x1="561.93"
|
||||
id="i"
|
||||
>
|
||||
<stop
|
||||
stop-color="#00a8f4"
|
||||
offset="0"
|
||||
style="--darkreader-inline-stopcolor: var(--darkreader-background-00a8f4, #0086c3);"
|
||||
data-darkreader-inline-stopcolor=""
|
||||
/>
|
||||
<stop
|
||||
stop-color="#00e5d0"
|
||||
offset="1"
|
||||
style="--darkreader-inline-stopcolor: var(--darkreader-background-00e5d0, #00b7a6);"
|
||||
data-darkreader-inline-stopcolor=""
|
||||
/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#i"
|
||||
y2="491.58"
|
||||
x2="630.69"
|
||||
y1="567.48"
|
||||
x1="630.69"
|
||||
id="c"
|
||||
/>
|
||||
<linearGradient
|
||||
xlink:href="#i"
|
||||
y2="493.77"
|
||||
x2="480.34"
|
||||
y1="596.04"
|
||||
x1="480.34"
|
||||
id="a"
|
||||
/>
|
||||
<linearGradient
|
||||
xlink:href="#i"
|
||||
y2="470.31"
|
||||
x2="392.86"
|
||||
y1="567.48"
|
||||
x1="392.86"
|
||||
id="b"
|
||||
/>
|
||||
<linearGradient
|
||||
xlink:href="#i"
|
||||
y2="491.73"
|
||||
x2="804.79"
|
||||
y1="567.48"
|
||||
x1="804.79"
|
||||
id="f"
|
||||
/>
|
||||
<linearGradient
|
||||
xlink:href="#i"
|
||||
y2="463.46"
|
||||
x2="883.97"
|
||||
y1="569.67"
|
||||
x1="883.97"
|
||||
id="g"
|
||||
/>
|
||||
<linearGradient
|
||||
xlink:href="#i"
|
||||
y2="465.94"
|
||||
x2="671.56"
|
||||
y1="567.48"
|
||||
x1="671.56"
|
||||
id="d"
|
||||
/>
|
||||
<linearGradient
|
||||
xlink:href="#i"
|
||||
y2="491.58"
|
||||
x2="724.23"
|
||||
y1="569.67"
|
||||
x1="724.23"
|
||||
id="e"
|
||||
/>
|
||||
<linearGradient
|
||||
xlink:href="#i"
|
||||
y2="346.78"
|
||||
x2="202.09"
|
||||
y1="659.7"
|
||||
x1="202.09"
|
||||
id="h"
|
||||
/>
|
||||
</defs>
|
||||
<g>
|
||||
<path
|
||||
d="M580.29,495c-5.59-2.48-12.07-3.71-19.45-3.71-4.86,0-9.4.36-13.62,1.09-4.22.73-7.94,1.82-11.15,3.28-3.2,1.46-5.73,3.3-7.58,5.54-1.85,2.24-2.77,4.95-2.77,8.16v84.5h21.27v-28.09c1.05.58,2.16,1.1,3.35,1.5,3.01,1.02,5.92,1.67,8.74,1.97,2.82.29,4.95.44,6.41.44,6.9,0,12.75-1.6,17.56-4.81,4.81-3.21,8.52-7.7,11.15-13.48,2.62-5.78,3.93-12.46,3.93-20.03,0-10.1-1.58-18.07-4.73-23.89-3.16-5.83-7.53-9.98-13.11-12.46ZM574.46,543.37c-1.31,3.16-3.23,5.42-5.75,6.77-2.53,1.36-5.73,2.04-9.62,2.04-1.75,0-3.74-.24-5.97-.73-2.24-.48-4.28-1.26-6.12-2.33v-34.67c0-1.46.99-2.64,2.99-3.57,1.99-.92,5.32-1.38,9.98-1.38,4.27,0,7.6.85,9.98,2.55,2.38,1.7,4.05,4.1,5.03,7.21.97,3.11,1.46,6.9,1.46,11.36,0,5.34-.66,9.59-1.97,12.75Z"
|
||||
class="st8"
|
||||
/>
|
||||
<path
|
||||
d="M638.78,491.58c-4.57,0-9.11.61-13.62,1.82-4.52,1.22-8.52,2.57-12.02,4.08-3.5,1.51-5.93,2.84-7.28,4.01v66h21.71v-56.96c1.65-.29,3.69-.48,6.12-.58,2.43-.1,5-.12,7.72-.07,2.72.05,5.34.17,7.87.36,2.52.2,4.61.49,6.26.87v-15.88c-1.36-1.07-3.35-1.94-5.97-2.62-2.62-.68-6.22-1.02-10.78-1.02Z"
|
||||
class="st2"
|
||||
/>
|
||||
<path
|
||||
d="M481.21,542.1l-15.66-48.33h-24.91l29.61,72.88c-.37,1.26-.77,2.39-1.21,3.39-1.17,2.67-2.65,4.56-4.44,5.68-1.8,1.12-3.91,1.68-6.34,1.68-1.46,0-2.87-.12-4.22-.36-1.36-.24-2.82-.66-4.37-1.24v17.63c1.65.78,3.64,1.41,5.97,1.89,2.33.48,4.71.73,7.14.73,3.5,0,6.9-.87,10.2-2.62,3.3-1.75,6.48-4.61,9.54-8.6,3.06-3.98,5.9-9.37,8.52-16.17l28.99-74.89h-23.31l-15.52,48.33Z"
|
||||
class="st0"
|
||||
/>
|
||||
<polygon
|
||||
points="410.78 509.65 375.08 509.65 375.08 470.31 351.48 470.31 351.48 567.48 375.08 567.48 375.08 528.88 410.78 528.88 410.78 567.48 434.23 567.48 434.23 470.31 410.78 470.31 410.78 509.65"
|
||||
class="st1"
|
||||
/>
|
||||
<path
|
||||
d="M825.12,494.28c-4.66-1.7-10.3-2.55-16.9-2.55-3.11,0-6.48.24-10.13.73-3.64.49-7.26,1.12-10.85,1.89-3.59.78-6.9,1.68-9.91,2.7-3.01,1.02-5.44,2.02-7.28,2.99v67.45h21.71v-55.36c1.17-.58,2.86-1.14,5.1-1.67,2.23-.53,4.47-.8,6.7-.8,2.62,0,4.86.32,6.7.95,1.84.63,3.3,1.51,4.37,2.62,1.07,1.12,1.87,2.45,2.4,4.01.53,1.56.8,3.26.8,5.1v45.16h21.71v-49.54c0-6.51-1.24-11.65-3.71-15.44-2.48-3.79-6.05-6.53-10.71-8.23Z"
|
||||
class="st5"
|
||||
/>
|
||||
<path
|
||||
d="M899.27,463.46v31.53c-.93-.55-1.9-1.01-2.91-1.37-2.72-.97-5.34-1.58-7.87-1.82-2.53-.24-4.57-.36-6.12-.36-11.75,0-20.57,3.45-26.44,10.34-5.88,6.9-8.81,16.32-8.81,28.26,0,7.67.99,14.08,2.99,19.23,1.99,5.15,4.73,9.2,8.23,12.17,3.5,2.96,7.65,5.08,12.46,6.34,4.81,1.26,10.08,1.89,15.81,1.89,4.27,0,8.45-.46,12.53-1.38,4.08-.92,7.74-2.26,11-4.01,3.25-1.75,5.85-3.96,7.79-6.63,1.94-2.67,2.91-5.8,2.91-9.4v-84.79h-21.56ZM899.27,545.92c0,1.85-1.09,3.23-3.28,4.15-2.19.92-5.03,1.38-8.52,1.38-4.57,0-8.18-.85-10.85-2.55-2.67-1.7-4.59-4.08-5.75-7.14-1.17-3.06-1.75-6.72-1.75-11,0-5.44.7-9.76,2.11-12.97,1.41-3.2,3.52-5.49,6.34-6.85,2.82-1.36,6.26-2.04,10.34-2.04,1.84,0,3.76.27,5.75.8,1.99.54,3.86,1.29,5.61,2.26v33.95Z"
|
||||
class="st6"
|
||||
/>
|
||||
<rect
|
||||
height="101.55"
|
||||
width="21.56"
|
||||
y="465.94"
|
||||
x="660.78"
|
||||
class="st3"
|
||||
/>
|
||||
<path
|
||||
d="M750.3,498.72c-2.38-1.94-4.98-3.42-7.79-4.44-2.82-1.02-5.63-1.72-8.45-2.11-2.82-.39-5.39-.58-7.72-.58-6.61,0-12.55.68-17.85,2.04-5.3,1.36-9.2,2.82-11.73,4.37v19.23c2.72-2.14,6.24-3.96,10.56-5.46,4.32-1.5,8.62-2.26,12.89-2.26,5.24,0,9.3,1,12.17,2.99,2.86,1.99,4.3,5.22,4.3,9.69v4.68c-.7-.4-1.44-.8-2.26-1.19-2.48-1.17-5.27-2.09-8.38-2.77-3.11-.68-6.32-1.02-9.62-1.02-5.93,0-10.83.92-14.71,2.77-3.89,1.85-6.77,4.47-8.67,7.87-1.89,3.4-2.84,7.38-2.84,11.95,0,4.86.82,8.91,2.48,12.16,1.65,3.26,3.96,5.83,6.92,7.72,2.96,1.89,6.41,3.25,10.34,4.08,3.93.82,8.13,1.24,12.6,1.24,7.48,0,13.86-.58,19.16-1.75,5.29-1.17,9.35-3.03,12.16-5.61,2.82-2.57,4.23-5.95,4.23-10.13l.15-34.82c0-4.47-.73-8.21-2.19-11.22-1.46-3.01-3.38-5.49-5.75-7.43ZM733.7,553.35c-1.99.68-5.46,1.02-10.42,1.02-1.94,0-3.81-.39-5.61-1.17-1.8-.78-3.23-1.89-4.3-3.35-1.07-1.46-1.6-3.11-1.6-4.95,0-3.01,1.09-5.22,3.28-6.63,2.19-1.41,5.66-2.11,10.42-2.11,3.11,0,6.02.34,8.74,1.02.87.22,1.69.47,2.48.74v11.36c0,2.04-1,3.4-2.99,4.08Z"
|
||||
class="st4"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M311.03,491.55c-9.09-20.56-22.42-39.71-35.29-58.22-2.38-3.41-4.62-6.64-6.84-9.87-3.15-4.6-7.42-10.49-12.36-17.31-11.29-15.59-28.92-39.62-40.84-59.36v49.42c12.28,17.62,24.2,33.49,30.57,42.78,13.94,20.33,30.09,42,39.67,63.66,28.78,65.13-11.7,128.85-82.05,129.61h-1.26c-.18,0-.35,0-.53,0-.18,0-.35,0-.53,0h-1.26c-70.35-.76-110.84-64.48-82.05-129.61,9.58-21.67,25.72-43.33,39.67-63.66,6.36-9.28,18.28-25.16,30.57-42.78v-49.42c-11.92,19.75-29.55,43.78-40.84,59.36-4.94,6.81-9.21,12.7-12.36,17.31-2.22,3.23-4.46,6.45-6.84,9.87-12.88,18.52-26.21,37.67-35.29,58.22-8.83,19.97-12.7,40.38-11.49,60.65,1.16,19.65,7.28,38.57,17.68,54.73,10.28,15.97,24.74,29.21,41.81,38.29,17.64,9.37,37.44,14.25,58.87,14.48.52,0,1.03,0,1.56,0,.18,0,.35,0,.53,0,.18,0,.35,0,.53,0,.52,0,1.03,0,1.56,0,21.43-.23,41.23-5.1,58.87-14.48,17.07-9.08,31.52-22.32,41.81-38.29,10.4-16.16,16.52-35.08,17.68-54.73,1.2-20.27-2.67-40.68-11.49-60.65Z"
|
||||
class="st7"
|
||||
/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.9 KiB |
|
|
@ -1,18 +1,27 @@
|
|||
export async function getNews() {
|
||||
const posts = Object.entries(import.meta.glob('/src/content/news/*.md', { eager: true }))
|
||||
const posts = Object.entries(
|
||||
import.meta.glob('/src/content/news/*.md', { eager: true })
|
||||
)
|
||||
.flatMap(([path, { metadata }]) => {
|
||||
const slug = path.split('/').at(-1)?.replace('.md', '')
|
||||
const rawDate = metadata.date
|
||||
const date = new Date(typeof rawDate === 'number' ? rawDate * 1000 : rawDate).getTime()
|
||||
const date = new Date(
|
||||
typeof rawDate === 'number' ? rawDate * 1000 : rawDate
|
||||
).getTime()
|
||||
|
||||
if (!slug || !path || Number.isNaN(date)) {
|
||||
console.error(`Invalid file ${path} ${JSON.stringify({ ...metadata, date, slug })}`)
|
||||
console.error(
|
||||
`Invalid file ${path} ${JSON.stringify({ ...metadata, date, slug })}`
|
||||
)
|
||||
return []
|
||||
}
|
||||
|
||||
return { slug, ...metadata, date }
|
||||
})
|
||||
.sort(({ date: a }, { date: b }) => new Date(b).getTime() - new Date(a).getTime())
|
||||
.sort(
|
||||
({ date: a }, { date: b }) =>
|
||||
new Date(b).getTime() - new Date(a).getTime()
|
||||
)
|
||||
|
||||
return posts
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,19 @@
|
|||
import './styles.css'
|
||||
import '@fontsource-variable/inter'
|
||||
import '@fontsource/ibm-plex-mono/500.css'
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
/** @type {Props} */
|
||||
let { children } = $props()
|
||||
</script>
|
||||
|
||||
<Navbar />
|
||||
|
||||
<main class="mx-auto flex min-h-screen w-full flex-col">
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ export const load = async ({ fetch }) => ({
|
|||
.then((response) => response.json())
|
||||
.then((news) => news.slice(0, 3)),
|
||||
|
||||
sponsors: (await fetch('/api/sponsors').then((resposne) => resposne.json())) as SponsorsRanked,
|
||||
sponsors: (await fetch('/api/sponsors').then((resposne) =>
|
||||
resposne.json()
|
||||
)) as SponsorsRanked,
|
||||
|
||||
communityProfiles: (await fetch('/api/community').then((response) =>
|
||||
response.json()
|
||||
|
|
@ -20,7 +22,11 @@ function getHeroBackgroundTiles() {
|
|||
const workspacesPerRow = 4
|
||||
const workspaceHeight = 240
|
||||
const gapLength = 32
|
||||
const colors = [baseColors.blue[500], baseColors.cyan[400], baseColors.sky[500]]
|
||||
const colors = [
|
||||
baseColors.blue[500],
|
||||
baseColors.cyan[400],
|
||||
baseColors.sky[500]
|
||||
]
|
||||
const images = [
|
||||
'/imgs/chan/joy.svg',
|
||||
'/imgs/chan/surprise.svg',
|
||||
|
|
@ -28,9 +34,13 @@ function getHeroBackgroundTiles() {
|
|||
'/imgs/waylnad.webp'
|
||||
]
|
||||
|
||||
const leftColumns = Array.from({ length: 3 }, () => generateRow(workspacesPerRow))
|
||||
const leftColumns = Array.from({ length: 3 }, () =>
|
||||
generateRow(workspacesPerRow)
|
||||
)
|
||||
|
||||
const rightColumns = Array.from({ length: 3 }, () => generateRow(workspacesPerRow))
|
||||
const rightColumns = Array.from({ length: 3 }, () =>
|
||||
generateRow(workspacesPerRow)
|
||||
)
|
||||
|
||||
/** Used to transform the rows by their own lenght*/
|
||||
const height = workspacesPerRow * (workspaceHeight + gapLength)
|
||||
|
|
@ -57,13 +67,19 @@ function getHeroBackgroundTiles() {
|
|||
}
|
||||
|
||||
function generateTiles() {
|
||||
const result = Math.random() > 0.5 ? [generateTile()] : [generateTile(), generateTile()]
|
||||
const result =
|
||||
Math.random() > 0.5
|
||||
? [generateTile()]
|
||||
: [generateTile(), generateTile()]
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function generateTile() {
|
||||
return { color: getRandomColor(), image: Math.random() > 0.7 ? getRandomImage() : undefined }
|
||||
return {
|
||||
color: getRandomColor(),
|
||||
image: Math.random() > 0.7 ? getRandomImage() : undefined
|
||||
}
|
||||
}
|
||||
|
||||
/** @returns {string} */
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
import Sponsors from './home-slices/SponsorsSlice.svelte'
|
||||
import CommunitySlice from './home-slices/CommunitySlice.svelte'
|
||||
|
||||
export let data
|
||||
let { data } = $props()
|
||||
</script>
|
||||
|
||||
<div class="overflow-hidden">
|
||||
|
|
@ -39,10 +39,16 @@
|
|||
|
||||
<svelte:head>
|
||||
<title>Hyprland</title>
|
||||
<meta name="description" content="Hyprland - Dynamic tiling Wayland compositor with the looks." />
|
||||
<meta
|
||||
name="description"
|
||||
content="Hyprland - Dynamic tiling Wayland compositor with the looks."
|
||||
/>
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Hyprland - Dynamic tiling Wayland compositor with the looks."
|
||||
/>
|
||||
<meta property="og:title" content="Hyprland: Dynamic tiling window compositor with the looks" />
|
||||
<meta
|
||||
property="og:title"
|
||||
content="Hyprland: Dynamic tiling window compositor with the looks"
|
||||
/>
|
||||
</svelte:head>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy'
|
||||
|
||||
import { navigating, page } from '$app/stores'
|
||||
import GithubIcon from '~icons/ri/github-fill'
|
||||
import DiscordIcon from '~icons/prime/discord'
|
||||
|
|
@ -9,9 +11,11 @@
|
|||
import { discordLink } from '$lib/constants'
|
||||
import { forgejoLink } from '$lib/constants'
|
||||
|
||||
let isExpanded = false
|
||||
let isExpanded = $state(false)
|
||||
|
||||
$: if ($navigating) isExpanded = false
|
||||
run(() => {
|
||||
if ($navigating) isExpanded = false
|
||||
})
|
||||
|
||||
function toggleExpanded() {
|
||||
isExpanded = !isExpanded
|
||||
|
|
@ -30,7 +34,7 @@
|
|||
|
||||
<button
|
||||
class="z-50 rounded-full bg-black/50 p-2 md:backdrop-blur nav:hidden"
|
||||
on:click={toggleExpanded}
|
||||
onclick={toggleExpanded}
|
||||
aria-label="Open Navigation"
|
||||
>
|
||||
{#if isExpanded}
|
||||
|
|
@ -60,19 +64,36 @@
|
|||
<li>
|
||||
<a href="https://account.hypr.land">Account</a>
|
||||
</li>
|
||||
<li aria-current={$page.url.pathname === '/about' ? 'page' : undefined}>
|
||||
<li
|
||||
aria-current={$page.url.pathname === '/about'
|
||||
? 'page'
|
||||
: undefined}
|
||||
>
|
||||
<a href="/hall_of_fame">Hall of fame</a>
|
||||
</li>
|
||||
<li aria-current={$page.url.pathname === '/news' ? 'page' : undefined}>
|
||||
<li
|
||||
aria-current={$page.url.pathname === '/news'
|
||||
? 'page'
|
||||
: undefined}
|
||||
>
|
||||
<a href="/news">News</a>
|
||||
</li>
|
||||
<li aria-current={$page.url.pathname === '/plugins' ? 'page' : undefined}>
|
||||
<li
|
||||
aria-current={$page.url.pathname === '/plugins'
|
||||
? 'page'
|
||||
: undefined}
|
||||
>
|
||||
<a href="/plugins">Plugins</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="flex flex-row items-center gap-3 px-4">
|
||||
<li>
|
||||
<a href={discordLink} class="social-icon" aria-label="Join us on Discord" target="_blank">
|
||||
<a
|
||||
href={discordLink}
|
||||
class="social-icon"
|
||||
aria-label="Join us on Discord"
|
||||
target="_blank"
|
||||
>
|
||||
<DiscordIcon class="h-full w-full" />
|
||||
</a>
|
||||
</li>
|
||||
|
|
@ -99,7 +120,11 @@
|
|||
</ul>
|
||||
|
||||
<ul class="flex gap-4">
|
||||
<li aria-current={$page.url.pathname === '/support' ? 'page' : undefined}>
|
||||
<li
|
||||
aria-current={$page.url.pathname === '/support'
|
||||
? 'page'
|
||||
: undefined}
|
||||
>
|
||||
<a
|
||||
class="rounded-full px-4 py-2 outline outline-cyan-500 hover:outline-cyan-200"
|
||||
href="/support">Support us</a
|
||||
|
|
|
|||
|
|
@ -36,14 +36,22 @@ const extraProfiles: CommunityProfile[] = [
|
|||
}
|
||||
]
|
||||
|
||||
const validSizes = [16, 20, 24, 32, 40, 48, 64, 80, 96, 100, 128, 160, 240, 320, 640]
|
||||
const validSizes = [
|
||||
16, 20, 24, 32, 40, 48, 64, 80, 96, 100, 128, 160, 240, 320, 640
|
||||
]
|
||||
|
||||
export async function GET() {
|
||||
const allProfiles = [...profiles.filter(({ image }) => !!image), ...extraProfiles]
|
||||
const allProfiles = [
|
||||
...profiles.filter(({ image }) => !!image),
|
||||
...extraProfiles
|
||||
]
|
||||
.map(({ image, size, ...profile }) => ({
|
||||
...profile,
|
||||
size,
|
||||
image: image + '?size=' + validSizes.find((_, index) => size <= validSizes[index])
|
||||
image:
|
||||
image +
|
||||
'?size=' +
|
||||
validSizes.find((_, index) => size <= validSizes[index])
|
||||
}))
|
||||
.sort(({ size: a }, { size: b }) => b - a)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@
|
|||
import Contest from './Contest.svelte'
|
||||
import FamedRice from './FamedRice.svelte'
|
||||
|
||||
export let data
|
||||
let { data } = $props()
|
||||
</script>
|
||||
|
||||
<div class="fancy-top-gradient" />
|
||||
<div class="fancy-top-gradient"></div>
|
||||
|
||||
<section>
|
||||
<div class="hero-wrapper">
|
||||
|
|
@ -19,10 +19,15 @@
|
|||
</div>
|
||||
</div>
|
||||
<Title>
|
||||
<TitleHeading slot="title" class="">Hall of Fame</TitleHeading>
|
||||
<TitleSubtile slot="subtitle" class="class-w-[40ch]">
|
||||
The chronicles of the triumphant from bygone rice contests held within our Discord
|
||||
</TitleSubtile>
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Hall of Fame</TitleHeading>
|
||||
{/snippet}
|
||||
{#snippet subtitle()}
|
||||
<TitleSubtile class="class-w-[40ch]">
|
||||
The chronicles of the triumphant from bygone rice contests
|
||||
held within our Discord
|
||||
</TitleSubtile>
|
||||
{/snippet}
|
||||
</Title>
|
||||
</div>
|
||||
|
||||
|
|
@ -45,7 +50,10 @@
|
|||
|
||||
<svelte:head>
|
||||
<title>Hall of Fame | Hyprland</title>
|
||||
<meta name="description" content="The winners from Hyprlands rice contests" />
|
||||
<meta
|
||||
name="description"
|
||||
content="The winners from Hyprlands rice contests"
|
||||
/>
|
||||
<meta property="og:title" content="Hyprland's Hall of Fame" />
|
||||
<meta
|
||||
property="og:description"
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
<script>
|
||||
import { inview } from 'svelte-inview'
|
||||
|
||||
/** @type {string} */
|
||||
export let name
|
||||
/** @type {number} */
|
||||
export let number
|
||||
/** @type {string} */
|
||||
export let date
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {string} name
|
||||
* @property {number} number
|
||||
* @property {string} date
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
/** @type {Props} */
|
||||
let { name, number, date, children } = $props()
|
||||
|
||||
/** Used to show the background gradient. Show if user scrolls, but do not disable when the user scrolls past from the top. */
|
||||
let enabled = false
|
||||
let enabled = $state(false)
|
||||
|
||||
function setEnabled({ detail }) {
|
||||
// Show the effect when the user scrolls in and keep it enabled until the user scrolls up from it again
|
||||
|
|
@ -31,10 +35,16 @@
|
|||
<header
|
||||
class="flex flex-col items-center gap-2 p-6 mix-blend-color-dodge"
|
||||
use:inview={{ threshold: 0.45 }}
|
||||
on:inview_change={setEnabled}
|
||||
oninview_change={setEnabled}
|
||||
>
|
||||
<div class="text-xl font-bold text-neutral-300/80 sm:text-2xl">Contest #{number}</div>
|
||||
<h2 class="text-center text-6xl font-bold text-neutral-200/80 sm:text-9xl">{name}</h2>
|
||||
<div class="text-xl font-bold text-neutral-300/80 sm:text-2xl">
|
||||
Contest #{number}
|
||||
</div>
|
||||
<h2
|
||||
class="text-center text-6xl font-bold text-neutral-200/80 sm:text-9xl"
|
||||
>
|
||||
{name}
|
||||
</h2>
|
||||
<div
|
||||
class="mt-2 rounded-full bg-slate-100/5 px-4 py-1 text-center text-xl font-bold text-slate-200/80 shadow"
|
||||
>
|
||||
|
|
@ -48,7 +58,7 @@
|
|||
</div>
|
||||
|
||||
<div class="flex flex-col gap-40">
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
@ -75,7 +85,10 @@
|
|||
height: 300px;
|
||||
width: 100%;
|
||||
z-index: -10;
|
||||
background: linear-gradient(theme(colors.black / 40%), transparent);
|
||||
background: linear-gradient(
|
||||
theme(colors.black / 40%),
|
||||
transparent
|
||||
);
|
||||
mix-blend-mode: color-burn;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
|
@ -93,25 +106,66 @@
|
|||
transform-origin: top;
|
||||
}
|
||||
.background {
|
||||
--c1: color-mix(in hsl shorter hue, var(--color), hsl(0, 100%, 0%) 10%);
|
||||
--c2: color-mix(in hsl shorter hue, var(--color), hsl(0, 100%, 0%) 15%);
|
||||
--c3: color-mix(in hsl shorter hue, var(--color), hsl(0, 100%, 0%) 20%);
|
||||
--c4: color-mix(in hsl shorter hue, var(--color), hsl(0, 100%, 0%) 30%);
|
||||
--c1: color-mix(
|
||||
in hsl shorter hue,
|
||||
var(--color),
|
||||
hsl(0, 100%, 0%) 10%
|
||||
);
|
||||
--c2: color-mix(
|
||||
in hsl shorter hue,
|
||||
var(--color),
|
||||
hsl(0, 100%, 0%) 15%
|
||||
);
|
||||
--c3: color-mix(
|
||||
in hsl shorter hue,
|
||||
var(--color),
|
||||
hsl(0, 100%, 0%) 20%
|
||||
);
|
||||
--c4: color-mix(
|
||||
in hsl shorter hue,
|
||||
var(--color),
|
||||
hsl(0, 100%, 0%) 30%
|
||||
);
|
||||
|
||||
position: absolute;
|
||||
translate: -50% -50%;
|
||||
width: 100%;
|
||||
height: 1000px;
|
||||
z-index: -10;
|
||||
background: url('/imgs/grain.webp'),
|
||||
radial-gradient(140px 100px at 50% 45%, var(--color), transparent),
|
||||
background:
|
||||
url('/imgs/grain.webp'),
|
||||
radial-gradient(
|
||||
140px 100px at 50% 45%,
|
||||
var(--color),
|
||||
transparent
|
||||
),
|
||||
radial-gradient(145px 110px at 50% 45%, var(--c1), transparent),
|
||||
radial-gradient(210px 140px, var(--c2, theme(colors.blue.600)), transparent),
|
||||
radial-gradient(300px 200px, var(--c2, theme(colors.sky.600)), transparent),
|
||||
radial-gradient(600px 220px, var(--c3, theme(colors.blue.700)), transparent),
|
||||
radial-gradient(1100px 420px, var(--c4, theme(colors.blue.800 / 60%)), transparent);
|
||||
radial-gradient(
|
||||
210px 140px,
|
||||
var(--c2, theme(colors.blue.600)),
|
||||
transparent
|
||||
),
|
||||
radial-gradient(
|
||||
300px 200px,
|
||||
var(--c2, theme(colors.sky.600)),
|
||||
transparent
|
||||
),
|
||||
radial-gradient(
|
||||
600px 220px,
|
||||
var(--c3, theme(colors.blue.700)),
|
||||
transparent
|
||||
),
|
||||
radial-gradient(
|
||||
1100px 420px,
|
||||
var(--c4, theme(colors.blue.800 / 60%)),
|
||||
transparent
|
||||
);
|
||||
|
||||
mask-image: radial-gradient(100% 100% at 50% 50%, black 20%, transparent 50%);
|
||||
mask-image: radial-gradient(
|
||||
100% 100% at 50% 50%,
|
||||
black 20%,
|
||||
transparent 50%
|
||||
);
|
||||
|
||||
opacity: 0.25;
|
||||
scale: 0.95 1;
|
||||
|
|
|
|||
|
|
@ -4,30 +4,47 @@
|
|||
import PlayIconFill from '~icons/mingcute/play-fill'
|
||||
import PlayIconOutline from '~icons/mingcute/play-line'
|
||||
|
||||
export let name: string
|
||||
export let creator: string
|
||||
export let dotfilesLink: string
|
||||
export let creatorProfilePicture: string
|
||||
export let thumbnail: string
|
||||
export let video: string | undefined = undefined
|
||||
/**
|
||||
* Specify the blurred background image to be used.
|
||||
* Defaults to `"generated_<thumbnail>"` * */
|
||||
export let blurredThumbnail: string | undefined = undefined
|
||||
export let pretitel: string
|
||||
interface Props {
|
||||
name: string
|
||||
creator: string
|
||||
dotfilesLink: string
|
||||
creatorProfilePicture: string
|
||||
thumbnail: string
|
||||
video?: string | undefined
|
||||
/**
|
||||
* Specify the blurred background image to be used.
|
||||
* Defaults to `"generated_<thumbnail>"` * */
|
||||
blurredThumbnail?: string | undefined
|
||||
pretitel: string
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
let toShow: 'thumbnail' | 'video' = 'thumbnail'
|
||||
let {
|
||||
name,
|
||||
creator,
|
||||
dotfilesLink,
|
||||
creatorProfilePicture,
|
||||
thumbnail,
|
||||
video = undefined,
|
||||
blurredThumbnail = undefined,
|
||||
pretitel,
|
||||
...rest
|
||||
}: Props = $props()
|
||||
|
||||
let toShow: 'thumbnail' | 'video' = $state('thumbnail')
|
||||
|
||||
let background = blurredThumbnail ?? getGeneratedPath(thumbnail)
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="flex flex-col items-center gap-10 px-4 {$$restProps.class}"
|
||||
class="flex flex-col items-center gap-10 px-4 {rest.class}"
|
||||
style:--bg="url('{background}')"
|
||||
>
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="relative mb-2 rounded-full px-3 py-1 text-lg font-bold">
|
||||
<div
|
||||
class="relative mb-2 rounded-full px-3 py-1 text-lg font-bold"
|
||||
>
|
||||
{pretitel}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -47,7 +64,9 @@
|
|||
alt={creator + ' profile picture'}
|
||||
loading="lazy"
|
||||
/>
|
||||
<div class="font-medium text-slate-300 transition-colors group-hover:text-white">
|
||||
<div
|
||||
class="font-medium text-slate-300 transition-colors group-hover:text-white"
|
||||
>
|
||||
{creator}
|
||||
</div>
|
||||
</a>
|
||||
|
|
@ -57,24 +76,37 @@
|
|||
<div class="rice group relative" class:hasVideo={video}>
|
||||
{#if toShow === 'thumbnail'}
|
||||
{#if video}
|
||||
<button on:click={() => (toShow = 'video')}>
|
||||
<img src={thumbnail} alt={`${name} by ${creator} thumbnail`} class="" loading="lazy" />
|
||||
<button onclick={() => (toShow = 'video')}>
|
||||
<img
|
||||
src={thumbnail}
|
||||
alt={`${name} by ${creator} thumbnail`}
|
||||
class=""
|
||||
loading="lazy"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-gray-900/60 p-4 text-white/80 opacity-0 outline outline-gray-100/10 duration-300 hover:scale-105 hover:bg-gray-900/80 hover:text-white group-hover:opacity-100"
|
||||
on:click={() => (toShow = 'video')}><PlayIconFill class="size-8 " /></button
|
||||
onclick={() => (toShow = 'video')}
|
||||
><PlayIconFill class="size-8 " /></button
|
||||
>
|
||||
|
||||
<button
|
||||
class="absolute bottom-6 left-6 rounded-full bg-gray-900/60 p-2 text-white/80 outline outline-gray-100/10 duration-300 hover:scale-105 hover:bg-gray-900/80 hover:text-white"
|
||||
on:click={() => (toShow = 'video')}><PlayIconOutline class="size-5 " /></button
|
||||
onclick={() => (toShow = 'video')}
|
||||
><PlayIconOutline class="size-5 " /></button
|
||||
>
|
||||
{:else}
|
||||
<img src={thumbnail} alt={`${name} by ${creator} thumbnail`} class="" loading="lazy" />
|
||||
<img
|
||||
src={thumbnail}
|
||||
alt={`${name} by ${creator} thumbnail`}
|
||||
class=""
|
||||
loading="lazy"
|
||||
/>
|
||||
{/if}
|
||||
{:else if toShow === 'video'}
|
||||
<!-- svelte-ignore a11y-media-has-caption -->
|
||||
<video autoplay controls poster={thumbnail} src={video} />
|
||||
<!-- svelte-ignore a11y_media_has_caption -->
|
||||
<video autoplay controls poster={thumbnail} src={video}
|
||||
></video>
|
||||
{/if}
|
||||
</div>
|
||||
<!-- blur background -->
|
||||
|
|
@ -85,7 +117,7 @@
|
|||
alt={`${name} by ${creator} thumbnail`}
|
||||
loading="lazy"
|
||||
/>
|
||||
<div class="grain_" />
|
||||
<div class="grain_"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -155,14 +187,19 @@
|
|||
contain: layout size style paint;
|
||||
|
||||
background-blend-mode: hard-light;
|
||||
mask-image: radial-gradient(farthest-side, black 80%, transparent);
|
||||
mask-image: radial-gradient(
|
||||
farthest-side,
|
||||
black 80%,
|
||||
transparent
|
||||
);
|
||||
background-image: url('/imgs/grain.webp');
|
||||
}
|
||||
|
||||
.title_ {
|
||||
filter: saturate(1.5) brightness(1);
|
||||
@apply relative -mt-1 mb-5 py-1 text-4xl font-bold text-transparent sm:text-6xl;
|
||||
background-image: linear-gradient(
|
||||
background-image:
|
||||
linear-gradient(
|
||||
-195deg,
|
||||
theme(colors.white / 80%) 50%,
|
||||
rgba(255, 255, 255, 0.4)
|
||||
|
|
|
|||
|
|
@ -34,29 +34,35 @@ export const contests: Contest[] = [
|
|||
name: '𝕽𝖎𝖛𝖊𝖓𝖉𝖊𝖑𝖑',
|
||||
creator: 'zacoons',
|
||||
pretitel: '#1',
|
||||
dotfilesLink: 'https://codeberg.org/zacoons/rivendell-hyprdots',
|
||||
dotfilesLink:
|
||||
'https://codeberg.org/zacoons/rivendell-hyprdots',
|
||||
creatorProfilePicture:
|
||||
'https://codeberg.org/avatars/c4669a53a4de58a5f700fa461405b1fd151d90da48cf927639e3855570e0ca49?size=24',
|
||||
thumbnail: '/ricing_competitions/4/zacoons.webp',
|
||||
video: 'https://dl.hypr.land/contest-videos/hyprrice_zacoons.mp4'
|
||||
video:
|
||||
'https://dl.hypr.land/contest-videos/hyprrice_zacoons.mp4'
|
||||
},
|
||||
{
|
||||
name: '𝕯𝖎𝖘𝖓𝖆𝖞 𝖙𝖞𝖕𝖊 𝖘𝖍𝖎',
|
||||
creator: 'VDawg',
|
||||
pretitel: '#2',
|
||||
dotfilesLink: 'https://github.com/vdawg-git/fantasy-rice',
|
||||
creatorProfilePicture: 'https://avatars.githubusercontent.com/u/28539403?s=24&v=4',
|
||||
creatorProfilePicture:
|
||||
'https://avatars.githubusercontent.com/u/28539403?s=24&v=4',
|
||||
thumbnail: '/ricing_competitions/4/vdawg.webp',
|
||||
video: 'https://dl.hypr.land/contest-videos/disnay_type_shit_vdawg.mp4'
|
||||
video:
|
||||
'https://dl.hypr.land/contest-videos/disnay_type_shit_vdawg.mp4'
|
||||
},
|
||||
{
|
||||
name: 'Duskhide',
|
||||
creator: 'Flafy',
|
||||
pretitel: '#3',
|
||||
dotfilesLink: 'https://github.com/flafydev/fantasy_rice',
|
||||
creatorProfilePicture: 'https://avatars.githubusercontent.com/u/44374434?s=24&v=4',
|
||||
creatorProfilePicture:
|
||||
'https://avatars.githubusercontent.com/u/44374434?s=24&v=4',
|
||||
thumbnail: '/ricing_competitions/4/flafy.webp',
|
||||
video: 'https://dl.hypr.land/contest-videos/fantasy_flafy.webm'
|
||||
video:
|
||||
'https://dl.hypr.land/contest-videos/fantasy_flafy.webm'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -71,7 +77,8 @@ export const contests: Contest[] = [
|
|||
creator: 'Flafy',
|
||||
pretitel: '#1',
|
||||
dotfilesLink: 'https://github.com/flafydev/nixos-config/',
|
||||
creatorProfilePicture: 'https://avatars.githubusercontent.com/u/44374434?s=24&v=4',
|
||||
creatorProfilePicture:
|
||||
'https://avatars.githubusercontent.com/u/44374434?s=24&v=4',
|
||||
thumbnail: '/ricing_competitions/3/flafy.webp',
|
||||
video: 'https://dl.hypr.land/videos/flafy-3-space.mp4'
|
||||
},
|
||||
|
|
@ -79,8 +86,10 @@ export const contests: Contest[] = [
|
|||
name: 'Globes',
|
||||
creator: 'Aylur',
|
||||
pretitel: '#2',
|
||||
dotfilesLink: 'https://github.com/Aylur/dotfiles/tree/ags-pre-ts',
|
||||
creatorProfilePicture: 'https://avatars.githubusercontent.com/u/104676705?s=24&v=4',
|
||||
dotfilesLink:
|
||||
'https://github.com/Aylur/dotfiles/tree/ags-pre-ts',
|
||||
creatorProfilePicture:
|
||||
'https://avatars.githubusercontent.com/u/104676705?s=24&v=4',
|
||||
thumbnail: '/ricing_competitions/3/aylur.webp',
|
||||
video: 'https://dl.hypr.land/videos/aylur-3-space.mp4'
|
||||
},
|
||||
|
|
@ -89,9 +98,11 @@ export const contests: Contest[] = [
|
|||
creator: 'VDawg',
|
||||
pretitel: '#3',
|
||||
dotfilesLink: 'https://github.com/vdawg-git/space_dots',
|
||||
creatorProfilePicture: 'https://avatars.githubusercontent.com/u/28539403?s=24&v=4',
|
||||
creatorProfilePicture:
|
||||
'https://avatars.githubusercontent.com/u/28539403?s=24&v=4',
|
||||
thumbnail: '/ricing_competitions/3/vdawg.webp',
|
||||
video: 'https://dl.hypr.land/contest-videos/golden_era_vdawg.mp4'
|
||||
video:
|
||||
'https://dl.hypr.land/contest-videos/golden_era_vdawg.mp4'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -106,8 +117,10 @@ export const contests: Contest[] = [
|
|||
name: 'Hybrid Summer',
|
||||
creator: 'end_4',
|
||||
pretitel: '#1',
|
||||
dotfilesLink: 'https://github.com/end-4/dots-hyprland/tree/archive/hybrid-summer',
|
||||
creatorProfilePicture: 'https://avatars.githubusercontent.com/u/97237370?s=24&v=4',
|
||||
dotfilesLink:
|
||||
'https://github.com/end-4/dots-hyprland/tree/archive/hybrid-summer',
|
||||
creatorProfilePicture:
|
||||
'https://avatars.githubusercontent.com/u/97237370?s=24&v=4',
|
||||
thumbnail: '/ricing_competitions/2/end_4.webp',
|
||||
video: 'https://dl.hypr.land/videos/end4-2-summer.mp4'
|
||||
},
|
||||
|
|
@ -115,8 +128,10 @@ export const contests: Contest[] = [
|
|||
name: 'Unnamed',
|
||||
creator: 'Flafy',
|
||||
pretitel: '#2',
|
||||
dotfilesLink: 'https://github.com/FlafyDev/flutter_background_bar',
|
||||
creatorProfilePicture: 'https://avatars.githubusercontent.com/u/44374434?s=24&v=4',
|
||||
dotfilesLink:
|
||||
'https://github.com/FlafyDev/flutter_background_bar',
|
||||
creatorProfilePicture:
|
||||
'https://avatars.githubusercontent.com/u/44374434?s=24&v=4',
|
||||
thumbnail: '/ricing_competitions/2/flafy.webp',
|
||||
video: 'https://dl.hypr.land/videos/flafy-2-summer.mp4'
|
||||
},
|
||||
|
|
@ -124,8 +139,10 @@ export const contests: Contest[] = [
|
|||
name: 'Day and Night',
|
||||
creator: 'Mathisbuilder',
|
||||
pretitel: '#3',
|
||||
dotfilesLink: 'https://github.com/MathisP75/summer-day-and-night',
|
||||
creatorProfilePicture: 'https://avatars.githubusercontent.com/u/98901170?s=24&v=4',
|
||||
dotfilesLink:
|
||||
'https://github.com/MathisP75/summer-day-and-night',
|
||||
creatorProfilePicture:
|
||||
'https://avatars.githubusercontent.com/u/98901170?s=24&v=4',
|
||||
thumbnail: '/ricing_competitions/2/day-night.webp'
|
||||
}
|
||||
]
|
||||
|
|
@ -141,8 +158,10 @@ export const contests: Contest[] = [
|
|||
name: 'Unnamed',
|
||||
creator: 'Flafy',
|
||||
pretitel: '#1',
|
||||
dotfilesLink: 'https://github.com/FlafyDev/flutter_workspaces_2',
|
||||
creatorProfilePicture: 'https://avatars.githubusercontent.com/u/25975326?s=24&v=4',
|
||||
dotfilesLink:
|
||||
'https://github.com/FlafyDev/flutter_workspaces_2',
|
||||
creatorProfilePicture:
|
||||
'https://avatars.githubusercontent.com/u/25975326?s=24&v=4',
|
||||
thumbnail: '/ricing_competitions/1/flafy.webp',
|
||||
video: 'https://dl.hypr.land/videos/flafy-1-winter.mp4'
|
||||
},
|
||||
|
|
@ -150,8 +169,10 @@ export const contests: Contest[] = [
|
|||
name: 'Aurora',
|
||||
creator: 'flick0',
|
||||
pretitel: '#2 (ex aequo)',
|
||||
dotfilesLink: 'https://github.com/flick0/dotfiles/tree/aurora',
|
||||
creatorProfilePicture: 'https://avatars.githubusercontent.com/u/77581181?s=24&v=4',
|
||||
dotfilesLink:
|
||||
'https://github.com/flick0/dotfiles/tree/aurora',
|
||||
creatorProfilePicture:
|
||||
'https://avatars.githubusercontent.com/u/77581181?s=24&v=4',
|
||||
thumbnail: '/ricing_competitions/1/flicko.webp',
|
||||
video: 'https://dl.hypr.land/videos/flicko-1-winter.mp4'
|
||||
},
|
||||
|
|
@ -160,7 +181,8 @@ export const contests: Contest[] = [
|
|||
creator: 'amadeus',
|
||||
pretitel: '#2 (ex aequo)',
|
||||
dotfilesLink: 'https://github.com/AmadeusWM/hyprland-winter',
|
||||
creatorProfilePicture: 'https://avatars.githubusercontent.com/u/63149896?s=24&v=4',
|
||||
creatorProfilePicture:
|
||||
'https://avatars.githubusercontent.com/u/63149896?s=24&v=4',
|
||||
thumbnail: '/ricing_competitions/1/amadeus.webp'
|
||||
},
|
||||
{
|
||||
|
|
@ -168,7 +190,8 @@ export const contests: Contest[] = [
|
|||
creator: 'Lyasm',
|
||||
pretitel: '#3 (ex aequo)',
|
||||
dotfilesLink: '#',
|
||||
creatorProfilePicture: 'https://avatars.githubusercontent.com/u/111616244?s=24&v=4',
|
||||
creatorProfilePicture:
|
||||
'https://avatars.githubusercontent.com/u/111616244?s=24&v=4',
|
||||
thumbnail: '/ricing_competitions/1/lyasm.webp'
|
||||
},
|
||||
{
|
||||
|
|
@ -176,7 +199,8 @@ export const contests: Contest[] = [
|
|||
creator: 'lauroro',
|
||||
pretitel: '#3 (ex aequo)',
|
||||
dotfilesLink: 'https://github.com/lauroro/hyprland-dotfiles',
|
||||
creatorProfilePicture: 'https://avatars.githubusercontent.com/u/88981092?s=24&v=4',
|
||||
creatorProfilePicture:
|
||||
'https://avatars.githubusercontent.com/u/88981092?s=24&v=4',
|
||||
thumbnail: '/ricing_competitions/1/lauroro.webp'
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -10,14 +10,19 @@
|
|||
import Chan from './community/Chan.svelte'
|
||||
import type { CommunityProfile } from '$lib/Types'
|
||||
|
||||
export let communityProfiles: readonly CommunityProfile[]
|
||||
interface Props {
|
||||
communityProfiles: readonly CommunityProfile[]
|
||||
}
|
||||
|
||||
let { communityProfiles }: Props = $props()
|
||||
|
||||
const biggestSize = communityProfiles.reduce(
|
||||
(previousSize, { size }) => (size > previousSize ? size : previousSize),
|
||||
(previousSize, { size }) =>
|
||||
size > previousSize ? size : previousSize,
|
||||
0
|
||||
)
|
||||
|
||||
let restrictionElement: HTMLElement
|
||||
let restrictionElement: HTMLElement | undefined = $state()
|
||||
</script>
|
||||
|
||||
<section
|
||||
|
|
@ -25,10 +30,16 @@
|
|||
bind:this={restrictionElement}
|
||||
>
|
||||
<Title>
|
||||
<TitleHeading slot="title" class="">Join a great<br />community</TitleHeading>
|
||||
<TitleSubtile slot="subtitle" class="class-w-[40ch]">
|
||||
Get help from Distro Hoppers, Haiku writers,<br />Hydrohomies, and human_(probably)
|
||||
</TitleSubtile>
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Join a great<br />community</TitleHeading
|
||||
>
|
||||
{/snippet}
|
||||
{#snippet subtitle()}
|
||||
<TitleSubtile class="class-w-[40ch]">
|
||||
Get help from Distro Hoppers, Haiku writers,<br />Hydrohomies,
|
||||
and human_(probably)
|
||||
</TitleSubtile>
|
||||
{/snippet}
|
||||
</Title>
|
||||
|
||||
<div class="group mt-16 flex flex-col items-center">
|
||||
|
|
@ -48,18 +59,24 @@
|
|||
</div>
|
||||
|
||||
<div class="absolute w-[1024px] select-none">
|
||||
<div class="flex h-full origin-bottom-right select-none flex-wrap gap-4">
|
||||
<div
|
||||
class="flex h-full origin-bottom-right select-none flex-wrap gap-4"
|
||||
>
|
||||
{#each communityProfiles as props}
|
||||
{@const relativeSize = props.size / biggestSize}
|
||||
<DiscordProfilePicture
|
||||
{...props}
|
||||
weight={relativeSize}
|
||||
spawnDelay={Math.pow(1 - props.size / biggestSize, 4) * 4654}
|
||||
getRestrictionElement={() => restrictionElement}
|
||||
spawnDelay={Math.pow(1 - props.size / biggestSize, 4) *
|
||||
4654}
|
||||
getRestrictionElement={() => restrictionElement!}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<Chan {biggestSize} getRestrictionElement={() => restrictionElement} />
|
||||
<Chan
|
||||
{biggestSize}
|
||||
getRestrictionElement={() => restrictionElement!}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -82,13 +99,15 @@
|
|||
filter 840ms;
|
||||
transition-delay: 240ms, 180ms, 20ms;
|
||||
transform: translateY(-25%);
|
||||
filter: drop-shadow(0px 0px 0px cyan) drop-shadow(0px 0px 0px blue);
|
||||
filter: drop-shadow(0px 0px 0px cyan)
|
||||
drop-shadow(0px 0px 0px blue);
|
||||
|
||||
&:hover,
|
||||
.group:hover & {
|
||||
scale: 1.2 1.2;
|
||||
rotate: 360deg;
|
||||
filter: drop-shadow(4px 4px 14px #0fffef7a) drop-shadow(-4px -4px 12px purple);
|
||||
filter: drop-shadow(4px 4px 14px #0fffef7a)
|
||||
drop-shadow(-4px -4px 12px purple);
|
||||
animation: bounce 0.7s infinite 180ms both;
|
||||
}
|
||||
&:active {
|
||||
|
|
|
|||
|
|
@ -1,27 +1,41 @@
|
|||
<script>
|
||||
/** @type {string | undefined} */
|
||||
export let image = undefined
|
||||
/** @type {string | undefined} */
|
||||
export let name = undefined
|
||||
/**
|
||||
* @type {string | undefined }
|
||||
* Classes for the command text
|
||||
* @typedef {Object} Props
|
||||
* @property {string | undefined} [image]
|
||||
* @property {string | undefined} [name]
|
||||
* @property {import('svelte').Snippet} [imageExtra]
|
||||
* @property {import('svelte').Snippet} [children]
|
||||
*/
|
||||
|
||||
/** @type {Props} */
|
||||
let {
|
||||
image = undefined,
|
||||
name = undefined,
|
||||
imageExtra,
|
||||
children
|
||||
} = $props()
|
||||
</script>
|
||||
|
||||
<div class="group relative flex flex-col items-center gap-2 md:flex-row md:gap-4">
|
||||
<div
|
||||
class="group relative flex flex-col items-center gap-2 md:flex-row md:gap-4"
|
||||
>
|
||||
{#if image && name}
|
||||
<div
|
||||
class="relative flex h-32 w-32 flex-col items-center justify-center gap-3 rounded-full text-lg font-medium text-primary transition-transform group-focus-within:-translate-y-1"
|
||||
>
|
||||
<img src={image} class="h-20 w-32 object-contain" alt="{name} Logo" loading="lazy" />{name}
|
||||
<img
|
||||
src={image}
|
||||
class="h-20 w-32 object-contain"
|
||||
alt="{name} Logo"
|
||||
loading="lazy"
|
||||
/>{name}
|
||||
|
||||
<slot name="imageExtra" />
|
||||
{@render imageExtra?.()}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Command button slot -->
|
||||
<div class="mb-2 w-full">
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,22 +1,28 @@
|
|||
<script>
|
||||
import { animateIn, getGeneratedPath } from '$lib/Helper.ts'
|
||||
|
||||
/** @type {string}
|
||||
* The path to the image. Usually the file within `static`, but can also be an URL
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {string} image
|
||||
* @property {string | undefined} [imageClass]
|
||||
* @property {string | undefined} [containerClass]
|
||||
* @property {string} [blurredBackground]
|
||||
*/
|
||||
export let image
|
||||
/** @type {string | undefined} */
|
||||
export let imageClass = undefined
|
||||
/** @type {string | undefined} */
|
||||
export let containerClass = undefined
|
||||
/** @type {string}
|
||||
* The path to the image. Usually the file within `static`, but can also be an URL. Defaults to `generated_<image>`
|
||||
*/
|
||||
export let blurredBackground = undefined
|
||||
|
||||
/** @type {Props} */
|
||||
let {
|
||||
image,
|
||||
imageClass = undefined,
|
||||
containerClass = undefined,
|
||||
blurredBackground = undefined
|
||||
} = $props()
|
||||
</script>
|
||||
|
||||
<div class="rice {containerClass} group">
|
||||
<div class="h-full w-full" use:animateIn={{ slide: 20, duration: 800 }}>
|
||||
<div
|
||||
class="h-full w-full"
|
||||
use:animateIn={{ slide: 20, duration: 800 }}
|
||||
>
|
||||
<img
|
||||
src={image}
|
||||
alt="Rice desktop"
|
||||
|
|
@ -52,7 +58,11 @@
|
|||
opacity: 0.9;
|
||||
/* filter: brightness(2.5); */
|
||||
z-index: -10;
|
||||
mask-image: radial-gradient(50% 50% at 50% 50%, black, transparent);
|
||||
mask-image: radial-gradient(
|
||||
50% 50% at 50% 50%,
|
||||
black,
|
||||
transparent
|
||||
);
|
||||
contain: content layout size style;
|
||||
|
||||
@apply -z-10 transition-[filter] duration-500;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<script>
|
||||
<script lang="ts">
|
||||
import Card from '$lib/components/Card.svelte'
|
||||
import GameIcon from '~icons/gg/games'
|
||||
import SpecialWorkspaceIcon from '~icons/gg/shutterstock'
|
||||
|
|
@ -19,10 +19,16 @@
|
|||
import TitleHeading from '$lib/components/Title/TitleHeading.svelte'
|
||||
</script>
|
||||
|
||||
<section class="relative flex max-w-screen-xl flex-col items-center px-3 md:px-8">
|
||||
<section
|
||||
class="relative flex max-w-screen-xl flex-col items-center px-3 md:px-8"
|
||||
>
|
||||
<Title>
|
||||
<TitlePre slot="pre">TLDR</TitlePre>
|
||||
<TitleHeading slot="title" class="">Features</TitleHeading>
|
||||
{#snippet pre()}
|
||||
<TitlePre>TLDR</TitlePre>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Features</TitleHeading>
|
||||
{/snippet}
|
||||
</Title>
|
||||
|
||||
<CardsContainer
|
||||
|
|
@ -30,12 +36,18 @@
|
|||
>
|
||||
<Card class="row-span-2 min-h-[20rem]" color="purple">
|
||||
<div class="flex h-full flex-col justify-end p-8 sm:p-12">
|
||||
<h2 class="mb-6 text-5xl font-bold text-white lg:text-8xl">Smooth</h2>
|
||||
<h2 class="mb-6 text-5xl font-bold text-white lg:text-8xl">
|
||||
Smooth
|
||||
</h2>
|
||||
<p class="max-w-[60ch]">
|
||||
Smooth transitions. Eye-pleasing animations. Great performance. Highly responsive.
|
||||
Smooth transitions. Eye-pleasing animations. Great
|
||||
performance. Highly responsive.
|
||||
</p>
|
||||
|
||||
<div class="_wrapper absolute inset-0 select-none" aria-hidden="true">
|
||||
<div
|
||||
class="_wrapper absolute inset-0 select-none"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="feature-image">
|
||||
<img
|
||||
src={smoothDefaultImage}
|
||||
|
|
@ -57,12 +69,18 @@
|
|||
</Card>
|
||||
<Card class="min-h-[20rem]" color="purple">
|
||||
<div class="flex h-full flex-col justify-end p-8 sm:p-12">
|
||||
<h2 class="mb-6 text-5xl font-bold text-white">Easy to configure</h2>
|
||||
<h2 class="mb-6 text-5xl font-bold text-white">
|
||||
Easy to configure
|
||||
</h2>
|
||||
<p class="max-w-[60ch]">
|
||||
Live reloading config. Easy configuration format. Sensible defaults. Great documentation.
|
||||
Live reloading config. Easy configuration format. Sensible
|
||||
defaults. Great documentation.
|
||||
</p>
|
||||
|
||||
<div class="_wrapper absolute inset-0 select-none" aria-hidden="true">
|
||||
<div
|
||||
class="_wrapper absolute inset-0 select-none"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="feature-image">
|
||||
<img
|
||||
src={configDefaultImage}
|
||||
|
|
@ -84,12 +102,17 @@
|
|||
</Card>
|
||||
<Card class="min-h-[20rem]" color="purple">
|
||||
<div class="flex h-full flex-col justify-end p-8 sm:p-12">
|
||||
<h2 class="mb-6 text-5xl font-bold text-white">Dynamic tiling</h2>
|
||||
<h2 class="mb-6 text-5xl font-bold text-white">
|
||||
Dynamic tiling
|
||||
</h2>
|
||||
<p class="max-w-[60ch]">
|
||||
Automatic tiling that just works. Supports multiple fine-tuneable layouts, with even more
|
||||
as plugins.
|
||||
Automatic tiling that just works. Supports multiple
|
||||
fine-tuneable layouts, with even more as plugins.
|
||||
</p>
|
||||
<div class="_wrapper absolute inset-0 select-none" aria-hidden="true">
|
||||
<div
|
||||
class="_wrapper absolute inset-0 select-none"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="feature-image">
|
||||
<img
|
||||
src={tileDefaultImage}
|
||||
|
|
@ -122,7 +145,11 @@
|
|||
<GameIcon class="h-8 w-8" />
|
||||
Tearing support
|
||||
</a>
|
||||
<a href="https://wiki.hyprland.org/IPC/" target="_blank" class="icon-feature hover:underline">
|
||||
<a
|
||||
href="https://wiki.hyprland.org/IPC/"
|
||||
target="_blank"
|
||||
class="icon-feature hover:underline"
|
||||
>
|
||||
<IpcIcon class="h-8 w-8" />
|
||||
Socket-based IPC
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -7,22 +7,38 @@
|
|||
import FameRicePreview from './FameRicePreview.svelte'
|
||||
</script>
|
||||
|
||||
<section class="" use:animateIn={{ fade: 0, slide: 24, duration: 3000, threshold: 0.1 }}>
|
||||
<section
|
||||
class=""
|
||||
use:animateIn={{
|
||||
fade: 0,
|
||||
slide: 24,
|
||||
duration: 3000,
|
||||
threshold: 0.1
|
||||
}}
|
||||
>
|
||||
<div class="z-20 -mb-40 px-4">
|
||||
<Title>
|
||||
<TitlePre slot="pre">Memorials of the ricing legends</TitlePre>
|
||||
<TitleHeading slot="title" class="">Hall of Fame</TitleHeading>
|
||||
{#snippet pre()}
|
||||
<TitlePre>Memorials of the ricing legends</TitlePre>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Hall of Fame</TitleHeading>
|
||||
{/snippet}
|
||||
</Title>
|
||||
</div>
|
||||
|
||||
<div class="atmosphere" />
|
||||
<div class="atmosphere"></div>
|
||||
|
||||
<div
|
||||
class="relative -mt-8 flex w-full max-w-[1100px] flex-col items-center justify-end gap-16 overflow-hidden [perspective:100px] md:px-16 lg:gap-24"
|
||||
use:animateIn={{ slide: 24, fade: 0.5, duration: 800 }}
|
||||
>
|
||||
<a class="absolute bottom-24 left-1/2 z-20 -translate-x-1/2" href="/hall_of_fame">
|
||||
<Button size="lg" type="fancyOutline">Go to Hall of Fame</Button>
|
||||
<a
|
||||
class="absolute bottom-24 left-1/2 z-20 -translate-x-1/2"
|
||||
href="/hall_of_fame"
|
||||
>
|
||||
<Button size="lg" type="fancyOutline">Go to Hall of Fame</Button
|
||||
>
|
||||
</a>
|
||||
|
||||
<FameRicePreview
|
||||
|
|
@ -41,7 +57,7 @@
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div class="glow" />
|
||||
<div class="glow"></div>
|
||||
</section>
|
||||
|
||||
<style lang="postcss">
|
||||
|
|
@ -60,10 +76,24 @@
|
|||
width: 200vw;
|
||||
max-width: 2400px;
|
||||
max-height: 1000px;
|
||||
background: url('/imgs/grain.webp'),
|
||||
radial-gradient(closest-side, theme(colors.blue.500 / 30%), transparent),
|
||||
radial-gradient(15% 20%, theme(colors.cyan.500 / 70%), transparent);
|
||||
mask-image: radial-gradient(closest-side, white, rgba(0, 0, 0, 0.8) 80%, transparent);
|
||||
background:
|
||||
url('/imgs/grain.webp'),
|
||||
radial-gradient(
|
||||
closest-side,
|
||||
theme(colors.blue.500 / 30%),
|
||||
transparent
|
||||
),
|
||||
radial-gradient(
|
||||
15% 20%,
|
||||
theme(colors.cyan.500 / 70%),
|
||||
transparent
|
||||
);
|
||||
mask-image: radial-gradient(
|
||||
closest-side,
|
||||
white,
|
||||
rgba(0, 0, 0, 0.8) 80%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
.glow {
|
||||
|
|
@ -76,13 +106,19 @@
|
|||
left: 0;
|
||||
right: 0;
|
||||
pointer-events: none;
|
||||
background-image: url('/imgs/grain.webp'),
|
||||
background-image:
|
||||
url('/imgs/grain.webp'),
|
||||
radial-gradient(
|
||||
ellipse at bottom,
|
||||
theme(colors.pink.400),
|
||||
theme(colors.indigo.700 / 50%) 50%,
|
||||
theme(colors.indigo.950 / 0%) 80%
|
||||
);
|
||||
mask-image: radial-gradient(ellipse at bottom, white, rgba(0, 0, 0, 1) 90%, transparent);
|
||||
mask-image: radial-gradient(
|
||||
ellipse at bottom,
|
||||
white,
|
||||
rgba(0, 0, 0, 1) 90%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@
|
|||
import HeroBackground from './HeroBackground.svelte'
|
||||
import HyprlandLogo from '$lib/images/logos/HyprlandLogo.svelte'
|
||||
|
||||
export let backgroundData
|
||||
let { backgroundData } = $props()
|
||||
|
||||
let isVisible = true
|
||||
let isVisible = $state(true)
|
||||
let isMobile = false
|
||||
|
||||
onMount(() => {
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<section
|
||||
class="relative flex h-[100svh] min-h-[36rem] w-full flex-col items-center justify-center overflow-x-hidden"
|
||||
use:inview
|
||||
on:inview_change={({ detail: { inView } }) => {
|
||||
oninview_change={({ detail: { inView } }) => {
|
||||
isVisible = inView
|
||||
}}
|
||||
>
|
||||
|
|
@ -30,13 +30,16 @@
|
|||
<h1
|
||||
class="ani-in title pointer-events-auto mb-4 max-w-[20ch] text-center text-4xl font-bold !leading-[1] fill-mode-backwards [animation-delay:384ms] sm:text-6xl md:text-7xl lg:text-8xl lg:tracking-tight"
|
||||
>
|
||||
Modern compositor <br /><span class="hyprgradient title-gradient">with the looks</span>
|
||||
Modern compositor <br /><span
|
||||
class="hyprgradient title-gradient">with the looks</span
|
||||
>
|
||||
</h1>
|
||||
<p
|
||||
class="ani-in mb-8 text-center text-base font-medium text-slate-400 fill-mode-backwards [animation-delay:794ms] sm:mb-12 sm:max-w-lg sm:px-0 sm:text-xl md:max-w-lg md:text-2xl lg:max-w-[50ch]"
|
||||
>
|
||||
Hyprland provides the latest Wayland features, dynamic tiling, all the eyecandy, powerful
|
||||
plugins and much more, while still being lightweight and responsive
|
||||
Hyprland provides the latest Wayland features, dynamic tiling,
|
||||
all the eyecandy, powerful plugins and much more, while still
|
||||
being lightweight and responsive
|
||||
</p>
|
||||
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -1,19 +1,29 @@
|
|||
<script>
|
||||
export let backgroundData
|
||||
let { backgroundData } = $props()
|
||||
|
||||
const { workspacesPerRow, gapLength, workspaceHeight, height, leftColumns, rightColumns } =
|
||||
backgroundData
|
||||
const {
|
||||
workspacesPerRow,
|
||||
gapLength,
|
||||
workspaceHeight,
|
||||
height,
|
||||
leftColumns,
|
||||
rightColumns
|
||||
} = backgroundData
|
||||
|
||||
const REVEAL_DURATION = 800 // in ms
|
||||
</script>
|
||||
|
||||
<div class="wrapper" aria-hidden="true" style:--reveal-duration={REVEAL_DURATION + 'ms'}>
|
||||
<div
|
||||
class="wrapper"
|
||||
aria-hidden="true"
|
||||
style:--reveal-duration={REVEAL_DURATION + 'ms'}
|
||||
>
|
||||
<div
|
||||
class="inner-wrapper"
|
||||
style={`--amount: ${workspacesPerRow}; --workspace-gap: ${gapLength}px;--workspace-height: ${workspaceHeight}px; --length: ${height}px;`}
|
||||
>
|
||||
<!-- Gradient background -->
|
||||
<div class="top-light" />
|
||||
<div class="top-light"></div>
|
||||
|
||||
<div class="columns left" aria-hidden="true">
|
||||
{#each leftColumns as column}
|
||||
|
|
@ -23,9 +33,17 @@
|
|||
{#each workspace as tiles}
|
||||
<div class="tiles">
|
||||
{#each tiles as { color, image }}
|
||||
<div class="tile" style:--color={color} class:hasImage={image}>
|
||||
<div
|
||||
class="tile"
|
||||
style:--color={color}
|
||||
class:hasImage={image}
|
||||
>
|
||||
{#if image}
|
||||
<img src={image} class="h-full w-full object-contain" alt="" />
|
||||
<img
|
||||
src={image}
|
||||
class="h-full w-full object-contain"
|
||||
alt=""
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
|
|
@ -45,9 +63,17 @@
|
|||
{#each workspace as tiles}
|
||||
<div class="tiles">
|
||||
{#each tiles as { color, image }}
|
||||
<div class="tile" style:--color={color} class:hasImage={image}>
|
||||
<div
|
||||
class="tile"
|
||||
style:--color={color}
|
||||
class:hasImage={image}
|
||||
>
|
||||
{#if image}
|
||||
<img src={image} class="h-full w-full object-contain" alt="" />
|
||||
<img
|
||||
src={image}
|
||||
class="h-full w-full object-contain"
|
||||
alt=""
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
|
|
@ -94,7 +120,11 @@
|
|||
|
||||
&::after {
|
||||
content: ' ';
|
||||
background: radial-gradient(80% 250%, theme(colors.black) 10%, transparent 50%);
|
||||
background: radial-gradient(
|
||||
80% 250%,
|
||||
theme(colors.black) 10%,
|
||||
transparent 50%
|
||||
);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
|
|
@ -161,7 +191,11 @@
|
|||
&:hover {
|
||||
opacity: 1;
|
||||
scale: 1.02;
|
||||
background-color: color-mix(in hsl, var(--color), transparent 20%);
|
||||
background-color: color-mix(
|
||||
in hsl,
|
||||
var(--color),
|
||||
transparent 20%
|
||||
);
|
||||
box-shadow:
|
||||
0px 0px 10px var(--color),
|
||||
0px 0px 40px var(--color);
|
||||
|
|
@ -181,7 +215,8 @@
|
|||
|
||||
& img {
|
||||
opacity: 0;
|
||||
transition: opacity var(--reveal-duration) cubic-bezier(1, -0.4, 0.165, 1);
|
||||
transition: opacity var(--reveal-duration)
|
||||
cubic-bezier(1, -0.4, 0.165, 1);
|
||||
pointer-events: none;
|
||||
}
|
||||
&:hover img {
|
||||
|
|
@ -191,7 +226,8 @@
|
|||
}
|
||||
|
||||
.top-light {
|
||||
background: url('/imgs/grain.webp'),
|
||||
background:
|
||||
url('/imgs/grain.webp'),
|
||||
radial-gradient(
|
||||
100% 80% at top,
|
||||
theme(colors.cyan.500 / 50%) 0%,
|
||||
|
|
|
|||
|
|
@ -17,27 +17,41 @@
|
|||
|
||||
<section class="relative w-full">
|
||||
<div class="mb-16 md:mb-32">
|
||||
<div class="h-[2px] w-full bg-gradient-to-l from-transparent via-cyan-400/50 to-transparent" />
|
||||
<div
|
||||
class="h-[2px] w-full bg-gradient-to-l from-transparent via-cyan-400/50 to-transparent"
|
||||
></div>
|
||||
</div>
|
||||
<div class="top-gradient"></div>
|
||||
|
||||
<div class="relative mx-auto mb-12 max-w-5xl px-8 md:mb-20">
|
||||
<Title class="mb-10">
|
||||
<TitleHeading slot="title" class="">Hyprperks</TitleHeading>
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Hyprperks</TitleHeading>
|
||||
{/snippet}
|
||||
<TitleSubtile class="max-w-[55ch]">
|
||||
<a href={hyprperksLink} class=" text-primary hover:underline" target="_blank"
|
||||
<a
|
||||
href={hyprperksLink}
|
||||
class=" text-primary hover:underline"
|
||||
target="_blank"
|
||||
>Subscribe for first-party configurations, priority support</a
|
||||
>
|
||||
and to support the development. Fully optional, but crafted with love
|
||||
and to support the development. Fully optional, but crafted with
|
||||
love
|
||||
</TitleSubtile>
|
||||
</Title>
|
||||
|
||||
<CardsContainer
|
||||
class="group flex w-full flex-col gap-8 text-lg font-medium text-white/70 md:grid lg:grid-cols-2 lg:grid-rows-2 lg:gap-4"
|
||||
>
|
||||
<Card class="col-span-2 row-span-2 aspect-video min-h-[20rem]" color="cyan">
|
||||
<Card
|
||||
class="col-span-2 row-span-2 aspect-video min-h-[20rem]"
|
||||
color="cyan"
|
||||
>
|
||||
<div class="grid h-full">
|
||||
<div class="col-start-1 col-end-2 row-start-1 row-end-2 m-0.5" aria-hidden="true">
|
||||
<div
|
||||
class="col-start-1 col-end-2 row-start-1 row-end-2 m-0.5"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<Video
|
||||
muted
|
||||
sources={['https://dl.hypr.land/videos/hyprde2.mp4']}
|
||||
|
|
@ -51,7 +65,9 @@
|
|||
|
||||
<Card class="row-span-2 min-h-[20rem]" color="purple">
|
||||
<div class="flex h-full flex-col justify-end p-8 sm:p-12">
|
||||
<h2 class="mb-6 text-2xl font-bold text-white lg:text-5xl">First-class support</h2>
|
||||
<h2 class="mb-6 text-2xl font-bold text-white lg:text-5xl">
|
||||
First-class support
|
||||
</h2>
|
||||
<p class="max-w-[60ch] text-balance">
|
||||
Always working, actively developed, one-click-update
|
||||
</p>
|
||||
|
|
@ -81,8 +97,13 @@
|
|||
<Card class="min-h-[20rem]" color="purple">
|
||||
<div class="flex h-full flex-col justify-end p-8 sm:p-12">
|
||||
<div class="z-20 mix-blend-color-dodge">
|
||||
<h2 class="mb-6 text-5xl font-bold text-slate-300">Easy</h2>
|
||||
<p class="max-w-[60ch]">Designed to stay out of your way, and let you do your thing.</p>
|
||||
<h2 class="mb-6 text-5xl font-bold text-slate-300">
|
||||
Easy
|
||||
</h2>
|
||||
<p class="max-w-[60ch]">
|
||||
Designed to stay out of your way, and let you do your
|
||||
thing.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<img
|
||||
|
|
@ -98,10 +119,12 @@
|
|||
<Card class="min-h-[20rem]" color="purple">
|
||||
<div class="flex h-full flex-col justify-end">
|
||||
<div class="z-20 p-8 mix-blend-color-dodge sm:p-12">
|
||||
<h2 class="mb-6 text-5xl font-bold text-slate-300">Beautiful</h2>
|
||||
<h2 class="mb-6 text-5xl font-bold text-slate-300">
|
||||
Beautiful
|
||||
</h2>
|
||||
<p class="max-w-[60ch]">
|
||||
Customizable, with automatic theming for everything. Crafted with love from the
|
||||
Hyprland team
|
||||
Customizable, with automatic theming for everything.
|
||||
Crafted with love from the Hyprland team
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -118,7 +141,9 @@
|
|||
<div
|
||||
class=" col-span-full mt-8 flex size-full w-full items-center justify-center self-center"
|
||||
>
|
||||
<Button type="fancyOutline" href={hyprperksLink} size="xl">Subscribe now</Button>
|
||||
<Button type="fancyOutline" href={hyprperksLink} size="xl"
|
||||
>Subscribe now</Button
|
||||
>
|
||||
</div>
|
||||
</CardsContainer>
|
||||
</div>
|
||||
|
|
@ -137,7 +162,12 @@
|
|||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
mask-image: linear-gradient(to left, transparent, black, transparent);
|
||||
mask-image: linear-gradient(
|
||||
to left,
|
||||
transparent,
|
||||
black,
|
||||
transparent
|
||||
);
|
||||
contain: strict;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<script>
|
||||
<script lang="ts">
|
||||
import ActiveGitIcon from '~icons/gg/git-branch'
|
||||
import VaxryImage from '$lib/images/vaxry-github.webp'
|
||||
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
take
|
||||
} from 'rxjs'
|
||||
import GitTile from '$lib/components/GitTile.svelte'
|
||||
import { lerp } from '$lib/Helper.ts'
|
||||
import { lerp } from '$lib/Helper'
|
||||
import { cubicInOut, expoInOut } from 'svelte/easing'
|
||||
import DiscordProfilePicture from '$lib/components/DiscordProfilePicture.svelte'
|
||||
import { setContext } from 'svelte'
|
||||
|
|
@ -49,11 +49,15 @@
|
|||
)
|
||||
)
|
||||
),
|
||||
scan((level, value) => Math.min(ASCENION_CLICKS, Math.max(level + value, 0))),
|
||||
scan((level, value) =>
|
||||
Math.min(ASCENION_CLICKS, Math.max(level + value, 0))
|
||||
),
|
||||
startWith(0)
|
||||
)
|
||||
/** How many clicks are left in percent */
|
||||
const relativeLevel$ = clickLevel$.pipe(map((clicks) => clicks / ASCENION_CLICKS))
|
||||
const relativeLevel$ = clickLevel$.pipe(
|
||||
map((clicks) => clicks / ASCENION_CLICKS)
|
||||
)
|
||||
/** Tween/Ease the percents for a nicer look */
|
||||
const cubicRelativeLevel$ = relativeLevel$.pipe(map(cubicInOut))
|
||||
const expoRelativeLevel$ = relativeLevel$.pipe(map(expoInOut))
|
||||
|
|
@ -69,25 +73,36 @@
|
|||
const tiles$ = click$.pipe(
|
||||
switchMap(() =>
|
||||
merge(
|
||||
of(Math.floor(lerp(MIN_TILES_PER_CLICK, MAX_TILES_PER_CLICK, $cubicRelativeLevel$))),
|
||||
of(
|
||||
Math.floor(
|
||||
lerp(
|
||||
MIN_TILES_PER_CLICK,
|
||||
MAX_TILES_PER_CLICK,
|
||||
$cubicRelativeLevel$
|
||||
)
|
||||
)
|
||||
),
|
||||
// Remove the tiles after a timeout, if no new ones came in
|
||||
timer(MAX_LIFESPAN_TILE)
|
||||
)
|
||||
),
|
||||
scan(
|
||||
(acc, value) => (value === 0 ? [] : [...acc, ...Array.from({ length: value }, () => 1)]),
|
||||
[]
|
||||
(acc, value) =>
|
||||
value === 0
|
||||
? []
|
||||
: [...acc, ...Array.from({ length: value }, () => 1)],
|
||||
[] as number[]
|
||||
),
|
||||
startWith([])
|
||||
startWith([] as number[])
|
||||
)
|
||||
|
||||
$: hue = lerp(200, 130, $cubicRelativeLevel$)
|
||||
$: scale = lerp(0.9, 2, $cubicRelativeLevel$)
|
||||
$: translateY = lerp(0, 10, $cubicRelativeLevel$)
|
||||
let hue = $derived(lerp(200, 130, $cubicRelativeLevel$))
|
||||
let scale = $derived(lerp(0.9, 2, $cubicRelativeLevel$))
|
||||
let translateY = $derived(lerp(0, 10, $cubicRelativeLevel$))
|
||||
|
||||
/** @type {HTMLDivElement} */
|
||||
let containerElement
|
||||
let isAnimationComplete = false
|
||||
let containerElement = $state()
|
||||
let isAnimationComplete = $state(false)
|
||||
|
||||
const vaxrySize = 220
|
||||
const contextId = Symbol('hypractive context')
|
||||
|
|
@ -101,17 +116,24 @@
|
|||
}
|
||||
|
||||
function onClickUnlocked() {
|
||||
window.open('https://github.com/hyprwm/Hyprland/commits/main/', '_blank')
|
||||
window.open(
|
||||
'https://github.com/hyprwm/Hyprland/commits/main/',
|
||||
'_blank'
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="relative overflow-visible">
|
||||
<button
|
||||
class="flex items-center gap-3 font-bold text-slate-400 shadow-black drop-shadow-lg transition-colors hover:underline active:scale-95"
|
||||
on:click={isAnimationComplete ? onClickUnlocked : onClick}
|
||||
style:color={$relativeLevel$ > 0 ? `hsl(${hue} 64% 53%)` : undefined}
|
||||
onclick={isAnimationComplete ? onClickUnlocked : onClick}
|
||||
style:color={$relativeLevel$ > 0
|
||||
? `hsl(${hue} 64% 53%)`
|
||||
: undefined}
|
||||
style:scale={$relativeLevel$ > 0 ? scale : undefined}
|
||||
style:translate={$relativeLevel$ > 0 ? `0px -${translateY}px` : undefined}
|
||||
style:translate={$relativeLevel$ > 0
|
||||
? `0px -${translateY}px`
|
||||
: undefined}
|
||||
>
|
||||
<ActiveGitIcon class="h-8 w-8" />
|
||||
<span class="transition-colors"> Hypractive development </span>
|
||||
|
|
@ -120,7 +142,11 @@
|
|||
<div class="pointer-events-none absolute left-1/2 top-1/2 -z-10">
|
||||
{#each $tiles$ as _}
|
||||
<GitTile
|
||||
lifeSpan={lerp(MIN_LIFESPAN_TILE, MAX_LIFESPAN_TILE, $cubicRelativeLevel$)}
|
||||
lifeSpan={lerp(
|
||||
MIN_LIFESPAN_TILE,
|
||||
MAX_LIFESPAN_TILE,
|
||||
$cubicRelativeLevel$
|
||||
)}
|
||||
maxSpeed={lerp(10, 38, $expoRelativeLevel$)}
|
||||
minSpeed={lerp(1, 9, $expoRelativeLevel$)}
|
||||
/>
|
||||
|
|
@ -132,14 +158,14 @@
|
|||
<div
|
||||
class="vaxx-wrapper absolute bottom-[240px] left-1/2 z-50 aspect-square -translate-x-[100px] rounded-full animate-in fade-in-0 zoom-in-95 slide-in-from-bottom-[500px] slide-in-from-left-20 [animation-duration:2.5s]"
|
||||
style:width={vaxrySize + 'px'}
|
||||
on:animationend={() => (isAnimationComplete = true)}
|
||||
onanimationend={() => (isAnimationComplete = true)}
|
||||
>
|
||||
<DiscordProfilePicture
|
||||
image={VaxryImage}
|
||||
size={vaxrySize}
|
||||
weight={10}
|
||||
coordinates={[0, 0]}
|
||||
class="outline-orange-300"
|
||||
{contextId}
|
||||
isAnimating={false}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -149,8 +175,10 @@
|
|||
<div
|
||||
class="bg-gradient"
|
||||
style:opacity={$hasAscended$ ? 1 : $relativeLevel$}
|
||||
style="--relativeLevel: {$hasAscended$ ? 1 : $expoRelativeLevel$ - 0.2}"
|
||||
/>
|
||||
style="--relativeLevel: {$hasAscended$
|
||||
? 1
|
||||
: $expoRelativeLevel$ - 0.2}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -15,33 +15,49 @@
|
|||
|
||||
<section class="pb-6">
|
||||
<Title>
|
||||
<TitleHeading slot="title" class="">Install now</TitleHeading>
|
||||
<TitleSubtile slot="subtitle" class="">For your favourite distro</TitleSubtile>
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Install now</TitleHeading>
|
||||
{/snippet}
|
||||
{#snippet subtitle()}
|
||||
<TitleSubtile class="">For your favourite distro</TitleSubtile>
|
||||
{/snippet}
|
||||
</Title>
|
||||
|
||||
<div class="flex flex-col items-center gap-12 md:gap-6" use:animateIn={{ slide: 24, fade: 0 }}>
|
||||
<div
|
||||
class="flex flex-col items-center gap-12 md:gap-6"
|
||||
use:animateIn={{ slide: 24, fade: 0 }}
|
||||
>
|
||||
<div
|
||||
class="links_ flex flex-col gap-12 px-4 md:gap-6 md:rounded-3xl md:bg-gradient-to-tr md:from-blue-500/40 md:to-transparent md:p-8 md:shadow-xl md:outline md:outline-1 md:outline-blue-500"
|
||||
>
|
||||
<DistroOption name="Arch" image={archLogo}>
|
||||
<CommandButton command="pacman -S hyprland">
|
||||
<div slot="extra" class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2">
|
||||
AUR git version: <a
|
||||
class=" "
|
||||
target="_blank"
|
||||
href="https://aur.archlinux.org/packages/hyprland-git/">hyprland-git</a
|
||||
{#snippet extra()}
|
||||
<div
|
||||
class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2"
|
||||
>
|
||||
</div>
|
||||
AUR git version: <a
|
||||
class=" "
|
||||
target="_blank"
|
||||
href="https://aur.archlinux.org/packages/hyprland-git/"
|
||||
>hyprland-git</a
|
||||
>
|
||||
</div>
|
||||
{/snippet}
|
||||
</CommandButton>
|
||||
</DistroOption>
|
||||
|
||||
<DistroOption name="NixOS" image={nixLogo}>
|
||||
<CommandButton command="programs.hyprland.enable">
|
||||
<div slot="extra" class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2">
|
||||
<a href="https://wiki.hypr.land/Nix/" target="_blank"
|
||||
>See more details and git version ↗</a
|
||||
{#snippet extra()}
|
||||
<div
|
||||
class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2"
|
||||
>
|
||||
</div>
|
||||
<a href="https://wiki.hypr.land/Nix/" target="_blank"
|
||||
>See more details and git version ↗</a
|
||||
>
|
||||
</div>
|
||||
{/snippet}
|
||||
</CommandButton>
|
||||
</DistroOption>
|
||||
|
||||
|
|
@ -51,17 +67,22 @@
|
|||
|
||||
<DistroOption name="openSUSE" image={suseLogo}>
|
||||
<CommandButton command="zypper in hyprland">
|
||||
<div slot="extra" class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2">
|
||||
or install “hyprland” via YaST2 Software.
|
||||
</div>
|
||||
{#snippet extra()}
|
||||
<div
|
||||
class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2"
|
||||
>
|
||||
or install “hyprland” via YaST2 Software.
|
||||
</div>
|
||||
{/snippet}
|
||||
</CommandButton>
|
||||
<img
|
||||
class=" absolute inset-0 -z-10 translate-y-1 rotate-0 scale-90 opacity-0 transition-all duration-700 [transition-delay:2s] group-hover:-translate-x-3 group-hover:-translate-y-0 group-hover:-rotate-12 group-hover:scale-100 group-hover:opacity-90"
|
||||
src={amongus}
|
||||
slot="imageExtra"
|
||||
alt=""
|
||||
srcset=""
|
||||
/>
|
||||
{#snippet imageExtra()}
|
||||
<img
|
||||
class=" absolute inset-0 -z-10 translate-y-1 rotate-0 scale-90 opacity-0 transition-all duration-700 [transition-delay:2s] group-hover:-translate-x-3 group-hover:-translate-y-0 group-hover:-rotate-12 group-hover:scale-100 group-hover:opacity-90"
|
||||
src={amongus}
|
||||
alt=""
|
||||
srcset=""
|
||||
/>
|
||||
{/snippet}
|
||||
</DistroOption>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,17 @@
|
|||
import TitlePre from '$lib/components/Title/TitlePre.svelte'
|
||||
import Title from '$lib/components/Title/TitleWrapper.svelte'
|
||||
|
||||
export let news
|
||||
let { news } = $props()
|
||||
</script>
|
||||
|
||||
<section class="relative mb-12 max-w-5xl px-8 md:mb-20">
|
||||
<Title>
|
||||
<TitlePre slot="pre">Read while it's fresh!</TitlePre>
|
||||
<TitleHeading slot="title" class="">Latest news</TitleHeading>
|
||||
{#snippet pre()}
|
||||
<TitlePre>Read while it's fresh!</TitlePre>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Latest news</TitleHeading>
|
||||
{/snippet}
|
||||
</Title>
|
||||
|
||||
<ul class="mt-8 flex flex-col gap-10">
|
||||
|
|
@ -30,7 +34,8 @@
|
|||
translate: -25% 0;
|
||||
margin-top: -100px;
|
||||
|
||||
background: url('/imgs/grain.webp'),
|
||||
background:
|
||||
url('/imgs/grain.webp'),
|
||||
radial-gradient(
|
||||
50% 50% at 50% 50%,
|
||||
theme(colors.cyan.500 / 50%) 0%,
|
||||
|
|
|
|||
|
|
@ -7,15 +7,22 @@
|
|||
import clsx from 'clsx'
|
||||
import Video from '$lib/components/Video.svelte'
|
||||
import { animateIn } from '$lib/Helper.ts'
|
||||
import { Subject, debounceTime, map, tap, throttle, throttleTime } from 'rxjs'
|
||||
import {
|
||||
Subject,
|
||||
debounceTime,
|
||||
map,
|
||||
tap,
|
||||
throttle,
|
||||
throttleTime
|
||||
} from 'rxjs'
|
||||
import { onMount } from 'svelte'
|
||||
import { fade } from 'svelte/transition'
|
||||
import Button from '$lib/components/Button.svelte'
|
||||
|
||||
/** @type {HTMLVideoElement[]}*/
|
||||
const videos = []
|
||||
let activeIndex = 0
|
||||
let isHoveringVideo = false
|
||||
const videos = $state([])
|
||||
let activeIndex = $state(0)
|
||||
let isHoveringVideo = $state(false)
|
||||
const isVideoCroppedInput$ = new Subject()
|
||||
/** @type {import('rxjs').Subject<boolean>}*/
|
||||
const isVideoCropped$ = isVideoCroppedInput$.pipe(
|
||||
|
|
@ -41,7 +48,8 @@
|
|||
{
|
||||
icon: IconIpc,
|
||||
title: 'Bindings and IPC.',
|
||||
description: 'Control your desktop with your favourite languages or simply via IPC.',
|
||||
description:
|
||||
'Control your desktop with your favourite languages or simply via IPC.',
|
||||
poster: '/videos/aylur_thumb.png',
|
||||
src: '/videos/aylur',
|
||||
subtext: `Setup by <a href="https://github.com/Aylur/dotfiles" target="_blank">Aylur</a>, creator of
|
||||
|
|
@ -55,13 +63,17 @@
|
|||
}
|
||||
|
||||
function onPlay(currentIndex) {
|
||||
videos.filter((_, index) => index !== currentIndex).forEach((video) => video.play())
|
||||
videos
|
||||
.filter((_, index) => index !== currentIndex)
|
||||
.forEach((video) => video.play())
|
||||
}
|
||||
function onPause(activeIndex, currentIndex) {
|
||||
// Prevent infinite loop when active video gets paused and other videos also get paused as a result
|
||||
if (currentIndex !== activeIndex) return
|
||||
|
||||
videos.filter((_, index) => index !== currentIndex).forEach((video) => video.pause())
|
||||
videos
|
||||
.filter((_, index) => index !== currentIndex)
|
||||
.forEach((video) => video.pause())
|
||||
}
|
||||
function toggleVideoSlide() {
|
||||
isHoveringVideo ? slideVideoOut() : slideVideoIn()
|
||||
|
|
@ -78,9 +90,11 @@
|
|||
})
|
||||
</script>
|
||||
|
||||
<svelte:window on:resize={() => isVideoCroppedInput$.next(0)} />
|
||||
<svelte:window onresize={() => isVideoCroppedInput$.next(0)} />
|
||||
|
||||
<section class="relative z-0 flex min-h-max w-full flex-col items-center py-20">
|
||||
<section
|
||||
class="relative z-0 flex min-h-max w-full flex-col items-center py-20"
|
||||
>
|
||||
<div
|
||||
class="mx-auto grid max-w-7xl grid-cols-1 gap-8 transition-all lg:grid-cols-2 lg:gap-12"
|
||||
use:animateIn={{ slide: 24 }}
|
||||
|
|
@ -94,22 +108,24 @@
|
|||
<div class="txt-shadow_ mt-8 flex flex-col gap-6">
|
||||
<h2 class=" text-6xl font-bold">Unlock full power</h2>
|
||||
<p class="text-lg font-bold text-slate-300">
|
||||
Get the latest features Linux offers. Have full control over your workflow by customizing
|
||||
and extending it how you want.
|
||||
Get the latest features Linux offers. Have full control over
|
||||
your workflow by customizing and extending it how you want.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex h-full flex-col gap-4">
|
||||
{#each items as { icon, title, description }, index}
|
||||
{@const isActive = index === activeIndex}
|
||||
{@const SvelteComponent = icon}
|
||||
<button
|
||||
class={clsx(
|
||||
'flex gap-3 rounded-xl px-4 py-4 outline-0 outline-cyan-400/50 transition-all sm:-ml-4',
|
||||
isActive && 'bg-blue-300/5 shadow-md outline outline-1 backdrop-blur-sm '
|
||||
isActive &&
|
||||
'bg-blue-300/5 shadow-md outline outline-1 backdrop-blur-sm '
|
||||
)}
|
||||
on:mouseenter={() => setActiveItem(index)}
|
||||
onmouseenter={() => setActiveItem(index)}
|
||||
>
|
||||
<svelte:component this={icon} class="h-8 w-8 shrink-0 text-primary" />
|
||||
<SvelteComponent class="h-8 w-8 shrink-0 text-primary" />
|
||||
<p
|
||||
class={clsx(
|
||||
'txt-shadow_ text-left text-lg font-medium transition-colors ',
|
||||
|
|
@ -134,12 +150,15 @@
|
|||
target="_blank"
|
||||
>
|
||||
<div>
|
||||
Also see <span class="text-cyan-500">Awesome Hyprland</span>
|
||||
Also see <span class="text-cyan-500"
|
||||
>Awesome Hyprland</span
|
||||
>
|
||||
</div>
|
||||
<IconLinkOut />
|
||||
</a>
|
||||
<p class="font-medium text-slate-400">
|
||||
A list of plugins, bindings, apps and more made by the community
|
||||
A list of plugins, bindings, apps and more made by the
|
||||
community
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -153,7 +172,7 @@
|
|||
>
|
||||
{#if $isVideoCropped$}
|
||||
<button
|
||||
on:click={toggleVideoSlide}
|
||||
onclick={toggleVideoSlide}
|
||||
class:rotate-180={isHoveringVideo}
|
||||
class="group absolute -left-6 top-1/2 z-50 rounded-full bg-blue-400/5 p-2 outline outline-white/10 backdrop-blur-sm transition-transform"
|
||||
out:fade
|
||||
|
|
@ -189,7 +208,9 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<PatternBackground class="absolute inset-0 h-[110%] w-full text-slate-800 opacity-40" />
|
||||
<PatternBackground
|
||||
class="absolute inset-0 h-[110%] w-full text-slate-800 opacity-40"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<style lang="postcss">
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@
|
|||
import { inview } from 'svelte-inview'
|
||||
import Video from '$lib/components/Video.svelte'
|
||||
import { onMount } from 'svelte'
|
||||
/** @type {{ [key: string]: any }} */
|
||||
let { ...rest } = $props()
|
||||
|
||||
/** @type HTMLVideoElement */
|
||||
let videoElement
|
||||
let videoElement = $state()
|
||||
|
||||
let isVisible = false
|
||||
let isManuallyPaused = false
|
||||
let isChrome = false
|
||||
let isVisible = $state(false)
|
||||
let isManuallyPaused = $state(false)
|
||||
let isChrome = $state(false)
|
||||
|
||||
onMount(() => {
|
||||
isChrome = navigator.userAgent.toLowerCase().includes('chrome')
|
||||
|
|
@ -21,18 +23,18 @@
|
|||
})
|
||||
</script>
|
||||
|
||||
<section class={$$restProps.class} class:isVisible>
|
||||
<section class={rest.class} class:isVisible>
|
||||
<div
|
||||
class="wrapper"
|
||||
role="banner"
|
||||
use:inview={{ threshold: 0.5 }}
|
||||
on:inview_enter={() => {
|
||||
oninview_enter={() => {
|
||||
isVisible = true
|
||||
if (!isManuallyPaused && isChrome) {
|
||||
videoElement.play().catch(() => {})
|
||||
}
|
||||
}}
|
||||
on:inview_leave={() => {
|
||||
oninview_leave={() => {
|
||||
isVisible = false
|
||||
isManuallyPaused = videoElement.paused
|
||||
videoElement.pause()
|
||||
|
|
@ -56,7 +58,7 @@
|
|||
>
|
||||
</div>
|
||||
|
||||
<div class="preview-rice-bg" aria="hidden" />
|
||||
<div class="preview-rice-bg" aria="hidden"></div>
|
||||
</section>
|
||||
|
||||
<style lang="postcss">
|
||||
|
|
@ -66,7 +68,7 @@
|
|||
contain: layout style content;
|
||||
}
|
||||
.wrapper {
|
||||
@apply mx-3 rounded-xl;
|
||||
@apply mx-3 rounded-xl;
|
||||
transition: all cubic-bezier(0.9, -1, 0.065, 1.8) 1060ms;
|
||||
position: relative;
|
||||
box-shadow: 0px 0px 44px theme(colors.primary / 80%);
|
||||
|
|
@ -109,8 +111,13 @@
|
|||
width: 1100px;
|
||||
height: 200%;
|
||||
|
||||
background-image: url('/imgs/grain.webp'),
|
||||
radial-gradient(closest-side, theme(colors.sky.500), theme(colors.indigo.500 / 0%));
|
||||
background-image:
|
||||
url('/imgs/grain.webp'),
|
||||
radial-gradient(
|
||||
closest-side,
|
||||
theme(colors.sky.500),
|
||||
theme(colors.indigo.500 / 0%)
|
||||
);
|
||||
|
||||
mask-image: radial-gradient(
|
||||
closest-side,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@
|
|||
import Button from '$lib/components/Button.svelte'
|
||||
import PatternBackground from '$lib/PatternBackground.svelte'
|
||||
|
||||
export let sponsors: SponsorsRanked
|
||||
interface Props {
|
||||
sponsors: SponsorsRanked
|
||||
}
|
||||
|
||||
let { sponsors }: Props = $props()
|
||||
const hasSponsors = Object.values(sponsors)
|
||||
.filter((value) => !Array.isArray(value))
|
||||
.every((array) => array.length === 0)
|
||||
|
|
@ -16,15 +20,23 @@
|
|||
</script>
|
||||
|
||||
{#if hasSponsors}
|
||||
<div class="relative flex w-full flex-col items-center justify-center">
|
||||
<div
|
||||
class="relative flex w-full flex-col items-center justify-center"
|
||||
>
|
||||
<PatternBackground
|
||||
class="absolute -inset-12 inset-x-0 h-[100%] w-full text-slate-800 opacity-40"
|
||||
class="absolute -inset-24 inset-x-0 h-[120%] w-full text-slate-800 opacity-40"
|
||||
/>
|
||||
|
||||
<section class="relative mb-12 flex max-w-5xl flex-col gap-2 px-8 md:mb-20">
|
||||
<section
|
||||
class="relative mb-12 flex max-w-5xl flex-col gap-2 px-8 md:mb-20"
|
||||
>
|
||||
<Title class="px-0">
|
||||
<TitlePre slot="pre">A huge thank you!</TitlePre>
|
||||
<TitleHeading size="small" slot="title">Our sponsors</TitleHeading>
|
||||
{#snippet pre()}
|
||||
<TitlePre>A huge thank you!</TitlePre>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<TitleHeading size="small">Our sponsors</TitleHeading>
|
||||
{/snippet}
|
||||
</Title>
|
||||
|
||||
<div
|
||||
|
|
@ -40,7 +52,12 @@
|
|||
</h2>
|
||||
<div class="flex justify-center gap-14 md:gap-16">
|
||||
{#each sponsors.diamond as sponsor}
|
||||
<Sponsor {sponsor} showImage showSlogan class="h-18 w-50 md:h-30 md:w-72" />
|
||||
<Sponsor
|
||||
{sponsor}
|
||||
showImage
|
||||
showSlogan
|
||||
class="h-18 w-50 md:h-30 md:w-72"
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -55,7 +72,12 @@
|
|||
</h2>
|
||||
<div class="flex justify-center gap-14 md:gap-16">
|
||||
{#each sponsors.platinum as sponsor}
|
||||
<Sponsor {sponsor} showImage showSlogan class="h-16 w-40 md:h-20 md:w-60" />
|
||||
<Sponsor
|
||||
{sponsor}
|
||||
showImage
|
||||
showSlogan
|
||||
class="h-16 w-40 md:h-20 md:w-60"
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -82,14 +104,24 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-wrap justify-center gap-x-16 gap-y-10">
|
||||
<div
|
||||
class="flex flex-wrap justify-center gap-x-16 gap-y-10"
|
||||
>
|
||||
{#if sponsors.silver.length > 0}
|
||||
<div class="flex flex-col gap-2">
|
||||
<h2 class="text-center font-bold text-slate-300">Silver</h2>
|
||||
<h2 class="text-center font-bold text-slate-300">
|
||||
Silver
|
||||
</h2>
|
||||
|
||||
<div class="flex flex-wrap gap-x-4 gap-y-2 font-medium">
|
||||
<div
|
||||
class="flex flex-wrap gap-x-4 gap-y-2 font-medium"
|
||||
>
|
||||
{#each sponsors.silver as sponsor}
|
||||
<Sponsor {sponsor} showImage class={smallImages} />
|
||||
<Sponsor
|
||||
{sponsor}
|
||||
showImage
|
||||
class={smallImages}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -97,9 +129,13 @@
|
|||
|
||||
{#if sponsors.bronze.length > 0}
|
||||
<div class="flex flex-col gap-2">
|
||||
<h2 class="text-center font-bold text-slate-300">Bronze</h2>
|
||||
<h2 class="text-center font-bold text-slate-300">
|
||||
Bronze
|
||||
</h2>
|
||||
|
||||
<div class="flex flex-wrap gap-x-4 gap-y-2 font-medium">
|
||||
<div
|
||||
class="flex flex-wrap gap-x-4 gap-y-2 font-medium"
|
||||
>
|
||||
{#each sponsors.bronze as sponsor}
|
||||
<Sponsor {sponsor} class={smallImages} />
|
||||
{/each}
|
||||
|
|
@ -113,8 +149,10 @@
|
|||
class="col-span-full mt-8 flex size-full w-full items-center justify-center self-center"
|
||||
style="margin-top: 5rem; margin-bottom: -5rem;"
|
||||
>
|
||||
<Button type="outline" href="https://account.hypr.land/sponsors" size="xl"
|
||||
>Sponsor us</Button
|
||||
<Button
|
||||
type="outline"
|
||||
href="https://account.hypr.land/sponsors"
|
||||
size="xl">Sponsor us</Button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -124,8 +162,12 @@
|
|||
<section
|
||||
class="group -my-5 mt-16 flex flex-col gap-3 self-center text-center text-slate-300 md:-my-[8rem]"
|
||||
>
|
||||
<p class="text-2xl font-medium text-slate-500">We currently have no sponsors :(</p>
|
||||
<p class="text-slate-200">Are you interested or know one person who might be?</p>
|
||||
<p class="text-2xl font-medium text-slate-500">
|
||||
We currently have no sponsors :(
|
||||
</p>
|
||||
<p class="text-slate-200">
|
||||
Are you interested or know one person who might be?
|
||||
</p>
|
||||
<a
|
||||
href="https://account.hypr.land/sponsors"
|
||||
class="text-cyan-400 decoration-primary hover:text-cyan-300 hover:underline"
|
||||
|
|
|
|||
|
|
@ -1,18 +1,24 @@
|
|||
<script lang="ts">
|
||||
import DiscordProfilePicture from '$lib/components/DiscordProfilePicture.svelte'
|
||||
|
||||
export let biggestSize: number
|
||||
export let getRestrictionElement: (() => HTMLElement) | undefined = undefined
|
||||
interface Props {
|
||||
biggestSize: number
|
||||
getRestrictionElement?: (() => HTMLElement) | undefined
|
||||
}
|
||||
|
||||
let { biggestSize, getRestrictionElement = undefined }: Props =
|
||||
$props()
|
||||
const size = 90
|
||||
|
||||
let isDragging = false
|
||||
let isHover = false
|
||||
let image: string
|
||||
$: image = isDragging
|
||||
? 'imgs/chan/surprise.svg'
|
||||
: isHover
|
||||
? 'imgs/chan/wink.svg'
|
||||
: 'imgs/chan/joy.svg'
|
||||
let isDragging = $state(false)
|
||||
let isHover = $state(false)
|
||||
let image: string = $derived(
|
||||
isDragging
|
||||
? 'imgs/chan/surprise.svg'
|
||||
: isHover
|
||||
? 'imgs/chan/wink.svg'
|
||||
: 'imgs/chan/joy.svg'
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="absolute z-20">
|
||||
|
|
@ -23,8 +29,8 @@
|
|||
weight={size / biggestSize}
|
||||
{getRestrictionElement}
|
||||
class={'bg-blue-300 outline-cyan-500'}
|
||||
on:dragStart={() => (isDragging = true)}
|
||||
on:dragEnd={() => (isDragging = false)}
|
||||
on:hover={() => (isHover = true)}
|
||||
ondragStart={() => (isDragging = true)}
|
||||
ondragEnd={() => (isDragging = false)}
|
||||
onhover={() => (isHover = true)}
|
||||
></DiscordProfilePicture>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,26 +1,51 @@
|
|||
<script lang="ts">
|
||||
import { createThresholdStream, lerp, preloadImage } from '$lib/Helper'
|
||||
import {
|
||||
createThresholdStream,
|
||||
lerp,
|
||||
preloadImage
|
||||
} from '$lib/Helper'
|
||||
import DiscordProfilePicture from '$lib/components/DiscordProfilePicture.svelte'
|
||||
import { Subject, filter, first, map, merge, of, startWith, switchMap, timer } from 'rxjs'
|
||||
import {
|
||||
Subject,
|
||||
filter,
|
||||
first,
|
||||
map,
|
||||
merge,
|
||||
of,
|
||||
startWith,
|
||||
switchMap,
|
||||
timer
|
||||
} from 'rxjs'
|
||||
import { onDestroy } from 'svelte'
|
||||
|
||||
export let biggestSize: number
|
||||
export let getRestrictionElement: (() => HTMLElement) | undefined = undefined
|
||||
interface Props {
|
||||
biggestSize: number
|
||||
getRestrictionElement?: (() => HTMLElement) | undefined
|
||||
}
|
||||
|
||||
const thePozArmy = Object.values(import.meta.glob('$lib/images/poz/*', { eager: true })).map(
|
||||
(x) => x.default as string
|
||||
)
|
||||
let { biggestSize, getRestrictionElement = undefined }: Props =
|
||||
$props()
|
||||
|
||||
const thePozArmy = Object.values(
|
||||
import.meta.glob('$lib/images/poz/*', { eager: true })
|
||||
).map((x) => x.default as string)
|
||||
|
||||
const size = 90
|
||||
const origin = [710, 615] as const
|
||||
let newPosition: readonly [number, number]
|
||||
let newPosition: readonly [number, number] = $state()
|
||||
const clicksTarget = 9
|
||||
const shakeMax = 24
|
||||
const clicksInput$ = new Subject()
|
||||
const level$ = clicksInput$.pipe(
|
||||
createThresholdStream({ clicksTarget, clicksEachMs: 250, fallof: 10 })
|
||||
createThresholdStream({
|
||||
clicksTarget,
|
||||
clicksEachMs: 250,
|
||||
fallof: 10
|
||||
})
|
||||
)
|
||||
const relativeLevel$ = level$.pipe(
|
||||
map((clicks) => clicks / clicksTarget)
|
||||
)
|
||||
const relativeLevel$ = level$.pipe(map((clicks) => clicks / clicksTarget))
|
||||
const hasFinished$ = relativeLevel$.pipe(
|
||||
filter((clicks) => clicks >= 1),
|
||||
first(),
|
||||
|
|
@ -74,10 +99,14 @@
|
|||
isAnimating={false}
|
||||
tag="poz"
|
||||
{getRestrictionElement}
|
||||
on:enteredView={({ detail: { dragCoordinates } }) => {
|
||||
onenteredView={({ detail: { dragCoordinates } }) => {
|
||||
dragCoordinates.update(([x, y]) => {
|
||||
x += lerp(400, 0, (size / maxSize) * (1 - Math.random())) * (Math.random() > 0.5 ? 1 : -1)
|
||||
y += lerp(400, 0, (size / maxSize) * (1 - Math.random())) * (Math.random() > 0.5 ? 1 : -1)
|
||||
x +=
|
||||
lerp(400, 0, (size / maxSize) * (1 - Math.random())) *
|
||||
(Math.random() > 0.5 ? 1 : -1)
|
||||
y +=
|
||||
lerp(400, 0, (size / maxSize) * (1 - Math.random())) *
|
||||
(Math.random() > 0.5 ? 1 : -1)
|
||||
return [x, y]
|
||||
})
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@
|
|||
import TitleSubtile from '$lib/components/Title/TitleSubtile.svelte'
|
||||
import TitleHeading from '$lib/components/Title/TitleHeading.svelte'
|
||||
|
||||
export let data
|
||||
let { data } = $props()
|
||||
|
||||
/** @type {HTMLElement} */
|
||||
let asciiElement
|
||||
let asciiElement = $state()
|
||||
|
||||
const { posts } = data
|
||||
|
||||
|
|
@ -18,7 +18,9 @@
|
|||
|
||||
// Taken from https://github.com/NotAShelf/hyprascii/blob/main/web/script.js
|
||||
onMount(async () => {
|
||||
const logoBlob = await fetch(LogoPng).then((response) => response.blob())
|
||||
const logoBlob = await fetch(LogoPng).then((response) =>
|
||||
response.blob()
|
||||
)
|
||||
objectUrl = URL.createObjectURL(logoBlob)
|
||||
const img = document.createElement('img')
|
||||
img.src = objectUrl
|
||||
|
|
@ -48,8 +50,13 @@
|
|||
const chars = ' .-=+'
|
||||
for (let y = 0; y < cvs.height; y++) {
|
||||
for (let x = 0; x < cvs.width; x++) {
|
||||
const idx = 4 * ((sx < 0 ? cvs.width - x - 1 : x) + cvs.width * y)
|
||||
const br = getLuminance(pixels[idx] / 256, pixels[idx + 1] / 256, pixels[idx + 2] / 256)
|
||||
const idx =
|
||||
4 * ((sx < 0 ? cvs.width - x - 1 : x) + cvs.width * y)
|
||||
const br = getLuminance(
|
||||
pixels[idx] / 256,
|
||||
pixels[idx + 1] / 256,
|
||||
pixels[idx + 2] / 256
|
||||
)
|
||||
text.push(chars[Math.floor(br * chars.length)])
|
||||
}
|
||||
text.push('\n')
|
||||
|
|
@ -68,15 +75,18 @@
|
|||
<title>Hyprland News</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="fancy-top-gradient" />
|
||||
<div class="fancy-top-gradient"></div>
|
||||
|
||||
<section>
|
||||
<header class="header">
|
||||
<pre class="spinner-wrapper" bind:this={asciiElement} />
|
||||
<pre class="spinner-wrapper" bind:this={asciiElement}></pre>
|
||||
|
||||
<Title>
|
||||
<TitleHeading slot="title" class="">News</TitleHeading>
|
||||
<TitleSubtile>Fresh updates straight from the oven</TitleSubtile>
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">News</TitleHeading>
|
||||
{/snippet}
|
||||
<TitleSubtile>Fresh updates straight from the oven</TitleSubtile
|
||||
>
|
||||
</Title>
|
||||
</header>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,14 @@ import { error } from '@sveltejs/kit'
|
|||
|
||||
export async function load({ params, fetch }) {
|
||||
try {
|
||||
const post = await import(`../../../content/news/${params.slug}.md`)
|
||||
const post = await import(
|
||||
`../../../content/news/${params.slug}.md`
|
||||
)
|
||||
const other = await fetch('/api/news')
|
||||
.then((response) => response.json())
|
||||
.then((news) => news.filter((entry) => entry.slug !== params.slug).slice(0, 4))
|
||||
.then((news) =>
|
||||
news.filter((entry) => entry.slug !== params.slug).slice(0, 4)
|
||||
)
|
||||
|
||||
return {
|
||||
content: post.default,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import NewsThumb from '$lib/components/news-thumb.svelte'
|
||||
import clsx from 'clsx'
|
||||
|
||||
export let data
|
||||
let { data } = $props()
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
<meta property="og:title" content={data.meta.title} />
|
||||
</svelte:head>
|
||||
|
||||
<div class="fancy-top-gradient fad animate-in" />
|
||||
<div class="fancy-top-gradient fad animate-in"></div>
|
||||
|
||||
<article
|
||||
class="mx-auto mt-navbar flex max-w-4xl flex-col gap-4 px-6 pt-20 md:gap-8 md:px-8 lg:mt-32 lg:gap-14"
|
||||
|
|
@ -23,7 +23,9 @@
|
|||
class="flex flex-col items-start gap-8 duration-1000 animate-in fade-in-0 slide-in-from-bottom-4"
|
||||
>
|
||||
<h1 class="text-4xl font-bold lg:text-6xl">{data.meta.title}</h1>
|
||||
<div class="flex items-center gap-8 text-base font-medium text-slate-400">
|
||||
<div
|
||||
class="flex items-center gap-8 text-base font-medium text-slate-400"
|
||||
>
|
||||
{#if data.meta.author}
|
||||
<svelte:element
|
||||
this={data.meta.author.link ? 'a' : 'div'}
|
||||
|
|
@ -32,7 +34,8 @@
|
|||
target="_blank"
|
||||
class={clsx(
|
||||
'flex items-center gap-3 rounded-full bg-slate-700/50 px-4 py-2 text-base font-medium text-slate-300 ',
|
||||
data.meta.author.link && 'transition-colors hover:bg-slate-700/70 hover:text-slate-200'
|
||||
data.meta.author.link &&
|
||||
'transition-colors hover:bg-slate-700/70 hover:text-slate-200'
|
||||
)}
|
||||
>
|
||||
{#if data.meta.author.picture}
|
||||
|
|
@ -49,7 +52,9 @@
|
|||
</div>
|
||||
</svelte:element>
|
||||
{/if}
|
||||
<time class="" datetime={new Date(data.meta.date * 1000).toISOString()}
|
||||
<time
|
||||
class=""
|
||||
datetime={new Date(data.meta.date * 1000).toISOString()}
|
||||
>{formatDate(data.meta.date * 1000)}</time
|
||||
>
|
||||
</div>
|
||||
|
|
@ -59,14 +64,16 @@
|
|||
<div
|
||||
class="prose prose-slate prose-invert transition-none delay-500 animate-in fade-in-0 fill-mode-backwards [animation-duration:2500ms] lg:prose-xl prose-a:text-cyan-400 prose-img:rounded-lg"
|
||||
>
|
||||
<svelte:component this={data.content} />
|
||||
<data.content />
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{#if data.other.length > 0}
|
||||
<section class="mx-auto mt-72 max-w-screen-lg">
|
||||
<Title class="mb-6">
|
||||
<TitleHeading slot="title" class="">More news</TitleHeading>
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">More news</TitleHeading>
|
||||
{/snippet}
|
||||
</Title>
|
||||
|
||||
<ul class="grid grid-cols-2 gap-x-7 gap-y-16">
|
||||
|
|
|
|||
|
|
@ -10,10 +10,12 @@
|
|||
import TitleHeading from '$lib/components/Title/TitleHeading.svelte'
|
||||
import Button from '$lib/components/Button.svelte'
|
||||
|
||||
export let data
|
||||
let { data } = $props()
|
||||
|
||||
const { plugins } = data
|
||||
const featuredPlugins = plugins.filter(({ featured }) => featured).slice(0, 4)
|
||||
const featuredPlugins = plugins
|
||||
.filter(({ featured }) => featured)
|
||||
.slice(0, 4)
|
||||
const pluginsByCategory = R.pipe(
|
||||
plugins,
|
||||
R.groupBy(({ category }) => category),
|
||||
|
|
@ -48,11 +50,19 @@
|
|||
>
|
||||
<div class="top-light"></div>
|
||||
|
||||
<header class="mt-24 flex flex-col items-center justify-center md:mt-32">
|
||||
<header
|
||||
class="mt-24 flex flex-col items-center justify-center md:mt-32"
|
||||
>
|
||||
<Title>
|
||||
<TitlePre slot="pre">Plugins</TitlePre>
|
||||
<TitleHeading slot="title" class="">Unlock full power</TitleHeading>
|
||||
<TitleSubtile>Easily load up plugins and customize everything</TitleSubtile>
|
||||
{#snippet pre()}
|
||||
<TitlePre>Plugins</TitlePre>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Unlock full power</TitleHeading>
|
||||
{/snippet}
|
||||
<TitleSubtile
|
||||
>Easily load up plugins and customize everything</TitleSubtile
|
||||
>
|
||||
</Title>
|
||||
|
||||
<div
|
||||
|
|
@ -74,11 +84,17 @@
|
|||
>
|
||||
<!-- Secondary navigation -->
|
||||
<div class="hidden lg:block">
|
||||
<nav class="sticky top-32 z-40 max-h-max min-w-52 shrink-0 grow-0 flex-col gap-2 px-8">
|
||||
<ul class="flex flex-col gap-4 text-sm font-medium text-slate-400">
|
||||
<nav
|
||||
class="sticky top-32 z-40 max-h-max min-w-52 shrink-0 grow-0 flex-col gap-2 px-8"
|
||||
>
|
||||
<ul
|
||||
class="flex flex-col gap-4 text-sm font-medium text-slate-400"
|
||||
>
|
||||
{#each pluginsByCategory as category}
|
||||
<li>
|
||||
<a href={'#' + category[0]} class=" px-2 py-1 transition-colors hover:text-white"
|
||||
<a
|
||||
href={'#' + category[0]}
|
||||
class=" px-2 py-1 transition-colors hover:text-white"
|
||||
>{category[0]}</a
|
||||
>
|
||||
</li>
|
||||
|
|
@ -101,7 +117,11 @@
|
|||
plugin.banner &&
|
||||
featuredPlugins
|
||||
.slice(0, index)
|
||||
.reduce((total, { banner }) => (banner ? total + 1 : total), 0) < 2
|
||||
.reduce(
|
||||
(total, { banner }) =>
|
||||
banner ? total + 1 : total,
|
||||
0
|
||||
) < 2
|
||||
? 'col-span-8 h-[18rem] md:col-span-6 lg:col-span-8 '
|
||||
: 'col-span-6 h-[16rem] lg:col-span-4 ',
|
||||
|
||||
|
|
@ -119,7 +139,10 @@
|
|||
>
|
||||
{#each pluginsByCategory as [category, plugins]}
|
||||
<div class="flex w-full flex-col gap-2">
|
||||
<h3 id={category} class="scroll-mt-32 text-lg font-medium text-slate-300">
|
||||
<h3
|
||||
id={category}
|
||||
class="scroll-mt-32 text-lg font-medium text-slate-300"
|
||||
>
|
||||
{category}
|
||||
</h3>
|
||||
<div class="flex w-full flex-wrap gap-4">
|
||||
|
|
@ -141,7 +164,8 @@
|
|||
|
||||
<style lang="postcss">
|
||||
.top-light {
|
||||
background: url('/imgs/grain.webp'),
|
||||
background:
|
||||
url('/imgs/grain.webp'),
|
||||
radial-gradient(
|
||||
100% 80% at top,
|
||||
theme(colors.cyan.500 / 50%) 0%,
|
||||
|
|
|
|||
|
|
@ -6,22 +6,34 @@
|
|||
|
||||
// Dont forget to put this component inside of CardsContainer.svelte
|
||||
// Also pass a hight class to the element, as otherwise the banner might not align properly,
|
||||
// due to the parent container being unable to use height: full as there wont be a reference
|
||||
|
||||
export let plugin
|
||||
export let color = undefined
|
||||
export let showCategory = false
|
||||
export let taglineMaxLength = 0
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {any} plugin - due to the parent container being unable to use height: full as there wont be a reference
|
||||
* @property {any} [color]
|
||||
* @property {boolean} [showCategory]
|
||||
* @property {number} [taglineMaxLength]
|
||||
*/
|
||||
|
||||
/** @type {Props & { [key: string]: any }} */
|
||||
let {
|
||||
plugin,
|
||||
color = undefined,
|
||||
showCategory = false,
|
||||
taglineMaxLength = 0,
|
||||
...rest
|
||||
} = $props()
|
||||
|
||||
/** @type {HTMLVideoElement}*/
|
||||
let videoElement
|
||||
let videoElement = $state()
|
||||
</script>
|
||||
|
||||
<Card
|
||||
on:mouseenter={() => videoElement && videoElement.play().catch(console.error)}
|
||||
on:mouseenter={() =>
|
||||
videoElement && videoElement.play().catch(console.error)}
|
||||
on:mouseleave={() => videoElement && videoElement.pause()}
|
||||
{color}
|
||||
class={$$restProps.class}
|
||||
class={rest.class}
|
||||
gradientOpacity={0.5}
|
||||
><a
|
||||
href={plugin.url}
|
||||
|
|
@ -47,7 +59,11 @@
|
|||
)}
|
||||
style:--background={`url("${getGeneratedPath(plugin.logo)}")`}
|
||||
>
|
||||
<img class="size-full" src={plugin.logo} alt={'Logo of ' + plugin.name} />
|
||||
<img
|
||||
class="size-full"
|
||||
src={plugin.logo}
|
||||
alt={'Logo of ' + plugin.name}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Placeholder logo -->
|
||||
|
|
@ -73,7 +89,10 @@
|
|||
<p
|
||||
class="overflow-hiddenx text-nowrapx max-w-[60ch] text-ellipsis text-sm font-medium text-slate-400 @xl:overflow-auto @xl:text-pretty @xl:text-base"
|
||||
>
|
||||
{trimText(plugin.tagline, taglineMaxLength || Number.POSITIVE_INFINITY)}
|
||||
{trimText(
|
||||
plugin.tagline,
|
||||
taglineMaxLength || Number.POSITIVE_INFINITY
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -100,7 +119,11 @@
|
|||
loop
|
||||
></video>
|
||||
{:else}
|
||||
<img src={plugin.banner} class="absolute inset-0 size-full object-cover" alt="" />
|
||||
<img
|
||||
src={plugin.banner}
|
||||
class="absolute inset-0 size-full object-cover"
|
||||
alt=""
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -124,7 +147,11 @@
|
|||
z-index: -1;
|
||||
opacity: 50%;
|
||||
/* filter: brightness(2); */
|
||||
mask-image: radial-gradient(closest-side, black 0%, transparent 99%);
|
||||
mask-image: radial-gradient(
|
||||
closest-side,
|
||||
black 0%,
|
||||
transparent 99%
|
||||
);
|
||||
}
|
||||
}
|
||||
.logo-container:not(.banner) {
|
||||
|
|
@ -139,7 +166,11 @@
|
|||
height: 100%;
|
||||
z-index: -20;
|
||||
/* translate: 0 -2px; */
|
||||
mask-image: radial-gradient(450% 120% at 0% 100%, black 18%, white);
|
||||
mask-image: radial-gradient(
|
||||
450% 120% at 0% 100%,
|
||||
black 18%,
|
||||
white
|
||||
);
|
||||
mask-mode: luminance;
|
||||
@apply rounded-3xl;
|
||||
contain: strict;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
<script>
|
||||
/** @type {string}*/
|
||||
export let tag
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
* @property {string} tag
|
||||
*/
|
||||
|
||||
/** @type {Props} */
|
||||
let { tag } = $props()
|
||||
|
||||
const colors = {
|
||||
Design: 'text-pink-200 bg-pink-500/15',
|
||||
|
|
@ -12,7 +17,8 @@
|
|||
</script>
|
||||
|
||||
<div
|
||||
class={'min-w-max rounded-full p-1 px-2.5 text-xs font-bold ' + colors[tag] ?? 'bg-slate-100/5'}
|
||||
class={'min-w-max rounded-full p-1 px-2.5 text-xs font-bold ' +
|
||||
colors[tag] ?? 'bg-slate-100/5'}
|
||||
>
|
||||
{tag}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,12 @@ html.lock-scroll,
|
|||
}
|
||||
|
||||
.hyprgradient {
|
||||
background-image: linear-gradient(to bottom right, #00e6cf, #00c4e3, #0081c6);
|
||||
background-image: linear-gradient(
|
||||
to bottom right,
|
||||
#00e6cf,
|
||||
#00c4e3,
|
||||
#0081c6
|
||||
);
|
||||
}
|
||||
|
||||
::selection {
|
||||
|
|
@ -82,7 +87,16 @@ html.lock-scroll,
|
|||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1000;
|
||||
mask-image: radial-gradient(var(--sizethis), white 70%, transparent);
|
||||
background: url('/imgs/grain.webp'),
|
||||
radial-gradient(var(--sizethis), theme(colors.cyan.500 / 40%), transparent);
|
||||
mask-image: radial-gradient(
|
||||
var(--sizethis),
|
||||
white 70%,
|
||||
transparent
|
||||
);
|
||||
background:
|
||||
url('/imgs/grain.webp'),
|
||||
radial-gradient(
|
||||
var(--sizethis),
|
||||
theme(colors.cyan.500 / 40%),
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import Button from '$lib/components/Button.svelte'
|
||||
import CloseIcon from '~icons/mingcute/close-line'
|
||||
|
||||
let kofiModal: HTMLDialogElement | undefined
|
||||
let kofiModal: HTMLDialogElement | undefined = $state()
|
||||
|
||||
function lockScroll() {
|
||||
document.querySelector('html')?.classList.add('lock-scroll')
|
||||
|
|
@ -19,47 +19,66 @@
|
|||
<title>{'Support | Hyprland'}</title>
|
||||
<meta name="description" content="Support Hyprland Development" />
|
||||
<meta property="og:title" content="Donate to Hyprland" />
|
||||
<meta property="og:description" content="Support the continuation of Hyprland Development" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Support the continuation of Hyprland Development"
|
||||
/>
|
||||
</svelte:head>
|
||||
|
||||
<div class="top-light"></div>
|
||||
|
||||
<section class="min-h-screen w-full md:mt-32">
|
||||
<Title class="mb-4 md:mb-8">
|
||||
<TitlePre slot="pre">Help continue Hyprland development</TitlePre>
|
||||
<TitleHeading slot="title" class="">Support Hyprland</TitleHeading>
|
||||
{#snippet pre()}
|
||||
<TitlePre>Help continue Hyprland development</TitlePre>
|
||||
{/snippet}
|
||||
{#snippet title()}
|
||||
<TitleHeading class="">Support Hyprland</TitleHeading>
|
||||
{/snippet}
|
||||
</Title>
|
||||
|
||||
<div
|
||||
class="prose prose-slate prose-invert m-0 mx-auto flex flex-col px-6 transition-none delay-500 animate-in fade-in-0 slide-in-from-bottom-6 fill-mode-backwards [animation-delay:800ms] [animation-duration:1500ms] lg:prose-xl prose-a:text-cyan-400 prose-img:rounded-lg"
|
||||
>
|
||||
<p>
|
||||
Hyprland development is done by volunteers, and led by one person in their free time. If you
|
||||
want to show a token of appreciation, or help the development continue, consider supporting
|
||||
the project!
|
||||
Hyprland development is done by volunteers, and led by one
|
||||
person in their free time. If you want to show a token of
|
||||
appreciation, or help the development continue, consider
|
||||
supporting the project!
|
||||
</p>
|
||||
|
||||
<h2>Subscribe to Hyprperks</h2>
|
||||
<p class="!mb-0 !pb-0">We offer hyprperks, a 5€+tax/mo subscription that is like a monthly donation,
|
||||
but you also get access to member-only forums (with dev Q&A, support straight from the devs)
|
||||
and Hyprland DE, a fully preconfigured, easy to use, one-click updating dotfiles
|
||||
<p class="!mb-0 !pb-0">
|
||||
We offer hyprperks, a 5€+tax/mo subscription that is like a
|
||||
monthly donation, but you also get access to member-only forums
|
||||
(with dev Q&A, support straight from the devs) and Hyprland DE,
|
||||
a fully preconfigured, easy to use, one-click updating dotfiles
|
||||
straight from the Hyprland team.
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
Check it out <a href="https://account.hypr.land/pricing" target="_blank">here</a>
|
||||
Check it out
|
||||
<a href="https://account.hypr.land/pricing" target="_blank"
|
||||
>here</a
|
||||
>
|
||||
</p>
|
||||
|
||||
<h2>Donate</h2>
|
||||
<p class="!mb-0 !pb-0">You can donate once, or monthly, via the following channels:</p>
|
||||
<p class="!mb-0 !pb-0">
|
||||
You can donate once, or monthly, via the following channels:
|
||||
</p>
|
||||
|
||||
<ul class="">
|
||||
<li>
|
||||
PayPal: <a href="https://ko-fi.com/vaxry" target="_blank">ko-fi.com/vaxry</a>
|
||||
PayPal: <a href="https://ko-fi.com/vaxry" target="_blank"
|
||||
>ko-fi.com/vaxry</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
Crypto: please see the pinned post <a href="https://ko-fi.com/vaxry" target="_blank">here</a
|
||||
Crypto: please see the pinned post <a
|
||||
href="https://ko-fi.com/vaxry"
|
||||
target="_blank">here</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
@ -67,11 +86,13 @@
|
|||
<h2>Do I get anything?</h2>
|
||||
|
||||
<p>
|
||||
If you decide to donate, you will be listed in the special thanks part of the next Hyprland
|
||||
release notes, and, if you are a member of the Discord server, a role to signify you have
|
||||
If you decide to donate, you will be listed in the special
|
||||
thanks part of the next Hyprland release notes, and, if you are
|
||||
a member of the Discord server, a role to signify you have
|
||||
supported the project.
|
||||
<br /><br />
|
||||
Outside of that, you get the satisfaction that you rock and support the software you use and love!
|
||||
Outside of that, you get the satisfaction that you rock and support
|
||||
the software you use and love!
|
||||
</p>
|
||||
|
||||
<div
|
||||
|
|
@ -87,8 +108,9 @@
|
|||
>
|
||||
|
||||
<p>
|
||||
Hyprland is, and will always stay Free and Open Source software. Donating is purely
|
||||
voluntary.<br />We will never lock out features behind a paywall.
|
||||
Hyprland is, and will always stay Free and Open Source
|
||||
software. Donating is purely voluntary.<br />We will never
|
||||
lock out features behind a paywall.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -101,11 +123,12 @@
|
|||
<div class="relative h-[712px] min-h-[712px] shadow-2xl">
|
||||
<form class="absolute -right-2 -top-4 z-10">
|
||||
<button
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
unlockScroll()
|
||||
}}
|
||||
formmethod="dialog"
|
||||
class="rounded-full bg-white p-1 shadow-md"><CloseIcon class="size-5" /></button
|
||||
class="rounded-full bg-white p-1 shadow-md"
|
||||
><CloseIcon class="size-5" /></button
|
||||
>
|
||||
</form>
|
||||
<div class="modal-content overflow-hidden rounded-2xl">
|
||||
|
|
@ -133,7 +156,8 @@
|
|||
}
|
||||
|
||||
.top-light {
|
||||
background: url('/imgs/grain.webp'),
|
||||
background:
|
||||
url('/imgs/grain.webp'),
|
||||
radial-gradient(
|
||||
100% 80% at top,
|
||||
theme(colors.cyan.500 / 50%) 0%,
|
||||
|
|
|
|||
|
|
@ -10,8 +10,12 @@ const mdsvexOptions = {
|
|||
extensions: ['.md'],
|
||||
highlight: {
|
||||
highlighter: async (code, lang = 'text') => {
|
||||
const highlighter = await getHighlighter({ theme: 'github-dark' })
|
||||
const html = escapeSvelte(highlighter.codeToHtml(code, { lang }))
|
||||
const highlighter = await getHighlighter({
|
||||
theme: 'github-dark'
|
||||
})
|
||||
const html = escapeSvelte(
|
||||
highlighter.codeToHtml(code, { lang })
|
||||
)
|
||||
return `{@html \`${html}\` }`
|
||||
}
|
||||
},
|
||||
|
|
@ -23,7 +27,10 @@ const mdsvexOptions = {
|
|||
const config = {
|
||||
extensions: ['.svelte', '.md'],
|
||||
kit: { adapter: adapter() },
|
||||
preprocess: [vitePreprocess(), mdsvex(mdsvexOptions)]
|
||||
preprocess: [
|
||||
vitePreprocess({ script: true }),
|
||||
mdsvex(mdsvexOptions)
|
||||
]
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
|
|||
|
|
@ -3,10 +3,17 @@ const colors = require('tailwindcss/colors')
|
|||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ['./src/**/**/*.{html,js,svelte,ts}', './src/content/profiles.json'],
|
||||
content: [
|
||||
'./src/**/**/*.{html,js,svelte,ts}',
|
||||
'./src/content/profiles.json'
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: { black: '#090b0c', primary: '#58E1FF', secondary: '#00A2F8' },
|
||||
colors: {
|
||||
black: '#090b0c',
|
||||
primary: '#58E1FF',
|
||||
secondary: '#00A2F8'
|
||||
},
|
||||
fontFamily: {
|
||||
...fontFamily,
|
||||
sans: ['Inter Variable', ...fontFamily['sans']],
|
||||
|
|
@ -19,7 +26,7 @@ export default {
|
|||
|
||||
screens: {
|
||||
'3xl': '2560px',
|
||||
'nav': '1124px'
|
||||
nav: '1124px'
|
||||
},
|
||||
|
||||
typography: {
|
||||
Loading…
Add table
Reference in a new issue