mirror of
https://github.com/hyprwm/hyprland-website.git
synced 2024-11-17 02:45:59 +01:00
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:
parent
5934c40d93
commit
24b9b73de5
23 changed files with 138 additions and 89 deletions
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(/\..*$/, '')
|
||||
}
|
||||
|
|
|
@ -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]]
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -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">
|
||||
|
|
|
@ -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}*/
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'} */
|
||||
|
|
|
@ -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}`
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { getGeneratedPath } from '$lib/Helper.mjs'
|
||||
import { getGeneratedPath } from '$lib/Helper.ts'
|
||||
import { inview } from 'svelte-inview'
|
||||
|
||||
/** @type {string} */
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in a new issue