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
|
||||
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 }}
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
- name: Retrieve Vercel Preview URL
|
||||
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
|
||||
run: npm run build
|
||||
|
||||
- name: Start Preview and Get Preview URL
|
||||
run: npm run preview -- --port=${{ env.PORT }} & echo $! > preview_pid
|
||||
- name: Await Vercel Deployment
|
||||
uses: UnlyEd/github-action-await-vercel@v1.2.43
|
||||
env:
|
||||
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
|
||||
with:
|
||||
deployment-url: ${{ steps.vercel_preview_url.outputs.preview_url }}
|
||||
timeout: 120
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm add -g @unlighthouse/cli puppeteer
|
||||
run: pnpm install -g @unlighthouse/cli puppeteer
|
||||
|
||||
- name: Run Unlighthouse
|
||||
run: |
|
||||
unlighthouse-ci \
|
||||
--site "http://localhost:${{ env.PORT }}" \
|
||||
--site "${{ steps.vercel_preview_url.outputs.preview_url }}" \
|
||||
--reporter jsonExpanded \
|
||||
--build-static
|
||||
|
||||
- name: Upload report to Cloudflare pages
|
||||
uses: cloudflare/wrangler-action@v3.3.2
|
||||
uses: cloudflare/wrangler-action@2.0.0
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
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="16x16" href="%sveltekit.assets%/favicon-16x16.png" />
|
||||
<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="theme-color" content="#ffffff" />
|
||||
<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="">
|
||||
<svelte:fragment slot="lead">
|
||||
<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>
|
||||
</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 Navigation from '../lib/components/Navigation.svelte';
|
||||
import Header from '$lib/components/Header.svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
export let data;
|
||||
|
||||
$: ({ pathname } = data);
|
||||
|
||||
import { dev } from '$app/environment';
|
||||
import { inject } from '@vercel/analytics';
|
||||
|
@ -47,7 +52,11 @@
|
|||
<Header {progress} />
|
||||
</svelte:fragment>
|
||||
|
||||
<slot />
|
||||
{#key pathname}
|
||||
<div in:fade={{ duration: 300, delay: 400 }} out:fade={{ duration: 300 }}>
|
||||
<slot />
|
||||
</div>
|
||||
{/key}
|
||||
|
||||
<svelte:fragment slot="pageFooter">
|
||||
<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">
|
||||
<img
|
||||
src={bartvdbraakImage}
|
||||
loading="lazy"
|
||||
alt="Bart van der Braak with a noire effect"
|
||||
class="h-full w-full object-cover profile-fit"
|
||||
/>
|
||||
|
|
|
@ -31,9 +31,8 @@ const projects: Project[] = [
|
|||
headerImage: keyweaveImage,
|
||||
headerSubTitle: 'Open Source Project',
|
||||
title: 'Keyweave',
|
||||
description: `Keyweave is an open-source tool crafted 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 user-friendly design, making it an ideal choice for managing your application's secrets.`,
|
||||
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.`,
|
||||
logo: keyweaveLogo,
|
||||
contributors: [],
|
||||
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>
|
||||
</div>
|
||||
<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>
|
||||
<Scene />
|
||||
</Canvas>
|
||||
|
@ -35,15 +35,17 @@
|
|||
</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}
|
||||
<ToolCard
|
||||
name={tool.name}
|
||||
title={tool.title}
|
||||
description={tool.description}
|
||||
logo={tool.logo}
|
||||
toolUrl={tool.toolUrl}
|
||||
/>
|
||||
<li>
|
||||
<ToolCard
|
||||
name={tool.name}
|
||||
title={tool.title}
|
||||
description={tool.description}
|
||||
logo={tool.logo}
|
||||
toolUrl={tool.toolUrl}
|
||||
/>
|
||||
</li>
|
||||
{/each}
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -14,7 +14,12 @@
|
|||
>
|
||||
<div class="flex flex-col 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">
|
||||
<h4 class="mb-0">{name}</h4>
|
||||
<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"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<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">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</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">
|
||||
<path d="M0 2560 l0 -2560 2560 0 2560 0 0 2560 0 2560 -2560 0 -2560 0 0
|
||||
-2560z m4610 0 l0 -2050 -2050 0 -2050 0 0 2050 0 2050 2050 0 2050 0 0 -2050z"/>
|
||||
<path d="M2820 4080 c0 -242 2 -270 16 -270 30 0 222 -69 296 -106 113 -57
|
||||
214 -129 311 -223 196 -189 315 -403 373 -671 26 -119 26 -381 0 -500 -85
|
||||
-393 -326 -710 -672 -887 -90 -46 -273 -113 -309 -113 -13 0 -15 -38 -15 -270
|
||||
l0 -270 765 0 765 0 0 1790 0 1790 -765 0 -765 0 0 -270z"/>
|
||||
<path d="M2382 3569 c-90 -16 -238 -69 -318 -115 -244 -139 -406 -343 -491
|
||||
-621 -25 -82 -27 -100 -27 -273 0 -173 2 -191 27 -273 58 -187 145 -334 274
|
||||
-458 131 -126 259 -201 438 -256 84 -25 102 -27 275 -27 173 0 191 2 275 27
|
||||
355 108 602 355 712 713 25 83 27 101 27 274 0 173 -2 191 -27 273 -58 187
|
||||
-135 317 -266 448 -132 132 -264 210 -448 265 -75 22 -111 27 -243 30 -85 1
|
||||
-179 -1 -208 -7z"/>
|
||||
<path d="M0 3500 l0 -3500 1575 0 1575 0 0 350 0 350 -1225 0 -1225 0 0 2800
|
||||
0 2800 1225 0 1225 0 0 350 0 350 -1575 0 -1575 0 0 -3500z"/>
|
||||
<path d="M3850 6475 l0 -175 1225 0 1225 0 0 -2800 0 -2800 -1225 0 -1225 0 0
|
||||
-175 0 -175 1400 0 1400 0 0 3150 0 3150 -1400 0 -1400 0 0 -175z"/>
|
||||
<path d="M3850 5581 l0 -368 40 -7 c147 -23 466 -166 622 -278 516 -373 793
|
||||
-984 728 -1606 -56 -532 -349 -1008 -798 -1297 -146 -94 -426 -211 -552 -231
|
||||
l-40 -7 0 -368 0 -369 1050 0 1050 0 0 2450 0 2450 -1050 0 -1050 0 0 -369z"/>
|
||||
<path d="M3322 4889 c-175 -24 -368 -89 -517 -176 -121 -69 -212 -142 -317
|
||||
-252 -187 -194 -301 -410 -360 -684 -31 -145 -31 -407 0 -554 63 -294 193
|
||||
-527 409 -733 200 -192 431 -311 708 -366 134 -27 396 -25 532 4 294 63 526
|
||||
193 734 409 187 195 302 415 361 686 31 146 31 408 0 554 -64 294 -193 526
|
||||
-410 734 -194 187 -415 303 -680 359 -114 24 -350 34 -460 19z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
@ -1,19 +1,19 @@
|
|||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
|
|