Merge pull request #157 from bartvdbraak/feat/105-implement-seo-optimizations-merge
Merging of SEO optimizations, sitemap and `robots.txt`
33
.github/workflows/unlighthouse.yaml
vendored
|
@ -29,32 +29,45 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4.1.1
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v2.4.0
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.0
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Retrieve Vercel Preview URL
|
||||||
run: npm install
|
uses: zentered/vercel-preview-url@v1.1.9
|
||||||
|
id: vercel_preview_url
|
||||||
|
env:
|
||||||
|
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
|
||||||
|
with:
|
||||||
|
vercel_project_id: ${{ vars.VERCEL_PROJECT_ID }}
|
||||||
|
|
||||||
- name: Build production
|
- name: Await Vercel Deployment
|
||||||
run: npm run build
|
uses: UnlyEd/github-action-await-vercel@v1.2.43
|
||||||
|
env:
|
||||||
- name: Start Preview and Get Preview URL
|
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
|
||||||
run: npm run preview -- --port=${{ env.PORT }} & echo $! > preview_pid
|
with:
|
||||||
|
deployment-url: ${{ steps.vercel_preview_url.outputs.preview_url }}
|
||||||
|
timeout: 120
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm add -g @unlighthouse/cli puppeteer
|
run: pnpm install -g @unlighthouse/cli puppeteer
|
||||||
|
|
||||||
- name: Run Unlighthouse
|
- name: Run Unlighthouse
|
||||||
run: |
|
run: |
|
||||||
unlighthouse-ci \
|
unlighthouse-ci \
|
||||||
--site "http://localhost:${{ env.PORT }}" \
|
--site "${{ steps.vercel_preview_url.outputs.preview_url }}" \
|
||||||
--reporter jsonExpanded \
|
--reporter jsonExpanded \
|
||||||
--build-static
|
--build-static
|
||||||
|
|
||||||
- name: Upload report to Cloudflare pages
|
- name: Upload report to Cloudflare pages
|
||||||
uses: cloudflare/wrangler-action@v3.3.2
|
uses: cloudflare/wrangler-action@2.0.0
|
||||||
with:
|
with:
|
||||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
command: pages deploy .unlighthouse --project-name="${{ env.CLOUDFLARE_PROJECT }}" --branch=${{ env.CLOUDFLARE_BRANCH }}
|
command: pages deploy .unlighthouse --project-name="${{ env.CLOUDFLARE_PROJECT }}" --branch=${{ env.CLOUDFLARE_BRANCH }}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="%sveltekit.assets%/favicon-32x32.png" />
|
<link rel="icon" type="image/png" sizes="32x32" href="%sveltekit.assets%/favicon-32x32.png" />
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="%sveltekit.assets%/favicon-16x16.png" />
|
<link rel="icon" type="image/png" sizes="16x16" href="%sveltekit.assets%/favicon-16x16.png" />
|
||||||
<link rel="manifest" href="%sveltekit.assets%/site.webmanifest" />
|
<link rel="manifest" href="%sveltekit.assets%/site.webmanifest" />
|
||||||
<link rel="mask-icon" href="%sveltekit.assets%/safari-pinned-tab.svg" color="#5bbad5" />
|
<link rel="mask-icon" href="%sveltekit.assets%/safari-pinned-tab.svg" color="#000000" />
|
||||||
<meta name="msapplication-TileColor" content="#da532c" />
|
<meta name="msapplication-TileColor" content="#da532c" />
|
||||||
<meta name="theme-color" content="#ffffff" />
|
<meta name="theme-color" content="#ffffff" />
|
||||||
<title></title>
|
<title></title>
|
||||||
|
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 48 KiB |
|
@ -20,7 +20,7 @@
|
||||||
<AppBar background="">
|
<AppBar background="">
|
||||||
<svelte:fragment slot="lead">
|
<svelte:fragment slot="lead">
|
||||||
<a href="/" class="md:ml-4 ml-1">
|
<a href="/" class="md:ml-4 ml-1">
|
||||||
<img src="./logo@3x.png" alt="hellob.art logo" class="max-h-8" />
|
<img width="212" height="32" src="./logo@3x.png" alt="hellob.art logo" />
|
||||||
</a>
|
</a>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
|
||||||
|
|
4
src/lib/site-config.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export const SITE_URL = 'https://hellob.art';
|
||||||
|
export const SITE_TITLE = 'hellob.art';
|
||||||
|
export const SITE_DESCRIPTION = '';
|
||||||
|
export const DEFAULT_OG_IMAGE = '';
|
|
@ -5,6 +5,11 @@
|
||||||
import Footer from '../lib/components/Footer.svelte';
|
import Footer from '../lib/components/Footer.svelte';
|
||||||
import Navigation from '../lib/components/Navigation.svelte';
|
import Navigation from '../lib/components/Navigation.svelte';
|
||||||
import Header from '$lib/components/Header.svelte';
|
import Header from '$lib/components/Header.svelte';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
|
export let data;
|
||||||
|
|
||||||
|
$: ({ pathname } = data);
|
||||||
|
|
||||||
import { dev } from '$app/environment';
|
import { dev } from '$app/environment';
|
||||||
import { inject } from '@vercel/analytics';
|
import { inject } from '@vercel/analytics';
|
||||||
|
@ -47,7 +52,11 @@
|
||||||
<Header {progress} />
|
<Header {progress} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
|
||||||
|
{#key pathname}
|
||||||
|
<div in:fade={{ duration: 300, delay: 400 }} out:fade={{ duration: 300 }}>
|
||||||
<slot />
|
<slot />
|
||||||
|
</div>
|
||||||
|
{/key}
|
||||||
|
|
||||||
<svelte:fragment slot="pageFooter">
|
<svelte:fragment slot="pageFooter">
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|
7
src/routes/+layout.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export const load = ({ url }) => {
|
||||||
|
const { pathname } = url;
|
||||||
|
|
||||||
|
return {
|
||||||
|
pathname
|
||||||
|
};
|
||||||
|
};
|
|
@ -21,7 +21,6 @@
|
||||||
<div class="h-64 overflow-hidden rounded-lg bg-gray-100 shadow-lg md:h-auto sticky top-10">
|
<div class="h-64 overflow-hidden rounded-lg bg-gray-100 shadow-lg md:h-auto sticky top-10">
|
||||||
<img
|
<img
|
||||||
src={bartvdbraakImage}
|
src={bartvdbraakImage}
|
||||||
loading="lazy"
|
|
||||||
alt="Bart van der Braak with a noire effect"
|
alt="Bart van der Braak with a noire effect"
|
||||||
class="h-full w-full object-cover profile-fit"
|
class="h-full w-full object-cover profile-fit"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -31,9 +31,8 @@ const projects: Project[] = [
|
||||||
headerImage: keyweaveImage,
|
headerImage: keyweaveImage,
|
||||||
headerSubTitle: 'Open Source Project',
|
headerSubTitle: 'Open Source Project',
|
||||||
title: 'Keyweave',
|
title: 'Keyweave',
|
||||||
description: `Keyweave is an open-source tool crafted to seamlessly fetch secrets from Azure Key Vault
|
description: `Keyweave is an open-source tool crafted in Rust to seamlessly fetch secrets from Azure Key Vault
|
||||||
and weave them into a convenient .env file. Developed in Rust, Keyweave stands out for its efficiency
|
and weave them into a convenient .env file.`,
|
||||||
and user-friendly design, making it an ideal choice for managing your application's secrets.`,
|
|
||||||
logo: keyweaveLogo,
|
logo: keyweaveLogo,
|
||||||
contributors: [],
|
contributors: [],
|
||||||
date: '11-05-2023'
|
date: '11-05-2023'
|
||||||
|
|
41
src/routes/sitemap.xml/+server.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { SITE_URL } from '$lib/site-config';
|
||||||
|
|
||||||
|
/** @type {import('@sveltejs/kit').RequestHandler} */
|
||||||
|
export async function GET() {
|
||||||
|
const pages = ['projects', 'toolbox'];
|
||||||
|
const body = sitemap(pages);
|
||||||
|
|
||||||
|
return new Response(body, {
|
||||||
|
headers: {
|
||||||
|
'Cache-Control': `public, max-age=${86400}`, // 24 hours
|
||||||
|
'Content-Type': 'application/xml'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const sitemap = (pages: string[]) => `<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<urlset
|
||||||
|
xmlns="https://www.sitemaps.org/schemas/sitemap/0.9"
|
||||||
|
xmlns:news="https://www.google.com/schemas/sitemap-news/0.9"
|
||||||
|
xmlns:xhtml="https://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:mobile="https://www.google.com/schemas/sitemap-mobile/1.0"
|
||||||
|
xmlns:image="https://www.google.com/schemas/sitemap-image/1.1"
|
||||||
|
xmlns:video="https://www.google.com/schemas/sitemap-video/1.1"
|
||||||
|
>
|
||||||
|
<url>
|
||||||
|
<loc>${SITE_URL}</loc>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>0.7</priority>
|
||||||
|
</url>
|
||||||
|
${pages
|
||||||
|
.map(
|
||||||
|
(page) => `
|
||||||
|
<url>
|
||||||
|
<loc>${SITE_URL}/${page}</loc>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>0.7</priority>
|
||||||
|
</url>
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.join('')}
|
||||||
|
</urlset>`;
|
|
@ -1,20 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import { Canvas } from '@threlte/core';
|
|
||||||
import Scene from '../toolbox/Scene.svelte';
|
|
||||||
import { Theatre } from '@threlte/theatre';
|
|
||||||
import { T } from '@threlte/core';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>hellob.art — theatre</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<main class="container h-screen w-full">
|
|
||||||
<Canvas>
|
|
||||||
<Theatre>
|
|
||||||
<Scene />
|
|
||||||
<T.AxesHelper />
|
|
||||||
<T.GridHelper />
|
|
||||||
</Theatre>
|
|
||||||
</Canvas>
|
|
||||||
</main>
|
|
|
@ -1,21 +0,0 @@
|
||||||
<script>
|
|
||||||
import { T, useFrame } from '@threlte/core';
|
|
||||||
import { OrbitControls } from '@threlte/extras';
|
|
||||||
import Warp from '../toolbox/models/Warp.svelte';
|
|
||||||
|
|
||||||
let githubRotation = 0;
|
|
||||||
useFrame((state, delta) => {
|
|
||||||
githubRotation += 0.1 * delta;
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<T.PerspectiveCamera position={[0, 5, 10]} makeDefault>
|
|
||||||
<OrbitControls target.y={1.5} />
|
|
||||||
</T.PerspectiveCamera>
|
|
||||||
<T.DirectionalLight intensity={0.5} position.x={2} position.y={3} />
|
|
||||||
<T.AmbientLight intensity={0.9} />
|
|
||||||
|
|
||||||
<Warp rotation.y={githubRotation} />
|
|
||||||
|
|
||||||
<T.AxesHelper></T.AxesHelper>
|
|
||||||
<T.GridHelper></T.GridHelper>
|
|
|
@ -27,7 +27,7 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="md:col-span-1 flex justify-end">
|
<div class="md:col-span-1 flex justify-end">
|
||||||
<div class="h-60 w-full">
|
<div class="h-60 md:h-full w-full">
|
||||||
<Canvas>
|
<Canvas>
|
||||||
<Scene />
|
<Scene />
|
||||||
</Canvas>
|
</Canvas>
|
||||||
|
@ -35,8 +35,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="columns-1 md:columns-2 lg:columns-3 gap-6 w-full mb-12">
|
<ul class="columns-1 md:columns-2 lg:columns-3 gap-6 w-full mb-12">
|
||||||
{#each tools as tool}
|
{#each tools as tool}
|
||||||
|
<li>
|
||||||
<ToolCard
|
<ToolCard
|
||||||
name={tool.name}
|
name={tool.name}
|
||||||
title={tool.title}
|
title={tool.title}
|
||||||
|
@ -44,6 +45,7 @@
|
||||||
logo={tool.logo}
|
logo={tool.logo}
|
||||||
toolUrl={tool.toolUrl}
|
toolUrl={tool.toolUrl}
|
||||||
/>
|
/>
|
||||||
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,7 +14,12 @@
|
||||||
>
|
>
|
||||||
<div class="flex flex-col gap-6">
|
<div class="flex flex-col gap-6">
|
||||||
<div class="flex gap-6">
|
<div class="flex gap-6">
|
||||||
<img src={logo} alt={name + ' logo'} class="h-12 w-12 rounded-sm object-contain" />
|
<img
|
||||||
|
src={logo}
|
||||||
|
alt={name + ' logo'}
|
||||||
|
class="h-12 w-12 rounded-sm object-contain"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
<h4 class="mb-0">{name}</h4>
|
<h4 class="mb-0">{name}</h4>
|
||||||
<p class="text-faded text-sm font-normal">{title}</p>
|
<p class="text-faded text-sm font-normal">{title}</p>
|
||||||
|
|
6
static/robots.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
Sitemap: https://hellob.art/sitemap.xml
|
||||||
|
|
||||||
|
# https://developers.google.com/search/docs/advanced/sitemaps/build-sitemap#addsitemap
|
||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
|
@ -2,24 +2,24 @@
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
|
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
|
||||||
preserveAspectRatio="xMidYMid meet">
|
preserveAspectRatio="xMidYMid meet">
|
||||||
<metadata>
|
<metadata>
|
||||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||||
</metadata>
|
</metadata>
|
||||||
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
|
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
|
||||||
fill="#000000" stroke="none">
|
fill="#000000" stroke="none">
|
||||||
<path d="M0 2560 l0 -2560 2560 0 2560 0 0 2560 0 2560 -2560 0 -2560 0 0
|
<path d="M0 3500 l0 -3500 1575 0 1575 0 0 350 0 350 -1225 0 -1225 0 0 2800
|
||||||
-2560z m4610 0 l0 -2050 -2050 0 -2050 0 0 2050 0 2050 2050 0 2050 0 0 -2050z"/>
|
0 2800 1225 0 1225 0 0 350 0 350 -1575 0 -1575 0 0 -3500z"/>
|
||||||
<path d="M2820 4080 c0 -242 2 -270 16 -270 30 0 222 -69 296 -106 113 -57
|
<path d="M3850 6475 l0 -175 1225 0 1225 0 0 -2800 0 -2800 -1225 0 -1225 0 0
|
||||||
214 -129 311 -223 196 -189 315 -403 373 -671 26 -119 26 -381 0 -500 -85
|
-175 0 -175 1400 0 1400 0 0 3150 0 3150 -1400 0 -1400 0 0 -175z"/>
|
||||||
-393 -326 -710 -672 -887 -90 -46 -273 -113 -309 -113 -13 0 -15 -38 -15 -270
|
<path d="M3850 5581 l0 -368 40 -7 c147 -23 466 -166 622 -278 516 -373 793
|
||||||
l0 -270 765 0 765 0 0 1790 0 1790 -765 0 -765 0 0 -270z"/>
|
-984 728 -1606 -56 -532 -349 -1008 -798 -1297 -146 -94 -426 -211 -552 -231
|
||||||
<path d="M2382 3569 c-90 -16 -238 -69 -318 -115 -244 -139 -406 -343 -491
|
l-40 -7 0 -368 0 -369 1050 0 1050 0 0 2450 0 2450 -1050 0 -1050 0 0 -369z"/>
|
||||||
-621 -25 -82 -27 -100 -27 -273 0 -173 2 -191 27 -273 58 -187 145 -334 274
|
<path d="M3322 4889 c-175 -24 -368 -89 -517 -176 -121 -69 -212 -142 -317
|
||||||
-458 131 -126 259 -201 438 -256 84 -25 102 -27 275 -27 173 0 191 2 275 27
|
-252 -187 -194 -301 -410 -360 -684 -31 -145 -31 -407 0 -554 63 -294 193
|
||||||
355 108 602 355 712 713 25 83 27 101 27 274 0 173 -2 191 -27 273 -58 187
|
-527 409 -733 200 -192 431 -311 708 -366 134 -27 396 -25 532 4 294 63 526
|
||||||
-135 317 -266 448 -132 132 -264 210 -448 265 -75 22 -111 27 -243 30 -85 1
|
193 734 409 187 195 302 415 361 686 31 146 31 408 0 554 -64 294 -193 526
|
||||||
-179 -1 -208 -7z"/>
|
-410 734 -194 187 -415 303 -680 359 -114 24 -350 34 -460 19z"/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |