Add Rss feed (#42)

* darken inline code bg in articles

* improve install button

* add rss feed

* small cleanup

* news: single row entries

* news: fancy spinner

* news: single col

* news: fancy spinner
This commit is contained in:
Visual-Dawg 2024-01-30 14:11:23 +01:00 committed by GitHub
parent f414e369bb
commit 821d96b466
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 134 additions and 35 deletions

View file

@ -5,6 +5,7 @@
<link rel="icon" href="%sveltekit.assets%/favicon.ico" /> <link rel="icon" href="%sveltekit.assets%/favicon.ico" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<meta property="og:image" content="/imgs/og-img.png" /> <meta property="og:image" content="/imgs/og-img.png" />
<link rel="alternate" type="application/rss+xml" title="Hyprland News" href="/rss" />
<style></style> <style></style>
%sveltekit.head% %sveltekit.head%
</head> </head>

View file

@ -46,11 +46,12 @@ export function animateIn(node, options = {}) {
function callback() { function callback() {
timeoutId = setTimeout( timeoutId = setTimeout(
effects.forEach(([effect]) => { () =>
if (effect === 'slide') node.style.removeProperty('translate') effects.forEach(([effect]) => {
else if (effect === 'fade') node.style.removeProperty('opacity') if (effect === 'slide') node.style.removeProperty('translate')
else if (effect === 'zoom') node.style.removeProperty('scale') else if (effect === 'fade') node.style.removeProperty('opacity')
}), else if (effect === 'zoom') node.style.removeProperty('scale')
}),
options.delay ?? 0 options.delay ?? 0
) )
} }

View file

@ -2,6 +2,7 @@
import DiscordIcon from '~icons/prime/discord' import DiscordIcon from '~icons/prime/discord'
import GithubIcon from '~icons/ri/github-fill' import GithubIcon from '~icons/ri/github-fill'
import { discordLink } from '$lib/constants.mjs' import { discordLink } from '$lib/constants.mjs'
import RssIcon from '~icons/mingcute/rss-fill'
/** @type {[string, string, string, string]} */ /** @type {[string, string, string, string]} */
let team = [ let team = [
@ -75,13 +76,21 @@
aria-label="Go to our Github"><GithubIcon class="h-12 w-12 " /></a aria-label="Go to our Github"><GithubIcon class="h-12 w-12 " /></a
> >
</li> </li>
<li class="">
<a
href="/rss"
class="text-slate-400 hover:text-slate-200"
target="_blank"
aria-label="Rss Feed"><RssIcon class="h-12 w-12 " /></a
>
</li>
</ul> </ul>
</div> </div>
<div class="flex w-full flex-wrap gap-4 text-sm font-medium text-slate-400"> <div class="flex w-full flex-wrap gap-4 text-sm font-medium text-slate-400">
<p>Hyprland is licensed under the BSD 3-Clause "New" or "Revised" License.</p> <p>Hyprland is licensed under the BSD 3-Clause "New" or "Revised" License.</p>
<p>© Hyprland Development {new Date().getFullYear()}.</p> <p>© Hyprland Development {new Date().getFullYear()}.</p>
<p>Doki doki waku waku.</p> <p>Stay hydrated</p>
</div> </div>
</div> </div>

View file

@ -8,7 +8,7 @@
</script> </script>
<li class="flex gap-14" use:animateIn={{ fade: 0, slide: 24 }}> <li class="flex gap-14" use:animateIn={{ fade: 0, slide: 24 }}>
<div class="flex flex-col gap-4 rounded"> <article class="flex flex-col gap-4 rounded">
<div class="flex flex-col gap-4 text-sm font-medium text-slate-400"> <div class="flex flex-col gap-4 text-sm font-medium text-slate-400">
<p class="font-bold text-slate-400">{formatDate(entry.date)}</p> <p class="font-bold text-slate-400">{formatDate(entry.date)}</p>
</div> </div>
@ -18,7 +18,7 @@
<a <a
href={link} href={link}
class="group flex max-w-max items-center gap-4 font-medium text-slate-300 transition-all hover:text-white" class="group flex max-w-max items-center gap-4 font-medium text-slate-300 transition-all hover:text-white"
>Read up<ArrowRight class="transition-transform group-hover:translate-x-0.5" /></a >Read up <ArrowRight class="transition-transform group-hover:translate-x-0.5" /></a
> >
</div> </article>
</li> </li>

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View file

@ -27,12 +27,7 @@
<div <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" 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 <img src={image} class="h-20 w-32 object-contain" alt="{name} Logo" loading="lazy" />{name}
src={image}
class="h-20 w-32 object-contain"
alt="Distrubution Logo"
loading="lazy"
/>{name}
<slot name="imageExtra" /> <slot name="imageExtra" />
</div> </div>
@ -48,7 +43,7 @@
<span>{command}</span> <span>{command}</span>
</div> </div>
<ClipboardIcon <ClipboardIcon
class="h-6 w-6 text-white opacity-0 transition-opacity duration-100 hover:!opacity-100 group-hover:opacity-80 group-active:opacity-100" 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> </div>
</slot> </slot>

View file

@ -1,18 +1,64 @@
<script> <script>
import LogoPng from '$lib/images/logos/HyprlandLogo.png'
import Title from '$lib/components/Title.svelte' import Title from '$lib/components/Title.svelte'
import NewsThumb from '$lib/components/news-thumb.svelte' import NewsThumb from '$lib/components/news-thumb.svelte'
import { onDestroy, onMount } from 'svelte'
import { onMount } from 'svelte'
export let data export let data
const posts = data.posts /** @type {HTMLElement} */
let asciiElement
const latest = posts.at(0) const { posts } = data
const others = posts.slice(1)
onMount(() => { let interval
console.log({ data }) let objectUrl
// Taken from https://github.com/NotAShelf/hyprascii/blob/main/web/script.js
onMount(async () => {
const logoBlob = await fetch(LogoPng).then((response) => response.blob())
objectUrl = URL.createObjectURL(logoBlob)
const img = document.createElement('img')
img.src = objectUrl
const cvs = document.createElement('canvas')
cvs.width = 72
cvs.height = 36
const ctx = cvs.getContext('2d', { willReadFrequently: true })
const getLuminance = (r, g, b) => {
return Math.sqrt(0.299 * r * r + 0.587 * g * g + 0.114 * b * b)
}
ctx.fillStyle = '#000000'
interval = setInterval(() => {
ctx.fillRect(0, 0, cvs.width, cvs.height)
const t = new Date().getTime() * 0.0007
let px = ((-Math.cos(t) + 1) / 2) * cvs.width
let sx = Math.cos(t) * cvs.width
ctx.drawImage(img, px, 0, sx, cvs.height)
const idata = ctx.getImageData(0, 0, cvs.width, cvs.height)
const pixels = idata.data
const text = []
const chars = ' .-=+'
for (let y = 0; y < cvs.height; y++) {
for (let x = 0; x < cvs.width; x++) {
const idx = 4 * ((sx < 0 ? cvs.width - x - 1 : x) + cvs.width * y)
const br = getLuminance(pixels[idx] / 256, pixels[idx + 1] / 256, pixels[idx + 2] / 256)
text.push(chars[Math.floor(br * chars.length)])
}
text.push('\n')
}
asciiElement.innerText = text.join('')
}, 40)
})
onDestroy(() => {
clearInterval(interval)
URL.revokeObjectURL(objectUrl)
}) })
</script> </script>
@ -22,7 +68,9 @@
<section> <section>
<header class="header"> <header class="header">
<Title class="mb-8"> <pre class="spinner-wrapper" bind:this={asciiElement} />
<Title class="mb-0 duration-1000 animate-in fade-in-0">
<span slot="title">News</span><span slot="subtitle"> <span slot="title">News</span><span slot="subtitle">
Fresh updates straight from the oven Fresh updates straight from the oven
</span> </span>
@ -30,12 +78,9 @@
</header> </header>
<ul <ul
class="row-auto grid grid-cols-1 gap-14 delay-500 duration-1000 animate-in fade-in-0 slide-in-from-bottom-6 fill-mode-backwards lg:grid-cols-2" class="row-auto flex flex-col gap-14 animate-in fade-in-0 slide-in-from-bottom-6 fill-mode-backwards [animation-delay:800ms] [animation-duration:1500ms]"
> >
<div class="col-span-full flex lg:justify-center"> {#each posts as entry}
<NewsThumb entry={latest} />
</div>
{#each others as entry}
<NewsThumb {entry} /> <NewsThumb {entry} />
{/each} {/each}
</ul> </ul>
@ -57,6 +102,14 @@
min-height: 500px; min-height: 500px;
max-height: 900px; max-height: 900px;
width: 100%; width: 100%;
margin-bottom: min(-10vh, -6rem); margin-bottom: 8rem;
}
.spinner-wrapper {
@apply mb-4 flex items-center justify-center bg-gradient-to-tr from-blue-500/0 to-cyan-500 bg-clip-text text-transparent animate-in fade-in-0;
animation-duration: 2000ms;
font-size: min(1vh, 1rem);
/* There are 36 rows */
height: min(36vh, 36rem);
} }
</style> </style>

44
src/routes/rss/+server.js Normal file
View file

@ -0,0 +1,44 @@
const siteURL = 'https://hyprland.org'
const siteTitle = 'Hyprland'
const siteDescription = 'Tiling window manager with the looks'
export const prerender = true
export const GET = async ({ fetch }) => {
const allNews = await fetch('api/news')
.then((response) => response.json())
.then((news) => news.sort((a, b) => new Date(b.date) - new Date(a.date)))
const body = renderXml(allNews)
const options = {
headers: {
'Cache-Control': 'max-age=0, s-maxage=3600',
'Content-Type': 'application/xml'
}
}
return new Response(body, options)
}
function renderXml(posts) {
return `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<atom:link href="${siteURL}/rss" rel="self" type="application/rss+xml" />
<title>${siteTitle} News</title>
<link>${siteURL}/news</link>
<description>${siteDescription}</description>
${posts
.map(
(post) => `<item>
<guid isPermaLink="true">${siteURL}/news/${post.slug}</guid>
<title>${post.title}</title>
<link>${siteURL}/news/${post.slug}</link>
<pubDate>${new Date(post.date).toUTCString()}</pubDate>
</item>`
)
.join(' ')}
</channel>
</rss>
`
}

View file

@ -1,10 +1,6 @@
const { fontFamily } = require('tailwindcss/defaultTheme') const { fontFamily } = require('tailwindcss/defaultTheme')
const colors = require('tailwindcss/colors') const colors = require('tailwindcss/colors')
console.log({ colors })
// const sansFamily =
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
export default { export default {
content: ['./src/**/**/*.{html,js,svelte,ts}'], content: ['./src/**/**/*.{html,js,svelte,ts}'],
@ -26,7 +22,7 @@ export default {
css: { css: {
code: { code: {
padding: '0.2em 0.4em', padding: '0.2em 0.4em',
'background-color': colors.slate[700], 'background-color': colors.slate[800],
'border-radius': '6px', 'border-radius': '6px',
'font-weight': 'inherit' 'font-weight': 'inherit'
}, },