feat: add ci
This commit is contained in:
parent
93baa3b6b0
commit
e800314589
48
.github/workflows/default.yaml
vendored
Normal file
48
.github/workflows/default.yaml
vendored
Normal 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'
|
@ -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;
|
||||||
|
@ -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) : "")}
|
||||||
|
@ -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 && (
|
||||||
|
@ -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,
|
||||||
|
@ -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>
|
||||||
|
59
src/components/ThemeToggle.svelte
Normal file
59
src/components/ThemeToggle.svelte
Normal 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>
|
@ -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 }>
|
|
||||||
|
|
||||||
|
@ -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 />
|
||||||
|
@ -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
71
src/pages/tag/[tag].astro
Normal 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
41
src/pages/tag/index.astro
Normal 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>
|
@ -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: {
|
||||||
|
Loading…
Reference in New Issue
Block a user