Add Plugins page and more (#48)

* some progress, updating packages

* migrate to SvelteKit 2

* progress

* more stuff

* hy3 banner

* add links to features

* fix distro option extra image

* update community section

* add plugins page link  to plugins slice

* community more profiles

* add more community profiles

* fix RSS not showing in prod (#45)

* plugin page: better banner hover playback

* improve community slice intro timing

* remove fillter texts

* improve command button

* plugin install command wording improvment

* footer adjustments

* add dwindle-autogroup

* stuff

* community profile pictures hosted by Discord :P

* fix autoplay previewRice Chrome

* navbar: no entry delay

* small community profile changes

* optimize plugins layout

* fix sorting

* improve card performance

* clean up test content from .md files

* did more stuff

* change desc of some plugins

* work on design and layout

* make main bg darker
Creates less banding and looks a bit better, while also providing better contrast

* nicer trim text

* more stuff

* fix messup

* update plugins readme

* add a profile picture

* plugins: add gradient bg

* remove placeholder plugins

* plugins: improve mobile layout

* community: small stuff

* remove csgo vulkan fix logo

* plugins: fix bg jumping on Firefox

* plugin slug: improve animation staggering

* small css fixes

* pluginsSlice: animate only with slide
Opacity change looks a bit off here

* community: hide errored images

---------

Co-authored-by: Vaxry <vaxry@vaxry.net>
This commit is contained in:
Visual-Dawg 2024-03-06 23:04:26 +02:00 committed by GitHub
parent 9b37a46b61
commit d982e5761d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
86 changed files with 2794 additions and 1238 deletions

2
.gitignore vendored
View file

@ -2,6 +2,8 @@ old
.svelte-kit .svelte-kit
build build
**/scratch.js
# For the generated blurred images # For the generated blurred images
**/generated_* **/generated_*

View file

@ -29,4 +29,3 @@ Feel free
## Credits ## Credits
- [VDawg](https://github.com/Visual-Dawg) - for the new site and design. - [VDawg](https://github.com/Visual-Dawg) - for the new site and design.
- [System-x64](https://github.com/System-x64) - for the original site code

View file

@ -8,7 +8,7 @@
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",
"format": "prettier --plugin-search-dir . --write .", "format": "prettier --plugin-search-dir . --write .",
"postinstall": "./scripts/generate-blurred-images.sh" "postinstall": "node ./scripts/generate-blurred-images.mjs"
}, },
"keywords": [ "keywords": [
"hyprland" "hyprland"
@ -24,41 +24,44 @@
"node": ">=16.0.0" "node": ">=16.0.0"
}, },
"devDependencies": { "devDependencies": {
"@iconify/json": "^2.2.140", "@iconify/json": "^2.2.185",
"@interactjs/types": "^1.10.20", "@interactjs/types": "^1.10.26",
"@sveltejs/adapter-static": "^2.0.3", "@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^1.27.4", "@sveltejs/kit": "^2.5.0",
"autoprefixer": "^10.4.16", "@sveltejs/vite-plugin-svelte": "^3.0.0",
"eslint": "^8.53.0", "autoprefixer": "^10.4.17",
"eslint-config-prettier": "^9.0.0", "eslint": "^8.56.0",
"eslint-plugin-svelte": "^2.35.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.35.1",
"mdsvex": "^0.11.0", "mdsvex": "^0.11.0",
"postcss": "^8.4.31", "postcss": "^8.4.35",
"prettier": "^3.0.3", "prettier": "^3.2.5",
"prettier-plugin-svelte": "^3.1.0", "prettier-plugin-svelte": "^3.2.1",
"simplex-noise": "^4.0.1", "simplex-noise": "^4.0.1",
"svelte": "^4.2.3", "svelte": "^4.2.11",
"svelte-check": "^3.6.0", "svelte-check": "^3.6.4",
"tailwindcss": "^3.3.5", "tailwindcss": "^3.4.1",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"typescript": "^5.2.2", "typescript": "^5.3.3",
"unplugin-icons": "^0.17.3", "unplugin-icons": "^0.18.5",
"vite": "^4.5.0" "vite": "^5.1.4"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@fontsource-variable/work-sans": "^5.0.16", "@fontsource-variable/work-sans": "^5.0.17",
"@fontsource/ibm-plex-mono": "^5.0.8", "@fontsource/ibm-plex-mono": "^5.0.8",
"@tailwindcss/container-queries": "^0.1.1",
"@tailwindcss/typography": "^0.5.10", "@tailwindcss/typography": "^0.5.10",
"clsx": "^2.0.0", "clsx": "^2.1.0",
"interactjs": "^1.10.20", "globby": "^14.0.1",
"prettier-plugin-tailwindcss": "^0.5.7", "interactjs": "^1.10.26",
"prettier-plugin-tailwindcss": "^0.5.11",
"rehype-slug": "^6.0.0", "rehype-slug": "^6.0.0",
"remark-unwrap-images": "^4.0.0", "remark-unwrap-images": "^4.0.0",
"remeda": "^1.29.0", "remeda": "^1.43.0",
"rxjs": "^7.8.1", "rxjs": "^7.8.1",
"shiki": "^0.14.7", "shiki": "^1.1.6",
"svelte-inview": "^4.0.1", "svelte-inview": "^4.0.2",
"ts-pattern": "^5.0.5" "ts-pattern": "^5.0.8"
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,98 @@
#!/usr/bin/env node
import { globby } from 'globby'
import { spawnSync } from 'node:child_process'
import { getFileNameWithoutExtension } from '../src/lib/Helper.mjs'
// This script should be run from the root of the application
const root = new URL('..', import.meta.url)
const imageDirectories = ['static/imgs/ricing_competitions', 'static/imgs/plugins/logos']
const generatedPrefix = 'generated_'
// This value seems to work well
const maxBrightness = 65535
const brightnessThreshold = maxBrightness * 0.5
/** All files which have no `generated_` version */
const filePaths = await globby(imageDirectories, {
cwd: root,
expandDirectories: {
extensions: ['svg', 'webp', 'jpg', 'png', 'gif', 'bmp', 'jpeg']
},
gitignore: false
}).then((filePaths) => {
const fileNames = filePaths.map(getFileNameWithoutExtension)
return filePaths
.filter(
(file) =>
!getFileNameWithoutExtension(file).startsWith(generatedPrefix) &&
!fileNames.includes(generatedPrefix + getFileNameWithoutExtension(file))
)
.map((filePath) => new URL(filePath, root))
})
for (const filePath of filePaths) {
const extension = filePath.pathname.split('.').at(-1)
const generatedFileName = generatedPrefix + getFileNameWithoutExtension(filePath.href) + '.webp'
const outPath = new URL('.', filePath).pathname + generatedFileName
const currentBrightness = Number(
exec(`convert "${filePath.pathname}" -colorspace Gray -format "%[mean]" info: `)
)
// Boost the brightness if the image is very dark
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' : ''
exec(
`magick convert ${svgCommands} -brightness-contrast ${brightnessIncrease}x40 -modulate 100,1000,100 ${filePath.pathname} ${outPath}`
)
// Modify colors with LUT
// spawnSync(`magick "${outPath}" "./scripts/hald-clut.color.io.png" -hald-clut "${outPath}"`)
// Also make them smaller to reduce file size
exec(
`magick convert -modulate 100,250,100 -scale 10% -gaussian-blur 0x20 -resize 500% -quality 50 "${outPath}" "${outPath}" `
)
console.log(`Blurred ${filePath.pathname}`)
}
function exec(command) {
const { stdout, error } = spawnSync(command, { shell: true })
if (error) throw error
return stdout
}
// // parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
// // cd "$parent_path"
// find "../static/imgs/ricing_competitions/" -type f \
// \( -iname "*.jpg" -o -iname "*.png" -o -iname "*.gif" -o -iname "*.bmp" -o -iname "*.jpeg" -o -iname "*.webp" \) -not -name "generated_*" -print0 |
// while IFS= read -r -d '' filepath; do
// echo "$filepath" gets blurred
// directory=$(dirname "$filepath")
// filename=$(basename "$filepath")
// generated_filename="${directory}/generated_${filename}"
// brightness=$( convert $filepath -colorspace Gray -format "%[mean]" info: )
// max_brightness="65535" # The possible maximum brightness possible from the previous command
// brightness_threshold=$( python -c "print( $max_brightness * 0.5 )" )
// # Boost the brightness if the image is very dark
// brightness_boost=$( python -c "print( max( (1 - ($brightness / $brightness_threshold)) * 50 , 0) )" )
// # Modify colors with LUT
// magick convert -brightness-contrast ${brightness_boost}x40 -modulate 100,1000,100 "$filepath" "$generated_filename"
// magick "$generated_filename" "./hald-clut.color.io.png" -hald-clut "$generated_filename"
// # Also make them smaller to reduce file size
// magick convert -modulate 100,250,100 -scale 10% -gaussian-blur 0x20 -resize 500% -quality 50 "$generated_filename" "$generated_filename"
// # magick convert -scale 10% -brightness-contrast ${brightness_boost}x25 -modulate 100,500,100 -gaussian-blur 0x20 -resize 1000% "$filepath" "$generated_filename"
// done

View file

@ -1,30 +0,0 @@
#!/usr/bin/env bash
parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
cd "$parent_path"
find "../static/imgs/ricing_competitions/" -type f \
\( -iname "*.jpg" -o -iname "*.png" -o -iname "*.gif" -o -iname "*.bmp" -o -iname "*.jpeg" -o -iname "*.webp" \) -not -name "generated_*" -print0 |
while IFS= read -r -d '' filepath; do
echo "$filepath" gets blurred
directory=$(dirname "$filepath")
filename=$(basename "$filepath")
generated_filename="${directory}/generated_${filename}"
brightness=$( convert $filepath -colorspace Gray -format "%[mean]" info: )
max_brightness="65535" # The possible maximum brightness possible from the previous command
brightness_threshold=$( python -c "print( $max_brightness * 0.5 )" )
# Boost the brightness if the image is very dark
brightness_boost=$( python -c "print( max( (1 - ($brightness / $brightness_threshold)) * 50 , 0) )" )
# Modify colors with LUT
magick convert -brightness-contrast ${brightness_boost}x40 -modulate 100,1000,100 "$filepath" "$generated_filename"
magick "$generated_filename" "./hald-clut.color.io.png" -hald-clut "$generated_filename"
# Also make them smaller to reduce file size
magick convert -modulate 100,250,100 -scale 10% -gaussian-blur 0x20 -resize 500% -quality 50 "$generated_filename" "$generated_filename"
# magick convert -scale 10% -brightness-contrast ${brightness_boost}x25 -modulate 100,500,100 -gaussian-blur 0x20 -resize 1000% "$filepath" "$generated_filename"
done

View file

@ -0,0 +1,10 @@
---
name: Borders++
url: https://github.com/hyprwm/hyprland-plugins
category: Design
tagline: Extra window borders
logo: /plugins-data/logos/Borders-plus-plus-logo.svg
featured: true
# banner: /imgs/profile_pictures/chan_1.webp
# weight: 100
---

View file

@ -0,0 +1,6 @@
---
name: CSGO Vulkan Fix
url: https://github.com/hyprwm/hyprland-plugins
category: Miscellaneous
tagline: CSGO custom resolutions fix
---

View file

@ -0,0 +1,7 @@
---
name: dwindle-autogroup
url: https://github.com/ItsDrike/hyprland-dwindle-autogroup
category: Layout
tagline: Auto-grouping for dwindle mode
featured: false
---

View file

@ -0,0 +1,22 @@
---
name: hy3
url: https://github.com/outfoxxed/hy3
category: Layout
tagline: i3 / Sway like layout
logo: /plugins-data/logos/hy3-logo.svg
banner: /plugins-data/hy3_banner.mp4
featured: true
---
### Features
- i3 like tiling
- Node based window manipulation (you can interact with multiple windows at once)
- Greatly improved tabbed node groups over base hyprland
- Optional autotiling
Additional features may be suggested in the repo issues or the [matrix room](https://matrix.to/#/#hy3:outfoxxed.me).
### Demo
<video controls="controls" src="https://user-images.githubusercontent.com/83010835/255322916-85ae8196-8b12-4e15-b060-9872db10839f.mp4"></video>

View file

@ -0,0 +1,8 @@
---
name: Hyprbars
url: https://github.com/hyprwm/hyprland-plugins
category: Design
tagline: Bring back title bars
logo: /plugins-data/logos/hyprbars-logo.svg
featured: true
---

View file

@ -0,0 +1,8 @@
---
name: Hyprtrails
url: https://github.com/hyprwm/hyprland-plugins
category: Design
tagline: Smooth window trails
logo: /plugins-data/logos/hyprtrails-logo.svg
featured: true
---

View file

@ -0,0 +1,8 @@
---
name: Hyprwinwrap
url: https://github.com/hyprwm/hyprland-plugins
category: Quality of Life
tagline: Any app as wallpaper
logo: /plugins-data/logos/hyprwinwrap-logo.svg
featured: true
---

View file

@ -0,0 +1,18 @@
# Frontmatter
The `.md` files can have the following meta data:
- name: Name of the plugin
- tagline: Very concise description of the plugin
- url: Link to the Github repository
- docs: Link to the documentation. Optional
- website: Link to the website. Optional
- logo: Relative link to the logo placed in the `/static/plugins-data/logos/` directory (without the `/static/` though)
- tags: Tags for the plugin. Capitalized
- featured: Whether the plugin is featured at the top. A maximum of 4 is shown
- weight: Determines the sort order. A higher weight comes first.
## Banner
Please only use darkmode images/videos.
Do not use yellow and yellowish colors, unless nesssecary.

View file

@ -93,9 +93,11 @@ export function getIsMobile() {
return check return check
} }
/** Get the `generated_<filename>` if blurredThumbnail is not set manually **/ /** Get the `generated_<filename>` for the provided path **/
export function getBlurredPath(path) { export function getGeneratedPath(path, extension = 'webp') {
return `${path.substring(0, path.lastIndexOf('/'))}/generated_${path.split('/').at(-1)}` const directory = path.substring(0, path.lastIndexOf('/'))
const filename = getFileNameWithoutExtension(path)
return `${directory}/generated_${filename}.${extension}`
} }
/** Get a random item from an array */ /** Get a random item from an array */
@ -110,3 +112,22 @@ export function formatDate(date, dateStyle = 'medium', locales = 'en') {
return dateFormatter.format(dateToFormat) return dateFormatter.format(dateToFormat)
} }
/**
*
* @param {string} text
* @param {number} maxLenght
* @returns
*/
export function trimText(text, maxLenght) {
if (text.length < maxLenght - 1) return text
const lastSpace = text.slice(0, maxLenght).lastIndexOf(' ')
return text.slice(0, lastSpace ?? maxLenght) + '…'
}
/** Get the filename of a filepath without its extension */
export function getFileNameWithoutExtension(filePath) {
return filePath.split('/').at(-1).replace(/\..*$/, '')
}

View file

@ -7,7 +7,7 @@
export let type = 'primary' export let type = 'primary'
$: classes = clsx( $: classes = clsx(
'animate rounded text-sm font-bold hover:scale-[1.03] active:scale-95', 'animate rounded text-sm font-bold hover:scale-[1.01] active:scale-100',
'primary' == type && 'bg-slate-200 text-black', 'primary' == type && 'bg-slate-200 text-black',
'outline' == type && 'bg-transparent text-white outline outline-2 outline-slate-200', 'outline' == type && 'bg-transparent text-white outline outline-2 outline-slate-200',
'fancyOutline' == type && 'fancy', 'fancyOutline' == type && 'fancy',
@ -19,7 +19,7 @@
</script> </script>
{#if type === 'fancyOutline'} {#if type === 'fancyOutline'}
<div class="relative"> <div class="relative max-w-max">
<button class={classes} on:click><slot>NO LABEL PROVIDED</slot></button> <button class={classes} on:click><slot>NO LABEL PROVIDED</slot></button>
<span <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" 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"

View file

@ -1,13 +1,15 @@
<script> <script>
import clsx from 'clsx' import clsx from 'clsx'
import { getContext, onMount } from 'svelte' import { getContext, onMount } from 'svelte'
import { mouseContext } from './FeaturesSlice.svelte' import { cardsContext } from '$lib/components/CardsContainer.svelte'
import { spring } from 'svelte/motion' import { spring } from 'svelte/motion'
import { getIsMobile } from '$lib/Helper.mjs' import { getIsMobile } from '$lib/Helper.mjs'
export let title /** @type {'cyan' | 'purple'}*/
export let color = 'cyan' export let color = 'cyan'
/** @type {number | number}*/
export let gradientOpacity = undefined
const { x: mouseX, y: mouseY, isHoverCards } = getContext(mouseContext) const { mouseCoordinates$, isHoverCards, enableBorders = true } = getContext(cardsContext)
/** @type HTMLDivElement */ /** @type HTMLDivElement */
let container let container
@ -15,10 +17,10 @@
const damping = 0.2 const damping = 0.2
const fillX = spring(999, { damping, stiffness: 0.021, precision: 0.3 }) const fillX = spring(0, { damping, stiffness: 0.021, precision: 0.3 })
const fillY = spring(999, { damping, stiffness: 0.021, precision: 0.3 }) const fillY = spring(0, { damping, stiffness: 0.021, precision: 0.3 })
const borderX = spring(999, { damping, stiffness: 0.03, precision: 0.3 }) const borderX = spring(0, { damping, stiffness: 0.03, precision: 0.3 })
const borderY = spring(999, { damping, stiffness: 0.03, precision: 0.3 }) const borderY = spring(0, { damping, stiffness: 0.03, precision: 0.3 })
const bounceBack = 2 const bounceBack = 2
const soft = 0.8 const soft = 0.8
@ -28,7 +30,7 @@
let hasMouseEntered = false let hasMouseEntered = false
$: { $: {
if (container && $mouseX !== undefined) { if (container && $mouseCoordinates$?.x !== undefined) {
updateGradient() updateGradient()
} }
} }
@ -42,11 +44,13 @@
const { x: rectX, y: rectY, width, height } = container.getBoundingClientRect() const { x: rectX, y: rectY, width, height } = container.getBoundingClientRect()
const normX = $mouseX - rectX const normX = $mouseCoordinates$.x - rectX
const normY = $mouseY - rectY const normY = $mouseCoordinates$.y - rectY
if (enableBorders) {
$borderX = normX $borderX = normX
$borderY = normY $borderY = normY
}
if (!isMouseOver) { if (!isMouseOver) {
hasMouseEntered = false hasMouseEntered = false
@ -55,29 +59,30 @@
} }
// Instantly update the blob positon without easing when the mouse has just entered // Instantly update the blob positon without easing when the mouse has just entered
if (hasMouseEntered === false) { /* if (hasMouseEntered === false) {
fillX.set(normX, { hard: true }) fillX.set(normX, { hard: true })
fillY.set(normY, { hard: true }) fillY.set(normY, { hard: true })
hasMouseEntered = true hasMouseEntered = true
return return
} }
*/
if ($mouseX < rectX) fillX.set(rectX + bounceBack, { soft }) if ($mouseCoordinates$.x < rectX) fillX.set(rectX + bounceBack, { soft })
else if ($mouseX > rectX + width) fillX.set(rectX + width - bounceBack, { soft }) else if ($mouseCoordinates$.x > rectX + width) fillX.set(rectX + width - bounceBack, { soft })
else fillX.set(normX) else fillX.set(normX)
if ($mouseY < rectY) fillY.set(rectY + bounceBack, { soft: 1 }) if ($mouseCoordinates$.y < rectY) fillY.set(rectY + bounceBack, { soft: 1 })
if ($mouseY > rectY + height) fillX.set(rectY - height - bounceBack, { soft }) if ($mouseCoordinates$.y > rectY + height) fillX.set(rectY - height - bounceBack, { soft })
else fillY.set(normY) else fillY.set(normY)
} }
</script> </script>
<div <div
class={clsx('card group min-h-[20rem]', $$restProps.class)} class={clsx('card group ', $$restProps.class)}
style:--x={$fillX} style:--x={$fillX}
style:--y={$fillY} style:--y={$fillY}
style:--borderX={$borderX} style:--borderX={enableBorders && $borderX}
style:--borderY={$borderY} style:--borderY={enableBorders && $borderY}
class:isHoverCards={$isHoverCards} class:isHoverCards={$isHoverCards}
bind:this={container} bind:this={container}
on:mouseenter={() => (isMouseOver = true)} on:mouseenter={() => (isMouseOver = true)}
@ -87,20 +92,22 @@
}} }}
class:purpleGradient={color === 'purple'} class:purpleGradient={color === 'purple'}
role="contentinfo" role="contentinfo"
on:mouseenter
on:mouseleave
> >
<div class="z-10 flex h-full w-full flex-col justify-end p-8 sm:p-12"> <div class="z-10 h-full w-full">
<h1 class="mb-6 text-5xl font-bold text-white">{title}</h1>
<slot>Nothing in the slot here</slot> <slot>Nothing in the slot here</slot>
</div> </div>
<div class="gradient max-sm:hidden" /> <div class="gradient max-sm:hidden" style:opacity={gradientOpacity} />
<div class="gradient_black max-sm:hidden" /> <div class="gradient_black max-sm:hidden" />
{#if enableBorders}
<div class="border-gradient max-sm:hidden" /> <div class="border-gradient max-sm:hidden" />
{/if}
</div> </div>
<style lang="postcss"> <style lang="postcss">
.card { .card {
@apply relative flex h-full w-full items-end justify-end rounded-3xl transition-colors duration-300; @apply relative flex items-end justify-end rounded-3xl transition-colors duration-300;
z-index: 2; z-index: 2;
contain: paint style layout; contain: paint style layout;
@ -112,7 +119,7 @@
/* The card border */ /* The card border */
@media screen(md) { @media screen(md) {
background: theme(colors.slate.900); background: theme(colors.slate.800);
&:hover { &:hover {
background: theme(colors.blue.900); background: theme(colors.blue.900);
@ -125,13 +132,7 @@
z-index: 2; z-index: 2;
border-radius: inherit; border-radius: inherit;
contain: strict; contain: strict;
background: black background: black;
radial-gradient(
circle at bottom right,
theme(colors.neutral.900 / 80%),
theme(colors.neutral.500 / 10%),
black
);
} }
/* This gradient is visible on the borders when hovering */ /* This gradient is visible on the borders when hovering */
@ -143,12 +144,10 @@
height: 100%; height: 100%;
opacity: 0%; opacity: 0%;
transform-origin: top left; transform-origin: top left;
transition: opacity 120ms ease-in-out; transition: opacity 240ms ease-in-out;
left: 0%; left: 0%;
top: 0%; top: 0%;
content: '';
pointer-events: none; pointer-events: none;
filter: brightness(1.5) saturate(4);
contain: strict; contain: strict;
background: radial-gradient( background: radial-gradient(
620px circle at calc(var(--borderX) * 1px) calc(var(--borderY) * 1px), 620px circle at calc(var(--borderX) * 1px) calc(var(--borderY) * 1px),
@ -180,27 +179,27 @@
min-width: 200%; min-width: 200%;
min-height: 200%; min-height: 200%;
aspect-ratio: 1 / 1; aspect-ratio: 1 / 1;
scale: 0.5 0.5;
translate: -25% 0%; translate: -25% 0%;
transform-origin: top left; transform-origin: top left;
left: 50%; left: 50%;
opacity: 0%; opacity: 50%;
transition: all 820ms; transition: all 120ms ease-in-out;
content: ''; content: '';
pointer-events: none; pointer-events: none;
background: radial-gradient( opacity: 0%;
contain: strict;
background: url('/imgs/grain.webp'),
radial-gradient(
ellipse at calc(var(--x) * 1px) calc(var(--y) * 1px), ellipse at calc(var(--x) * 1px) calc(var(--y) * 1px),
var(--color1, theme(colors.cyan.500 / 100%)), var(--color1, theme(colors.cyan.500 / 100%)),
var(--color2, theme(colors.blue.700 / 40%)) 25%, var(--color2, theme(colors.blue.700 / 40%)) 25%,
var(--color3, theme(colors.blue.900 / 15%)) 50% var(--color3, theme(colors.blue.900 / 15%)) 50%
); );
.card:hover & {
scale: 1 1;
}
.group:hover & { .group:hover & {
opacity: 100%; opacity: 100%;
transition: all 820ms;
} }
} }
} }

View file

@ -0,0 +1,53 @@
<script context="module">
export const cardsContext = Symbol('mouseContext')
</script>
<script>
import { getIsMobile } from '$lib/Helper.mjs'
import { BehaviorSubject, Subject, throttle, throttleTime } from 'rxjs'
import { onMount, setContext } from 'svelte'
import { writable } from 'svelte/store'
export let enableBorders = true
const fps = 1000 / 60
/** @type {HTMLElement}*/
let containerElement
let isMobile = false
const context = setContext(cardsContext, {
mouseCoordinates$: new BehaviorSubject({ x: 0, y: 0 }).pipe(throttleTime(fps)),
isHoverCards: writable(false),
enableBorders
})
function onMouseEnter() {
containerElement.addEventListener('mousemove', trackMouse)
context.isHoverCards.set(true)
}
function onMouseLeave() {
containerElement.removeEventListener('mousemove', trackMouse)
context.isHoverCards.set(false)
}
function trackMouse({ clientX, clientY }) {
context.mouseCoordinates$.next({ x: clientX, y: clientY })
}
onMount(() => {
isMobile = getIsMobile()
return () => containerElement.removeEventListener('mousemove', trackMouse)
})
</script>
<div
class={$$restProps.class}
bind:this={containerElement}
role="contentinfo"
on:mouseenter={!isMobile && onMouseEnter}
on:mouseleave={!isMobile && onMouseLeave}
>
<slot />
</div>

View file

@ -0,0 +1,75 @@
<script>
import { onDestroy } from 'svelte'
import ClipboardIcon from '~icons/mingcute/copy-2-line'
/** @type {string} */
export let command
/** @type {string | undefined }*/
export let commandClass = undefined
/** @type {string | undefined }*/
export let containerClass = undefined
let isShowingCopied = false
let timeoutId
async function copyCommand() {
await navigator.clipboard.writeText(command).then(() => (isShowingCopied = true))
clearTimeout(timeoutId)
timeoutId = setTimeout(() => (isShowingCopied = false), 1400)
}
onDestroy(() => {
clearTimeout(timeoutId)
})
</script>
<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}
on:click
>
<slot>
<div class="relative flex w-full justify-between gap-4">
<div class="flex gap-4">
<div class="select-none font-bold text-primary">></div>
<span class={commandClass}>{command}</span>
</div>
<ClipboardIcon
class="hidden h-6 w-6 text-white opacity-50 transition-opacity duration-100 hover:!opacity-100 group-hover:opacity-80 group-active:opacity-100 md:block"
/>
</div>
</slot>
</button>
<div
class="pointer-events-none absolute left-1/2 z-20 hidden w-full max-w-max select-none rounded-full bg-black/10 px-2 text-green-400 backdrop-blur [translate:-50%_0px] max-md:-bottom-6 md:-top-8"
class:copy={isShowingCopied}
>
Copied to clipboard ✔
</div>
{#if $$slots.extra}
<div class="mt-3 flex w-full font-sans text-xs opacity-80">
<slot name="extra" />
</div>
{/if}
</div>
<style lang="postcss">
.copy {
animation: 80ms cubic-bezier(0.5, 0.2, 0, 1.5) 1 copy;
display: block;
}
@keyframes copy {
from {
opacity: 0.8;
scale: 0.98;
}
to {
opacity: 1;
scale: 1;
}
}
</style>

View file

@ -2,7 +2,7 @@
import clsx from 'clsx' import clsx from 'clsx'
import { createEventDispatcher, getContext, onDestroy, onMount } from 'svelte' import { createEventDispatcher, getContext, onDestroy, onMount } from 'svelte'
import { spring } from 'svelte/motion' import { spring } from 'svelte/motion'
import { contextId as ctxId } from '../../routes/CommunitySlice.svelte' import { contextId as ctxId } from '../../routes/home-slices/CommunitySlice.svelte'
import { lerp } from '$lib/Helper.mjs' import { lerp } from '$lib/Helper.mjs'
import { inview } from 'svelte-inview' import { inview } from 'svelte-inview'
@ -33,7 +33,7 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const relativeSize = size / biggestSize const relativeSize = size / biggestSize
const delay = (biggestSize - size) * 5 const delay = Math.pow(1 - size / biggestSize, 4) * 4654
const dragCoordinates = spring([0, 0], { const dragCoordinates = spring([0, 0], {
damping: lerp(0.2, 0.03, relativeSize), damping: lerp(0.2, 0.03, relativeSize),
stiffness: lerp(0.2, 0.01, relativeSize), stiffness: lerp(0.2, 0.01, relativeSize),
@ -48,6 +48,8 @@
let interactionjs let interactionjs
function onViewEnter() { function onViewEnter() {
if (imageElement.__error) return
setTimeout(() => (hasEnteredView = true), 550) setTimeout(() => (hasEnteredView = true), 550)
// Only load the library if the element entered the view, to improve performance // Only load the library if the element entered the view, to improve performance
@ -106,7 +108,7 @@
)} )}
style:translate={`calc( ${$dragCoordinates[0]}px ) ${$dragCoordinates[1]}px`} style:translate={`calc( ${$dragCoordinates[0]}px ) ${$dragCoordinates[1]}px`}
use:inview={{ unobserveOnEnter: true, threshold: 0.2 }} use:inview={{ unobserveOnEnter: true, threshold: 0.2 }}
class:_animate={isAnimating && hasEnteredView} class:_animate={hasImageLoaded && isAnimating && hasEnteredView}
on:inview_enter={onViewEnter} on:inview_enter={onViewEnter}
> >
<div class="" bind:this={imageWrapper}> <div class="" bind:this={imageWrapper}>
@ -120,6 +122,11 @@
on:mouseenter={(event) => dispatch('hover', event)} on:mouseenter={(event) => dispatch('hover', event)}
class:hover:scale-125={!!quote} class:hover:scale-125={!!quote}
loading="lazy" loading="lazy"
referrerpolicy="no-referrer"
crossorigin="anonymous"
width={size}
height={size}
onerror="this.__error = true"
/> />
<slot /> <slot />
</div> </div>

View file

@ -8,8 +8,7 @@
let team = [ let team = [
['Fufexan', 'Supporting Developer', 'cyan', 'https://github.com/fufexan'], ['Fufexan', 'Supporting Developer', 'cyan', 'https://github.com/fufexan'],
['NotAShelf', 'Real Chad', 'teal', 'https://github.com/NotAShelf'], ['NotAShelf', 'Real Chad', 'teal', 'https://github.com/NotAShelf'],
['VDawg', 'Webdesign-and dev', 'emerald', 'https://github.com/Visual-Dawg'], ['VDawg', 'Webdesign-and dev', 'emerald', 'https://github.com/Visual-Dawg']
['System-x64', 'Webdev', 'green', 'https://github.com/System-x64']
] ]
function createRole(role, color) { function createRole(role, color) {
return `<span class='text-${color}-500'><span class='text-${color}-600'>[ </span>${role}<span class='text-${color}-600'> ]</span></span>` return `<span class='text-${color}-500'><span class='text-${color}-600'>[ </span>${role}<span class='text-${color}-600'> ]</span></span>`
@ -20,7 +19,7 @@
class="max-w-screen relative mt-16 flex items-center justify-center border-t border-blue-400/50 bg-black/50 md:mt-24 lg:mt-32" class="max-w-screen relative mt-16 flex items-center justify-center border-t border-blue-400/50 bg-black/50 md:mt-24 lg:mt-32"
> >
<div class="footer-inner"> <div class="footer-inner">
<div class="flex flex-col gap-4 rounded-lg"> <div class="flex grow flex-col gap-4 rounded-lg">
<div class="pretitle">Humans</div> <div class="pretitle">Humans</div>
<ul class="flex flex-col gap-3 font-medium"> <ul class="flex flex-col gap-3 font-medium">
<li> <li>
@ -99,7 +98,7 @@
<style lang="postcss"> <style lang="postcss">
.footer-inner { .footer-inner {
@apply flex max-w-5xl flex-wrap items-start justify-between gap-12 px-8 py-14 text-slate-300; @apply flex max-w-screen-2xl flex-wrap items-start justify-between gap-12 px-8 py-14 text-slate-300;
} }
.pretitle { .pretitle {

View file

@ -8,7 +8,7 @@
<Navbar /> <Navbar />
<main class="mx-auto flex min-h-screen w-full flex-col overflow-hidden"> <main class="mx-auto flex min-h-screen w-full flex-col">
<slot /> <slot />
</main> </main>

View file

@ -1,19 +1,20 @@
<script> <script>
import Community from './CommunitySlice.svelte' import Community from './home-slices/CommunitySlice.svelte'
import FeaturesSlice from './FeaturesSlice.svelte' import FeaturesSlice from './home-slices/FeaturesSlice.svelte'
import HallOfFameSlice from './HallOfFameSlice.svelte' import HallOfFameSlice from './home-slices/HallOfFameSlice.svelte'
import Hero from './Hero.svelte' import Hero from './home-slices/Hero.svelte'
import InstallSlice from './InstallSlice.svelte' import InstallSlice from './home-slices/InstallSlice.svelte'
import PreviewRiceSlice from './PreviewRiceSlice.svelte' import PreviewRiceSlice from './home-slices/PreviewRiceSlice.svelte'
import PluginsSlice from './PluginsSlice.svelte' import PluginsSlice from './home-slices/PluginsSlice.svelte'
import NewsSlice from './NewsSlice.svelte' import NewsSlice from './home-slices/NewsSlice.svelte'
export let data export let data
</script> </script>
<Hero backgroundData={data.backgroundData} /> <div class="overflow-hidden">
<Hero backgroundData={data.backgroundData} />
<div class="-mt-8 flex flex-col items-center gap-20 md:gap-[16rem]"> <div class="-mt-8 flex flex-col items-center gap-20 md:gap-[16rem]">
<PreviewRiceSlice class="mb-12" /> <PreviewRiceSlice class="mb-12" />
<FeaturesSlice /> <FeaturesSlice />
@ -27,6 +28,7 @@
<NewsSlice news={data.news} /> <NewsSlice news={data.news} />
<InstallSlice /> <InstallSlice />
</div>
</div> </div>
<svelte:head> <svelte:head>
@ -38,6 +40,3 @@
/> />
<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> </svelte:head>
<style>
</style>

View file

@ -1,282 +0,0 @@
<script context="module">
export const contextId = Symbol('community context')
</script>
<script>
import Button from '$lib/components/Button.svelte'
import DiscordIcon from '~icons/prime/discord'
import DiscordProfilePicture from '$lib/components/DiscordProfilePicture.svelte'
import { setContext } from 'svelte'
import Title from '$lib/components/Title.svelte'
import background from '$lib/images/community-bg.webp'
import amongUsGreenImage from '$lib/images/amongus/green.webp'
import { discordLink } from '$lib/constants.mjs'
let sectionElement
let isDraggingChan = false
/** @type {{image: string coordinates: [number, number] containerClass: string}[], size: number, quote?: string } */
const profiles = [
{
image: '/imgs/profile_pictures/vaxry.webp',
coordinates: [187, 296],
size: 172,
class: 'outline-red-500'
},
{
image: '/imgs/profile_pictures/fufexan.webp',
coordinates: [735, 441],
size: 164,
class: 'outline-yellow-500'
},
{
image: '/imgs/profile_pictures/raf-notashelf.webp',
coordinates: [391, 615],
size: 149,
class: 'outline-orange-500'
},
{
image: '/imgs/profile_pictures/Mathisbuilder.webp',
coordinates: [568, 594],
size: 120,
class: 'outline-amber-500'
},
{
image: '/imgs/profile_pictures/dani_666..webp',
coordinates: [525, 764],
size: 80,
class: 'outline-red-500'
},
{
image: '/imgs/profile_pictures/end_4.webp',
coordinates: [648, 709],
size: 128,
class: 'outline-cyan-400'
},
{
image: '/imgs/profile_pictures/ardishco.webp',
coordinates: [65, 208],
size: 100,
class: 'outline-slate-200'
},
{
image: '/imgs/profile_pictures/kcrmson.webp',
coordinates: [53, 399],
size: 75,
class: 'outline-sky-500'
},
{
image: '/imgs/profile_pictures/loseardes77.webp',
coordinates: [24, 341],
size: 49,
class: 'outline-green-500'
},
{
image: '/imgs/profile_pictures/captainiveau.webp',
coordinates: [47, 86],
size: 48,
class: 'outline-red-500'
},
{
image: '/imgs/profile_pictures/etrigan63.webp',
coordinates: [824, 738],
size: 58,
class: 'outline-amber-500'
},
{
image: '/imgs/profile_pictures/jsw.webp',
coordinates: [41, 566],
size: 49,
class: 'outline-sky-500'
},
{
image: 'imgs/chan/joy.svg',
coordinates: [284, 533],
size: 90,
class: 'outline-cyan-500 bg-blue-300',
onDragStart: ({ detail: { currentTarget } }) => {
isDraggingChan = true
currentTarget.src = 'imgs/chan/surprise.svg'
},
onDragEnd: ({ detail: { currentTarget } }) => {
isDraggingChan = false
currentTarget.src = 'imgs/chan/tongueout.svg'
},
onHover: ({ detail: { srcElement } }) =>
!isDraggingChan && (srcElement.src = 'imgs/chan/wink.svg')
},
{
image: '/imgs/profile_pictures/7.webp',
coordinates: [273, 760],
size: 52,
quote: '"meds"',
class: 'outline-cyan-500'
},
{
image: '/imgs/profile_pictures/outfoxxed.webp',
coordinates: [648, 364],
size: 80,
class: 'outline-orange-500'
},
{
image: '/imgs/profile_pictures/kirottum.webp',
coordinates: [772, 651],
size: 62,
class: 'outline-purple-500'
},
{
image: '/imgs/profile_pictures/beardwarrior.webp',
coordinates: [736, 277],
size: 87,
class: 'outline-amber-500'
},
{
image: '/imgs/profile_pictures/neoney.webp',
coordinates: [898, 364],
size: 68,
class: 'outline-green-500'
},
{
image: amongUsGreenImage,
coordinates: [873, 224],
size: 79,
class: 'outline-green-500'
},
{
image: '/imgs/profile_pictures/SimplyKyle!.webp',
coordinates: [859, 159],
size: 39,
class: 'outline-rose-500'
},
{
image: '/imgs/profile_pictures/sioodmy.webp',
coordinates: [974, 107],
size: 48,
class: 'outline-amber-500'
},
{
image: '/imgs/profile_pictures/flafy.webp',
coordinates: [147, 553],
size: 87,
class: 'outline-pink-500'
},
{
image: '/imgs/profile_pictures/vagahbond.webp',
coordinates: [65, 643],
size: 74,
class: 'outline-amber-500 '
},
{
image: '/imgs/profile_pictures/flick0.webp',
coordinates: [263, 653],
size: 65,
class: 'outline-stone-500 '
},
{
image: '/imgs/profile_pictures/jacekpoz.svg',
coordinates: [893, 622],
size: 80,
class: 'outline-yellow-500 bg-black ',
quote: '"piss blob"'
},
{
image: '/imgs/profile_pictures/aylur.webp',
coordinates: [354, 798],
size: 80,
class: 'outline-amber-500 bg-black '
},
{
image: '/imgs/profile_pictures/aleph.nought.webp',
coordinates: [583, 824],
size: 40,
class: 'outline-blue-500 bg-black '
}
].sort(({ size: a }, { size: b }) => b - a)
setContext(contextId, {
biggestSize: profiles.reduce(
(previousSize, { size }) => (size > previousSize ? size : previousSize),
1
),
smallestSize: profiles.reduce(
(previousSize, { size }) => (size < previousSize ? size : previousSize),
Number.POSITIVE_INFINITY
),
getSectionElement: () => sectionElement
})
</script>
<section
class="relative -mb-[200px] flex h-[1100px] min-h-max w-screen flex-col items-center"
bind:this={sectionElement}
>
<Title>
<span slot="title">Join a great<br />community</span>
<span slot="subtitle">
Get help from Distro Hoppers, Haiku writers,<br />Hydrohomies, and human_(probably)
</span>
</Title>
<div class="group mt-16 flex flex-col items-center">
<a
class="discord p-4"
href={discordLink}
target="_blank"
rel="noopener"
aria-label="Join us on Discord"
>
<DiscordIcon class="h-full w-full " />
</a>
<a href={discordLink}>
<Button type="fancyOutline">Join us on Discord</Button>
</a>
</div>
<div class="absolute w-[1024px] select-none">
<div class="flex h-full origin-bottom-right select-none flex-wrap gap-4">
{#each profiles as { onDragEnd, onDragStart, onHover, ...props }}
<DiscordProfilePicture
{...props}
on:dragStart={onDragStart}
on:dragEnd={onDragEnd}
on:hover={onHover}
/>
{/each}
</div>
</div>
<img
src={background}
class="absolute top-0 -z-10 min-w-[1400px] select-none"
alt=""
aria-hidden="true"
loading="lazy"
/>
</section>
<style lang="postcss">
.discord {
width: 9rem;
height: 9rem;
transition:
rotate 500ms cubic-bezier(0.5, 0, 0.5, 1),
scale 420ms cubic-bezier(0.5, 0.1, 0, 1),
filter 840ms;
transition-delay: 240ms, 180ms, 20ms;
transform: translateY(-25%);
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);
animation: bounce 0.7s infinite 180ms both;
}
&:active {
scale: 1;
transition: scale 80ms;
}
}
</style>

View file

@ -1,213 +0,0 @@
<script context="module">
export const mouseContext = Symbol('mouseContext')
</script>
<script>
import FeatureCard from './FeatureCard.svelte'
import { setContext, onMount } from 'svelte'
import { writable } from 'svelte/store'
import PluginsIcon from '~icons/gg/arrange-back'
import ShortcutsIcon from '~icons/gg/push-chevron-right-o'
import TouchpadIcon from '~icons/gg/touchpad'
import Title from '$lib/components/Title.svelte'
import Hypractive from './Hypractive.svelte'
import { getIsMobile } from '$lib/Helper.mjs'
import configDefaultImage from '$lib/images/features/config_default.webp'
import configHoverImage from '$lib/images/features/config_hover.webp'
import smoothDefaultImage from '$lib/images/features/smooth_default.webp'
import smoothHoverImage from '$lib/images/features/smooth_hover.webp'
import tileDefaultImage from '$lib/images/features/tiling_default.webp'
import tileHoverImage from '$lib/images/features/tiling_hover.webp'
let isMobile = false
const context = setContext(mouseContext, {
x: writable(0),
y: writable(0),
isHoverCards: writable(false)
})
/** @type HTMLDivElement */
let featuresContainer
onMount(() => {
isMobile = getIsMobile()
})
function onMouseEnter() {
featuresContainer.addEventListener('mousemove', trackMouse)
context.isHoverCards.set(true)
}
function onMouseLeave() {
featuresContainer.removeEventListener('mousemove', trackMouse)
context.isHoverCards.set(false)
}
function trackMouse({ clientX, clientY }) {
context.x.set(clientX)
context.y.set(clientY)
}
</script>
<section class="relative flex flex-col items-center px-3 md:px-8">
<Title>
<span slot="pre">TLDR</span>
<span slot="title">Features</span>
</Title>
<div
class="group grid w-full flex-wrap gap-6 text-lg font-medium text-white/70 lg:grid-cols-2 lg:grid-rows-2"
role="contentinfo"
on:mouseenter={!isMobile && onMouseEnter}
on:mouseleave={!isMobile && onMouseLeave}
bind:this={featuresContainer}
>
<FeatureCard title="Smooth" class="row-span-2" color="purple">
<p class="max-w-[60ch]">
Smooth transitions. Great animations. High performance. Instant input.
</p>
<div class="_wrapper absolute inset-0 select-none" aria-hidden="true">
<div class="feature-image">
<img
src={smoothDefaultImage}
class="feature-image_inner"
alt=""
aria-hidden="true"
loading="lazy"
/>
<img
src={smoothHoverImage}
class="feature-image_inner-hover"
alt=""
aria-hidden="true"
loading="lazy"
/>
</div>
</div>
</FeatureCard>
<FeatureCard title="Easy to configure" color="purple">
<p class="max-w-[60ch]">
Live reloading config. Easy plain-text format. Sensible defaults. Great documentation.
</p>
<div class="_wrapper absolute inset-0 select-none" aria-hidden="true">
<div class="feature-image">
<img
src={configDefaultImage}
class="feature-image_inner"
alt=""
aria-hidden="true"
loading="lazy"
/>
<img
src={configHoverImage}
class="feature-image_inner-hover"
alt=""
aria-hidden="true"
loading="lazy"
/>
</div>
</div>
</FeatureCard>
<FeatureCard class="" title="Dynamic tiling" color="purple">
<p class="max-w-[60ch]">
Automatic tiling that just works. Supports multiple fine-tuneable layouts.
</p>
<div class="_wrapper absolute inset-0 select-none" aria-hidden="true">
<div class="feature-image">
<img
src={tileDefaultImage}
class="feature-image_inner"
alt=""
aria-hidden="true"
loading="lazy"
/>
<img
src={tileHoverImage}
class="feature-image_inner-hover"
alt=""
aria-hidden="true"
loading="lazy"
/>
</div>
</div>
</FeatureCard>
</div>
<div class="z-10 mt-14 flex flex-col flex-wrap justify-center gap-8 text-lg sm:flex-row">
<a
href="https://github.com/hyprland-community/awesome-hyprland#plugins"
target="_blank"
class="icon-feature hover:underline"
>
<PluginsIcon class="h-8 w-8" />
Plugin system
</a>
<Hypractive />
<a
href="https://wiki.hyprland.org/Configuring/Binds/#global-keybinds"
target="_blank"
class="icon-feature hover:underline"
>
<ShortcutsIcon class="h-8 w-8" />
Global shortcuts for apps
</a>
<a
href="https://wiki.hyprland.org/Configuring/Variables/#gestures"
class="icon-feature hover:underline"
target="_blank"
>
<TouchpadIcon class="h-8 w-8" />Touchpad gestures
</a>
</div>
</section>
<style lang="postcss">
.icon-feature {
@apply flex items-center justify-center gap-3 font-bold text-slate-400;
}
.feature-image {
position: absolute;
inset: 0 0 0 0;
opacity: 0.5;
z-index: -10;
display: flex;
align-items: center;
justify-content: center;
transition: opacity 180ms ease-in-out;
._wrapper:hover & {
opacity: 1;
}
& img {
position: absolute;
transition: opacity 1500ms ease-in-out;
pointer-events: none;
width: 400px;
aspect-ratio: 1;
right: -80px;
top: 50%;
translate: 0px -50%;
@media screen(md) {
width: 600px;
right: -80px;
}
}
}
.feature-image_inner-hover {
opacity: 0;
}
._wrapper:hover {
& .feature-image_inner {
opacity: 0;
}
& .feature-image_inner-hover {
opacity: 1 !important;
filter: saturate(1.3);
}
}
</style>

View file

@ -1,83 +0,0 @@
<script>
import { onDestroy } from 'svelte'
import ClipboardIcon from '~icons/mingcute/copy-2-line'
/** @type {string} */
export let command
/** @type {string} */
export let image
/** @type {string} */
export let name
let isShowingCopied = false
let timeoutId
async function copyCommand() {
await navigator.clipboard.writeText(command).then(() => (isShowingCopied = true))
clearTimeout(timeoutId)
timeoutId = setTimeout(() => (isShowingCopied = false), 1400)
}
onDestroy(() => {
clearTimeout(timeoutId)
})
</script>
<div class="group flex flex-col items-center gap-2 md:flex-row md:gap-4">
<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}
<slot name="imageExtra" />
</div>
<div class="relative mb-2 flex grow flex-col font-mono md:mb-6">
<button
class="flex min-w-[18rem] items-center justify-center gap-4 rounded-full border border-primary py-3 pl-6 pr-6 text-base font-medium transition-transform active:scale-[1.01]"
on:click={$$slots.default ? undefined : copyCommand}
>
<slot>
<div class="relative flex w-full justify-between gap-4">
<div class="flex gap-4">
<div class="select-none font-bold text-primary">></div>
<span>{command}</span>
</div>
<ClipboardIcon
class="hidden h-6 w-6 text-white opacity-0 transition-opacity duration-100 hover:!opacity-100 group-hover:opacity-80 group-active:opacity-100 sm:block"
/>
</div>
</slot>
</button>
<div
class="pointer-events-none absolute left-1/2 z-20 hidden w-full max-w-max select-none rounded-full bg-black/10 px-2 text-green-400 backdrop-blur [translate:-50%_0px] max-md:-bottom-6 md:-top-8"
class:copy={isShowingCopied}
>
Copied to clipboard ✔
</div>
{#if $$slots.extra}
<div class="absolute -bottom-6 flex w-full justify-center font-sans text-xs opacity-80">
<slot name="extra" />
</div>
{/if}
</div>
</div>
<style lang="postcss">
.copy {
animation: 80ms cubic-bezier(0.5, 0.2, 0, 1.5) 1 copy;
display: block;
}
@keyframes copy {
from {
opacity: 0.8;
scale: 0.98;
}
to {
opacity: 1;
scale: 1;
}
}
</style>

View file

@ -17,7 +17,7 @@
</script> </script>
<header <header
class="fixed inset-x-0 top-0 z-50 flex items-center justify-between rounded-full px-6 pt-2 duration-1000 animate-in fade-in-0 slide-in-from-top-1 fill-mode-backwards [animation-delay:0ms] lg:[animation-delay:1250ms]" class="fixed inset-x-0 top-0 z-50 flex items-center justify-between rounded-full px-6 pt-2 duration-1000 animate-in fade-in-0 slide-in-from-top-1 fill-mode-backwards [animation-delay:0ms]"
> >
<a <a
href="/" href="/"
@ -59,6 +59,9 @@
<li aria-current={$page.url.pathname === '/news' ? 'page' : undefined}> <li aria-current={$page.url.pathname === '/news' ? 'page' : undefined}>
<a href="/news">News</a> <a href="/news">News</a>
</li> </li>
<li aria-current={$page.url.pathname === '/plugins' ? 'page' : undefined}>
<a href="/plugins">Plugins</a>
</li>
</ul> </ul>
<ul class="flex flex-row items-center gap-3 px-4"> <ul class="flex flex-row items-center gap-3 px-4">
<li> <li>

View file

@ -0,0 +1,33 @@
import { json } from '@sveltejs/kit'
/** Get the plugins of the `content/plugins/` directory */
async function getPlugins() {
const plugins = Object.entries(import.meta.glob('/src/content/plugins/*.md', { eager: true }))
.flatMap(([path, { metadata }]) => {
const slug = path.split('/').at(-1)?.replace('.md', '')
// Filter out the `readme.md`
if (slug === 'readme') return []
if (!slug || !path) {
console.error(`Invalid file ${path} ${JSON.stringify({ ...metadata, slug })}`)
return []
}
return { slug, ...metadata }
})
.sort(
(a, b) =>
(b.featured ?? 0) - (a.featured ?? 0) ||
(b.weight ?? 0) - (a.weight ?? 0) ||
((b.logo && 1) ?? 0) - ((a.logo && 1) ?? 0) ||
((b.banner && 1) ?? 0) - ((a.banner && 1) ?? 0)
)
return plugins
}
export async function GET() {
const news = await getPlugins()
return json(news)
}

View file

@ -1,5 +1,5 @@
<script> <script>
import { getBlurredPath } from '$lib/Helper.mjs' import { getGeneratedPath } from '$lib/Helper.mjs'
import { inview } from 'svelte-inview' import { inview } from 'svelte-inview'
/** @type {string} */ /** @type {string} */
@ -20,7 +20,7 @@
/** @type {string} */ /** @type {string} */
export let pretitel export let pretitel
let background = blurredThumbnail ?? getBlurredPath(thumbnail) let background = blurredThumbnail ?? getGeneratedPath(thumbnail)
</script> </script>
<div <div

View file

@ -0,0 +1,473 @@
<script context="module">
export const contextId = Symbol('community context')
</script>
<script>
import Button from '$lib/components/Button.svelte'
import DiscordIcon from '~icons/prime/discord'
import DiscordProfilePicture from '$lib/components/DiscordProfilePicture.svelte'
import { setContext } from 'svelte'
import Title from '$lib/components/Title.svelte'
import background from '$lib/images/community-bg.webp'
import amongUsGreenImage from '$lib/images/amongus/green.webp'
import { discordLink } from '$lib/constants.mjs'
let sectionElement
let isDraggingChan = false
// Okay everything multiplied by 24
const validSizes = [16, 20, 24, 32, 40, 48, 64, 80, 96, 100, 128, 160, 240, 320, 640]
/** @type {{image: string coordinates: [number, number] containerClass: string}[], size: number, quote?: string } */
const profiles = [
{
// vaxry
image:
'https://cdn.discordapp.com/avatars/372809091208445953/a_33fd25e26c0ba17c05566bc3179c6476.gif?size=48',
coordinates: [187, 296],
size: 172,
class: 'outline-red-500'
},
{
// fufexan
image:
'https://cdn.discordapp.com/avatars/444952344308744203/9ee39cd422568dad9e70319df27b2560.webp',
coordinates: [735, 441],
size: 164,
class: 'outline-yellow-500'
},
{
// notashelf
image:
'https://cdn.discordapp.com/avatars/419880181101232129/2f57c8e5e681c5d8608d07c83053815f.webp',
coordinates: [391, 615],
size: 149,
class: 'outline-orange-500'
},
{
// Mathisbuilder
image:
'https://cdn.discordapp.com/avatars/378704069726044170/415dcb2ef8d1ef635e35e1d04d523cba.webp',
coordinates: [568, 594],
size: 120,
class: 'outline-amber-500'
},
{
// dani_2048
image:
'https://cdn.discordapp.com/avatars/623781003382751243/abea149ded923e8f4cd6f56bff016368.webp',
coordinates: [525, 764],
size: 80,
class: 'outline-orange-500'
},
{
// end_4
image:
'https://cdn.discordapp.com/avatars/862314649307054080/2dd9cbd31f5c53811ece2f5abdeab2bf.webp',
coordinates: [648, 709],
size: 128,
class: 'outline-cyan-400'
},
{
// liquidovski
image:
'https://cdn.discordapp.com/avatars/283259320802476042/7aa4969a2ce25638da49b54dcd862a35.webp',
coordinates: [65, 208],
size: 100,
class: 'outline-lime-500'
},
{
// roasted.cheese
image:
'https://cdn.discordapp.com/avatars/967764496527532083/5dc06d112653a529ecbae3b6f4225a93.webp',
coordinates: [53, 399],
size: 75,
class: 'outline-sky-500'
},
{
// donnan
image:
'https://cdn.discordapp.com/avatars/163678036401586177/0930f3f6f0839b4f9c59846e185aa8ad.webp?size=128',
coordinates: [24, 341],
size: 49,
class: 'outline-yellow-500'
},
{
// captainiveau
image:
'https://cdn.discordapp.com/avatars/223160360461402122/c6552a1c768613bffb83bf88e4ce2632.webp?size=128',
coordinates: [47, 86],
size: 48,
class: 'outline-red-500'
},
{
// sioodmy
image:
'https://cdn.discordapp.com/avatars/979473046404476998/c9a4af31050a785d896f7880f9d268e3.webp',
coordinates: [1038, 446],
size: 52,
class: 'outline-amber-500'
},
{
image: '/imgs/profile_pictures/_anon.webp',
coordinates: [-85, 566],
size: 40,
class: 'outline-sky-500'
},
{
// Angry snow man
// psidotdiamparenthesis4pt5tol4poi
image:
'https://cdn.discordapp.com/avatars/486802226577276929/4c1cd003e9c7b7c19a858b38cca7104a.webp',
coordinates: [273, 760],
size: 52,
quote: '"meds"',
class: 'outline-cyan-500'
},
{
// outfoxxed
image:
'https://cdn.discordapp.com/avatars/837425748435796060/248cd938377647404e3d8d1c53b639cf.webp',
coordinates: [648, 364],
size: 80,
class: 'outline-orange-500'
},
{
// kirottum
image:
'https://cdn.discordapp.com/avatars/480024733535174668/5453c57e69ff16f495d8dcbd597070e9.webp',
coordinates: [772, 651],
size: 62,
class: 'outline-purple-500'
},
{
// beardwarrior
image:
'https://cdn.discordapp.com/avatars/544368824842190861/1fbf29ea7ca9c2109b97af2ede3806fa.webp',
coordinates: [736, 277],
size: 101,
class: 'outline-lime-500'
},
{
// heisfer
image:
'https://cdn.discordapp.com/avatars/344854021166727180/0c8dd04188df26093164406cc1367f58.webp',
coordinates: [898, 364],
size: 68,
class: 'outline-yellow-500'
},
{
image: amongUsGreenImage,
coordinates: [873, 224],
size: 79,
class: 'outline-green-500'
},
{
// SimplyKyle!
image:
'https://cdn.discordapp.com/avatars/579120037034721281/c7b4341806d515f5d92bb53e322a4728.webp',
coordinates: [887, 159],
size: 39,
class: 'outline-rose-500'
},
{
// thesuncat
image:
'https://cdn.discordapp.com/avatars/246125351464337418/10c8bb5456d1ca1cebf2edff62b7001f.webp',
coordinates: [1023, 552],
size: 48,
class: 'outline-amber-500'
},
{
// flafy
image:
'https://cdn.discordapp.com/avatars/143031299152674816/4a143f00b0e014e9c3da37cd8599b106.webp',
coordinates: [147, 553],
size: 87,
class: 'outline-pink-500'
},
{
// vagahbond
image:
'https://cdn.discordapp.com/avatars/194157962498015232/ab11cc35bd9d057769254d1d3e29e468.webp',
coordinates: [65, 643],
size: 74,
class: 'outline-amber-500 '
},
{
// flick0
image:
'https://cdn.discordapp.com/avatars/482139697796349953/ce63690cf5adb8e096b4472bc0c175e8.webp',
coordinates: [263, 653],
size: 65,
class: 'outline-stone-500 '
},
{
// jacekpoz
image: '/imgs/profile_pictures/jacekpoz.svg',
coordinates: [893, 622],
size: 80,
class: 'outline-yellow-500 bg-black ',
quote: '"piss blob"'
},
{
// aylur
image:
'https://cdn.discordapp.com/avatars/231040215085481984/7f378337240110b76e6e9baa31f83670.webp',
coordinates: [354, 798],
size: 80,
class: 'outline-amber-500'
},
{
// aleph.nought
image:
'https://cdn.discordapp.com/avatars/784153590595321876/04cbb92b7fe3fea8a6cdb4e2fd8562c5.webp',
coordinates: [583, 824],
size: 69,
class: 'outline-white',
quote: 'Perfect being'
},
{
// jappie3
image:
'https://cdn.discordapp.com/avatars/408513270916579338/bac966d3bae8ad01546ee32584917be9.webp',
coordinates: [275, 844],
size: 40,
class: 'outline-rose-300'
},
{
// nwg.piotr
image:
'https://cdn.discordapp.com/avatars/860670651026767942/7122b752fa6097472f8c76cc4458e33b.webp',
coordinates: [939, 552],
size: 48,
class: 'outline-orange-500'
},
{
// xsty_
image:
'https://cdn.discordapp.com/avatars/520860407720837131/3e73c777553fcd67f2f17bc119b79f15.webp',
coordinates: [458, 913],
size: 35,
class: 'outline-orange-500 '
},
{
// petingoso
image:
'https://cdn.discordapp.com/avatars/390226958241366016/513b77487a9fa01b1256c4dc8e1b0c55.webp',
coordinates: [858, 707],
size: 45,
class: 'outline-blue-500 '
},
{
// eriedaberrie
image:
'https://cdn.discordapp.com/avatars/594256188422488069/1e532709dbd756c172c54add8536e558.webp',
coordinates: [8, 477],
size: 54,
class: 'outline-blue-500 bg-black '
},
{
// errornointernet
image:
'https://cdn.discordapp.com/avatars/531392146767347712/0b5e5671e7e8eb0e4f6ec547c686667b.webp',
coordinates: [950, 277],
size: 47,
class: 'outline-orange-500 bg-stone-800'
},
{
// kosslan
image:
'https://cdn.discordapp.com/avatars/194584980922433536/7bb58205667ad9a6bf2791b59b32c109.webp',
coordinates: [69, 561],
size: 54,
class: 'outline-blue-500 bg-black '
},
{
// lassebq
image:
'https://cdn.discordapp.com/avatars/561863734264594452/e9d1b128f8a32849ae4e2a0d86d16fdb.webp',
coordinates: [908, 463],
size: 54,
class: 'outline-orange-500 bg-black '
},
{
// poutineburgerman
image:
'https://cdn.discordapp.com/avatars/426690378830184448/09181b1b476f221b7850702158778c1a.webp',
coordinates: [823, 184],
size: 44,
class: 'outline-blue-500 bg-black '
},
{
// visualdawg
image:
'https://cdn.discordapp.com/avatars/219573494713810945/5202607a6cf5e34ed705308459d0115c.webp',
coordinates: [133, 493],
size: 49,
class: 'outline-stone-900 bg-black '
},
{
// wickedlyhost
image:
'https://cdn.discordapp.com/avatars/317785409763541002/279527807f4263888fa75b2d7e68daf7.webp',
coordinates: [119, 202],
size: 49,
class: 'outline-blue-500 bg-black '
},
{
// yavko
image:
'https://cdn.discordapp.com/guilds/961691461554950145/users/465960044094160908/avatars/11672d8e456e31a85a3d89b31fff913b.webp',
coordinates: [179, 671],
size: 69,
class: 'outline-yellow-500 bg-black '
},
{
// mcgoth
image:
'https://cdn.discordapp.com/avatars/706672850907430942/9330c50f778f6d3776da15335dfb5b3f.webp',
coordinates: [771, 818],
size: 49,
class: 'outline-stone-500 bg-black '
},
{
// brainless.bitch
image:
'https://cdn.discordapp.com/avatars/708440359587414067/2bcdf703f69ee35c4cf39b2a89c429e8.webp',
coordinates: [1018, 123],
size: 28,
class: 'outline-lime-500 bg-black '
},
//
// Extras
{
image: 'imgs/chan/joy.svg',
coordinates: [284, 533],
size: 90,
class: 'outline-cyan-500 bg-blue-300',
onDragStart: ({ detail: { currentTarget } }) => {
isDraggingChan = true
currentTarget.src = 'imgs/chan/surprise.svg'
},
onDragEnd: ({ detail: { currentTarget } }) => {
isDraggingChan = false
currentTarget.src = 'imgs/chan/tongueout.svg'
},
onHover: ({ detail: { srcElement } }) =>
!isDraggingChan && (srcElement.src = 'imgs/chan/wink.svg')
},
{
image: '/imgs/profile_pictures/chan_1.webp',
coordinates: [91, 799],
size: 55,
class: 'outline-sky-500'
},
{
image: '/imgs/profile_pictures/chan_cat.webp',
coordinates: [-10, 844],
size: 32,
class: 'outline-blue-800'
},
{
image: '/imgs/profile_pictures/chan_cat_2.webp',
coordinates: [1000, 744],
size: 32,
class: 'outline-cyan-500'
}
]
.map(({ image, size, ...profile }) => ({
...profile,
size,
image: image + '?size=' + validSizes.find((_, index) => size <= validSizes[index])
}))
.sort(({ size: a }, { size: b }) => b - a)
setContext(contextId, {
biggestSize: profiles.reduce(
(previousSize, { size }) => (size > previousSize ? size : previousSize),
1
),
smallestSize: profiles.reduce(
(previousSize, { size }) => (size < previousSize ? size : previousSize),
Number.POSITIVE_INFINITY
),
getSectionElement: () => sectionElement
})
</script>
<section
class="relative -mb-[200px] flex h-[1100px] min-h-max w-screen flex-col items-center"
bind:this={sectionElement}
>
<Title>
<span slot="title">Join a great<br />community</span>
<span slot="subtitle">
Get help from Distro Hoppers, Haiku writers,<br />Hydrohomies, and human_(probably)
</span>
</Title>
<div class="group mt-16 flex flex-col items-center">
<a
class="discord p-4"
href={discordLink}
target="_blank"
rel="noopener"
aria-label="Join us on Discord"
>
<DiscordIcon class="h-full w-full " />
</a>
<a href={discordLink}>
<Button type="fancyOutline">Join us on Discord</Button>
</a>
</div>
<div class="absolute w-[1024px] select-none">
<div class="flex h-full origin-bottom-right select-none flex-wrap gap-4">
{#each profiles as { onDragEnd, onDragStart, onHover, ...props }}
<DiscordProfilePicture
{...props}
on:dragStart={onDragStart}
on:dragEnd={onDragEnd}
on:hover={onHover}
/>
{/each}
</div>
</div>
<img
src={background}
class="absolute top-0 -z-10 min-w-[1400px] select-none"
alt=""
aria-hidden="true"
loading="lazy"
/>
</section>
<style lang="postcss">
.discord {
width: 9rem;
height: 9rem;
transition:
rotate 500ms cubic-bezier(0.5, 0, 0.5, 1),
scale 420ms cubic-bezier(0.5, 0.1, 0, 1),
filter 840ms;
transition-delay: 240ms, 180ms, 20ms;
transform: translateY(-25%);
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);
animation: bounce 0.7s infinite 180ms both;
}
&:active {
scale: 1;
transition: scale 80ms;
}
}
</style>

View file

@ -0,0 +1,27 @@
<script>
/** @type {string | undefined} */
export let image = undefined
/** @type {string | undefined} */
export let name = undefined
/**
* @type {string | undefined }
* Classes for the command text
*/
</script>
<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}
<slot name="imageExtra" />
</div>
{/if}
<!-- Command button slot -->
<div class="mb-2 w-full">
<slot />
</div>
</div>

View file

@ -1,5 +1,5 @@
<script> <script>
import { animateIn, getBlurredPath } from '$lib/Helper.mjs' import { animateIn, getGeneratedPath } from '$lib/Helper.mjs'
/** @type {string} /** @type {string}
* The path to the image. Usually the file within `static`, but can also be an URL * The path to the image. Usually the file within `static`, but can also be an URL
@ -24,7 +24,7 @@
/> />
<div class="rice-blurred"> <div class="rice-blurred">
<img <img
src={blurredBackground ?? getBlurredPath(image)} src={blurredBackground ?? getGeneratedPath(image)}
alt="Rice desktop" alt="Rice desktop"
aria-hidden="true" aria-hidden="true"
class="h-full w-full object-cover" class="h-full w-full object-cover"

View file

@ -0,0 +1,213 @@
<script>
import Card from '$lib/components/Card.svelte'
import GameIcon from '~icons/gg/games'
import SpecialWorkspaceIcon from '~icons/gg/shutterstock'
import ShortcutsIcon from '~icons/gg/push-chevron-right-o'
import TabIcon from '~icons/gg/tab'
import IpcIcon from '~icons/gg/media-podcast'
import TouchpadIcon from '~icons/gg/touchpad'
import Title from '$lib/components/Title.svelte'
import Hypractive from './Hypractive.svelte'
import configDefaultImage from '$lib/images/features/config_default.webp'
import configHoverImage from '$lib/images/features/config_hover.webp'
import smoothDefaultImage from '$lib/images/features/smooth_default.webp'
import smoothHoverImage from '$lib/images/features/smooth_hover.webp'
import tileDefaultImage from '$lib/images/features/tiling_default.webp'
import tileHoverImage from '$lib/images/features/tiling_hover.webp'
import CardsContainer from '$lib/components/CardsContainer.svelte'
</script>
<section class="relative flex flex-col items-center px-3 md:px-8">
<Title>
<span slot="pre">TLDR</span>
<span slot="title">Features</span>
</Title>
<CardsContainer
class="group grid w-full flex-wrap gap-6 text-lg font-medium text-white/70 lg:grid-cols-2 lg:grid-rows-2 lg:gap-4"
>
<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>
<p class="max-w-[60ch]">
Smooth transitions. Great animations. High performance. Instant input.
</p>
<div class="_wrapper absolute inset-0 select-none" aria-hidden="true">
<div class="feature-image">
<img
src={smoothDefaultImage}
class="feature-image_inner"
alt=""
aria-hidden="true"
loading="lazy"
/>
<img
src={smoothHoverImage}
class="feature-image_inner-hover"
alt=""
aria-hidden="true"
loading="lazy"
/>
</div>
</div>
</div>
</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>
<p class="max-w-[60ch]">
Live reloading config. Easy plain-text format. Sensible defaults. Great documentation.
</p>
<div class="_wrapper absolute inset-0 select-none" aria-hidden="true">
<div class="feature-image">
<img
src={configDefaultImage}
class="feature-image_inner"
alt=""
aria-hidden="true"
loading="lazy"
/>
<img
src={configHoverImage}
class="feature-image_inner-hover"
alt=""
aria-hidden="true"
loading="lazy"
/>
</div>
</div>
</div>
</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>
<p class="max-w-[60ch]">
Automatic tiling that just works. Supports multiple fine-tuneable layouts.
</p>
<div class="_wrapper absolute inset-0 select-none" aria-hidden="true">
<div class="feature-image">
<img
src={tileDefaultImage}
class="feature-image_inner"
alt=""
aria-hidden="true"
loading="lazy"
/>
<img
src={tileHoverImage}
class="feature-image_inner-hover"
alt=""
aria-hidden="true"
loading="lazy"
/>
</div>
</div>
</div>
</Card>
</CardsContainer>
<div
class="z-10 mt-14 flex max-w-screen-xl flex-col flex-wrap justify-center gap-8 text-lg sm:flex-row"
>
<a
href="https://wiki.hyprland.org/Configuring/Tearing/"
target="_blank"
class="icon-feature hover:underline"
>
<GameIcon class="h-8 w-8" />
Tearing support
</a>
<a href="https://wiki.hyprland.org/IPC/" target="_blank" class="icon-feature hover:underline">
<IpcIcon class="h-8 w-8" />
Socket-based IPC
</a>
<a
href="https://github.com/hyprland-community/awesome-hyprland#plugins"
target="_blank"
class="icon-feature hover:underline"
>
<TabIcon class="h-8 w-8" />
Window groups
</a>
<a
href="https://wiki.hyprland.org/Configuring/Dispatchers/#special-workspace"
target="_blank"
class="icon-feature hover:underline"
>
<SpecialWorkspaceIcon class="h-8 w-8" />
Special workspaces
</a>
<Hypractive />
<a
href="https://wiki.hyprland.org/Configuring/Binds/#global-keybinds"
target="_blank"
class="icon-feature hover:underline"
>
<ShortcutsIcon class="h-8 w-8" />
Global shortcuts for apps
</a>
<a
href="https://wiki.hyprland.org/Configuring/Variables/#gestures"
class="icon-feature hover:underline"
target="_blank"
>
<TouchpadIcon class="h-8 w-8" />Touchpad gestures
</a>
</div>
</section>
<style lang="postcss">
.icon-feature {
@apply flex items-center justify-center gap-3 font-bold text-slate-400;
}
.feature-image {
position: absolute;
inset: 0 0 0 0;
opacity: 0.5;
z-index: -10;
display: flex;
align-items: center;
justify-content: center;
transition: opacity 180ms ease-in-out;
._wrapper:hover & {
opacity: 1;
}
& img {
position: absolute;
transition: opacity 1500ms ease-in-out;
pointer-events: none;
width: 400px;
aspect-ratio: 1;
right: -80px;
top: 50%;
translate: 0px -50%;
@media screen(md) {
width: 600px;
right: -80px;
}
}
}
.feature-image_inner-hover {
opacity: 0;
}
._wrapper:hover {
& .feature-image_inner {
opacity: 0;
}
& .feature-image_inner-hover {
opacity: 1 !important;
filter: saturate(1.3);
}
}
p {
word-break: pretty;
}
</style>

View file

View file

@ -4,11 +4,11 @@
import nixLogo from '$lib/images/logos/nixos.svg' import nixLogo from '$lib/images/logos/nixos.svg'
import bsdLogo from '$lib/images/logos/freebsd.svg' import bsdLogo from '$lib/images/logos/freebsd.svg'
import suseLogo from '$lib/images/logos/opensuse.svg' import suseLogo from '$lib/images/logos/opensuse.svg'
import LinkOutIcon from '~icons/akar-icons/link-out' import DistroOption from './DistroOption.svelte'
import InstallButton from './InstallButton.svelte'
import Button from '$lib/components/Button.svelte' import Button from '$lib/components/Button.svelte'
import Title from '$lib/components/Title.svelte' import Title from '$lib/components/Title.svelte'
import amongus from '$lib/images/amongus/green.webp' import amongus from '$lib/images/amongus/green.webp'
import CommandButton from '$lib/components/CommandButton.svelte'
</script> </script>
<section class="pb-6"> <section class="pb-6">
@ -20,26 +20,38 @@
<div <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" 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"
> >
<InstallButton name="Arch" command="pacman -S hyprland" image={archLogo} <DistroOption name="Arch" image={archLogo}>
><div slot="extra"> <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 AUR git version: <a
class=" " class=" "
target="_blank" target="_blank"
href="https://aur.archlinux.org/packages/hyprland-git/">hyprland-git</a href="https://aur.archlinux.org/packages/hyprland-git/">hyprland-git</a
> >
</div></InstallButton </div>
> </CommandButton>
<InstallButton name="NixOS" command="programs.hyprland.enable" image={nixLogo} </DistroOption>
><div slot="extra">
<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.hyprland.org/Nix/" target="_blank" <a href="https://wiki.hyprland.org/Nix/" target="_blank"
>See more details and git version</a >See more details and git version</a
> >
</div> </div>
</InstallButton> </CommandButton>
<InstallButton name="FreeBSD" command="pkg install hyprland" image={bsdLogo} /> </DistroOption>
<InstallButton name="openSUSE" command="zypper in hyprland" image={suseLogo}
><div slot="extra">or install “hyprland” via YaST2 Software.</div>
<DistroOption name="FreeBSD" image={bsdLogo}>
<CommandButton command="pkg install hyprland" />
</DistroOption>
<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>
</CommandButton>
<img <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" 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} src={amongus}
@ -47,7 +59,7 @@
alt="" alt=""
srcset="" srcset=""
/> />
</InstallButton> </DistroOption>
</div> </div>
<a <a

View file

@ -10,6 +10,7 @@
import { Subject, debounceTime, map, tap, throttle, throttleTime } from 'rxjs' import { Subject, debounceTime, map, tap, throttle, throttleTime } from 'rxjs'
import { onMount } from 'svelte' import { onMount } from 'svelte'
import { fade } from 'svelte/transition' import { fade } from 'svelte/transition'
import Button from '$lib/components/Button.svelte'
/** @type {HTMLVideoElement[]}*/ /** @type {HTMLVideoElement[]}*/
const videos = [] const videos = []
@ -82,7 +83,7 @@
<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 <div
class="mx-auto grid max-w-7xl grid-cols-1 gap-8 transition-all lg:grid-cols-2 lg:gap-12" class="mx-auto grid max-w-7xl grid-cols-1 gap-8 transition-all lg:grid-cols-2 lg:gap-12"
use:animateIn={{ fade: 0.0, slide: 24 }} use:animateIn={{ slide: 24 }}
> >
<div <div
class={clsx( class={clsx(
@ -122,6 +123,10 @@
{/each} {/each}
</div> </div>
<Button type="fancyOutline" size="lg" class="max-w-max"
><a href="/plugins/">Checkout more plugins</a></Button
>
<div class="-mt-5 hidden gap-1 lg:mt-12 lg:flex lg:flex-col"> <div class="-mt-5 hidden gap-1 lg:mt-12 lg:flex lg:flex-col">
<a <a
class="txt-shadow_ flex w-max max-w-max shrink-0 items-center gap-3 rounded font-bold text-slate-400 hover:underline" class="txt-shadow_ flex w-max max-w-max shrink-0 items-center gap-3 rounded font-bold text-slate-400 hover:underline"
@ -129,8 +134,7 @@
target="_blank" target="_blank"
> >
<div> <div>
Check out <span class="text-cyan-500">Awesome Hyprland</span> Also see <span class="text-cyan-500">Awesome Hyprland</span>
for more
</div> </div>
<IconLinkOut /> <IconLinkOut />
</a> </a>

View file

@ -8,13 +8,15 @@
let isVisible = false let isVisible = false
let isManuallyPaused = false let isManuallyPaused = false
let isChrome = false
onMount(() => { onMount(() => {
isChrome = navigator.userAgent.toLowerCase().includes('chrome')
// Only autoplay on Chrome, because Firefox struggles with decoding the video // Only autoplay on Chrome, because Firefox struggles with decoding the video
if (navigator.userAgent.toLowerCase().includes('chrome')) { if (isChrome) {
// The inview_leave event fires at the start and Chromium reports the video as paused, even with autoplay on. // The inview_leave event fires at the start and Chromium reports the video as paused, even with autoplay on.
// This fixes it. Catch in case autoplay is blocked // This fixes it. Catch in case autoplay is blocked
videoElement.play().catch(() => {}) videoElement.play().catch(console.error)
} }
}) })
</script> </script>
@ -26,7 +28,7 @@
use:inview={{ threshold: 0.5 }} use:inview={{ threshold: 0.5 }}
on:inview_enter={() => { on:inview_enter={() => {
isVisible = true isVisible = true
if (!isManuallyPaused) { if (!isManuallyPaused && isChrome) {
videoElement.play().catch(() => {}) videoElement.play().catch(() => {})
} }
}} }}
@ -44,6 +46,7 @@
poster={'/videos/end_4_thumbnail.webp'} poster={'/videos/end_4_thumbnail.webp'}
videoClass="!rounded-2xl overflow-hidden" videoClass="!rounded-2xl overflow-hidden"
on:play={() => (isManuallyPaused = false)} on:play={() => (isManuallyPaused = false)}
autoplay={undefined}
/> />
</div> </div>
@ -58,7 +61,7 @@
<style lang="postcss"> <style lang="postcss">
section { section {
@apply animate-in fade-in-0 slide-in-from-bottom-10 fill-mode-backwards relative z-10 -mb-4 w-full max-w-[1400px] px-1 [animation-delay:1700ms] [animation-duration:2000ms] md:-mt-8 lg:px-8; @apply relative z-10 -mb-4 w-full max-w-[1400px] px-1 animate-in fade-in-0 slide-in-from-bottom-10 fill-mode-backwards [animation-delay:1700ms] [animation-duration:2000ms] md:-mt-8 lg:px-8;
contain: layout style content; contain: layout style content;
} }

View file

@ -1,3 +1,5 @@
import { error } from '@sveltejs/kit'
export async function load({ params, fetch }) { export async function load({ params, fetch }) {
try { try {
const post = await import(`../../../content/news/${params.slug}.md`) const post = await import(`../../../content/news/${params.slug}.md`)
@ -10,8 +12,8 @@ export async function load({ params, fetch }) {
meta: post.metadata, meta: post.metadata,
other other
} }
} catch (error) { } catch (error_) {
console.error(error) console.error(error_)
throw error(404, `Could not find ${params.slug}`) error(404, `Could not find ${params.slug}`)
} }
} }

View file

@ -0,0 +1,5 @@
export async function load({ fetch }) {
const response = await fetch('/api/plugins')
const plugins = await response.json()
return { plugins }
}

View file

@ -0,0 +1,172 @@
<script>
import Title from '$lib/components/Title.svelte'
import CardsContainer from '$lib/components/CardsContainer.svelte'
import PluginCard from './PluginCard.svelte'
import clsx from 'clsx'
import * as R from 'remeda'
import { getGeneratedPath } from '$lib/Helper.mjs'
export let data
const { plugins } = data
const featuredPlugins = plugins.filter(({ featured }) => featured).slice(0, 4)
const pluginsByCategory = R.pipe(
plugins,
R.groupBy(({ category }) => category),
R.mapValues(
R.sort(
(a, b) =>
(b.weight ?? 0) - (a.weight ?? 0) ||
Boolean(b.logo) - Boolean(a.logo) ||
b.name.localeCompare(a.name)
)
),
R.toPairs,
R.tap((x) => console.log({ x })),
R.sort(([a], [b]) => a.localeCompare(b))
)
</script>
<svelte:head>
<title>{'Plugins'}</title>
</svelte:head>
<section
class="flex min-h-screen w-full flex-col items-center justify-center gap-14 px-6 md:pr-8
lg:pl-0
"
>
<div class="top-light"></div>
<header class="header mt-24 md:mt-32">
<Title class="mb-0 duration-1000 animate-in fade-in-0">
<span slot="pre">Plugins</span>
<span slot="title">Unlock full power</span><span slot="subtitle">
Easily load up plugins and customize everything
</span>
</Title>
<!-- <div class="absolute top-0">
{#each plugins.filter(({ logo }) => logo) as { logo }, index}
<img
src={getGeneratedPath(logo)}
alt=""
width={index * 50}
height={index * 50}
class="bg-logo"
/>
{/each}
</div> -->
</header>
<section
class="m-0 mx-auto flex w-full max-w-screen-3xl
animate-in fade-in-0 slide-in-from-bottom-6 fill-mode-backwards [animation-delay:800ms] [animation-duration:1500ms]
"
>
<!-- 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">
{#each pluginsByCategory as category}
<li>
<a href={'#' + category[0]} class=" px-2 py-1 transition-colors hover:text-white"
>{category[0]}</a
>
</li>
{/each}
</ul>
</nav>
</div>
<div class="flex grow flex-col items-start gap-6 md:gap-16">
<div class="flex w-full flex-col gap-3">
<h2 class="text-4xl font-bold text-slate-300">Featured</h2>
<CardsContainer
class="
flex w-full grid-flow-dense grid-cols-12 flex-col gap-3 md:grid "
>
{#each featuredPlugins as plugin, index}
<PluginCard
{plugin}
class={clsx(
plugin.banner &&
featuredPlugins
.slice(0, index)
.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 ',
' md:min-h-full '
)}
color={index % 3 === 0 ? 'purple' : 'cyan'}
/>
{/each}
</CardsContainer>
</div>
<CardsContainer
class="md:flotherex-row flex w-full grow flex-col flex-wrap items-start justify-start gap-12 animate-in fade-in-0 "
enableBorders={false}
>
{#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">
{category}
</h3>
<div class="flex w-full flex-wrap gap-4">
{#each plugins as plugin, index}
<PluginCard
{plugin}
class="max-h-48 min-h-[14rem] w-full max-w-96 @sm:h-[18rem] sm:w-52 3xl:w-64 "
color={index % 3 === 0 ? 'purple' : 'cyan'}
taglineMaxLength={38}
/>
{/each}
</div>
</div>
{/each}
</CardsContainer>
</div>
</section>
</section>
<style lang="postcss">
.top-light {
background: url('/imgs/grain.webp'),
radial-gradient(
100% 80% at top,
theme(colors.cyan.500 / 50%) 0%,
theme(colors.sky.500 / 10%),
transparent
);
mask-image: radial-gradient(
100% 80% at top,
white,
rgba(0, 0, 0, 1),
rgba(0, 0, 0, 1),
transparent
);
width: 100%;
height: 100%;
position: absolute;
z-index: -10;
top: 0;
left: 0;
pointer-events: none;
contain: strict;
animation: parallax ease-in-out 500ms;
animation-duration: 1ms;
animation-timeline: scroll();
}
@keyframes parallax {
to {
translate: 0px 1000px;
}
}
</style>

View file

@ -0,0 +1,160 @@
<script>
import { getGeneratedPath, trimText } from '$lib/Helper.mjs'
import Card from '$lib/components/Card.svelte'
import clsx from 'clsx'
import Tag from './Tag.svelte'
// 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
/** @type {HTMLVideoElement}*/
let videoElement
</script>
<Card
on:mouseenter={() => videoElement && videoElement.play().catch(console.error)}
on:mouseleave={() => videoElement && videoElement.pause()}
{color}
class={$$restProps.class}
gradientOpacity={0.5}
><a
href="/plugins/{plugin.slug}"
class="relative flex size-full min-h-max items-stretch @container"
>
<!-- Main content -->
<div
class="flex min-h-max min-w-0 shrink grow-[3] flex-col justify-end p-5 @xs:p-6 @md:p-8 @2xl:max-w-max @4xl:min-w-20"
>
<!-- Logo -->
<div
class="logo-container relative mb-4 transition-transform
group-hover:scale-[1.02]"
>
{#if plugin.logo}
<div
class={clsx(
'logo relative rounded shadow-black ',
plugin.banner
? 'size-20 @md:size-28 max-2xl:@2xl:after:hidden'
: 'size-24 @md:size-28'
)}
style:--background={`url("${getGeneratedPath(plugin.logo)}")`}
>
<img class="size-full" src={plugin.logo} alt={'Logo of ' + plugin.name} />
</div>
{:else}
<!-- Placeholder logo -->
<div
aria-hidden="true"
class="letter-logo -ml-4 flex items-center justify-center bg-gradient-to-tr from-primary via-sky-400 to-blue-400 bg-clip-text text-center text-6xl font-bold leading-none text-transparent"
>
{plugin.name[0]}
</div>
{/if}
</div>
<!-- Texts -->
<div class="flex flex-col">
<h2
class="min-w-[8ch] max-w-full text-pretty text-base font-bold text-white [overflow-wrap:break-word] @xl:mb-3 @xl:text-5xl"
>
{plugin.name}
</h2>
<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)}
</p>
</div>
<!-- Category -->
{#if showCategory}
<div
class="relative mt-3 flex min-h-max items-center gap-2 overflow-hidden @xl:min-w-0 @xl:flex-wrap"
>
<Tag tag={plugin.category} />
</div>
{/if}
</div>
<!-- Banner -->
{#if plugin.banner}
<div class="banner-container hidden @xs:flex">
{#if plugin.banner.split('.').at(-1) === 'mp4'}
<video
src={plugin.banner}
class="absolute inset-0 z-50 size-full object-cover"
bind:this={videoElement}
playsinline
muted
loop
></video>
{:else}
<img src={plugin.banner} class="absolute inset-0 size-full object-cover" alt="" />
{/if}
</div>
{/if}
</a>
</Card>
<style lang="postcss">
.logo {
contain: layout size;
&::after {
content: ' ';
position: absolute;
left: 0%;
bottom: -2rem;
background-image: var(--background);
background-repeat: no-repeat;
background-size: cover;
height: 150%;
width: 120%;
translate: -12% 15%;
z-index: -1;
opacity: 50%;
/* filter: brightness(2); */
mask-image: radial-gradient(closest-side, black 0%, transparent 99%);
}
}
.logo-container:not(.banner) {
@container (max-width: theme(screens.md)) {
@apply flex shrink-[2] grow items-center justify-center max-2xl:@2xl:mx-0 max-2xl:@2xl:block max-2xl:@2xl:h-auto max-2xl:@2xl:grow-0;
}
}
.banner-container {
position: absolute;
inset: 2px;
height: 100%;
z-index: -20;
/* translate: 0 -2px; */
mask-image: radial-gradient(450% 120% at 0% 100%, black 18%, white);
mask-mode: luminance;
@apply rounded-3xl;
contain: strict;
@container (min-width: theme(screens.md)) {
position: relative;
/* width: 100%; */
height: 100%;
flex-grow: 1;
margin-right: 3px;
max-height: calc(100% - 4px);
clip-path: polygon(0 0, 100% 0, 100% 100%, 4rem 100%);
@apply rounded-l-none rounded-r-3xl;
flex-shrink: 2;
mask-image: unset;
}
}
.letter-logo {
text-shadow: 0 8px 24px theme(colors.primary / 60%);
}
</style>

View file

@ -0,0 +1,18 @@
<script>
/** @type {string}*/
export let tag
const colors = {
Design: 'text-pink-200 bg-pink-500/15',
'Quality of Life': 'text-orange-200 bg-orange-500/15',
Layout: 'text-blue-200 bg-blue-500/15',
Miscellaneous: 'text-purple-200 bg-purple-500/15',
Official: 'text-cyan-200 bg-cyan-500/15'
}
</script>
<div
class={'min-w-max rounded-full p-1 px-2.5 text-xs font-bold ' + colors[tag] ?? 'bg-slate-100/5'}
>
{tag}
</div>

View file

@ -0,0 +1,19 @@
import { error } from '@sveltejs/kit'
export async function load({ params, fetch }) {
try {
const post = await import(`../../../content/plugins/${params.slug}.md`)
const other = await fetch('/api/plugins')
.then((response) => response.json())
.then((news) => news.filter((entry) => entry.slug !== params.slug).slice(0, 4))
return {
content: post.default,
meta: post.metadata,
other
}
} catch (errorMessage) {
console.error(errorMessage)
error(404, `Could not find ${params.slug}`)
}
}

View file

@ -0,0 +1,188 @@
<script>
import CardsContainer from '$lib/components/CardsContainer.svelte'
import Title from '$lib/components/Title.svelte'
import clsx from 'clsx'
import PluginCard from '../PluginCard.svelte'
import Tag from '../Tag.svelte'
import { getGeneratedPath } from '$lib/Helper.mjs'
import GithubIcon from '~icons/ri/github-fill'
import BackIcon from '~icons/gg/arrow-left-r'
import InstallButton from '$lib/components/CommandButton.svelte'
import Button from '$lib/components/Button.svelte'
export let data
$: ({ meta } = data)
$: title = meta.name + ' Hyprland Plugin'
/** @type {HTMLVideoElement}*/
let videoElement
function playVideo() {
videoElement && videoElement.play().catch(console.error)
}
function pauseVideo() {
videoElement && videoElement.pause()
}
</script>
<svelte:head>
<title>{title}</title>
<meta property="og:type" content="article" />
<meta property="og:title" content={title} />
{#if meta.logo}
<meta property="og:image" content={meta.logo} />
{/if}
</svelte:head>
<article
class={clsx(
'mx-auto mt-navbar flex min-h-[min(100vh,900px)] w-full max-w-screen-lg flex-col pt-8 transition-none delay-500 animate-in fade-in-0 fill-mode-backwards [animation-duration:400ms] lg:px-0',
meta.banner | meta.logo ? 'md:pt-12' : 'mt-12'
)}
>
<!-- Back button -->
<a
href="/plugins"
class="mb-2 ml-2 flex items-center gap-3 text-slate-400 hover:text-slate-100 lg:ml-0"
><BackIcon />Back to all plugins</a
>
<!-- Banner -->
{#if meta.banner || meta.logo}
<div
class="relative flex aspect-video w-full min-w-full items-center justify-center overflow-hidden bg-neutral-950 animate-in fade-in-0 fill-mode-backwards [animation-delay:0.4s] [animation-duration:1.2s] sm:rounded-3xl md:h-[28rem]"
on:mouseenter={playVideo}
on:mouseleave={pauseVideo}
role="banner"
>
{#if meta.banner?.split('.').at(-1) === 'mp4'}
<video
src={meta.banner}
class="absolute left-1/2 top-1/2 z-10 size-full min-h-[44rem] min-w-full -translate-x-1/2 -translate-y-1/2"
bind:this={videoElement}
playsinline
muted
loop
></video>
{:else if meta.banner}
<img src={meta.banner} class="absolute inset-0 size-full object-cover" alt="" />
{:else if meta.logo}
<div class="grain absolute inset-0 flex size-full items-center justify-center">
<img src={meta.logo} class="z-30 size-48" alt="" />
<img
src={getGeneratedPath(meta.logo)}
class="absolute inset-0 object-cover opacity-30"
alt=""
/>
</div>
{/if}
</div>
{/if}
<!-- ( Logo, Heading, Install buttons ) and tags -->
<div
class={clsx(
'relative z-20 mx-6 mb-24 mt-6 flex flex-col flex-wrap justify-between gap-4 duration-1000 animate-in fade-in-0 slide-in-from-bottom-4 md:mx-8 md:gap-8 lg:flex-nowrap',
!meta.banner && !meta.logo && 'mt-24 lg:mt-44',
!meta.banner && meta.logo && 'sm:-mt-16 md:-mt-20 lg:-mt-32',
meta.banner && meta.logo && 'sm:-mt-24 md:-mt-52 lg:-mt-64 '
)}
>
<hgroup
class="flex w-full flex-col gap-4"
on:mouseenter={playVideo}
on:mouseleave={pauseVideo}
role="heading"
aria-level="1"
>
<!-- Logo -->
{#if meta.logo && meta.banner}
<img src={meta.logo} class="size-20 md:size-28 lg:size-40" alt={'Logo ' + meta.name} />
{/if}
<h1
class="_text-shadow max-w-max text-pretty bg-gradient-to-br from-white via-white to-white/50 bg-clip-text py-2 text-5xl font-bold text-transparent md:text-7xl xl:text-8xl"
>
{meta.name}
</h1>
</hgroup>
<div class="flex w-full flex-wrap gap-16 md:flex-nowrap md:gap-8">
<div class="flex w-full grow-[4] flex-col justify-between gap-8 md:gap-12">
<p class="font-medium text-slate-300 sm:text-lg">
{meta.tagline}
</p>
<InstallButton
containerClass="max-w-max"
commandClass="text-left break-all md:break-normal text-slate-300 group-hover:text-white"
command={`hyprpm add ${meta.url}`}
>
<div class="self-start justify-self-start" slot="extra">
<span class="text-red-200">Trust the plugin source before installing!</span>
<a
href="https://wiki.hyprland.org/Plugins/Using-Plugins/"
target="_blank"
class="w-full text-left hover:underline"
>Installation is done via hyprpm ↗
</a>
</div>
</InstallButton>
</div>
<!-- Tags, Github button -->
<div class="flex shrink-0 flex-col items-start gap-8 md:items-end lg:shrink-[2]">
<a href={meta.url} target="_blank">
<GithubIcon class="size-10" />
</a>
<Tag tag={meta.category} />
</div>
</div>
</div>
<!-- Markdown -->
<section
class="prose prose-slate prose-invert mx-auto px-6 lg:prose-xl prose-a:text-cyan-400 prose-img:rounded-lg sm:px-8"
>
<svelte:component this={data.content} />
</section>
</article>
<!-- More plugins -->
{#if data.other.length > 0}
<section
class="relative mx-auto mt-64 flex w-full max-w-screen-xl flex-col items-center px-0 delay-1000 duration-1000 animate-in fade-in-0 fill-mode-backwards sm:px-4 md:px-8"
>
<Title class="mb-6"><span slot="title">More plugins</span></Title>
<CardsContainer class="flex w-full grid-cols-2 flex-col gap-8 md:grid">
{#each data.other as plugin}
<PluginCard showCategory={true} {plugin} class="h-96 min-h-96" />
{/each}
</CardsContainer>
<div
class="m-4 mt-16 max-w-max rounded-xl bg-slate-900/20 px-12 py-8 outline outline-cyan-100/5"
>
<Button type="fancyOutline"><a href="/plugins">Back to all plugins</a></Button>
</div>
</section>
{/if}
<style lang="postcss">
.grain {
&::after {
content: ' ';
background: url('/imgs/grain.webp');
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 21;
}
}
._text-shadow {
filter: drop-shadow(0 0 20px theme(colors.black / 50%));
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View file

Before

Width:  |  Height:  |  Size: 678 B

After

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -1,88 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg fill="none" height="32" width="32" xmlns="http://www.w3.org/2000/svg"><clipPath id="prefix__a"><path d="M0 0h32v32H0z"/></clipPath><g clip-path="url(#prefix__a)"><path d="M31.103 20.992c0 2.01-.525 3.436-1.345 4.475-.827 1.05-2.016 1.784-3.476 2.29-2.961 1.029-6.772 1.039-10.182 1.039s-7.273-.01-10.287-1.04c-1.487-.508-2.701-1.244-3.546-2.296-.835-1.04-1.37-2.463-1.37-4.468 0-3.748.807-8.31 3.108-11.903 2.267-3.541 6.013-6.2 12.095-6.2 5.765 0 9.469 2.664 11.771 6.232 2.331 3.613 3.232 8.18 3.232 11.871z" fill="#fbc546" stroke="#000" stroke-width="1.794"/><g clip-rule="evenodd" fill-rule="evenodd"><path d="M19.454 22.413a.853.853 0 010 1.706H12.63a.853.853 0 010-1.706z" fill="#593604"/><path d="M27.133 19.853c0-.943-.764-1.706-1.706-1.706h-3.413a1.706 1.706 0 100 3.412h3.413c.942 0 1.706-.763 1.706-1.706zM11.776 19.853c0-.943-.764-1.706-1.706-1.706H6.657a1.706 1.706 0 100 3.412h3.413c.942 0 1.706-.763 1.706-1.706z" fill="#ed6b44"/><path d="M6.498 19a3.84 3.84 0 003.837-3.838v-.855a3.84 3.84 0 00-3.839-3.839h-.001a3.84 3.84 0 00-3.838 3.838v.855A3.84 3.84 0 006.497 19z" fill="#fff"/><path d="M5.217 14.726c0-.938.76-1.698 1.697-1.698h.017c.938 0 1.698.76 1.698 1.698v.002c0 .938-.76 1.698-1.698 1.698h-.017c-.937 0-1.697-.76-1.697-1.698v-.002z" fill="#000"/><path d="M25.586 19a3.84 3.84 0 01-3.837-3.838v-.856a3.84 3.84 0 013.837-3.838h.003a3.84 3.84 0 013.837 3.838v.855A3.84 3.84 0 0125.588 19h-.001z" fill="#fff"/><path d="M26.867 14.726c0-.938-.76-1.698-1.698-1.698h-.017c-.937 0-1.698.76-1.698 1.698v.002c0 .938.76 1.698 1.698 1.698h.017c.938 0 1.698-.76 1.698-1.698v-.002z" fill="#000"/></g></g></svg>
<svg
fill="none"
height="32"
viewBox="0 0 32 32"
width="32"
version="1.1"
id="svg113"
sodipodi:docname="jacekpoz revolt.svg"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
inkscape:export-filename="jacekpoz revolt.png"
inkscape:export-xdpi="1800"
inkscape:export-ydpi="1800"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs117" />
<sodipodi:namedview
id="namedview115"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
showgrid="false"
inkscape:zoom="37.28125"
inkscape:cx="9.0796312"
inkscape:cy="16"
inkscape:window-width="2560"
inkscape:window-height="1399"
inkscape:window-x="1920"
inkscape:window-y="41"
inkscape:window-maximized="1"
inkscape:current-layer="g111" />
<clipPath
id="a">
<path
d="m0 0h32v32h-32z"
id="path90" />
</clipPath>
<g
clip-path="url(#a)"
id="g111">
<path
d="m31.1028 20.9925c0 2.0091-.5252 3.4351-1.3444 4.4745-.8271 1.0495-2.0166 1.7841-3.4762 2.2907-2.9616 1.0282-6.7719 1.0379-10.1824 1.0379-3.4093 0-7.27255-.0094-10.28664-1.0393-1.48698-.5081-2.7015-1.2447-3.54601-2.2958-.83532-1.0397-1.370401-2.4634-1.370401-4.468 0-3.748.807291-8.3097 3.108411-11.90376 2.26699-3.54072 6.01294-6.19919 12.09464-6.19919 5.7647 0 9.4692 2.66321 11.7712 6.23125 2.3312 3.6131 3.2318 8.1799 3.2318 11.8717z"
fill="#fbc546"
stroke="#000"
stroke-width="1.7935"
id="path93" />
<g
clip-rule="evenodd"
fill-rule="evenodd"
id="g109">
<path
d="m19.4545 22.4125c.4709 0 .8531.3822.8531.8531 0 .471-.3822.8532-.8531.8532h-6.8253c-.4709 0-.8531-.3822-.8531-.8532 0-.4709.3822-.8531.8531-.8531z"
fill="#593604"
id="path95" />
<path
d="m27.1329 19.853c0-.9427-.7636-1.7063-1.7063-1.7063-1.0426 0-2.3701 0-3.4126 0-.9428 0-1.7064.7636-1.7064 1.7063 0 .9428.7636 1.7064 1.7064 1.7064h3.4126c.9427 0 1.7063-.7636 1.7063-1.7064z"
fill="#ed6b44"
id="path97" />
<path
d="m11.7761 19.853c0-.9427-.7636-1.7063-1.7064-1.7063-1.04251 0-2.37002 0-3.41258 0-.94274 0-1.70631.7636-1.70631 1.7063 0 .9428.76357 1.7064 1.70631 1.7064h3.41258c.9428 0 1.7064-.7636 1.7064-1.7064z"
fill="#ed6b44"
id="path99" />
<path
d="m 6.498,18.9998 c 1.0178,0 1.9938,-0.4044 2.7139,-1.1236 0.7192,-0.72 1.1236,-1.696 1.1236,-2.7139 0,-0.2841 0,-0.5707 0,-0.8548 0,-2.1201 -1.7191,-3.8392 -3.8392,-3.8392 -8e-4,0 -8e-4,0 -0.0017,0 -1.01781,0 -1.99382,0.4044 -2.71389,1.1236 -0.71921,0.72 -1.12361,1.696 -1.12361,2.7139 v 0.8548 c 0,2.1201 1.71912,3.8392 3.8392,3.8392 z"
fill="#ffffff"
id="path101" />
<path
d="m 5.21658,14.7256 c 0,-0.9376 0.76017,-1.6978 1.69782,-1.6978 h 0.017 c 0.9376,0 1.6978,0.7602 1.6978,1.6978 v 0.0026 c 0,0.9376 -0.7602,1.6978 -1.6978,1.6978 -0.0059,0 -0.0111,0 -0.017,0 -0.93765,0 -1.69782,-0.7602 -1.69782,-1.6978 0,-9e-4 0,-0.0017 0,-0.0026 z"
fill="#000000"
id="path103" />
<path
d="m 25.5857,18.9998 c -1.0178,0 -1.9939,-0.4044 -2.7139,-1.1236 -0.7192,-0.72 -1.1236,-1.696 -1.1236,-2.7139 0,-0.2849 0,-0.5716 0,-0.8565 0,-1.0179 0.4044,-1.9939 1.1236,-2.7139 0.72,-0.7192 1.6961,-1.1236 2.7139,-1.1236 h 0.0034 c 1.0178,0 1.9938,0.4044 2.7139,1.1236 0.7192,0.72 1.1236,1.696 1.1236,2.7139 v 0.8548 c 0,2.1201 -1.7191,3.8392 -3.8392,3.8392 -9e-4,0 -9e-4,0 -0.0017,0 z"
fill="#ffffff"
id="path105" />
<path
d="m 26.8671,14.7256 c 0,-0.9376 -0.7602,-1.6978 -1.6978,-1.6978 -0.006,0 -0.0111,0 -0.017,0 -0.9377,0 -1.6978,0.7602 -1.6978,1.6978 v 0.0026 c 0,0.9376 0.7601,1.6978 1.6978,1.6978 h 0.017 c 0.9376,0 1.6978,-0.7602 1.6978,-1.6978 0,-9e-4 0,-0.0017 0,-0.0026 z"
fill="#000000"
id="path107" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View file

@ -0,0 +1,48 @@
<svg width="88" height="88" viewBox="0 0 88 88" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="14" width="81.7647" height="60.0692" rx="24" fill="#0057FF" />
<rect x="10.0374" y="21.0374" width="67.6899" height="45.9944" rx="16.9626" fill="url(#paint0_radial_1823_494)" />
<rect x="10.0374" y="21.0374" width="67.6899" height="45.9944" rx="16.9626" stroke="url(#paint1_radial_1823_494)"
stroke-width="6.07474" />
<g filter="url(#filter0_i_1823_494)">
<path d="M31.2793 51.8893V36.1798H35.2511V51.8893H31.2793ZM25.4105 46.0204V42.0487H41.12V46.0204H25.4105Z"
fill="url(#paint2_linear_1823_494)" />
<path d="M52.5138 51.8893V36.1798H56.4855V51.8893H52.5138ZM46.6449 46.0204V42.0487H62.3544V46.0204H46.6449Z"
fill="url(#paint3_linear_1823_494)" />
</g>
<defs>
<filter id="filter0_i_1823_494" x="23.6748" y="34.4442" width="38.6796" height="17.4451"
filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result="hardAlpha" />
<feOffset dx="-1.73564" dy="-1.73564" />
<feGaussianBlur stdDeviation="1.73564" />
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1" />
<feColorMatrix type="matrix" values="0 0 0 0 0.0730794 0 0 0 0 0.610693 0 0 0 0 1 0 0 0 0.5 0" />
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_1823_494" />
</filter>
<radialGradient id="paint0_radial_1823_494" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"
gradientTransform="translate(36.0222 35.0719) rotate(34.7051) scale(38.9801 36.4968)">
<stop stop-color="#0085E6" />
<stop offset="1" stop-color="#003BD4" />
</radialGradient>
<radialGradient id="paint1_radial_1823_494" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"
gradientTransform="translate(78.0753 51.628) rotate(-162.865) scale(69.9544 53.6959)">
<stop stop-color="#2F76FF" />
<stop offset="1" stop-color="#00F0FF" />
</radialGradient>
<linearGradient id="paint2_linear_1823_494" x1="53.8894" y1="34.8966" x2="57.5442" y2="59.7195"
gradientUnits="userSpaceOnUse">
<stop stop-color="white" />
<stop offset="0.615" stop-color="#00B3FF" stop-opacity="0.455" />
<stop offset="1" stop-color="#1D84DA" stop-opacity="0" />
</linearGradient>
<linearGradient id="paint3_linear_1823_494" x1="53.8894" y1="34.8966" x2="57.5442" y2="59.7195"
gradientUnits="userSpaceOnUse">
<stop stop-color="white" />
<stop offset="0.615" stop-color="#00B3FF" stop-opacity="0.455" />
<stop offset="1" stop-color="#1D84DA" stop-opacity="0" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="100mm"
height="100mm"
viewBox="0 0 100 100"
version="1.1"
id="svg5"
sodipodi:docname="hy3.svg"
xml:space="preserve"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="true"
showguides="true"
inkscape:zoom="1.3202441"
inkscape:cx="187.08662"
inkscape:cy="183.29944"
inkscape:window-width="1761"
inkscape:window-height="1413"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g1739"><inkscape:grid
type="xygrid"
id="grid9"
spacingx="2.5"
spacingy="2.5"
units="mm"
visible="true" /><sodipodi:guide
position="40,60"
orientation="0.70710678,0.70710678"
id="guide65"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" /><sodipodi:guide
position="5,60"
orientation="0.70710678,0.70710678"
id="guide69"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" /><sodipodi:guide
position="40,94.999999"
orientation="0.70710678,0.70710678"
id="guide71"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" /><sodipodi:guide
position="17.5,82.499999"
orientation="0.70710678,-0.70710678"
id="guide75"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" /></sodipodi:namedview><defs
id="defs2"><linearGradient
inkscape:collect="always"
id="linearGradient1833"><stop
style="stop-color:#009ff9;stop-opacity:1;"
offset="0"
id="stop1829" /><stop
style="stop-color:#00e7cd;stop-opacity:1;"
offset="1"
id="stop1831" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1833"
id="linearGradient1835"
x1="0"
y1="62.5"
x2="100"
y2="25"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1833"
id="linearGradient882"
gradientUnits="userSpaceOnUse"
x1="0"
y1="62.5"
x2="100"
y2="25" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1833"
id="linearGradient884"
gradientUnits="userSpaceOnUse"
x1="0"
y1="62.5"
x2="100"
y2="25" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1833"
id="linearGradient886"
gradientUnits="userSpaceOnUse"
x1="0"
y1="62.5"
x2="100"
y2="25" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1833"
id="linearGradient888"
gradientUnits="userSpaceOnUse"
x1="0"
y1="62.5"
x2="100"
y2="25" /></defs><g
inkscape:groupmode="layer"
id="g1741"
inkscape:label="logo"
style="fill:url(#linearGradient1835);fill-opacity:1"><g
inkscape:label="i3"
inkscape:groupmode="layer"
id="g1731"
transform="translate(3.0000001,3.0000001)"
style="fill-opacity:1;fill:url(#linearGradient1835)"><g
id="g1727"
inkscape:label="inner"
style="fill-opacity:1;fill:url(#linearGradient1835)"><path
style="stroke-width:13;stroke-linecap:round;-inkscape-stroke:none;fill-opacity:1;fill:url(#linearGradient1835)"
d="m 44.999999,44.999999 20,20"
id="path1723" /><path
style="stroke-linecap:round;-inkscape-stroke:none;fill-opacity:1;fill:url(#linearGradient1835)"
d="m 45,38.5 a 6.5,6.5 0 0 0 -4.595703,1.904297 6.5,6.5 0 0 0 0,9.191406 l 20,20 a 6.5,6.5 0 0 0 9.191406,0 6.5,6.5 0 0 0 0,-9.191406 l -20,-20 A 6.5,6.5 0 0 0 45,38.5 Z"
id="path1725" /></g><path
style="stroke-linecap:round;stroke-miterlimit:3.9;-inkscape-stroke:none;fill-opacity:1;fill:url(#linearGradient1835)"
d="m 62.5,21 a 6.5,6.5 0 0 0 -4.595703,1.904297 6.5,6.5 0 0 0 0,9.191406 l 20,20 c 0,0 3.352817,3.478374 4.992187,8.396484 1.639371,4.918111 2.22574,10.194182 -4.992187,17.41211 -7.217928,7.217927 -12.493998,6.631558 -17.41211,4.992187 -4.918111,-1.63937 -8.396484,-4.992187 -8.396484,-4.992187 l -20,-20 a 6.5,6.5 0 0 0 -9.191406,0 6.5,6.5 0 0 0 0,9.191406 l 20,20 c 0,0 5.271639,5.397187 13.478515,8.132813 8.206877,2.735625 20.430837,2.149241 30.712891,-8.132813 C 97.377757,76.813649 97.964142,64.589688 95.228516,56.382812 92.492889,48.175937 87.095703,42.904297 87.095703,42.904297 l -20,-20 A 6.5,6.5 0 0 0 62.5,21 Z"
id="path1729" /></g><g
inkscape:groupmode="layer"
id="g1739"
inkscape:label="hyprland"
style="fill:url(#linearGradient888);fill-opacity:1"
transform="translate(2.5,2.5)"><path
d="m 1.807378,35.000035 2.191099,2.191098 0.630899,0.630901 c 0.08347,0.08347 0.18178,0.225239 0.292093,0.270611 0.09905,0.0406 0.215786,0.04969 0.319879,0.07229 0.259866,0.05628 0.521263,0.103675 0.781808,0.156022 0.859731,0.172563 1.725088,0.315621 2.59215,0.445898 1.458315,0.219022 2.927802,0.350315 4.382627,0.59347 2.17703,0.363792 4.352779,1.013617 6.160824,2.313695 0.300116,0.215693 0.611972,0.434982 0.884713,0.684021 0.207102,0.188992 0.398866,0.398116 0.597168,0.596417 0.143064,0.143065 0.304712,0.279986 0.43419,0.435439 0.542354,0.653629 0.967321,1.412358 1.303574,2.191948 0.421001,0.976188 0.629969,2.024936 0.704395,3.081014 0.158063,2.241573 -0.715136,4.469325 -2.152826,6.158351 -0.167668,0.196891 -0.370666,0.371915 -0.553544,0.554793 -0.182879,0.18288 -0.35809,0.38569 -0.554794,0.553545 -1.037907,0.883435 -2.266806,1.552699 -3.590499,1.900036 C 14.142856,58.377615 11.86709,58.055937 9.952143,57.087335 9.197967,56.705898 8.532698,56.218481 7.93761,55.623393 7.743136,55.428919 7.52981,55.242568 7.34996,55.034493 6.855831,54.46206 6.418019,53.830074 6.039816,53.173086 4.77324,50.972777 4.368186,48.37645 4.063224,45.89369 3.890155,44.484736 3.710343,43.077742 3.446049,41.682431 3.374179,41.302774 3.302136,40.923473 3.227029,40.544502 3.190539,40.359669 3.175529,40.136807 3.102209,39.963058 3.055899,39.853681 2.913899,39.755265 2.830599,39.671964 L 2.191176,39.032542 0.0086,36.849966 0,36.858566 c 0.149003,0.416712 0.200276,0.89887 0.287053,1.332822 0.17415,0.871034 0.338302,1.743335 0.483921,2.619431 0.67481,4.059158 0.743697,8.347414 2.47927,12.150796 0.368561,0.80764 0.804999,1.576403 1.30639,2.308498 0.356122,0.519988 0.767826,0.989327 1.180378,1.462588 0.240228,0.27557 0.492006,0.558695 0.768814,0.799909 2.122385,1.848882 4.977891,2.930875 7.807987,2.830528 1.339735,-0.04757 2.658059,-0.322528 3.893601,-0.846686 1.211838,-0.514267 2.266804,-1.259589 3.252459,-2.118721 2.388721,-2.082309 3.903991,-5.148567 3.903997,-8.338884 -6e-6,-1.474088 -0.285698,-2.969065 -0.850951,-4.33266 C 24.018859,43.534195 23.353854,42.451778 22.507,41.480272 22.210847,41.14041 21.855617,40.855368 21.527395,40.550124 19.903004,39.03963 17.810791,38.050148 15.701625,37.423932 12.888576,36.588672 9.961704,36.409809 7.075251,35.972947 5.923346,35.798597 4.77434,35.603617 3.631902,35.37513 3.253949,35.2995 2.874627,35.228631 2.498412,35.144513 2.274938,35.094433 2.035841,35.015629 1.807403,35 Z"
id="path1733"
inkscape:label="3"
style="fill:url(#linearGradient882);fill-opacity:1" /><path
d="m 19.307378,17.500035 2.191099,2.191098 0.630899,0.630901 c 0.08347,0.08347 0.18178,0.225239 0.292093,0.270611 0.09905,0.0406 0.215786,0.04969 0.319879,0.07229 0.259866,0.05628 0.521263,0.103675 0.781808,0.156022 0.859731,0.172563 1.725088,0.315621 2.59215,0.445898 1.458315,0.219022 2.927802,0.350315 4.382627,0.59347 2.17703,0.363792 4.352779,1.013617 6.160824,2.313695 0.300116,0.215693 0.611972,0.434982 0.884713,0.684021 0.207102,0.188992 0.398866,0.398116 0.597168,0.596417 0.143064,0.143065 0.304712,0.279986 0.43419,0.435439 0.542354,0.653629 0.967321,1.412358 1.303574,2.191948 0.421001,0.976188 0.629969,2.024936 0.704395,3.081014 0.158063,2.241573 -0.715136,4.469325 -2.152826,6.158351 -0.167668,0.196891 -0.370666,0.371915 -0.553544,0.554793 -0.182879,0.18288 -0.35809,0.38569 -0.554794,0.553545 -1.037907,0.883435 -2.266806,1.552699 -3.590499,1.900036 -2.088278,0.548031 -4.364044,0.226353 -6.278991,-0.742249 -0.754176,-0.381437 -1.419445,-0.868854 -2.014533,-1.463942 -0.194474,-0.194474 -0.4078,-0.380825 -0.58765,-0.5889 -0.494129,-0.572433 -0.931941,-1.204419 -1.310144,-1.861407 -1.266576,-2.200309 -1.67163,-4.796636 -1.976592,-7.279396 -0.173069,-1.408954 -0.352881,-2.815948 -0.617175,-4.211259 -0.07187,-0.379657 -0.143913,-0.758958 -0.21902,-1.137929 -0.03649,-0.184833 -0.0515,-0.407695 -0.12482,-0.581444 -0.04631,-0.109377 -0.18831,-0.207793 -0.27161,-0.291094 L 19.691176,21.532542 17.5086,19.349966 17.5,19.358566 c 0.149003,0.416712 0.200276,0.89887 0.287053,1.332822 0.17415,0.871034 0.338302,1.743335 0.483921,2.619431 0.67481,4.059158 0.743697,8.347414 2.47927,12.150796 0.368561,0.80764 0.804999,1.576403 1.30639,2.308498 0.356122,0.519988 0.767826,0.989327 1.180378,1.462588 0.240228,0.27557 0.492006,0.558695 0.768814,0.799909 2.122385,1.848882 4.977891,2.930875 7.807987,2.830528 1.339735,-0.04757 2.658059,-0.322528 3.893601,-0.846686 1.211838,-0.514267 2.266804,-1.259589 3.252459,-2.118721 2.388721,-2.082309 3.903991,-5.148567 3.903997,-8.338884 -6e-6,-1.474088 -0.285698,-2.969065 -0.850951,-4.33266 C 41.518859,26.034195 40.853854,24.951778 40.007,23.980272 39.710847,23.64041 39.355617,23.355368 39.027395,23.050124 37.403004,21.53963 35.310791,20.550148 33.201625,19.923932 30.388576,19.088672 27.461704,18.909809 24.575251,18.472947 23.423346,18.298597 22.27434,18.103617 21.131902,17.87513 20.753949,17.7995 20.374627,17.728631 19.998412,17.644513 19.774938,17.594433 19.535841,17.515629 19.307403,17.5 Z"
id="path1735"
inkscape:label="2"
style="fill:url(#linearGradient884);fill-opacity:1" /><path
d="m 36.807378,3.5e-5 2.191099,2.191098 0.630899,0.630901 c 0.08347,0.08347 0.18178,0.225239 0.292093,0.270611 0.09905,0.0406 0.215786,0.04969 0.319879,0.07229 0.259866,0.05628 0.521263,0.103675 0.781808,0.156022 0.859731,0.172563 1.725088,0.315621 2.59215,0.4458979 1.458315,0.219022 2.927802,0.350315 4.382627,0.59347 2.17703,0.363792 4.352779,1.013617 6.160824,2.313695 0.300116,0.215693 0.611972,0.434982 0.884713,0.684021 0.207102,0.188992 0.398866,0.398116 0.597168,0.596417 0.143064,0.143065 0.304712,0.279986 0.43419,0.435439 0.542354,0.653629 0.967321,1.4123581 1.303574,2.1919481 0.421001,0.976188 0.629969,2.024936 0.704395,3.081014 0.158063,2.241573 -0.715136,4.469325 -2.152826,6.158351 -0.167668,0.196891 -0.370666,0.371915 -0.553544,0.554793 -0.182879,0.18288 -0.35809,0.38569 -0.554794,0.553545 -1.037907,0.883435 -2.266806,1.552699 -3.590499,1.900036 -2.088278,0.548031 -4.364044,0.226353 -6.278991,-0.742249 -0.754176,-0.381437 -1.419445,-0.868854 -2.014533,-1.463942 -0.194474,-0.194474 -0.4078,-0.380825 -0.58765,-0.5889 C 41.855831,19.46206 41.418019,18.830074 41.039816,18.173086 39.77324,15.972777 39.368186,13.37645 39.063224,10.89369 38.890155,9.4847359 38.710343,8.0777419 38.446049,6.6824309 c -0.07187,-0.379657 -0.143913,-0.758958 -0.21902,-1.137929 -0.03649,-0.184833 -0.0515,-0.407695 -0.12482,-0.581444 -0.04631,-0.109377 -0.18831,-0.207793 -0.27161,-0.291094 L 37.191176,4.0325419 35.0086,1.849966 35,1.858566 c 0.149003,0.416712 0.200276,0.89887 0.287053,1.332822 0.17415,0.8710339 0.338302,1.7433349 0.483921,2.6194309 0.67481,4.0591581 0.743697,8.3474141 2.47927,12.1507961 0.368561,0.80764 0.804999,1.576403 1.30639,2.308498 0.356122,0.519988 0.767826,0.989327 1.180378,1.462588 0.240228,0.27557 0.492006,0.558695 0.768814,0.799909 2.122385,1.848882 4.977891,2.930875 7.807987,2.830528 1.339735,-0.04757 2.658059,-0.322528 3.893601,-0.846686 1.211838,-0.514267 2.266804,-1.259589 3.252459,-2.118721 2.388721,-2.082309 3.903991,-5.148567 3.903997,-8.338884 -6e-6,-1.474088 -0.285698,-2.969065 -0.850951,-4.33266 C 59.018859,8.5341949 58.353854,7.4517779 57.507,6.4802719 57.210847,6.1404099 56.855617,5.8553679 56.527395,5.5501239 54.903004,4.0396299 52.810791,3.050148 50.701625,2.423932 47.888576,1.5886719 44.961704,1.4098089 42.075251,0.97294691 40.923346,0.79859691 39.77434,0.60361695 38.631902,0.37513 38.253949,0.2995 37.874627,0.228631 37.498412,0.144513 37.274938,0.094433 37.035841,0.015629 36.807403,0 Z"
id="path1737"
inkscape:label="1"
style="fill:url(#linearGradient886);fill-opacity:1" /></g></g></svg>

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -0,0 +1,68 @@
<svg width="88" height="88" viewBox="0 0 88 88" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3.22175" y="22.8962" width="80.5565" height="41.2076" rx="16.9634" fill="url(#paint0_radial_1823_1834)"/>
<rect x="3.22175" y="22.8962" width="80.5565" height="41.2076" rx="16.9634" stroke="url(#paint1_radial_1823_1834)" stroke-width="5.79238"/>
<g filter="url(#filter0_i_1823_1834)">
<circle cx="21.5778" cy="43.5" r="7.65116" fill="url(#paint2_linear_1823_1834)"/>
</g>
<g filter="url(#filter1_i_1823_1834)">
<circle cx="43.5" cy="43.5" r="7.65116" fill="url(#paint3_linear_1823_1834)"/>
</g>
<g filter="url(#filter2_i_1823_1834)">
<circle cx="65.4222" cy="43.5" r="7.65116" fill="url(#paint4_linear_1823_1834)"/>
</g>
<defs>
<filter id="filter0_i_1823_1834" x="12.2717" y="34.1939" width="16.9573" height="16.9573" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="-1.65497" dy="-1.65497"/>
<feGaussianBlur stdDeviation="1.65497"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0730794 0 0 0 0 0.610693 0 0 0 0 1 0 0 0 0.5 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_1823_1834"/>
</filter>
<filter id="filter1_i_1823_1834" x="34.1939" y="34.1939" width="16.9573" height="16.9573" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="-1.65497" dy="-1.65497"/>
<feGaussianBlur stdDeviation="1.65497"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0730794 0 0 0 0 0.610693 0 0 0 0 1 0 0 0 0.5 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_1823_1834"/>
</filter>
<filter id="filter2_i_1823_1834" x="56.116" y="34.1939" width="16.9573" height="16.9573" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="-1.65497" dy="-1.65497"/>
<feGaussianBlur stdDeviation="1.65497"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0730794 0 0 0 0 0.610693 0 0 0 0 1 0 0 0 0.5 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_1823_1834"/>
</filter>
<radialGradient id="paint0_radial_1823_1834" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(34.2989 35.4098) rotate(28.1037) scale(42.5262 35.3481)">
<stop stop-color="#0085E6"/>
<stop offset="1" stop-color="#003BD4"/>
</radialGradient>
<radialGradient id="paint1_radial_1823_1834" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(83.5263 50.3542) rotate(-166.627) scale(80.4347 49.3444)">
<stop stop-color="#2F76FF"/>
<stop offset="1" stop-color="#00F0FF"/>
</radialGradient>
<linearGradient id="paint2_linear_1823_1834" x1="25.7227" y1="34.5989" x2="33.3607" y2="56.6579" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="0.615" stop-color="#00B3FF" stop-opacity="0.455"/>
<stop offset="1" stop-color="#1D84DA" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint3_linear_1823_1834" x1="47.6449" y1="34.5989" x2="55.2828" y2="56.6579" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="0.615" stop-color="#00B3FF" stop-opacity="0.455"/>
<stop offset="1" stop-color="#1D84DA" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint4_linear_1823_1834" x1="69.5671" y1="34.5989" x2="77.205" y2="56.6579" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="0.615" stop-color="#00B3FF" stop-opacity="0.455"/>
<stop offset="1" stop-color="#1D84DA" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

@ -0,0 +1,24 @@
<svg width="88" height="88" viewBox="0 0 88 88" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1824_23)">
<path d="M30.6529 48.4957C42.4614 48.4149 36.7708 73.354 78.05 37.297C81.3825 34.3861 83.0488 32.9306 83.0703 32.4895C83.0918 32.0484 81.7097 30.5803 78.9454 27.6441C74.9936 23.4465 67.4717 19.6656 52.5343 29.3582C22.489 48.854 43.7731 16.11 14.8695 30.557C-8.76737 42.3715 6.27442 60.5648 12.8474 61.5578C14.312 61.7791 10.6977 59.9456 11.7726 56.7211C14.2839 49.187 18.3871 48.5796 30.6529 48.4957Z" fill="url(#paint0_radial_1824_23)"/>
<circle cx="46.4304" cy="45.8792" r="27.0079" fill="url(#paint1_radial_1824_23)"/>
<path d="M57.195 37.8283C45.3805 37.9091 46.9205 34.8742 5.26469 72.6271C4.64135 73.192 4.32968 73.4745 4.85261 74.2244C5.37553 74.9743 5.71333 74.7978 6.38893 74.4447C11.7349 71.6509 20.8663 66.3404 35.3137 56.9657C65.3589 37.47 44.0748 70.214 72.9784 55.767C96.6153 43.9525 81.5735 25.7591 75.0006 24.7661C73.536 24.5449 77.1502 26.3784 76.0754 29.6029C73.5641 37.1369 69.4608 37.7444 57.195 37.8283Z" fill="url(#paint2_radial_1824_23)"/>
</g>
<defs>
<radialGradient id="paint0_radial_1824_23" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(38.6435 26.0881) rotate(135.659) scale(33.0633 23.4711)">
<stop stop-color="#5EFFFF"/>
<stop offset="1" stop-color="#014FE6"/>
</radialGradient>
<radialGradient id="paint1_radial_1824_23" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(28.6052 26.9737) rotate(22.8645) scale(48.6562 48.6492)">
<stop offset="0.57" stop-color="#00E6CF"/>
<stop offset="1" stop-color="#014FE6"/>
</radialGradient>
<radialGradient id="paint2_radial_1824_23" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(49.2044 60.2358) rotate(-44.3415) scale(33.0633 23.4711)">
<stop stop-color="#9CEDFF"/>
<stop offset="1" stop-color="#014FE6"/>
</radialGradient>
<clipPath id="clip0_1824_23">
<rect width="88" height="88" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,29 @@
<svg width="88" height="88" viewBox="0 0 88 88" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="68" height="68" rx="12.75" fill="url(#paint0_radial_1824_34)"/>
<rect x="14.25" y="73.75" width="59.5" height="59.5" rx="12.75" transform="rotate(-90 14.25 73.75)" fill="url(#paint1_radial_1824_34)"/>
<rect x="18.5" y="63.125" width="38.25" height="51" rx="8.5" transform="rotate(-90 18.5 63.125)" fill="url(#paint2_linear_1824_34)"/>
<rect x="22.75" y="58.875" width="29.75" height="42.5" rx="8.5" transform="rotate(-90 22.75 58.875)" fill="url(#paint3_linear_1824_34)"/>
<rect x="25.9375" y="54.625" width="21.25" height="36.125" rx="6.375" transform="rotate(-90 25.9375 54.625)" fill="url(#paint4_linear_1824_34)"/>
<defs>
<radialGradient id="paint0_radial_1824_34" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(12.6562 11.0625) rotate(45.7192) scale(89.7875 76.8117)">
<stop offset="0.45" stop-color="#00BCDA"/>
<stop offset="0.855" stop-color="#014FE6"/>
</radialGradient>
<radialGradient id="paint1_radial_1824_34" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(18.5 127.406) rotate(-42.8789) scale(71.0466 65.0439)">
<stop offset="0.23" stop-color="#017FDA"/>
<stop offset="0.87" stop-color="#014FE6"/>
</radialGradient>
<linearGradient id="paint2_linear_1824_34" x1="37.625" y1="63.125" x2="37.625" y2="114.125" gradientUnits="userSpaceOnUse">
<stop stop-color="#5EE9FF"/>
<stop offset="1" stop-color="#014FE6"/>
</linearGradient>
<linearGradient id="paint3_linear_1824_34" x1="25.8125" y1="83.8207" x2="51.3741" y2="63.9353" gradientUnits="userSpaceOnUse">
<stop stop-color="#53E8FF"/>
<stop offset="1" stop-color="#014FE6"/>
</linearGradient>
<linearGradient id="paint4_linear_1824_34" x1="25.9375" y1="66.2174" x2="47.1875" y2="66.2174" gradientUnits="userSpaceOnUse">
<stop stop-color="#0084E3"/>
<stop offset="1" stop-color="#014FE6"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -1,7 +1,7 @@
import adapter from '@sveltejs/adapter-static' import adapter from '@sveltejs/adapter-static'
import { vitePreprocess } from '@sveltejs/kit/vite' import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
import { mdsvex, escapeSvelte } from 'mdsvex' import { mdsvex, escapeSvelte } from 'mdsvex'
import shiki from 'shiki' import {getHighlighter} from 'shiki'
import remarkUnwrapImages from 'remark-unwrap-images' import remarkUnwrapImages from 'remark-unwrap-images'
import rehypeSlug from 'rehype-slug' import rehypeSlug from 'rehype-slug'
@ -10,7 +10,7 @@ const mdsvexOptions = {
extensions: ['.md'], extensions: ['.md'],
highlight: { highlight: {
highlighter: async (code, lang = 'text') => { highlighter: async (code, lang = 'text') => {
const highlighter = await shiki.getHighlighter({ theme: 'github-dark' }) const highlighter = await getHighlighter({ theme: 'github-dark' })
const html = escapeSvelte(highlighter.codeToHtml(code, { lang })) const html = escapeSvelte(highlighter.codeToHtml(code, { lang }))
return `{@html \`${html}\` }` return `{@html \`${html}\` }`
} }

View file

@ -6,7 +6,7 @@ export default {
content: ['./src/**/**/*.{html,js,svelte,ts}'], content: ['./src/**/**/*.{html,js,svelte,ts}'],
theme: { theme: {
extend: { extend: {
colors: { black: '#0b0d0e', primary: '#58E1FF', secondary: '#00A2F8' }, colors: { black: '#090b0c', primary: '#58E1FF', secondary: '#00A2F8' },
fontFamily: { fontFamily: {
...fontFamily, ...fontFamily,
sans: ['Work Sans Variable', ...fontFamily['sans']], sans: ['Work Sans Variable', ...fontFamily['sans']],
@ -17,6 +17,9 @@ export default {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))' 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))'
}, },
margin: { navbar: '3.5rem' }, margin: { navbar: '3.5rem' },
screens: { '3xl': '2560px' },
typography: { typography: {
DEFAULT: { DEFAULT: {
css: { css: {
@ -37,7 +40,11 @@ export default {
} }
} }
}, },
plugins: [require('tailwindcss-animate'), require('@tailwindcss/typography')], plugins: [
require('tailwindcss-animate'),
require('@tailwindcss/typography'),
require('@tailwindcss/container-queries')
],
safelist: [ safelist: [
'animate-bounce', 'animate-bounce',
'outline-amber-500', 'outline-amber-500',