mirror of
https://github.com/hyprwm/hyprland-website.git
synced 2024-12-23 02:39:48 +01:00
add plugins section
This commit is contained in:
parent
e61b27c376
commit
34ebb30e62
9 changed files with 287 additions and 28 deletions
|
@ -1,6 +1,7 @@
|
||||||
/* eslint-disable no-useless-escape */
|
/* eslint-disable no-useless-escape */
|
||||||
import { inview } from 'svelte-inview'
|
import { inview } from 'svelte-inview'
|
||||||
import { pick } from 'remeda'
|
import { pick } from 'remeda'
|
||||||
|
import { Observable, debounceTime, share, startWith, throttleTime } from 'rxjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fade: The initial opacity from 0 to 1.
|
* Fade: The initial opacity from 0 to 1.
|
||||||
|
@ -100,3 +101,16 @@ export function getBlurredPath(path) {
|
||||||
export function getRandom(array) {
|
export function getRandom(array) {
|
||||||
return array.at(Math.floor(Math.random() * array.length))
|
return array.at(Math.floor(Math.random() * array.length))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fps = 1000 / 60 // 60 frames per second
|
||||||
|
export const mousePosition$ = new Observable((subscriber) => {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
if (globalThis.document === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
document.addEventListener('mousemove', nextPostion)
|
||||||
|
function nextPostion({ clientX, clientY }) {
|
||||||
|
subscriber.next({ clientX, clientY })
|
||||||
|
}
|
||||||
|
return () => document?.removeEventListener('mousemove', nextPostion)
|
||||||
|
}).pipe(throttleTime(fps), share(), startWith({ clientX: 0, clientY: 0 }))
|
||||||
|
|
|
@ -1,40 +1,78 @@
|
||||||
<script>
|
<script>
|
||||||
import { BehaviorSubject, Subject, map, startWith, throttle, throttleTime } from 'rxjs'
|
import {
|
||||||
import { onDestroy } from 'svelte'
|
BehaviorSubject,
|
||||||
|
combineLatest,
|
||||||
|
debounceTime,
|
||||||
|
delay,
|
||||||
|
distinctUntilChanged,
|
||||||
|
filter,
|
||||||
|
map,
|
||||||
|
of,
|
||||||
|
startWith,
|
||||||
|
switchMap,
|
||||||
|
timer
|
||||||
|
} from 'rxjs'
|
||||||
|
import { onDestroy, onMount } from 'svelte'
|
||||||
import { spring } from 'svelte/motion'
|
import { spring } from 'svelte/motion'
|
||||||
|
import { mousePosition$ } from './Helper.mjs'
|
||||||
|
|
||||||
|
/** The start position of the gradient. */
|
||||||
|
export let startPosition = [-1000, -1000]
|
||||||
|
|
||||||
/** @type {HTMLDivElement}*/
|
/** @type {HTMLDivElement}*/
|
||||||
let wrapperElement = undefined
|
let wrapperElement = undefined
|
||||||
let isMouseOver = false
|
/** @type {import('rxjs').BehaviorSubject<boolean>}*/
|
||||||
|
const isMouseOver$ = new BehaviorSubject(false).pipe(
|
||||||
|
switchMap((isTrue) => {
|
||||||
|
// Prevent elements over the background to disable the movement of the gradient, as the mouse will not be over the background anymore
|
||||||
|
return isTrue ? of(isTrue) : timer(5500).pipe(map(() => false))
|
||||||
|
}),
|
||||||
|
distinctUntilChanged()
|
||||||
|
)
|
||||||
|
|
||||||
const gradientSize = 240
|
/** @type {import('rxjs').BehaviorSubject<number>}*/
|
||||||
|
const gradientSize$ = new BehaviorSubject().pipe(
|
||||||
|
// Debounce resize events with some high number for performance
|
||||||
|
debounceTime(1),
|
||||||
|
map(() => wrapperElement.getBoundingClientRect().width * 3),
|
||||||
|
startWith(800)
|
||||||
|
)
|
||||||
|
|
||||||
const fps = 1000 / 60 // 60 frames per second
|
const gradientPosition$ = combineLatest([mousePosition$, gradientSize$, isMouseOver$]).pipe(
|
||||||
/** @type {import('rxjs').Subject<[number,number]>}*/
|
filter(([_, __, isMouseOver]) => isMouseOver),
|
||||||
const mouse$ = new Subject()
|
map(([{ clientX, clientY }, gradientSize]) => {
|
||||||
const gradientPosition$ = mouse$.pipe(
|
const { x, y } = wrapperElement?.getBoundingClientRect() ?? { x: 0, y: 0 }
|
||||||
throttleTime(fps),
|
|
||||||
map(([clientX, clientY]) => {
|
|
||||||
const { x, y } = wrapperElement.getBoundingClientRect()
|
|
||||||
return [clientX - x - gradientSize * 0.5, clientY - y - gradientSize * 0.5]
|
return [clientX - x - gradientSize * 0.5, clientY - y - gradientSize * 0.5]
|
||||||
}),
|
}),
|
||||||
startWith([0, 0])
|
startWith(startPosition)
|
||||||
)
|
)
|
||||||
const gradientWiggle = spring([0, 0])
|
const gradientWiggle = spring(startPosition, { damping: 0.95, stiffness: 0.1 })
|
||||||
const subscription = gradientPosition$.subscribe((data) => gradientWiggle.set(data))
|
const subscription = gradientPosition$.subscribe((data) => gradientWiggle.set(data))
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
subscription.unsubscribe()
|
subscription.unsubscribe()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let hasJustMounted = true
|
||||||
|
onMount(() => {
|
||||||
|
resizeGradient()
|
||||||
|
|
||||||
|
hasJustMounted = false
|
||||||
|
})
|
||||||
|
|
||||||
|
function resizeGradient() {
|
||||||
|
if (hasJustMounted || !isMouseOver$) return
|
||||||
|
|
||||||
|
gradientSize$.next()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:resize={resizeGradient} />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class={$$props.class + ' wrapper'}
|
class={$$props.class + ' wrapper'}
|
||||||
on:mouseleave={() => (isMouseOver = false)}
|
on:mouseleave={() => isMouseOver$.next(false)}
|
||||||
on:mousemove={({ clientX, clientY }) => {
|
on:mousemove={() => isMouseOver$.next(true)}
|
||||||
isMouseOver = true
|
|
||||||
mouse$.next([clientX, clientY])
|
|
||||||
}}
|
|
||||||
aria-hidden
|
aria-hidden
|
||||||
bind:this={wrapperElement}
|
bind:this={wrapperElement}
|
||||||
>
|
>
|
||||||
|
@ -42,8 +80,7 @@
|
||||||
class="gradient"
|
class="gradient"
|
||||||
style:--x={$gradientWiggle.at(0) + 'px'}
|
style:--x={$gradientWiggle.at(0) + 'px'}
|
||||||
style:--y={$gradientWiggle.at(1) + 'px'}
|
style:--y={$gradientWiggle.at(1) + 'px'}
|
||||||
style:--size={gradientSize + 'px'}
|
style:--size={$gradientSize$ + 'px'}
|
||||||
class:hidden={!isMouseOver}
|
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
@ -51,11 +88,11 @@
|
||||||
id="background-pattern-id"
|
id="background-pattern-id"
|
||||||
x="0"
|
x="0"
|
||||||
y="0"
|
y="0"
|
||||||
width="32"
|
width="30"
|
||||||
height="32"
|
height="30"
|
||||||
patternUnits="userSpaceOnUse"
|
patternUnits="userSpaceOnUse"
|
||||||
>
|
>
|
||||||
<rect x="0.5" y="0.5" width="23" height="23" rx="3.5" stroke="currentColor" />
|
<rect x="0.5" y="0.5" width="30" height="30" rx="0" stroke="currentColor" />
|
||||||
</pattern>
|
</pattern>
|
||||||
|
|
||||||
<rect
|
<rect
|
||||||
|
@ -72,14 +109,12 @@
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.wrapper {
|
.wrapper {
|
||||||
/* mask-image: radial-gradient(closest-side, black 50%, transparent); */
|
mask-image: linear-gradient(black 75%, transparent);
|
||||||
/* contain: strict; */
|
contain: strict;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
svg {
|
svg {
|
||||||
background: theme(colors.black);
|
background: theme(colors.black);
|
||||||
/* background: theme(colors.black); */
|
|
||||||
/* background-blend-mode: difference; */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gradient {
|
.gradient {
|
||||||
|
@ -89,7 +124,12 @@
|
||||||
mix-blend-mode: color-dodge;
|
mix-blend-mode: color-dodge;
|
||||||
height: var(--size);
|
height: var(--size);
|
||||||
width: var(--size);
|
width: var(--size);
|
||||||
background: radial-gradient(closest-side, theme(colors.cyan.700), transparent);
|
background: radial-gradient(
|
||||||
|
closest-side,
|
||||||
|
theme(colors.cyan.300),
|
||||||
|
theme(colors.blue.950 / 100%) 30%,
|
||||||
|
transparent
|
||||||
|
);
|
||||||
opacity: 100%;
|
opacity: 100%;
|
||||||
translate: var(--x) var(--y);
|
translate: var(--x) var(--y);
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
|
|
76
src/lib/components/Video.svelte
Normal file
76
src/lib/components/Video.svelte
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<script>
|
||||||
|
import clsx from 'clsx'
|
||||||
|
import PlayIcon from '~icons/mingcute/play-circle-line'
|
||||||
|
import { inview } from 'svelte-inview'
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let src
|
||||||
|
/** @type {string} */
|
||||||
|
export let poster
|
||||||
|
export let loop = true
|
||||||
|
export let muted = true
|
||||||
|
export let autoplay = false
|
||||||
|
export let hidden = false
|
||||||
|
/** @type {string}*/
|
||||||
|
export let videoClass = ''
|
||||||
|
let videoElement
|
||||||
|
let isPaused = !autoplay
|
||||||
|
|
||||||
|
function togglePlay() {
|
||||||
|
videoElement.paused ? videoElement.play() : videoElement.pause()
|
||||||
|
isPaused = videoElement.paused
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeFullscreen() {
|
||||||
|
videoElement.requestFullscreen()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="wrapper {$$props.class}"
|
||||||
|
role="banner"
|
||||||
|
use:inview={poster}
|
||||||
|
on:inview_enter
|
||||||
|
on:inview_leave
|
||||||
|
{hidden}
|
||||||
|
>
|
||||||
|
<video
|
||||||
|
bind:this={videoElement}
|
||||||
|
{src}
|
||||||
|
{muted}
|
||||||
|
disablepictureinpicture="true"
|
||||||
|
disableremoteplayback="true"
|
||||||
|
class="rounded-xl {videoClass}"
|
||||||
|
{loop}
|
||||||
|
preload="auto"
|
||||||
|
{poster}
|
||||||
|
on:click={togglePlay}
|
||||||
|
on:dblclick={makeFullscreen}
|
||||||
|
{autoplay}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class={clsx(
|
||||||
|
'z-20 opacity-0 transition-opacity ',
|
||||||
|
isPaused ? 'opacity-100' : 'pointer-events-none'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{#if isPaused}
|
||||||
|
<div
|
||||||
|
class="pointer-events-none absolute left-1/2 top-1/2 h-14 w-14 -translate-x-1/2 -translate-y-1/2 rounded-full opacity-80 hover:opacity-100"
|
||||||
|
>
|
||||||
|
<PlayIcon class="h-full w-full" />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="postcss">
|
||||||
|
.wrapper {
|
||||||
|
position: relative;
|
||||||
|
background: theme(colors.black);
|
||||||
|
}
|
||||||
|
|
||||||
|
video {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -5,6 +5,7 @@
|
||||||
import Hero from './Hero.svelte'
|
import Hero from './Hero.svelte'
|
||||||
import InstallSlice from './InstallSlice.svelte'
|
import InstallSlice from './InstallSlice.svelte'
|
||||||
import PreviewRiceSlice from './PreviewRiceSlice.svelte'
|
import PreviewRiceSlice from './PreviewRiceSlice.svelte'
|
||||||
|
import PluginsSlice from './PluginsSlice.svelte'
|
||||||
|
|
||||||
export let data
|
export let data
|
||||||
</script>
|
</script>
|
||||||
|
@ -21,6 +22,8 @@
|
||||||
|
|
||||||
<FeaturesSlice />
|
<FeaturesSlice />
|
||||||
|
|
||||||
|
<PluginsSlice />
|
||||||
|
|
||||||
<WallOfFameSlice />
|
<WallOfFameSlice />
|
||||||
|
|
||||||
<Community />
|
<Community />
|
||||||
|
|
126
src/routes/PluginsSlice.svelte
Normal file
126
src/routes/PluginsSlice.svelte
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
<script>
|
||||||
|
import PatternBackground from '$lib/PatternBackground.svelte'
|
||||||
|
import IconPlugin from '~icons/mingcute/plugin-2-line'
|
||||||
|
import IconIpc from '~icons/mingcute/hexagon-line'
|
||||||
|
import IconLinkOut from '~icons/mingcute/external-link-line'
|
||||||
|
import clsx from 'clsx'
|
||||||
|
import Video from '$lib/components/Video.svelte'
|
||||||
|
import { fade } from 'svelte/transition'
|
||||||
|
|
||||||
|
let activeIndex = 0
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
icon: IconPlugin,
|
||||||
|
title: 'Plugins.',
|
||||||
|
description:
|
||||||
|
'Customize everything with official and community extensions. Write your own easily with C++',
|
||||||
|
poster: '/videos/hypr_plugins_thumb.webp',
|
||||||
|
src: '/videos/outfoxxed.mp4'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: IconIpc,
|
||||||
|
title: 'Bindings and IPC.',
|
||||||
|
description: 'Control your desktop with your favourite languages or simply via IPC.',
|
||||||
|
poster: '/videos/aylur_thumb.png',
|
||||||
|
src: '/videos/aylur.mp4'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
function setActiveItem(index) {
|
||||||
|
activeIndex = index
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section class="relative z-0 flex min-h-max w-full flex-col items-center py-20">
|
||||||
|
<div class="mx-auto grid max-w-7xl grid-cols-1 gap-12 px-6 lg:grid-cols-2 lg:gap-24">
|
||||||
|
<div class="z-10 flex flex-col gap-10 px-6">
|
||||||
|
<div class="txt-shadow_ mt-8 flex flex-col gap-6">
|
||||||
|
<h2 class=" text-6xl font-bold">Unlock full power</h2>
|
||||||
|
<p class="text-lg font-bold text-slate-300">
|
||||||
|
Get the latest features Linux offers. Have full controll over your workflow by customizing
|
||||||
|
and extending it how you want.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex h-full flex-col gap-4">
|
||||||
|
{#each items as { icon, title, description }, index}
|
||||||
|
{@const isActive = index === activeIndex}
|
||||||
|
<button
|
||||||
|
class={clsx(
|
||||||
|
'flex gap-3 rounded-xl px-4 py-4 outline-0 outline-cyan-400/50 transition-all sm:-ml-4',
|
||||||
|
isActive && 'bg-blue-300/5 shadow-md outline outline-1 backdrop-blur-sm '
|
||||||
|
)}
|
||||||
|
on:mouseenter={() => setActiveItem(index)}
|
||||||
|
>
|
||||||
|
<svelte:component this={icon} class="h-8 w-8 shrink-0 text-primary" />
|
||||||
|
<p
|
||||||
|
class={clsx(
|
||||||
|
'txt-shadow_ text-left text-lg font-medium transition-colors ',
|
||||||
|
isActive ? 'text-slate-300' : 'text-slate-400'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span class="font-bold text-white">{title}</span>
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="-mt-5 hidden gap-1 lg:mt-12 lg:flex lg:flex-col">
|
||||||
|
<a
|
||||||
|
class="txt-shadow_ flex w-max max-w-max shrink-0 items-center gap-3 rounded font-bold text-slate-400 hover:underline"
|
||||||
|
href="https://github.com/hyprland-community/awesome-hyprland"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
Checkout <span class="text-cyan-500">Awesome Hyprland</span>
|
||||||
|
for more
|
||||||
|
</div>
|
||||||
|
<IconLinkOut />
|
||||||
|
</a>
|
||||||
|
<p class="font-medium text-slate-400">
|
||||||
|
A list of plugins, bindings and more by the community
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Prevent the video from making the container big on small phones. 300px seem to work well for the text -->
|
||||||
|
<div class="z-10 h-[20rem] min-w-0 sm:h-[25rem] md:h-[30rem] lg:h-[37rem]">
|
||||||
|
{#each items as { src, poster }, index}
|
||||||
|
<Video
|
||||||
|
{src}
|
||||||
|
{poster}
|
||||||
|
autoplay
|
||||||
|
class="z-10 aspect-video h-[inherit] origin-left rounded-lg object-cover object-left shadow-xl shadow-cyan-700/50 outline outline-2 outline-cyan-500 duration-500"
|
||||||
|
hidden={index !== activeIndex}
|
||||||
|
videoClass="h-[inherit] aspect-video"
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
<div
|
||||||
|
class="pt-5 text-sm font-medium text-slate-300 md:text-base [&>a:hover]:text-cyan-300 [&>a:hover]:underline [&>a]:font-bold"
|
||||||
|
>
|
||||||
|
{#if activeIndex === 0}
|
||||||
|
Setup with <a href="https://github.com/outfoxxed/hy3" target="_blank"> hy3</a>, by
|
||||||
|
<a href="https://github.com/outfoxxed/" target="_blank">Outfoxxed</a>, creator of hy3: i3
|
||||||
|
tiling for Hyprland. Other used plugins:
|
||||||
|
<a href="https://github.com/hyprwm/hyprland-plugins" target="_blank">Hyprtrails</a>,
|
||||||
|
<a href="https://github.com/hyprwm/hyprland-plugins" target="_blank">Hyprborders</a>
|
||||||
|
{:else if activeIndex === 1}
|
||||||
|
Setup by <a href="https://github.com/Aylur/dotfiles" target="_blank">Aylur</a>, using
|
||||||
|
<a href="https://github.com/Aylur/ags" target="_blank">Ags</a> to control Hyprland via IPC.
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<PatternBackground class="absolute inset-0 h-[110%] w-full text-slate-800 opacity-40" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style lang="postcss">
|
||||||
|
.txt-shadow_ {
|
||||||
|
text-shadow:
|
||||||
|
0px 0px 12px theme(colors.black / 90%),
|
||||||
|
0px 0px 24px theme(colors.black / 50%);
|
||||||
|
}
|
||||||
|
</style>
|
BIN
static/videos/aylur.mp4
Normal file
BIN
static/videos/aylur.mp4
Normal file
Binary file not shown.
BIN
static/videos/aylur_thumb.png
Normal file
BIN
static/videos/aylur_thumb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 859 KiB |
BIN
static/videos/hypr_plugins_thumb.webp
Normal file
BIN
static/videos/hypr_plugins_thumb.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
BIN
static/videos/outfoxxed.mp4
Normal file
BIN
static/videos/outfoxxed.mp4
Normal file
Binary file not shown.
Loading…
Reference in a new issue