Initial commit

This commit is contained in:
Bethlenfalvi, Lorinc (ext)
2025-01-28 00:28:14 +01:00
parent d154e5b725
commit 0c962c336b
52 changed files with 2958 additions and 1054 deletions

View File

@@ -1,55 +0,0 @@
---
// Import the global.css file here so that it is included on
// all pages through the use of the <BaseHead /> component.
import '../styles/global.css';
import { SITE_TITLE } from '../consts';
interface Props {
title: string;
description: string;
image?: string;
}
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
const { title, description, image = '/blog-placeholder-1.jpg' } = Astro.props;
---
<!-- Global Metadata -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="sitemap" href="/sitemap-index.xml" />
<link
rel="alternate"
type="application/rss+xml"
title={SITE_TITLE}
href={new URL('rss.xml', Astro.site)}
/>
<meta name="generator" content={Astro.generator} />
<!-- Font preloads -->
<link rel="preload" href="/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin />
<!-- Canonical URL -->
<link rel="canonical" href={canonicalURL} />
<!-- Primary Meta Tags -->
<title>{title}</title>
<meta name="title" content={title} />
<meta name="description" content={description} />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content={Astro.url} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={new URL(image, Astro.url)} />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content={Astro.url} />
<meta property="twitter:title" content={title} />
<meta property="twitter:description" content={description} />
<meta property="twitter:image" content={new URL(image, Astro.url)} />

View File

@@ -0,0 +1,83 @@
<script lang="ts">
import type { CollectionEntry } from "astro:content";
import { onMount } from "svelte";
import { SvelteMap } from "svelte/reactivity";
import { parseTime, printTime } from "../utils/time";
interface Props {
posts: CollectionEntry<"blog">[];
}
let { posts }: Props = $props();
let allTags = $derived([...new Set(posts.flatMap(item => item.data.tags))]);
let selectedTags = $state<SvelteMap<string, boolean>>();
onMount(() => {
const url = new URL(window.location.href);
const tagsQry = url.searchParams.get('tags');
const defaultTags = tagsQry ? new Set(decodeURIComponent(tagsQry).split('+')) : new Set();
selectedTags = new SvelteMap(allTags.map(tag => [tag, defaultTags.has(tag)]));
})
$effect(() => {
if (!selectedTags) return;
const newTagsQry = encodeURIComponent(
[...selectedTags.entries()].filter(([_, v]) => v).map(([k, _]) => k).join('+')
);
const url = new URL(window.location.href);
if (newTagsQry == url.searchParams.get("tags")) return;
if (newTagsQry == "") url.searchParams.delete("tags");
else url.searchParams.set("tags", newTagsQry);
window.history.pushState(null, "", url.toString());
})
let isFiltered = $derived(selectedTags && [...selectedTags.values()].includes(true));
let filteredPosts = $derived(isFiltered
? posts.filter(p => !p.data.unlisted && p.data.tags.some(t => selectedTags!.get(t)))
: posts.filter(p => !p.data.unlisted));
let shownPosts = $derived(filteredPosts.toReversed())
</script>
<div class="lg:flex flex-row-reverse justify-between items-start">
<div class="m-2 p-2 bg-side-bg lg:float-right lg:max-w-80 lg:mr-4">
<span class="inline-block m-0.5 pl-2 italic">filter:</span>
{#each allTags as tag}
<button
onclick={() => {
console.log("TRoggled tag")
selectedTags!.set(tag, !selectedTags!.get(tag))
}}
class={[
"m-0.5 rounded-4xl emph-bg px-2 cursor-pointer border-2 border-solid",
selectedTags?.get(tag) && "border-gray-400"
]}
>
{tag}
</button>
{/each}
</div>
<main class="max-w-[650px]">
{#each shownPosts as post}
<article>
<a href={`/blog/${post.id}`}>
<div class="
lg:grid grid-cols-[auto_min-content_min-content]
m-1 p-2 hover:bg-shade
">
<h2 class="font-bold">{post.data.title}</h2>
<address class="inline-block post-meta col-start-2 md:ml-3">
{post.data.author}
</address>
<time class="inline-block post-meta col-start-3 lg:ml-1"
datetime={parseTime(post.data.pubDate).toString()}
>
{printTime(parseTime(post.data.pubDate))}
</time>
<div class="col-span-3">{post.data.summary}</div>
</div>
</a>
</article>
{/each}
</main>
</div>

View File

@@ -0,0 +1,4 @@
---
const { text } = Astro.props;
---
<pre class="text-red-300">{text?.trim()}<slot/></pre>

View File

@@ -1,62 +0,0 @@
---
const today = new Date();
---
<footer>
&copy; {today.getFullYear()} Your name here. All rights reserved.
<div class="social-links">
<a href="https://m.webtoo.ls/@astro" target="_blank">
<span class="sr-only">Follow Astro on Mastodon</span>
<svg
viewBox="0 0 16 16"
aria-hidden="true"
width="32"
height="32"
astro-icon="social/mastodon"
><path
fill="currentColor"
d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a3.614 3.614 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522c0-.859.22-1.541.66-2.046.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764.442.505.661 1.187.661 2.046v4.203z"
></path></svg
>
</a>
<a href="https://twitter.com/astrodotbuild" target="_blank">
<span class="sr-only">Follow Astro on Twitter</span>
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32" astro-icon="social/twitter"
><path
fill="currentColor"
d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"
></path></svg
>
</a>
<a href="https://github.com/withastro/astro" target="_blank">
<span class="sr-only">Go to Astro's GitHub repo</span>
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32" astro-icon="social/github"
><path
fill="currentColor"
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
></path></svg
>
</a>
</div>
</footer>
<style>
footer {
padding: 2em 1em 6em 1em;
background: linear-gradient(var(--gray-gradient)) no-repeat;
color: rgb(var(--gray));
text-align: center;
}
.social-links {
display: flex;
justify-content: center;
gap: 1em;
margin-top: 1em;
}
.social-links a {
text-decoration: none;
color: rgb(var(--gray));
}
.social-links a:hover {
color: rgb(var(--gray-dark));
}
</style>

View File

@@ -1,17 +0,0 @@
---
interface Props {
date: Date;
}
const { date } = Astro.props;
---
<time datetime={date.toISOString()}>
{
date.toLocaleDateString('en-us', {
year: 'numeric',
month: 'short',
day: 'numeric',
})
}
</time>

View File

@@ -1,85 +0,0 @@
---
import HeaderLink from './HeaderLink.astro';
import { SITE_TITLE } from '../consts';
---
<header>
<nav>
<h2><a href="/">{SITE_TITLE}</a></h2>
<div class="internal-links">
<HeaderLink href="/">Home</HeaderLink>
<HeaderLink href="/blog">Blog</HeaderLink>
<HeaderLink href="/about">About</HeaderLink>
</div>
<div class="social-links">
<a href="https://m.webtoo.ls/@astro" target="_blank">
<span class="sr-only">Follow Astro on Mastodon</span>
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32"
><path
fill="currentColor"
d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a3.614 3.614 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522c0-.859.22-1.541.66-2.046.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764.442.505.661 1.187.661 2.046v4.203z"
></path></svg
>
</a>
<a href="https://twitter.com/astrodotbuild" target="_blank">
<span class="sr-only">Follow Astro on Twitter</span>
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32"
><path
fill="currentColor"
d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"
></path></svg
>
</a>
<a href="https://github.com/withastro/astro" target="_blank">
<span class="sr-only">Go to Astro's GitHub repo</span>
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32"
><path
fill="currentColor"
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
></path></svg
>
</a>
</div>
</nav>
</header>
<style>
header {
margin: 0;
padding: 0 1em;
background: white;
box-shadow: 0 2px 8px rgba(var(--black), 5%);
}
h2 {
margin: 0;
font-size: 1em;
}
h2 a,
h2 a.active {
text-decoration: none;
}
nav {
display: flex;
align-items: center;
justify-content: space-between;
}
nav a {
padding: 1em 0.5em;
color: var(--black);
border-bottom: 4px solid transparent;
text-decoration: none;
}
nav a.active {
text-decoration: none;
border-bottom-color: var(--accent);
}
.social-links,
.social-links a {
display: flex;
}
@media (max-width: 720px) {
.social-links {
display: none;
}
}
</style>

View File

@@ -1,24 +0,0 @@
---
import type { HTMLAttributes } from 'astro/types';
type Props = HTMLAttributes<'a'>;
const { href, class: className, ...props } = Astro.props;
const pathname = Astro.url.pathname.replace(import.meta.env.BASE_URL, '');
const subpath = pathname.match(/[^\/]+/g);
const isActive = href === pathname || href === '/' + (subpath?.[0] || '');
---
<a href={href} class:list={[className, { active: isActive }]} {...props}>
<slot />
</a>
<style>
a {
display: inline-block;
text-decoration: none;
}
a.active {
font-weight: bolder;
text-decoration: underline;
}
</style>

View File

@@ -0,0 +1,160 @@
---
import type { JSX } from "astro/jsx-runtime"
type LambdaToken = ['lambda', string, Token[], Token[]]
type Token =
| ['comment', string]
| LambdaToken
| ['operator', string]
| ['name', string]
| ['whitespace', string]
| ['placeholder', string]
| ['macro', string]
| ['keyword', string]
| ['string', string]
| ['number', string]
function matchParen(expr: string, greedy = false): string {
for (var i = 0, lvl = 0;
0 <= lvl && i < expr.length;
i++) {
if (expr[i] == '(' || expr[i] == '\\') lvl++
else if (expr[i] == ')' || expr[i] == '.') lvl--
else if (greedy && expr[i] == '\n' && lvl == 0) break
}
return expr.slice(0, i)
}
function parseLambda(expr: string): LambdaToken {
expr = expr.slice(1) // Get rid of \
const nameMatch = /^[\$a-zA-Z0-9_]+\s*/.exec(expr)
if (!nameMatch) throw new Error(`Missing name in "${expr}"`)
const name = nameMatch[0].trim()
const afterName = nameMatch[0].length
let type: Token[] = []
let afterType = afterName
if (expr[afterName] == ':') {
const typeStr = matchParen(expr.slice(afterName + 1)).slice(0, -1)
type = tokenizeExp(typeStr)
afterType += typeStr.length + 1
}
if (expr[afterType] != '.') throw new Error(`Missing dot in "${expr.slice(afterType)}"`)
const body = tokenizeExp(expr.slice(afterType + 1))
return ['lambda', name, type, body]
}
// Problem
// \f:\a:a.a.\a:a.a
function tokenizeExp(expr: string): Token[] {
if (expr == '') return []
const ws = /^(\s|\n)+/.exec(expr)
if (ws) return [['whitespace', ws[0]], ...tokenizeExp(expr.slice(ws[0].length))]
const keyword = /^(export|import|default|replacing)\s/.exec(expr)
if (keyword) return [['keyword', keyword[0]], ...tokenizeExp(expr.slice(keyword[0].length))]
const macro = /^:=|^=\-?([\d\_a-fA-F]+(\.[\d\_a-fA-F]+)?(p\-?[\d_]+)?)?=>/.exec(expr)
// const macro = /^[:<]=(([\d_]+(\.[\d_]+)?)?=>?)?/.exec(expr)
if (macro) return [['macro', macro[0]], ...tokenizeExp(expr.slice(macro[0].length))]
const number = /^\d\S*/.exec(expr)
if (number) return [['number', number[0]], ...tokenizeExp(expr.slice(number[0].length))]
const name = /^[A-Za-z0-9_]+/.exec(expr)
if (name) return [['name', name[0]], ...tokenizeExp(expr.slice(name[0].length))]
if (expr.startsWith("--[")) {
let end = expr.indexOf("]--") + "]--".length;
return [
['comment', expr.slice(0, end)],
...tokenizeExp(expr.slice(end))
]
}
if (expr.startsWith("--")) {
let end = expr.indexOf("\n");
return [
["comment", expr.slice(0, end)],
...tokenizeExp(expr.slice(end))
]
}
if (expr.startsWith('\\')) {
const lambda = matchParen(expr)
return [parseLambda(lambda), ...tokenizeExp(expr.slice(lambda.length))]
}
if (expr.startsWith('"')) {
let i = '"'.length;
for (; i <= expr.length; i++) {
if (expr[i] == '\\') i++;
if (expr[i] == '"') break;
}
return [
["string", expr.slice(0, i+1)],
...tokenizeExp(expr.slice(i+1))
]
}
const ph = /^\$[a-zA-Z0-9_]+/.exec(expr)
if (ph) return [['placeholder', ph[0]], ...tokenizeExp(expr.slice(ph[0].length))]
const opChars = /^[^\sa-zA-Z0-9_\$\\]+/.exec(expr)
if (opChars) return [['operator', opChars[0]], ...tokenizeExp(expr.slice(opChars[0].length))]
throw new Error(`Logic error: none of the regices in a complete cover matched "${expr}"`)
}
function nameStyle(level: number | undefined): JSX.CSSProperties {
return {
color: level === undefined
? "hsl(30, 50%, 70%)"
: `hsl(
calc(170 - ${level} * 5),
calc(50% + ${level} * 10%),
calc(70% - ${level} * 5%)
)`,
}
}
interface Props {
text?: string,
tokens?: Token[],
vlvlv?: Map<string, number>
}
let { text, tokens, vlvlv = new Map() }: Props = Astro.props;
const nextLvl = vlvlv.size + 1
const outTokens = tokens ? tokens : tokenizeExp(text!.trim());
---
<code style={{
whiteSpace: "pre-wrap",
padding: "unset",
background: "unset",
borderRadius: "unset",
border: "unset",
fontFamily: '"Droid Sans Mono", monospace',
fontSize: "small",
}}>
{outTokens.map(([name, value, ...extras], i) => { switch (name) {
case 'comment': return <span style={{ color: "#8f8" }}>{value}</span>
case 'name': return <span style={nameStyle(vlvlv.get(value))}>{value}</span>
case 'operator': return <span style={{ color: "white" }}>{value}</span>
case 'whitespace': return <span>{value}</span>
case 'placeholder': return <span style={{ color: "#bb5" }}>{value}</span>
case 'macro': return <span style={{ color: "#f55" }}>{value}</span>
case 'keyword': return <span style={{ color: "#39f" }}>{value}</span>
case 'string': return <span style={{ color: "#f8b" }}>{value}</span>
case 'number': return <span style={{ color: "#afa" }}>{value}</span>
case 'lambda':
const sub_vlvlv = new Map(vlvlv)
sub_vlvlv.set(value, nextLvl)
return <span data-name={value}>
<span style={{ color: "#999" }}>\</span>
<span style={nameStyle(vlvlv.get(value))}>{value}</span>
{extras[0]!.length? <>
<span style={{ color: "#999" }}>:</span>
<span>
<Astro.self vlvlv={sub_vlvlv} tokens={extras[0]!} />
</span>
</> :null}
<span style={{ color: "#999" }}>.</span>
<span>
<Astro.self vlvlv={sub_vlvlv} tokens={extras[1]!} />
</span>
</span>
}})}
</code>

View File

@@ -0,0 +1,20 @@
---
import type { HTMLAttributes } from 'astro/types';
interface Props extends HTMLAttributes<"a"> {
href: string,
zone?: string,
}
const { href, zone, ...attrs } = Astro.props;
const isActive = Astro.url.pathname == href
|| Astro.url.pathname.startsWith((zone ? zone : href) + '/');
---
<a href={href} class:list={[
"hover:bg-shade py-2 px-1 md:px-4 flex-auto md:flex-initial",
isActive && "font-bold text-white"
]} {...attrs}>
<slot/>
</a>