add plugins section

This commit is contained in:
VDawg 2023-11-13 21:03:30 +01:00
parent e61b27c376
commit 34ebb30e62
9 changed files with 287 additions and 28 deletions

View file

@ -1,6 +1,7 @@
/* eslint-disable no-useless-escape */
import { inview } from 'svelte-inview'
import { pick } from 'remeda'
import { Observable, debounceTime, share, startWith, throttleTime } from 'rxjs'
/**
* Fade: The initial opacity from 0 to 1.
@ -100,3 +101,16 @@ export function getBlurredPath(path) {
export function getRandom(array) {
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 }))

View file

@ -1,40 +1,78 @@
<script>
import { BehaviorSubject, Subject, map, startWith, throttle, throttleTime } from 'rxjs'
import { onDestroy } from 'svelte'
import {
BehaviorSubject,
combineLatest,
debounceTime,
delay,
distinctUntilChanged,
filter,
map,
of,
startWith,
switchMap,
timer
} from 'rxjs'
import { onDestroy, onMount } from 'svelte'
import { spring } from 'svelte/motion'
import { mousePosition$ } from './Helper.mjs'
/** The start position of the gradient. */
export let startPosition = [-1000, -1000]
/** @type {HTMLDivElement}*/
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
/** @type {import('rxjs').Subject<[number,number]>}*/
const mouse$ = new Subject()
const gradientPosition$ = mouse$.pipe(
throttleTime(fps),
map(([clientX, clientY]) => {
const { x, y } = wrapperElement.getBoundingClientRect()
const gradientPosition$ = combineLatest([mousePosition$, gradientSize$, isMouseOver$]).pipe(
filter(([_, __, isMouseOver]) => isMouseOver),
map(([{ clientX, clientY }, gradientSize]) => {
const { x, y } = wrapperElement?.getBoundingClientRect() ?? { x: 0, y: 0 }
return [clientX - x - gradientSize * 0.5, clientY - y - gradientSize * 0.5]
}),
startWith([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))
onDestroy(() => {
subscription.unsubscribe()
})
let hasJustMounted = true
onMount(() => {
resizeGradient()
hasJustMounted = false
})
function resizeGradient() {
if (hasJustMounted || !isMouseOver$) return
gradientSize$.next()
}
</script>
<svelte:window on:resize={resizeGradient} />
<div
class={$$props.class + ' wrapper'}
on:mouseleave={() => (isMouseOver = false)}
on:mousemove={({ clientX, clientY }) => {
isMouseOver = true
mouse$.next([clientX, clientY])
}}
on:mouseleave={() => isMouseOver$.next(false)}
on:mousemove={() => isMouseOver$.next(true)}
aria-hidden
bind:this={wrapperElement}
>
@ -42,8 +80,7 @@
class="gradient"
style:--x={$gradientWiggle.at(0) + 'px'}
style:--y={$gradientWiggle.at(1) + 'px'}
style:--size={gradientSize + 'px'}
class:hidden={!isMouseOver}
style:--size={$gradientSize$ + 'px'}
></div>
<svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg">
@ -51,11 +88,11 @@
id="background-pattern-id"
x="0"
y="0"
width="32"
height="32"
width="30"
height="30"
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>
<rect
@ -72,14 +109,12 @@
<style lang="postcss">
.wrapper {
/* mask-image: radial-gradient(closest-side, black 50%, transparent); */
/* contain: strict; */
mask-image: linear-gradient(black 75%, transparent);
contain: strict;
user-select: none;
}
svg {
background: theme(colors.black);
/* background: theme(colors.black); */
/* background-blend-mode: difference; */
}
.gradient {
@ -89,7 +124,12 @@
mix-blend-mode: color-dodge;
height: 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%;
translate: var(--x) var(--y);
z-index: 20;

View 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>

View file

@ -5,6 +5,7 @@
import Hero from './Hero.svelte'
import InstallSlice from './InstallSlice.svelte'
import PreviewRiceSlice from './PreviewRiceSlice.svelte'
import PluginsSlice from './PluginsSlice.svelte'
export let data
</script>
@ -21,6 +22,8 @@
<FeaturesSlice />
<PluginsSlice />
<WallOfFameSlice />
<Community />

View 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

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
static/videos/outfoxxed.mp4 Normal file

Binary file not shown.