feat: add ci

This commit is contained in:
max_richter 2024-04-03 16:18:30 +02:00
parent 93baa3b6b0
commit e800314589
13 changed files with 280 additions and 29 deletions

48
.github/workflows/default.yaml vendored Normal file
View File

@ -0,0 +1,48 @@
name: Deploy to GitHub Pages
on:
push:
branches: [ main ]
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
steps:
- name: Checkout your repository using git
uses: actions/checkout@v4
- name: 🗳️ Setup pnpm cache
uses: https://github.com/actions/cache@v3
with:
path: ${{ env.PNPM_STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ env.PNPM_CACHE_ID }}
restore-keys: |
${{ runner.os }}-pnpm-store
- name: Install, build, and upload your site
uses: withastro/action@v2
# with:
# path: . # The root location of your Astro project inside the repository. (optional)
# node-version: 20 # The specific version of Node that should be used to build your site. Defaults to 20. (optional)
# package-manager: pnpm@latest # The Node package manager that should be used to install dependencies and build your site. Automatically detected based on your lockfile. (optional)
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: 🚀 Deploy files via SFTP
uses: https://github.com/pressidium/lftp-mirror-action@v1
with:
host: ${{ secrets.FTP_HOST }}
port: ${{ secrets.FTP_PORT || 21 }}
user: ${{ secrets.FTP_USERNAME }}
pass: ${{ secrets.FTP_PASSWORD }}
onlyNewer: true
parallel: '4'
settings: 'sftp:auto-confirm=yes'
localDir: 'public'
remoteDir: '/share'
options: '--verbose'

View File

@ -9,16 +9,21 @@ h3 {
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
} }
article h2 { article>h2 {
font-size: 24px; font-size: 24px;
margin-top: 20px; margin-top: 20px;
} }
article h3 { article>h3 {
font-size: 20px; font-size: 20px;
margin-top: 16px; margin-top: 16px;
} }
article>p>a,
article a {
color: var(--fill) !important;
}
:root { :root {
--neutral-1000: #000000; --neutral-1000: #000000;
--neutral-800: #16161E; --neutral-800: #16161E;

View File

@ -92,8 +92,7 @@
<div <div
class="googley-eyes" class="googley-eyes"
on:click={() => ($visible = !$visible)} on:click={() => ($visible = !$visible)}
role="button" role="img"
tabindex="0"
aria-label="Toggle Googley Eyes" aria-label="Toggle Googley Eyes"
aria-hidden="true" aria-hidden="true"
on:keydown={(ev) => (ev.key === "Enter" ? ($visible = !$visible) : "")} on:keydown={(ev) => (ev.key === "Enter" ? ($visible = !$visible) : "")}

View File

@ -1,7 +1,7 @@
--- ---
import markdownToText from "@helpers/markdownToText"; import markdownToText from "@helpers/markdownToText";
import { Card } from "./card"; import { Card } from "./card";
import { useTranslatedPath } from "@i18n/utils"; import { useTranslatedPath, useTranslations } from "@i18n/utils";
import Image from "@components/Image.astro"; import Image from "@components/Image.astro";
interface Props { interface Props {
@ -25,6 +25,7 @@ const {
} = Astro.props.post; } = Astro.props.post;
const translatePath = useTranslatedPath(Astro); const translatePath = useTranslatedPath(Astro);
const t = useTranslations(Astro);
const imagePath = `../content/${collection}/${slug.split("/")[0]}/${headerImg}`; const imagePath = `../content/${collection}/${slug.split("/")[0]}/${headerImg}`;
@ -44,7 +45,7 @@ const link = translatePath(`/${collection}/${slug.split("/")[0]}`);
<Card.Description> <Card.Description>
{markdownToText(body).slice(0, 200)} {markdownToText(body).slice(0, 200)}
</Card.Description> </Card.Description>
<Card.ReadMoreButton link={link} /> <Card.ReadMoreButton link={link} text={t("read-more")} />
</Card.Content> </Card.Content>
{ {
image?.format && ( image?.format && (

View File

@ -11,8 +11,6 @@ interface Props {
const { src: image, alt, maxWidth } = Astro.props; const { src: image, alt, maxWidth } = Astro.props;
console.log({ image, alt, maxWidth });
const sizes = [ const sizes = [
{ {
width: 240, width: 240,

View File

@ -1,6 +1,7 @@
--- ---
import { useTranslations, useTranslatedPath } from "../i18n/utils"; import { useTranslations, useTranslatedPath } from "../i18n/utils";
import Logo from "./Logo.astro"; import Logo from "./Logo.astro";
import ToggleTheme from "@components/ThemeToggle.svelte";
function isActive(path: string) { function isActive(path: string) {
return Astro.url.pathname === path ? "active" : ""; return Astro.url.pathname === path ? "active" : "";
@ -10,10 +11,6 @@ const t = useTranslations(Astro);
const translatePath = useTranslatedPath(Astro); const translatePath = useTranslatedPath(Astro);
const paths = [ const paths = [
{
link: translatePath("/"),
component: Logo,
},
{ {
link: translatePath("/blog"), link: translatePath("/blog"),
text: t("nav.blog"), text: t("nav.blog"),
@ -33,16 +30,26 @@ const paths = [
]; ];
--- ---
<ul class="flex my-4 divide-x divide-x-1 divide-x-neutral"> <ul class="flex my-4 h-12">
<li><a href="#main-content" class="skip-link">Skip to main content</a></li>
<li
class="border-none bg-transparent my-2 mr-4 logo grid place-content-center"
>
<a
href={translatePath("/")}
class="text-neutral h-9 flex items-center justify-center lowercase"
>
<Logo />
</a>
</li>
{ {
paths.map(({ link, text, component }, i) => ( paths.map(({ link, text }, i) => (
<li <li
class={` class={`
flex items-center flex-1 border-t-1 border-b-1 border-neutral flex items-center flex-1 border-t-1 border-b-1 border-neutral
${isActive(link) ? "bg-light" : "bg"} ${isActive(link) ? "bg-light" : "bg"}
${i === 1 ? "rounded-bl-md" : ""} ${i === 0 ? "rounded-bl-md border-l-1" : "border-l-1"}
${i === paths.length - 1 ? "rounded-tr-md !border-r-1" : ""} ${i === paths.length - 1 ? "rounded-tr-md !border-r-1" : ""}
${component ? "border-none bg-transparent my-2 mr-4 logo" : ""}
`} `}
> >
<a <a
@ -50,20 +57,40 @@ const paths = [
href={link} href={link}
data-astro-prefetch data-astro-prefetch
> >
{component ? <Logo /> : text} {text}
</a> </a>
</li> </li>
)) ))
} }
<li class="w-8 h-6 flex justify-right items-center h-full">
<ToggleTheme client:load />
</li>
</ul> </ul>
<style> <style>
ul > li.logo { ul > li.logo {
background: none; background: none;
flex: 0; flex: 0;
height: 34px;
margin-left: 0px; margin-left: 0px;
--fill: #cb5a5a; --fill: #cb5a5a;
--background-fill: none; --background-fill: none;
} }
/* Visually hide the jump to content button while making it accessible */
.skip-link {
position: absolute;
left: -9999px;
top: auto;
width: 1px;
height: 1px;
overflow: hidden;
}
/* Make the jump to content button visible when focused */
.skip-link:focus {
position: absolute;
left: 0;
width: auto;
height: auto;
overflow: visible;
}
</style> </style>

View File

@ -0,0 +1,59 @@
<script lang="ts">
import { onMount } from "svelte";
import { writable } from "svelte/store";
let theme = writable("light");
onMount(() => {
theme.set(localStorage.getItem("theme") || "light");
});
$: if ($theme && "document" in globalThis) {
document.documentElement.classList.remove("light", "dark");
document.documentElement.classList.add($theme);
localStorage.setItem("theme", $theme);
}
function toggleTheme() {
console.log($theme);
theme.update((t) => (t === "light" ? "dark" : "light"));
}
</script>
<button class="block w-6 h-6" on:click={toggleTheme} title="toggle dark mode">
{#if $theme === "dark"}
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
class="icon icon-tabler icons-tabler-outline icon-tabler-sun"
><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path
d="M12 12m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0"
/><path
d="M3 12h1m8 -9v1m8 8h1m-9 8v1m-6.4 -15.4l.7 .7m12.1 -.7l-.7 .7m0 11.4l.7 .7m-12.1 -.7l-.7 .7"
/></svg
>
{:else}
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.25"
stroke-linecap="round"
stroke-linejoin="round"
class="icon icon-tabler icons-tabler-outline icon-tabler-moon"
><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path
d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"
/></svg
>
{/if}
</button>

View File

@ -7,22 +7,22 @@ comments: true
--- ---
import Image from "@components/Image.astro"; import Image from "@components/Image.astro";
import MAX_7053 from "./images/MAX_7053.jpg";
import MAX_7054 from "./images/MAX_7054.jpg";
import MAX_7055_Panorama from "./images/MAX_7055-Panorama.jpg";
import MAX_7076_Panorama from "./images/MAX_7076-Panorama.jpg";
Best images from a short trip to liguria in italy Best images from a short trip to liguria in italy
This image took way to long, had to do several attempts because its really hard to get focus and exposure right when you use self-timer. But i think in the end it payed off :) This image took way to long, had to do several attempts because its really hard to get focus and exposure right when you use self-timer. But i think in the end it payed off :)
import MAX_7053 from "./images/MAX_7053.jpg"; <Image src={MAX_7053} alt=""/>
<Image src={ MAX_7053 }>
Liminal spaces anyone? Liminal spaces anyone?
import MAX_7054 from "./images/MAX_7054.jpg"; <Image src={MAX_7054} alt=""/>
<Image src={ MAX_7054 }>
I had one day with a bit of rain, but that was actually really nice because i never saw this part of the coast in fog. I had one day with a bit of rain, but that was actually really nice because i never saw this part of the coast in fog.
import MAX_7055_Panorama from "./images/MAX_7055-Panorama.jpg"; <Image src={MAX_7055_Panorama} alt=""/>
<Image src={ MAX_7055_Panorama }>
Sestri Levante is just nice to look at. Sestri Levante is just nice to look at.
import MAX_7076_Panorama from "./images/MAX_7076-Panorama.jpg"; <Image src={MAX_7076_Panorama} alt=""/>
<Image src={ MAX_7076_Panorama }>

View File

@ -72,7 +72,7 @@ const { title, width = "compact" } = Astro.props;
<header> <header>
<Nav /> <Nav />
</header> </header>
<main class="flex flex-col mt-4xl gap-y-2xl"> <main id="main-content" class="flex flex-col mt-4xl gap-y-2xl">
<slot /> <slot />
</main> </main>
<LanguagePicker /> <LanguagePicker />

View File

@ -7,7 +7,9 @@ import HeroCard from "@components/HeroCard.astro";
const locale = getLocale(Astro.url); const locale = getLocale(Astro.url);
const pages = await getCollection("projects"); const pages = await getCollection("projects");
const posts = filterCollection(pages, locale); const posts = filterCollection(pages, locale)
.sort((a, b) => b.data.date - a.data.date)
.sort((a) => (a.data.featured ? -1 : 1));
--- ---
<Layout title="Dude"> <Layout title="Dude">

71
src/pages/tag/[tag].astro Normal file
View File

@ -0,0 +1,71 @@
---
import Layout from "@layouts/Layout.astro";
import { getCollection } from "astro:content";
import { useTranslatedPath } from "@i18n/utils";
const collections = ["blog", "photos", "projects"];
const tp = useTranslatedPath(Astro);
export async function getStaticPaths() {
const collections = ["blog", "photos", "projects"];
const posts = await Promise.all(
collections.map((collection) => {
return getCollection(collection, {
fields: ["slug", "title", "date", "tags"],
});
}),
);
const tags = new Set();
posts.flat().forEach((post) => {
if (!post.data?.tags) return;
post.data.tags.forEach((tag) => {
tags.add(tag.toLowerCase());
});
});
return [...tags.values()].map((tag) => {
return {
params: {
tag,
},
};
});
}
const { tag } = Astro.params;
const allPosts = (await Promise.all(
collections.map((collection) => {
return getCollection(collection, {
fields: ["slug", "title", "date", "tags"],
});
}),
)).flat();
const posts = allPosts.filter((post) => {
return post.data?.tags?.find(t => t.toLowerCase() === tag);
});
---
<Layout title="Max Richter">
<article>
<h1>Tags</h1>
{posts.length}
<div class="flex flex-col gap-2">
{
posts.map((post) => {
return <a href={tp("/"+post.collection+"/" + post.slug)}>{post.slug}</a>;
})
}
</div>
</article>
</Layout>

41
src/pages/tag/index.astro Normal file
View File

@ -0,0 +1,41 @@
---
import Layout from "@layouts/Layout.astro";
import { getCollection } from "astro:content";
import { useTranslatedPath } from "@i18n/utils";
const tp = useTranslatedPath(Astro);
const collections = ["blog", "photos", "projects"];
const posts = await Promise.all(
collections.map((collection) => {
return getCollection(collection, {
fields: ["slug", "title", "date", "tags"],
});
}),
);
const tags = new Set();
posts.flat().forEach((post) => {
if (!post.data?.tags) return;
post.data.tags.forEach((tag) => {
tags.add(tag.toLowerCase());
});
});
const _tags = [...tags.values()];
---
<Layout title="Max Richter">
<article>
<h1>Tags</h1>
<div class="flex flex-col gap-2">
{
_tags.map((t) => {
return <a href={tp("/tag/" + t)}>{t}</a>;
})
}
</div>
</article>
</Layout>

View File

@ -15,7 +15,7 @@ export default defineConfig({
"border-neutral": "border-neutral-300 dark:border-neutral-1000", "border-neutral": "border-neutral-300 dark:border-neutral-1000",
"border-light": "border-neutral-100 dark:border-neutral-500", "border-light": "border-neutral-100 dark:border-neutral-500",
"divide-x-neutral": ['divide-x-neutral-300', "dark:divide-x-neutral-1000"], "divide-x-neutral": ['divide-x-neutral-300', "dark:divide-x-neutral-1000"],
"gradient": "bg-gradient-to-br from-neutral-000 dark:from-neutral-500 to-neutral-200 dark:to-neutral-800", "gradient": "bg-gradient-to-br from-neutral-000 to-neutral-000 dark:from-neutral-500 dark:to-neutral-800",
}, },
theme: { theme: {
borderRadius: { borderRadius: {