Add Plugins submit button (#76)

* plugins.toml: improve text

* plugins: add cta for plugin submission, use more TS

* plugins: relabel submit pluign button

* improve plugins.toml instructions
This commit is contained in:
VDawg 2024-10-17 17:03:09 +03:00 committed by GitHub
parent 5934c40d93
commit 24b9b73de5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 138 additions and 89 deletions

View file

@ -63,6 +63,8 @@
"shiki": "^1.7.0",
"smol-toml": "^1.3.0",
"svelte-inview": "^4.0.2",
"tailwind-merge": "^2.5.4",
"ts-pattern": "^5.2.0"
}
},
"packageManager": "pnpm@9.12.1+sha512.e5a7e52a4183a02d5931057f7a0dbff9d5e9ce3161e33fa68ae392125b79282a8a8a470a51dfc8a0ed86221442eb2fb57019b0990ed24fab519bf0e1bc5ccfc4"
}

View file

@ -53,6 +53,9 @@ importers:
svelte-inview:
specifier: ^4.0.2
version: 4.0.2(svelte@4.2.18)
tailwind-merge:
specifier: ^2.5.4
version: 2.5.4
ts-pattern:
specifier: ^5.2.0
version: 5.2.0
@ -1541,6 +1544,9 @@ packages:
resolution: {integrity: sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==}
engines: {node: '>=16'}
tailwind-merge@2.5.4:
resolution: {integrity: sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==}
tailwindcss-animate@1.0.7:
resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==}
peerDependencies:
@ -3036,6 +3042,8 @@ snapshots:
magic-string: 0.30.5
periscopic: 3.1.0
tailwind-merge@2.5.4: {}
tailwindcss-animate@1.0.7(tailwindcss@3.4.4):
dependencies:
tailwindcss: 3.4.4

View file

@ -2,7 +2,6 @@
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)
@ -89,3 +88,8 @@ function exec(command) {
throw new Error(error)
}
}
/** Get the filename of a filepath without its extension */
export function getFileNameWithoutExtension(filePath) {
return filePath.split('/').at(-1)?.replace(/\..*$/, '')
}

View file

@ -1,13 +1,15 @@
#############################################
# To submit a plugin add a new entry to this file.
#############################################
# Structure:
# - name: Name of the plugin
# - tagline: Very concise description of the plugin
# - url: Link to the Github repository
# - logo: Relative link to the logo placed in the `/static/plugins-data/logos/` directory (without the `/static/` though)
# - url: Link to the Git repository/website
# - logo (optional): 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.
# Please only use darkmode images/videos.
# Do not use yellow and yellowish colors, unless nesssecary.
# - featured (optional): Whether the plugin is featured at the top. A maximum of 4 are shown
# - weight (optional): Determines the sort order. A higher weight comes first.
[[plugins]]

View file

@ -17,6 +17,9 @@ import {
import { inview } from 'svelte-inview'
import { pick } from 'remeda'
import { writable } from 'svelte/store'
import type { ClassValue } from 'clsx'
import clsx from 'clsx'
import { twMerge } from 'tailwind-merge'
/**
* Fade: The initial opacity from 0 to 1.
@ -24,12 +27,18 @@ import { writable } from 'svelte/store'
* Zoom: The scale from 0 to 1.
*
* Slide: Slide in in pixels.
*
* @param {{fade?: number, zoom?: number, slide?: number, duration?: number, delay?: number, threshold?: number}} options
* @param { HTMLElement } node
* @returns
*/
export function animateIn(node, options = {}) {
export function animateIn(
node: HTMLElement,
options: {
fade?: number
zoom?: number
slide?: number
duration?: number
delay?: number
threshold?: number
} = {}
) {
// Do nothing on mobile
if (getIsMobile()) return { destroy: () => undefined }
@ -53,9 +62,10 @@ export function animateIn(node, options = {}) {
})
.join(';')
// @ts-ignore
node.style = style
let timeoutId
let timeoutId: NodeJS.Timeout
node.addEventListener('inview_enter', callback)
@ -85,14 +95,14 @@ export function animateIn(node, options = {}) {
* @param {number} given
* @returns
*/
export function lerp(start, end, given) {
export function lerp(start: number, end: number, given: number) {
return (1 - given) * start + given * end
}
/**
* Taken from https://stackoverflow.com/questions/11381673/detecting-a-mobile-browser/11381730#11381730
*/
export function getIsMobile() {
export function getIsMobile(): boolean {
let check = false
;(function (a) {
if (
@ -104,23 +114,28 @@ export function getIsMobile() {
)
)
check = true
// @ts-expect-error
})(navigator.userAgent || navigator.vendor || window.opera)
return check
}
/** Get the `generated_<filename>` for the provided path **/
export function getGeneratedPath(path, extension = 'webp') {
export function getGeneratedPath(path: string, extension: string = 'webp') {
const directory = path.substring(0, path.lastIndexOf('/'))
const filename = getFileNameWithoutExtension(path)
return `${directory}/generated_${filename}.${extension}`
}
/** Get a random item from an array */
export function getRandom(array) {
return array.at(Math.floor(Math.random() * array.length))
export function getRandom<T>(array: T[]): T {
return array.at(Math.floor(Math.random() * array.length))!
}
export function formatDate(date, dateStyle = 'long', locales = 'en') {
export function formatDate(
date: string,
dateStyle: 'full' | 'long' | 'medium' | 'short' = 'long',
locales = 'en'
) {
const dateToFormat = new Date(date)
const dateFormatter = new Intl.DateTimeFormat(locales, { dateStyle })
@ -128,13 +143,7 @@ export function formatDate(date, dateStyle = 'long', locales = 'en') {
return dateFormatter.format(dateToFormat)
}
/**
*
* @param {string} text
* @param {number} maxLenght
* @returns
*/
export function trimText(text, maxLenght) {
export function trimText(text: string, maxLenght: number) {
if (text.length < maxLenght - 1) return text
const lastSpace = text.slice(0, maxLenght).lastIndexOf(' ')
@ -143,8 +152,8 @@ export function trimText(text, maxLenght) {
}
/** Get the filename of a filepath without its extension */
export function getFileNameWithoutExtension(filePath) {
return filePath.split('/').at(-1).replace(/\..*$/, '')
export function getFileNameWithoutExtension(filePath: string) {
return filePath.split('/').at(-1)?.replace(/\..*$/, '')
}
/**
@ -180,10 +189,8 @@ export function createThresholdStream({ clicksTarget = 69, clicksEachMs = 400, f
/**
* Tell the browser to preload an image
*
* @param {string} src
*/
export function preloadImage(src) {
export function preloadImage(src: string) {
return new Promise((resolve, reject) => {
const image = new Image()
image.src = src
@ -195,29 +202,27 @@ export function preloadImage(src) {
/**
* A writable store but as as an observable.
* Observables are much nicher than regular stores.
* @template T
* @param {T} init
* @returns {Observable<T> & { update: (updater: (state: T) => T) => void}}
*/
export function writableObservable(init) {
export function writableObservable<T>(
init: T
): Observable<T> & { update: (updater: (state: T) => T) => void } {
const { update, subscribe } = writable(init)
const observable = new Observable((subscriber) => {
const unsubscribe = subscribe((value) => subscriber.next(value))
return unsubscribe
})
// @ts-ignore
observable.update = update
// @ts-ignore
return observable
}
/**
* Convert a store to an observable
* @template T
* @param {import('svelte/store').Readable<T>} store
* @returns {Observable<T>}
*/
export function convertStoreToObservable(store) {
/** Convert a store to an observable */
export function convertStoreToObservable<T>(
store: import('svelte/store').Readable<T>
): Observable<T> {
return new Observable((subscriber) => {
return store.subscribe((value) => subscriber.next(value))
})
@ -225,10 +230,11 @@ export function convertStoreToObservable(store) {
/**
* Checks if two rectangles are intersecting
* @param {{size: number, coordinates: [x: number, y: number]}} rect1
* @param {{size: number, coordinates: [x: number, y: number]} rect2
*/
export function isIntersecting(rect1, rect2) {
export function isIntersecting(
rect1: { size: number; coordinates: [x: number, y: number] },
rect2: { size: number; coordinates: [x: number, y: number] }
) {
return !(
rect1.coordinates[0] + rect1.size < rect2.coordinates[0] ||
rect2.coordinates[0] + rect2.size < rect1.coordinates[0] ||
@ -236,3 +242,11 @@ export function isIntersecting(rect1, rect2) {
rect2.coordinates[1] + rect2.size < rect1.coordinates[1]
)
}
/**
* Merges class names using clsx and tailwind-merge.
* @returns The merged class name string.
*/
export function cn(...inputs: ClassValue[]): string {
return twMerge(clsx(inputs))
}

View file

@ -1,12 +1,13 @@
<script>
import clsx from 'clsx'
<script lang="ts">
import { cn } from '$lib/Helper'
import Card from './Card.svelte'
/** @type { 'md'|'lg'|'xl'}*/
export let size = 'md'
/** @type { 'primary'|'outline'|'fancyOutline' }*/
export let type = 'primary'
export let size: 'md' | 'lg' | 'xl' = 'md'
export let type: 'primary' | 'outline' | 'fancyOutline' = 'primary'
$: classes = clsx(
export let href: string | undefined = undefined
$: classes = cn(
'animate rounded text-sm font-bold hover:scale-[1.01] active:scale-100',
'primary' == type && 'bg-slate-200 text-black',
'outline' == type && 'bg-transparent text-white outline outline-2 outline-slate-200',
@ -20,7 +21,17 @@
{#if type === 'fancyOutline'}
<div class="relative max-w-max">
<button class={classes} on:click><slot>NO LABEL PROVIDED</slot></button>
<svelte:element
this={href ? 'a' : 'button'}
{...$$restProps}
{href}
role="button"
tabindex="0"
class={classes}
on:click
>
<slot>NO LABEL PROVIDED</slot>
</svelte:element>
<span
class="fancy-bg absolute inset-0 -z-10 h-full w-[110%] min-w-[5rem] scale-y-75 bg-cyan-500/90 px-4 py-2 blur-xl"
style="--easing: x; --duration: 8s;"
@ -35,7 +46,15 @@
/>
</div>
{:else}
<button class={classes} on:click><slot>NO LABEL PROVIDED</slot></button>
<svelte:element
this={href ? 'a' : 'button'}
{...$$restProps}
{href}
role="button"
tabindex="0"
class={classes}
on:click><slot>NO LABEL PROVIDED</slot></svelte:element
>
{/if}
<style lang="postcss">

View file

@ -3,7 +3,7 @@
import { getContext, onMount } from 'svelte'
import { cardsContext } from '$lib/components/CardsContainer.svelte'
import { spring } from 'svelte/motion'
import { getIsMobile } from '$lib/Helper.mjs'
import { getIsMobile } from '$lib/Helper.ts'
/** @type {'cyan' | 'purple'}*/
export let color = 'cyan'
/** @type {number | number}*/
@ -107,7 +107,7 @@
<style lang="postcss">
.card {
@apply relative flex 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;
contain: paint style layout;
@ -161,7 +161,7 @@
}
.gradient {
@apply pointer-events-none absolute inset-0 h-full w-full;
@apply pointer-events-none absolute inset-0 h-full w-full;
width: calc(100% - 2px);
border-radius: inherit;
height: calc(100% - 2px);

View file

@ -3,7 +3,7 @@
</script>
<script>
import { getIsMobile } from '$lib/Helper.mjs'
import { getIsMobile } from '$lib/Helper.ts'
import { BehaviorSubject, Subject, throttle, throttleTime } from 'rxjs'
import { onMount, setContext } from 'svelte'

View file

@ -3,7 +3,7 @@
import { createEventDispatcher, getContext, onDestroy, onMount } from 'svelte'
import { spring } from 'svelte/motion'
import { contextId as ctxId } from '../../routes/home-slices/CommunitySlice.svelte'
import { convertStoreToObservable, isIntersecting, lerp } from '$lib/Helper.mjs'
import { convertStoreToObservable, isIntersecting, lerp } from '$lib/Helper.ts'
import { inview } from 'svelte-inview'
import { Subject, distinctUntilChanged, throttle, throttleTime } from 'rxjs'

View file

@ -1,5 +1,5 @@
<script>
import { lerp } from '$lib/Helper.mjs'
import { lerp } from '$lib/Helper.ts'
import { createNoise2D } from 'simplex-noise'
import { onMount } from 'svelte'
import { expoIn } from 'svelte/easing'

View file

@ -1,5 +1,5 @@
<script>
import { animateIn } from '$lib/Helper.mjs'
import { animateIn } from '$lib/Helper.ts'
import clsx from 'clsx'
/** @type { 'left' | 'right' | 'center'} */

View file

@ -1,7 +1,7 @@
<script>
import ArrowRight from '~icons/mingcute/arrow-right-circle-line'
import { animateIn, formatDate } from '$lib/Helper.mjs'
import { animateIn, formatDate } from '$lib/Helper.ts'
export let entry
$: link = `/news/${entry.slug}`

View file

@ -1,5 +1,5 @@
<script>
import { getGeneratedPath } from '$lib/Helper.mjs'
import { getGeneratedPath } from '$lib/Helper.ts'
import { inview } from 'svelte-inview'
/** @type {string} */
@ -133,7 +133,7 @@
.title_ {
filter: saturate(1.5) brightness(1);
@apply relative -mt-1 mb-5 py-1 text-4xl font-bold text-transparent sm:text-6xl;
@apply relative -mt-1 mb-5 py-1 text-4xl font-bold text-transparent sm:text-6xl;
background-image: linear-gradient(
-195deg,
theme(colors.white / 80%) 50%,

View file

@ -15,7 +15,7 @@
import Poz from './community/Poz.svelte'
import { writable } from 'svelte/store'
import { Observable } from 'rxjs'
import { writableObservable } from '$lib/Helper.mjs'
import { writableObservable } from '$lib/Helper.ts'
import TitleHeading from '$lib/components/Title/TitleHeading.svelte'
import TitleSubtile from '$lib/components/Title/TitleSubtile.svelte'

View file

@ -1,5 +1,5 @@
<script>
import { animateIn, getGeneratedPath } from '$lib/Helper.mjs'
import { animateIn, getGeneratedPath } from '$lib/Helper.ts'
/** @type {string}
* The path to the image. Usually the file within `static`, but can also be an URL

View file

@ -1,5 +1,5 @@
<script lang="ts">
import { animateIn } from '$lib/Helper.mjs'
import { animateIn } from '$lib/Helper.ts'
import Button from '$lib/components/Button.svelte'
import TitleHeading from '$lib/components/Title/TitleHeading.svelte'
import TitlePre from '$lib/components/Title/TitlePre.svelte'

View file

@ -1,5 +1,5 @@
<script>
import { getIsMobile } from '$lib/Helper.mjs'
import { getIsMobile } from '$lib/Helper.ts'
import Button from '$lib/components/Button.svelte'
import { onMount } from 'svelte'
import { inview } from 'svelte-inview'

View file

@ -18,7 +18,7 @@
take
} from 'rxjs'
import GitTile from '$lib/components/GitTile.svelte'
import { lerp } from '$lib/Helper.mjs'
import { lerp } from '$lib/Helper.ts'
import { cubicInOut, expoInOut } from 'svelte/easing'
import DiscordProfilePicture from '$lib/components/DiscordProfilePicture.svelte'
import { setContext } from 'svelte'

View file

@ -1,5 +1,5 @@
<script>
import { animateIn } from '$lib/Helper.mjs'
import { animateIn } from '$lib/Helper.ts'
import archLogo from '$lib/images/logos/arch.svg'
import nixLogo from '$lib/images/logos/nixos.svg'
import bsdLogo from '$lib/images/logos/freebsd.svg'

View file

@ -6,7 +6,7 @@
import IconSlideLeft from '~icons/mingcute/align-arrow-left-line'
import clsx from 'clsx'
import Video from '$lib/components/Video.svelte'
import { animateIn } from '$lib/Helper.mjs'
import { animateIn } from '$lib/Helper.ts'
import { Subject, debounceTime, map, tap, throttle, throttleTime } from 'rxjs'
import { onMount } from 'svelte'
import { fade } from 'svelte/transition'

View file

@ -1,5 +1,5 @@
<script>
import { createThresholdStream, lerp, preloadImage } from '$lib/Helper.mjs'
import { createThresholdStream, lerp, preloadImage } from '$lib/Helper.ts'
import DiscordProfilePicture from '$lib/components/DiscordProfilePicture.svelte'
import { Subject, filter, first, map, merge, of, startWith, switchMap, timer } from 'rxjs'
import edgePoz from '$lib/images/poz/msedgepoz.webp'

View file

@ -4,10 +4,11 @@
import PluginCard from './PluginCard.svelte'
import clsx from 'clsx'
import * as R from 'remeda'
import { getGeneratedPath } from '$lib/Helper.mjs'
import { getGeneratedPath } from '$lib/Helper.ts'
import TitleSubtile from '$lib/components/Title/TitleSubtile.svelte'
import TitlePre from '$lib/components/Title/TitlePre.svelte'
import TitleHeading from '$lib/components/Title/TitleHeading.svelte'
import Button from '$lib/components/Button.svelte'
export let data
@ -41,24 +42,23 @@
>
<div class="top-light"></div>
<header class="header mt-24 md:mt-32">
<header class="mt-24 flex flex-col items-center justify-center md:mt-32">
<Title>
<TitlePre>Plugins</TitlePre>
<TitlePre slot="pre">Plugins</TitlePre>
<TitleHeading slot="title" class="">Unlock full power</TitleHeading>
<TitleSubtile>Easily load up plugins and customize everything</TitleSubtile>
</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> -->
<div
class="mt-8 animate-in fade-in-0 slide-in-from-bottom-2 fill-mode-backwards [animation-delay:300ms] [animation-duration:1500ms]"
>
<Button
href="https://github.com/hyprwm/hyprland-website/blob/main/src/content/plugins.toml"
target="_blank"
class="outline-primary "
type="outline">Submit a plugin</Button
>
</div>
</header>
<section

View file

@ -1,5 +1,5 @@
<script>
import { getGeneratedPath, trimText } from '$lib/Helper.mjs'
import { getGeneratedPath, trimText } from '$lib/Helper.ts'
import Card from '$lib/components/Card.svelte'
import clsx from 'clsx'
import Tag from './Tag.svelte'