mirror of
https://github.com/BradNut/personal-website-sveltekit
synced 2025-09-08 23:20:18 +00:00
commit
97551e7b5c
23 changed files with 1748 additions and 1088 deletions
12
.github/workflows/svelte_check.yml
vendored
12
.github/workflows/svelte_check.yml
vendored
|
|
@ -3,6 +3,8 @@ name: Run_Svelte_Check_on_PRs
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
WALLABAG_MAX_ARTICLES: ${{ secrets.WALLABAG_MAX_ARTICLES }}
|
WALLABAG_MAX_ARTICLES: ${{ secrets.WALLABAG_MAX_ARTICLES }}
|
||||||
WALLABAG_MAX_PAGES: ${{ secrets.WALLABAG_MAX_PAGES }}
|
WALLABAG_MAX_PAGES: ${{ secrets.WALLABAG_MAX_PAGES }}
|
||||||
|
|
@ -27,17 +29,17 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: pnpm-setup
|
- name: pnpm-setup
|
||||||
uses: pnpm/action-setup@v2
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
version: 8
|
version: 10
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18.18.2
|
node-version: 22.x
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
|
|
||||||
41
package.json
41
package.json
|
|
@ -18,44 +18,43 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.9.4",
|
"@biomejs/biome": "^1.9.4",
|
||||||
"@playwright/test": "^1.49.1",
|
"@playwright/test": "^1.52.0",
|
||||||
"@sveltejs/enhanced-img": "^0.4.4",
|
"@sveltejs/enhanced-img": "^0.4.4",
|
||||||
"@sveltejs/kit": "^2.15.0",
|
"@sveltejs/kit": "^2.20.7",
|
||||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||||
"@types/nprogress": "^0.2.3",
|
|
||||||
"@unpic/svelte": "^1.0.0",
|
"@unpic/svelte": "^1.0.0",
|
||||||
"@zerodevx/svelte-img": "^2.1.2",
|
"@zerodevx/svelte-img": "^2.1.2",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.21",
|
||||||
"just-intersect": "^4.3.0",
|
"just-intersect": "^4.3.0",
|
||||||
"postcss": "^8.4.49",
|
"postcss": "^8.5.3",
|
||||||
|
"postcss-custom-media": "^11.0.5",
|
||||||
"postcss-import": "^16.1.0",
|
"postcss-import": "^16.1.0",
|
||||||
"postcss-load-config": "^6.0.1",
|
"postcss-load-config": "^6.0.1",
|
||||||
"postcss-preset-env": "^10.1.4",
|
"postcss-preset-env": "^10.1.6",
|
||||||
"satori": "^0.12.0",
|
"satori": "^0.12.2",
|
||||||
"satori-html": "^0.3.2",
|
"satori-html": "^0.3.2",
|
||||||
"svelte": "^5.15.0",
|
"svelte": "^5.28.2",
|
||||||
"svelte-check": "^4.1.0",
|
"svelte-check": "^4.1.6",
|
||||||
"svelte-meta-tags": "^4.0.4",
|
"svelte-meta-tags": "^4.2.0",
|
||||||
"svelte-preprocess": "^6.0.3",
|
"svelte-preprocess": "^6.0.3",
|
||||||
"svelte-sequential-preprocessor": "^2.0.2",
|
"svelte-sequential-preprocessor": "^2.0.2",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.7.2",
|
"typescript": "^5.8.3",
|
||||||
"vanilla-lazyload": "^19.1.3",
|
"vanilla-lazyload": "^19.1.3",
|
||||||
"vite": "^6.0.5",
|
"vite": "^6.3.3",
|
||||||
"vite-imagetools": "^7.0.5",
|
"vite-imagetools": "^7.0.5",
|
||||||
"vitest": "^3.0.6"
|
"vitest": "^3.1.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@resvg/resvg-js": "^2.6.2",
|
"@resvg/resvg-js": "^2.6.2",
|
||||||
"@sveltejs/adapter-node": "^5.2.11",
|
"@sveltejs/adapter-node": "^5.2.11",
|
||||||
"@vercel/og": "^0.6.4",
|
"@vercel/og": "^0.6.8",
|
||||||
"bits-ui": "1.3.0",
|
"bits-ui": "1.4.1",
|
||||||
"flexsearch": "^0.7.43",
|
"flexsearch": "^0.8.158",
|
||||||
"ioredis": "^5.4.2",
|
"ioredis": "^5.6.1",
|
||||||
"lucide-svelte": "^0.475.0",
|
"lucide-svelte": "^0.503.0",
|
||||||
"nprogress": "^0.2.0",
|
"scrape-it": "^6.1.5",
|
||||||
"scrape-it": "^6.1.3",
|
"sharp": "^0.34.1",
|
||||||
"sharp": "^0.33.5",
|
|
||||||
"svelte-local-storage-store": "^0.6.4"
|
"svelte-local-storage-store": "^0.6.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1884
pnpm-lock.yaml
1884
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,6 @@
|
||||||
const postcssPresetEnv = require('postcss-preset-env');
|
const postcssPresetEnv = require('postcss-preset-env');
|
||||||
const atImport = require('postcss-import');
|
const atImport = require('postcss-import');
|
||||||
|
const postCssCustomMedia = require('postcss-custom-media');
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|
@ -11,6 +12,18 @@ const config = {
|
||||||
'custom-media-queries': true,
|
'custom-media-queries': true,
|
||||||
'media-query-ranges': true
|
'media-query-ranges': true
|
||||||
}
|
}
|
||||||
|
}),
|
||||||
|
postCssCustomMedia({
|
||||||
|
customMedia: {
|
||||||
|
'--below_small': '(width < 400px)',
|
||||||
|
'--below_med': '(width < 700px)',
|
||||||
|
'--below_large': '(width < 900px)',
|
||||||
|
'--below_xlarge': '(width < 1200px)',
|
||||||
|
'--above_small': '(width > 400px)',
|
||||||
|
'--above_med': '(width > 700px)',
|
||||||
|
'--above_large': '(width > 900px)',
|
||||||
|
'--above_xlarge': '(width > 1200px)',
|
||||||
|
}
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
|
||||||
19
src/app.d.ts
vendored
19
src/app.d.ts
vendored
|
|
@ -1,12 +1,19 @@
|
||||||
// See https://kit.svelte.dev/docs/types#app
|
// See https://kit.svelte.dev/docs/types#app
|
||||||
// for information about these interfaces
|
// for information about these interfaces
|
||||||
declare global {
|
declare global {
|
||||||
namespace App {
|
namespace App {
|
||||||
// interface Error {}
|
// interface Error {}
|
||||||
// interface Locals {}
|
// interface Locals {}
|
||||||
// interface PageData {}
|
// interface PageData {}
|
||||||
// interface Platform {}
|
// interface Platform {}
|
||||||
}
|
interface Document {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||||
|
startViewTransition: (callback: never) => void; // Add your custom property/method here
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// biome-ignore lint/complexity/noUselessEmptyExport: <explanation>
|
||||||
|
// biome-ignore lint/style/useExportType: <explanation>
|
||||||
export {};
|
export {};
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import type {
|
||||||
WallabagArticle,
|
WallabagArticle,
|
||||||
} from "$lib/types/article";
|
} from "$lib/types/article";
|
||||||
import { ArticleTag } from "$lib/types/articleTag";
|
import { ArticleTag } from "$lib/types/articleTag";
|
||||||
import type { PageQuery } from "$lib/types/pageQuery";
|
import type { PageQuery } from "./types/pageQuery";
|
||||||
import { URLSearchParams } from "node:url";
|
import { URLSearchParams } from "node:url";
|
||||||
import { redis } from "$lib/server/redis";
|
import { redis } from "$lib/server/redis";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
const userNames = {
|
const userNames = {
|
||||||
github: 'BradNut',
|
github: 'BradNut',
|
||||||
linkedIn: 'bradley-shellnut',
|
linkedIn: 'bradley-shellnut',
|
||||||
email: 'bradleyshellnut[at]pm.me',
|
email: 'website[at]bradleyshellnut.com',
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
<script module>
|
<script module>
|
||||||
import { ExternalLink } from 'lucide-svelte';
|
|
||||||
export { blueSkyIcon, dockerIcon, drizzleIcon, honoIcon, gitHubIcon, linkedInIcon, lucideIcon, nextDotJsIcon, reactIcon, svelteIcon, typescriptIcon, xIcon };
|
export { blueSkyIcon, dockerIcon, drizzleIcon, honoIcon, gitHubIcon, linkedInIcon, lucideIcon, nextDotJsIcon, reactIcon, svelteIcon, typescriptIcon, xIcon };
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
55
src/lib/util/page_loading_indicator.svelte
Normal file
55
src/lib/util/page_loading_indicator.svelte
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { onNavigate } from "$app/navigation";
|
||||||
|
|
||||||
|
let visible = $state(false);
|
||||||
|
let progress = $state(0);
|
||||||
|
let load_durations = $state<number[]>([]);
|
||||||
|
let average_load = $derived(
|
||||||
|
load_durations.reduce((a, b) => a + b, 0) / load_durations.length,
|
||||||
|
);
|
||||||
|
|
||||||
|
const increment = 1;
|
||||||
|
|
||||||
|
onNavigate((navigation) => {
|
||||||
|
const typical_load_time = average_load || 200; //ms
|
||||||
|
const frequency = typical_load_time / 100;
|
||||||
|
let start = performance.now();
|
||||||
|
// Start the progress bar
|
||||||
|
visible = true;
|
||||||
|
progress = 0;
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
// Increment the progress bar
|
||||||
|
progress += increment;
|
||||||
|
}, frequency);
|
||||||
|
// Resolve the promise when the page is done loading
|
||||||
|
navigation?.complete.then(() => {
|
||||||
|
progress = 100; // Fill out the progress bar
|
||||||
|
clearInterval(interval);
|
||||||
|
// after 100 ms hide the progress bar
|
||||||
|
setTimeout(() => {
|
||||||
|
visible = false;
|
||||||
|
}, 500);
|
||||||
|
// Log how long that one took
|
||||||
|
const end = performance.now();
|
||||||
|
const duration = end - start;
|
||||||
|
load_durations = [...load_durations, duration];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if visible}
|
||||||
|
<div class="progress" style="width: {progress}%;"></div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style lang="postcss">
|
||||||
|
.progress {
|
||||||
|
background: var(--lightGrey);
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 0.25rem;
|
||||||
|
z-index: 50;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,51 +1,50 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { MetaTags } from 'svelte-meta-tags';
|
import "../styles/styles.pcss";
|
||||||
import NProgress from "nprogress";
|
import { MetaTags } from "svelte-meta-tags";
|
||||||
import { browser } from "$app/environment";
|
import { dev } from "$app/environment";
|
||||||
import { navigating, page } from "$app/stores";
|
import { page } from "$app/state";
|
||||||
import { PUBLIC_SITE_URL } from '$env/static/public';
|
import Header from "../lib/components/header/index.svelte";
|
||||||
import "nprogress/nprogress.css";
|
import Footer from "../lib/components/footer/index.svelte";
|
||||||
import '$root/styles/styles.pcss';
|
import Analytics from "../lib/components/analytics/index.svelte";
|
||||||
import Header from '$lib/components/header/index.svelte';
|
import { onNavigate } from "$app/navigation";
|
||||||
import Footer from '$lib/components/footer/index.svelte';
|
import PageLoadingIndicator from "../lib/util/page_loading_indicator.svelte";
|
||||||
import Analytics from '$lib/components/analytics/index.svelte';
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children?: import('svelte').Snippet;
|
children?: import("svelte").Snippet;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { children }: Props = $props();
|
let { children }: Props = $props();
|
||||||
|
|
||||||
NProgress.configure({
|
const production = !dev || import.meta.env.NODE_ENV !== "production";
|
||||||
// Full list: https://github.com/rstacruz/nprogress#configuration
|
|
||||||
minimum: 0.16,
|
|
||||||
});
|
|
||||||
|
|
||||||
const dev = process.env.NODE_ENV !== 'production';
|
onNavigate(async (navigation) => {
|
||||||
|
if (!document.startViewTransition) return;
|
||||||
|
|
||||||
$effect(() => {
|
return new Promise((oldStateCaptureResolve) => {
|
||||||
if (browser && $navigating) {
|
document.startViewTransition(async () => {
|
||||||
NProgress.start();
|
oldStateCaptureResolve();
|
||||||
} else {
|
await navigation.complete;
|
||||||
NProgress.done();
|
});
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let metaTags = $derived({
|
let metaTags = $derived({
|
||||||
titleTemplate: '%s | Bradley Shellnut',
|
titleTemplate: "%s | Bradley Shellnut",
|
||||||
additionalMetaTags: [
|
additionalMetaTags: [
|
||||||
{
|
{
|
||||||
property: 'theme-color',
|
property: "theme-color",
|
||||||
content: '#272727'
|
content: "#272727",
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
...$page.data.metaTagsChild
|
...page.data.metaTagsChild,
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if !dev}
|
{#if production}
|
||||||
<Analytics />
|
<Analytics />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<PageLoadingIndicator />
|
||||||
<MetaTags {...metaTags} />
|
<MetaTags {...metaTags} />
|
||||||
|
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
|
|
@ -77,24 +76,15 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(#nprogress .bar) {
|
:global(p) {
|
||||||
background: var(--lightGrey);
|
word-wrap: normal;
|
||||||
|
font-size: var(--bodyTextSize);
|
||||||
|
color: var(--lightShade);
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(#nprogress .spinner-icon) {
|
|
||||||
border-top-color: var(--lightGrey);
|
|
||||||
border-left-color: var(--lightGrey);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(p) {
|
|
||||||
word-wrap: normal;
|
|
||||||
font-size: var(--bodyTextSize);
|
|
||||||
color: var(--lightShade);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(li) {
|
:global(li) {
|
||||||
word-wrap: normal;
|
word-wrap: normal;
|
||||||
font-size: var(--bodyTextSize);
|
font-size: var(--bodyTextSize);
|
||||||
color: var(--lightShade);
|
color: var(--lightShade);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ let totalArticles: number = $derived(articlesData.totalArticles);
|
||||||
const userNames = {
|
const userNames = {
|
||||||
github: 'BradNut',
|
github: 'BradNut',
|
||||||
linkedIn: 'bradley-shellnut',
|
linkedIn: 'bradley-shellnut',
|
||||||
email: 'bradleyshellnut@pm.me',
|
email: 'website@bradleyshellnut.com',
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,20 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import cruise from '$lib/assets/images/cruise.png?enhanced';
|
import orange_derp from "../../lib/assets/images/orange_derp.jpg?enhanced";
|
||||||
import orange_derp from '$lib/assets/images/orange_derp.jpg?enhanced';
|
import tortie_derp from "../../lib/assets/images/tortie_derp.jpg?enhanced";
|
||||||
import tortie_derp from '$lib/assets/images/tortie_derp.jpg?enhanced';
|
import turnip from "../../lib/assets/images/turnip.svg?enhanced";
|
||||||
import turnip from '$lib/assets/images/turnip.svg';
|
import type { Course } from "../../lib/types/courses";
|
||||||
import type { Course } from '$lib/types/courses';
|
import {
|
||||||
import { dockerIcon, drizzleIcon, nextDotJsIcon, reactIcon, svelteIcon, typescriptIcon } from '$lib/util/logoIcons.svelte';
|
dockerIcon,
|
||||||
import CourseCard from './CourseCard.svelte';
|
drizzleIcon,
|
||||||
import TechListItem from './TechListItem.svelte';
|
nextDotJsIcon,
|
||||||
import courseData from './course.json';
|
reactIcon,
|
||||||
import ExternalLink from '$lib/components/ExternalLink.svelte';
|
svelteIcon,
|
||||||
|
typescriptIcon,
|
||||||
|
honoIcon,
|
||||||
|
} from "../../lib/util/logoIcons.svelte";
|
||||||
|
import CourseCard from "./CourseCard.svelte";
|
||||||
|
import courseData from "./course.json";
|
||||||
|
import ExternalLink from "../../lib/components/ExternalLink.svelte";
|
||||||
|
|
||||||
const courses: Course[] = courseData.courses;
|
const courses: Course[] = courseData.courses;
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -18,66 +24,116 @@
|
||||||
<h1>About</h1>
|
<h1>About</h1>
|
||||||
<p>Hey! My name is Bradley Shellnut.</p>
|
<p>Hey! My name is Bradley Shellnut.</p>
|
||||||
<p>
|
<p>
|
||||||
I'm {new Date().getFullYear() - 1991} years old and I am a full stack
|
I'm {new Date().getFullYear() - 1991} years old and I am a full stack software
|
||||||
software engineer who's interested in new tech and not afraid to
|
engineer who's interested in new tech and not afraid to discover new interests.
|
||||||
discover new interests.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2>More deets</h2>
|
<h2>More deets</h2>
|
||||||
<p>
|
<p>
|
||||||
I graduated from Cal Poly San Luis Obispo in 2013 with a Bachelor's
|
I graduated from Cal Poly San Luis Obispo in 2013 with a Bachelor's degree
|
||||||
degree in Computer Engineering.{' '}
|
in Computer Engineering.{" "}
|
||||||
<span class="emoji" title="Software + Hardware">
|
<span class="emoji" title="Software + Hardware"> 💻 </span>
|
||||||
💻
|
|
||||||
</span>
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
At work I develop in Java Spring, Spring Boot, PostgreSQL, and React /
|
At work I develop in Java Spring, Spring Boot, PostgreSQL, and React /
|
||||||
Angular.
|
Angular.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
At home I delve into other frameworks, languages, and platforms such
|
At home I delve into other frameworks, languages, and platforms such as:
|
||||||
as:
|
|
||||||
</p>
|
</p>
|
||||||
<div class="tech-list">
|
<div class="tech-list">
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
iconData={{ type: 'svg', icon: svelteIcon, iconClass: 'center' }}
|
iconData={{ type: "svg", icon: svelteIcon, iconClass: "center" }}
|
||||||
linkData={{ href: 'https://svelte.dev', ariaLabel: 'Svelte', title: 'Svelte', target: '_blank', clazz: "tech-list-item", textDecoration: 'none' }}
|
linkData={{
|
||||||
textData={{ text: "Svelte", showIcon: true, location: 'bottom' }}
|
href: "https://svelte.dev",
|
||||||
|
ariaLabel: "Svelte",
|
||||||
|
title: "Svelte",
|
||||||
|
target: "_blank",
|
||||||
|
clazz: "tech-list-item",
|
||||||
|
textDecoration: "none",
|
||||||
|
}}
|
||||||
|
textData={{ text: "Svelte", showIcon: true, location: "bottom" }}
|
||||||
/>
|
/>
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
iconData={{ type: 'svg', icon: typescriptIcon, iconClass: 'center' }}
|
iconData={{ type: "svg", icon: honoIcon, iconClass: "center" }}
|
||||||
linkData={{ href: 'https://www.typescriptlang.org/', ariaLabel: 'TypeScript', title: 'TypeScript', target: '_blank', clazz: "tech-list-item", textDecoration: 'none' }}
|
linkData={{
|
||||||
textData={{ text: "TypeScript", showIcon: true, location: 'bottom' }}
|
href: "https://hono.dev",
|
||||||
|
ariaLabel: "Hono",
|
||||||
|
title: "Hono",
|
||||||
|
target: "_blank",
|
||||||
|
clazz: "tech-list-item",
|
||||||
|
textDecoration: "none",
|
||||||
|
}}
|
||||||
|
textData={{ text: "Hono", showIcon: true, location: "bottom" }}
|
||||||
/>
|
/>
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
iconData={{ type: 'svg', icon: drizzleIcon, iconClass: 'center' }}
|
iconData={{ type: "svg", icon: typescriptIcon, iconClass: "center" }}
|
||||||
linkData={{ href: 'https://orm.drizzle.team/', ariaLabel: 'Drizzle ORM', title: 'Drizzle ORM', target: '_blank', clazz: "tech-list-item", textDecoration: 'none' }}
|
linkData={{
|
||||||
textData={{ text: "Drizzle ORM", showIcon: true, location: 'bottom' }}
|
href: "https://www.typescriptlang.org/",
|
||||||
|
ariaLabel: "TypeScript",
|
||||||
|
title: "TypeScript",
|
||||||
|
target: "_blank",
|
||||||
|
clazz: "tech-list-item",
|
||||||
|
textDecoration: "none",
|
||||||
|
}}
|
||||||
|
textData={{ text: "TypeScript", showIcon: true, location: "bottom" }}
|
||||||
/>
|
/>
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
iconData={{ type: 'svg', icon: reactIcon, iconClass: 'center' }}
|
iconData={{ type: "svg", icon: drizzleIcon, iconClass: "center" }}
|
||||||
linkData={{ href: 'https://reactjs.org/', ariaLabel: 'React', title: 'React', target: '_blank', clazz: "tech-list-item", textDecoration: 'none' }}
|
linkData={{
|
||||||
textData={{ text: "React", showIcon: true, location: 'bottom' }}
|
href: "https://orm.drizzle.team/",
|
||||||
|
ariaLabel: "Drizzle ORM",
|
||||||
|
title: "Drizzle ORM",
|
||||||
|
target: "_blank",
|
||||||
|
clazz: "tech-list-item",
|
||||||
|
textDecoration: "none",
|
||||||
|
}}
|
||||||
|
textData={{ text: "Drizzle ORM", showIcon: true, location: "bottom" }}
|
||||||
/>
|
/>
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
iconData={{ type: 'svg', icon: nextDotJsIcon, iconClass: 'center' }}
|
iconData={{ type: "svg", icon: reactIcon, iconClass: "center" }}
|
||||||
linkData={{ href: 'https://nextjs.org/', ariaLabel: 'Next.js', title: 'Next.js', target: '_blank', clazz: "tech-list-item", textDecoration: 'none' }}
|
linkData={{
|
||||||
textData={{ text: "Next.js", showIcon: true, location: 'bottom' }}
|
href: "https://reactjs.org/",
|
||||||
|
ariaLabel: "React",
|
||||||
|
title: "React",
|
||||||
|
target: "_blank",
|
||||||
|
clazz: "tech-list-item",
|
||||||
|
textDecoration: "none",
|
||||||
|
}}
|
||||||
|
textData={{ text: "React", showIcon: true, location: "bottom" }}
|
||||||
/>
|
/>
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
iconData={{ type: 'svg', icon: dockerIcon, iconClass: 'center' }}
|
iconData={{ type: "svg", icon: nextDotJsIcon, iconClass: "center" }}
|
||||||
linkData={{ href: 'https://www.docker.com/', ariaLabel: 'Docker', title: 'Docker', target: '_blank', clazz: "tech-list-item", textDecoration: 'none' }}
|
linkData={{
|
||||||
textData={{ text: "Docker", showIcon: true, location: 'bottom' }}
|
href: "https://nextjs.org/",
|
||||||
|
ariaLabel: "Next.js",
|
||||||
|
title: "Next.js",
|
||||||
|
target: "_blank",
|
||||||
|
clazz: "tech-list-item",
|
||||||
|
textDecoration: "none",
|
||||||
|
}}
|
||||||
|
textData={{ text: "Next.js", showIcon: true, location: "bottom" }}
|
||||||
|
/>
|
||||||
|
<ExternalLink
|
||||||
|
iconData={{ type: "svg", icon: dockerIcon, iconClass: "center" }}
|
||||||
|
linkData={{
|
||||||
|
href: "https://www.docker.com/",
|
||||||
|
ariaLabel: "Docker",
|
||||||
|
title: "Docker",
|
||||||
|
target: "_blank",
|
||||||
|
clazz: "tech-list-item",
|
||||||
|
textDecoration: "none",
|
||||||
|
}}
|
||||||
|
textData={{ text: "Docker", showIcon: true, location: "bottom" }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2>Extracurricular</h2>
|
<h2>Extracurricular</h2>
|
||||||
<p>
|
<p>
|
||||||
Outside of work I like to take tutorials from many instructors like
|
Outside of work I like to take tutorials from many instructors like those
|
||||||
those below:
|
below:
|
||||||
</p>
|
</p>
|
||||||
<div class="extracurricular">
|
<div class="extracurricular">
|
||||||
{#each courses as course}
|
{#each courses as course}
|
||||||
|
|
@ -88,19 +144,17 @@
|
||||||
<div>
|
<div>
|
||||||
<h2>Other fun things about me…</h2>
|
<h2>Other fun things about me…</h2>
|
||||||
<div style="display: grid;">
|
<div style="display: grid;">
|
||||||
<p>
|
<p>Recently visited Taiwan and Japan.</p>
|
||||||
Recently cruised the Mediterranean.
|
|
||||||
</p>
|
|
||||||
<div
|
<div
|
||||||
style="
|
style="
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(200px, 400px);
|
grid-template-columns: minmax(200px, 400px);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
font-size: 5rem;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<enhanced:img src={cruise} alt="Clip art of a cruise ship. Cruise icons created by C-mo Box - Flaticon" />
|
🇹🇼 🇯🇵 🌸
|
||||||
<p class="center">Crusin'</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -108,7 +162,15 @@
|
||||||
<div class="cat-pics">
|
<div class="cat-pics">
|
||||||
<figure>
|
<figure>
|
||||||
<enhanced:img src={tortie_derp} alt="Tortie Cat" />
|
<enhanced:img src={tortie_derp} alt="Tortie Cat" />
|
||||||
<p class="center">Turnip <img class="icon" src={String(turnip)} width="25px" height="25px" alt="Turnip" /></p>
|
<p class="center">
|
||||||
|
Turnip <img
|
||||||
|
class="icon"
|
||||||
|
src={String(turnip)}
|
||||||
|
width="25px"
|
||||||
|
height="25px"
|
||||||
|
alt="Turnip"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
</figure>
|
</figure>
|
||||||
<figure>
|
<figure>
|
||||||
<enhanced:img src={orange_derp} alt="Tortie Cat" />
|
<enhanced:img src={orange_derp} alt="Tortie Cat" />
|
||||||
|
|
@ -173,4 +235,4 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 2rem;
|
gap: 2rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { json, error } from '@sveltejs/kit';
|
import { json, error } from '@sveltejs/kit';
|
||||||
import { BANDCAMP_USERNAME, PAGE_SIZE, USE_REDIS_CACHE } from '$env/static/private';
|
import { BANDCAMP_USERNAME, PAGE_SIZE, USE_REDIS_CACHE } from '$env/static/private';
|
||||||
import { fetchArticlesApi } from '$lib/api';
|
import { fetchArticlesApi } from '$lib/api';
|
||||||
import { redis } from '$root/lib/server/redis';
|
import { redis } from '$lib/server/redis';
|
||||||
import type { Album, BandCampResults } from '$lib/types/album';
|
import type { Album, BandCampResults } from '$lib/types/album';
|
||||||
import scrapeIt, { type ScrapeResult } from 'scrape-it';
|
import scrapeIt, { type ScrapeResult } from 'scrape-it';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,34 @@
|
||||||
import SocialImageCard from "$lib/components/socialImageCard.svelte";
|
import SocialImageCard from '$lib/components/socialImageCard.svelte';
|
||||||
import { componentToPng } from "$lib/renderImage";
|
import { componentToPng } from '$lib/renderImage';
|
||||||
|
|
||||||
const height = 630;
|
const height = 630;
|
||||||
const width = 1200;
|
const width = 1200;
|
||||||
|
|
||||||
/** @type {import('./$types').RequestHandler} */
|
/** @type {import('./$types').RequestHandler} */
|
||||||
export async function GET({ url }) {
|
export async function GET({ url }) {
|
||||||
try {
|
try {
|
||||||
const faviconImageName = "b_shell_nut_favicon.png";
|
const faviconImageName = 'b_shell_nut_favicon.png';
|
||||||
const image = `${new URL(url.origin).href}${faviconImageName}`;
|
const image = `${new URL(url.origin).href}${faviconImageName}`;
|
||||||
const header = url.searchParams.get("header") ?? undefined;
|
const header = url.searchParams.get('header') ?? undefined;
|
||||||
const page = url.searchParams.get("page") ?? undefined;
|
const page = url.searchParams.get('page') ?? undefined;
|
||||||
const content = url.searchParams.get("content") ?? "";
|
const content = url.searchParams.get('content') ?? '';
|
||||||
|
|
||||||
// @ts-expect-error: Argument of type 'typeof SocialImageCard__SvelteComponent_' is not assignable to parameter of type 'SvelteComponent<any, any, any>'
|
// @ts-expect-error: Argument of type 'typeof SocialImageCard__SvelteComponent_' is not assignable to parameter of type 'SvelteComponent<any, any, any>'
|
||||||
return componentToPng(
|
return componentToPng(
|
||||||
SocialImageCard,
|
SocialImageCard,
|
||||||
{
|
{
|
||||||
header,
|
header,
|
||||||
page,
|
page,
|
||||||
content,
|
content,
|
||||||
image,
|
image,
|
||||||
width: `${width}`,
|
width: `${width}`,
|
||||||
height: `${height}`,
|
height: `${height}`,
|
||||||
url: new URL(url.origin).href,
|
url: new URL(url.origin).href,
|
||||||
},
|
},
|
||||||
height,
|
height,
|
||||||
width
|
width,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,39 +5,39 @@ import { PUBLIC_SITE_URL } from '$env/static/public';
|
||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ url }) => {
|
export const load: PageServerLoad = async ({ url }) => {
|
||||||
const baseUrl = new URL(url.origin).href || PUBLIC_SITE_URL || 'https://bradleyshellnut.com';
|
const baseUrl = new URL(url.origin).href || PUBLIC_SITE_URL || 'https://bradleyshellnut.com';
|
||||||
const currentPageUrl = new URL(url.pathname, url.origin).href;
|
const currentPageUrl = new URL(url.pathname, url.origin).href;
|
||||||
|
|
||||||
const metaTags: MetaTagsProps = Object.freeze({
|
const metaTags: MetaTagsProps = Object.freeze({
|
||||||
title: 'Portfolio',
|
title: 'Portfolio',
|
||||||
description: "Bradley Shellnut's Portfolio",
|
description: "Bradley Shellnut's Portfolio",
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: 'Portfolio',
|
title: 'Portfolio',
|
||||||
description: "Bradley Shellnut's Portfolio",
|
description: "Bradley Shellnut's Portfolio",
|
||||||
url: currentPageUrl,
|
url: currentPageUrl,
|
||||||
siteName: 'Bradley Shellnut Personal Website',
|
siteName: 'Bradley Shellnut Personal Website',
|
||||||
type: 'website',
|
type: 'website',
|
||||||
locale: 'en_US',
|
locale: 'en_US',
|
||||||
images: [
|
images: [
|
||||||
{
|
{
|
||||||
url: `${baseUrl}og?header=Portfolio | bradleyshellnut.com&page=My portfolio of sites I have created.`,
|
url: `${baseUrl}og?header=Portfolio | bradleyshellnut.com&page=My portfolio of sites I have created.`,
|
||||||
alt: 'Bradley Shellnut Portfolio Page',
|
alt: 'Bradley Shellnut Portfolio Page',
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 630
|
height: 630,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
title: 'Portfolio',
|
title: 'Portfolio',
|
||||||
description: "Bradley Shellnut's Portfolio",
|
description: "Bradley Shellnut's Portfolio",
|
||||||
card: 'summary_large_image',
|
card: 'summary_large_image',
|
||||||
image: `${baseUrl}og?header=Portfolio | bradleyshellnut.com&page=My portfolio of sites I have created.`,
|
image: `${baseUrl}og?header=Portfolio | bradleyshellnut.com&page=My portfolio of sites I have created.`,
|
||||||
imageAlt: 'Bradley Shellnut Website Logo'
|
imageAlt: 'Bradley Shellnut Website Logo',
|
||||||
},
|
},
|
||||||
url: currentPageUrl
|
url: currentPageUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
metaTagsChild: metaTags
|
metaTagsChild: metaTags,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
import shellnutArchitectWebsite from "../../lib/assets/images/portfolio/Mark_Shellnut_Architect.png?enhanced";
|
import shellnutArchitectWebsite from "../../lib/assets/images/portfolio/Mark_Shellnut_Architect.png?enhanced";
|
||||||
import oldSite from "../../lib/assets/images/portfolio/Old_Website_Bradley_Shellnut.png?enhanced";
|
import oldSite from "../../lib/assets/images/portfolio/Old_Website_Bradley_Shellnut.png?enhanced";
|
||||||
import weddingWebsite from "../../lib/assets/images/portfolio/Wedding_Website.png?enhanced";
|
import weddingWebsite from "../../lib/assets/images/portfolio/Wedding_Website.png?enhanced";
|
||||||
import { gitHubIcon } from "$root/lib/util/logoIcons.svelte";
|
import { gitHubIcon } from "$lib/util/logoIcons.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet links(externalLinks: ExternalLinkType[])}
|
{#snippet links(externalLinks: ExternalLinkType[])}
|
||||||
|
|
@ -124,34 +124,43 @@
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>The previous version of my website was written using React and Gatsby which
|
<p>
|
||||||
you can view <ExternalLink
|
The previous version of my website was written using React and Gatsby
|
||||||
linkData={{
|
which you can view <ExternalLink
|
||||||
href: "https://wonderful-austin-9f17d2.netlify.app/",
|
linkData={{
|
||||||
ariaLabel: "React and Gatsby Personal Site version",
|
href: "https://wonderful-austin-9f17d2.netlify.app/",
|
||||||
}}
|
ariaLabel: "React and Gatsby Personal Site version",
|
||||||
textData={{ text: "here.", showIcon: true, location: "left" }}
|
}}
|
||||||
/></p>
|
textData={{ text: "here.", showIcon: true, location: "left" }}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Each iteration brings better code and my previous React version was
|
Each iteration brings better code and my previous React version was
|
||||||
improved after the suggestions on <ExternalLink
|
improved after the suggestions on <ExternalLink
|
||||||
linkData={{
|
linkData={{
|
||||||
href: "https://syntax.fm/show/444/syntax-highlight#t=33:19",
|
href: "https://syntax.fm/show/444/syntax-highlight#t=33:19",
|
||||||
ariaLabel: "Syntax.fm Podcast Number 444",
|
ariaLabel: "Syntax.fm Podcast Number 444",
|
||||||
}}
|
}}
|
||||||
textData={{ text: "Show 444", showIcon: true, location: "left" }}
|
textData={{ text: "Show 444", showIcon: true, location: "left" }}
|
||||||
/> of the <ExternalLink
|
/> of the <ExternalLink
|
||||||
linkData={{ href: "https://syntax.fm/", ariaLabel: "Syntax.fm" }}
|
linkData={{ href: "https://syntax.fm/", ariaLabel: "Syntax.fm" }}
|
||||||
textData={{ text: "Syntax Pocast.", showIcon: true, location: "left" }}
|
textData={{
|
||||||
/></p>
|
text: "Syntax Pocast.",
|
||||||
<p>You can view the previous archived version of the site before those
|
showIcon: true,
|
||||||
changes <ExternalLink
|
location: "left",
|
||||||
textData={{ text: "here.", showIcon: true, location: "left" }}
|
}}
|
||||||
linkData={{
|
/>
|
||||||
href: "https://web.archive.org/web/20210224002046/https://bradleyshellnut.com/",
|
</p>
|
||||||
ariaLabel: "Archive before Syntax Podcast",
|
<p>
|
||||||
}}
|
You can view the previous archived version of the site before those
|
||||||
/></p>
|
changes <ExternalLink
|
||||||
|
textData={{ text: "here.", showIcon: true, location: "left" }}
|
||||||
|
linkData={{
|
||||||
|
href: "https://web.archive.org/web/20210224002046/https://bradleyshellnut.com/",
|
||||||
|
ariaLabel: "Archive before Syntax Podcast",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
</Portfolio>
|
</Portfolio>
|
||||||
<Portfolio
|
<Portfolio
|
||||||
name="Wedding Website"
|
name="Wedding Website"
|
||||||
|
|
@ -169,16 +178,24 @@
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<p>The app was initially created for my wedding but what is linked here is a public demo of the application.</p>
|
<p>
|
||||||
<p>An application that allows viewing of wedding details and provides the ability to RSVP to the wedding.</p>
|
The app was initially created for my wedding but what is linked here is
|
||||||
|
a public demo of the application.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
An application that allows viewing of wedding details and provides the
|
||||||
|
ability to RSVP to the wedding.
|
||||||
|
</p>
|
||||||
<p>Tech stack:</p>
|
<p>Tech stack:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Next.js 13</li>
|
<li>Next.js 13</li>
|
||||||
<li>React 18</li>
|
<li>React 18</li>
|
||||||
<li><ExternalLink
|
<li>
|
||||||
linkData={{ href: "https://radix-ui.com/", ariaLabel: "Radix UI" }}
|
<ExternalLink
|
||||||
textData={{ text: "Radix UI", showIcon: true, location: "left" }}
|
linkData={{ href: "https://radix-ui.com/", ariaLabel: "Radix UI" }}
|
||||||
/></li>
|
textData={{ text: "Radix UI", showIcon: true, location: "left" }}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
<li>MongoDB</li>
|
<li>MongoDB</li>
|
||||||
<li>Styled Components</li>
|
<li>Styled Components</li>
|
||||||
<li>Next Iron Session</li>
|
<li>Next Iron Session</li>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Picture } from 'vite-imagetools';
|
import type { Picture } from "vite-imagetools";
|
||||||
import type { Snippet } from 'svelte';
|
import type { Snippet } from "svelte";
|
||||||
import type { ExternalLinkType } from '$lib/types/externalLinkType';
|
import type { ExternalLinkType } from "$lib/types/externalLinkType";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
links,
|
links,
|
||||||
|
|
@ -10,11 +10,20 @@
|
||||||
src,
|
src,
|
||||||
alt,
|
alt,
|
||||||
style,
|
style,
|
||||||
fetchPriority = 'auto',
|
fetchPriority = "auto",
|
||||||
loading = 'lazy',
|
loading = "lazy",
|
||||||
children
|
children,
|
||||||
}: { links: Snippet<ExternalLinkType[]>, externalLinks: ExternalLinkType[], name: string; src: string | Picture; alt: string;
|
}: {
|
||||||
style: string; fetchPriority?: 'high' | 'low' | 'auto'; loading?: 'lazy' | 'eager', children?: Snippet } = $props();
|
links: Snippet<ExternalLinkType[]>;
|
||||||
|
externalLinks: ExternalLinkType[];
|
||||||
|
name: string;
|
||||||
|
src: string | Picture;
|
||||||
|
alt: string;
|
||||||
|
style: string;
|
||||||
|
fetchPriority?: "high" | "low" | "auto";
|
||||||
|
loading?: "lazy" | "eager";
|
||||||
|
children?: Snippet;
|
||||||
|
} = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="portfolio">
|
<div class="portfolio">
|
||||||
|
|
|
||||||
|
|
@ -1,70 +1,147 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import ExternalLink from '$lib/components/ExternalLink.svelte';
|
import ExternalLink from "$lib/components/ExternalLink.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h1>Privacy</h1>
|
<h1>Privacy</h1>
|
||||||
<p>
|
<p>
|
||||||
Long story short, I believe everyone should know who has your personal
|
Long story short, I believe everyone should know who has your personal data,
|
||||||
data, how it is being collected/stored, and what it is being used for.
|
how it is being collected/stored, and what it is being used for.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
However, it is ultimately up to each person to determine how much data
|
However, it is ultimately up to each person to determine how much data they
|
||||||
they are willing to give to any business/entity.
|
are willing to give to any business/entity.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
For the sake of transparency I am using <ExternalLink textData={{ text: "Umami Analytics", showIcon: true, location: "left" }} linkData={{ href: "https://umami.is", ariaLabel: "Umami Analytics" }} /> to anonymously track visits to my site. You can completely block this if you want by either using an AdBlocker like <ExternalLink textData={{ text: "uBlock Origin", showIcon: true, location: "left" }} linkData={{ href: "https://ublockorigin.com/", ariaLabel: "uBlock Origin" }} />. Sending "Do Not Track" requests in your browser is supported but not overall it is not recommended to turn this on since it can be used to fingerprint you on the web.</p>
|
For the sake of transparency I am using <ExternalLink
|
||||||
|
textData={{ text: "Umami Analytics", showIcon: true, location: "left" }}
|
||||||
|
linkData={{ href: "https://umami.is", ariaLabel: "Umami Analytics" }}
|
||||||
|
/> to anonymously track visits to my site. You can completely block this if you
|
||||||
|
want by either using an AdBlocker like <ExternalLink
|
||||||
|
textData={{ text: "uBlock Origin", showIcon: true, location: "left" }}
|
||||||
|
linkData={{
|
||||||
|
href: "https://ublockorigin.com/",
|
||||||
|
ariaLabel: "uBlock Origin",
|
||||||
|
}}
|
||||||
|
/>. Sending "Do Not Track" requests in your browser is supported but not
|
||||||
|
overall it is not recommended to turn this on since it can be used to
|
||||||
|
fingerprint you on the web.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2>Useful Resources</h2>
|
<h2>Useful Resources</h2>
|
||||||
<p>
|
<p>
|
||||||
Here are a few sites/lists of privacy oriented software for you to
|
Here are a few sites/lists of privacy oriented software for you to check
|
||||||
check out:
|
out:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<ExternalLink linkData={{ href: "https://www.eff.org/privacybadger", ariaLabel: "Privacy Badger" }} textData={{ text: "Privacy Badger", showIcon: true, location: "left" }} />
|
<ExternalLink
|
||||||
<ExternalLink textData={{ text: "Awesome Privacy", showIcon: true, location: "left" }} linkData={{ href: "https://github.com/Lissy93/awesome-privacy", ariaLabel: "Awesome Privacy" }} />
|
textData={{ text: "Awesome Privacy", showIcon: true, location: "left" }}
|
||||||
|
linkData={{
|
||||||
|
href: "https://github.com/Lissy93/awesome-privacy",
|
||||||
|
ariaLabel: "Awesome Privacy",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<ExternalLink linkData={{ href: "https://privacyguides.org/", ariaLabel: "Privacy Guides" }} textData={{ text: "Privacy Guides", showIcon: true, location: "left" }} />
|
<ExternalLink
|
||||||
|
linkData={{
|
||||||
|
href: "https://privacyguides.org/",
|
||||||
|
ariaLabel: "Privacy Guides",
|
||||||
|
}}
|
||||||
|
textData={{ text: "Privacy Guides", showIcon: true, location: "left" }}
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<ExternalLink linkData={{ href: "https://ethical.net/resources/", ariaLabel: "Ethical Alternatives" }} textData={{ text: "Ethical Alternatives", showIcon: true, location: "left" }} />
|
<ExternalLink
|
||||||
|
linkData={{
|
||||||
|
href: "https://ethical.net/resources/",
|
||||||
|
ariaLabel: "Ethical Alternatives",
|
||||||
|
}}
|
||||||
|
textData={{
|
||||||
|
text: "Ethical Alternatives",
|
||||||
|
showIcon: true,
|
||||||
|
location: "left",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3>Privacy Centric Paid Services I use:</h3>
|
<h3>Privacy Centric Paid Services I use:</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
Article Saving: <ExternalLink textData={{ text: "Wallabag Article Saver", showIcon: true, location: "left" }} linkData={{ href: "https://wallabag.com/", ariaLabel: "Wallabag Article Saver" }} />
|
Article Saving: <ExternalLink
|
||||||
</li>
|
textData={{
|
||||||
<li>
|
text: "Wallabag Article Saver",
|
||||||
Anonymous Email Forwarding: <ExternalLink textData={{ text: "SimpleLogin (Now owned by Proton AG)", showIcon: true, location: "left" }} linkData={{ href: "https://simplelogin.io", ariaLabel: "SimpleLogin" }} />
|
showIcon: true,
|
||||||
</li>
|
location: "left",
|
||||||
<li>
|
}}
|
||||||
Email: <ExternalLink textData={{ text: "ProtonMail", showIcon: true, location: "left" }} linkData={{ href: "https://protonmail.com/", ariaLabel: "ProtonMail" }} />
|
linkData={{
|
||||||
</li>
|
href: "https://wallabag.com/",
|
||||||
<li>
|
ariaLabel: "Wallabag Article Saver",
|
||||||
Notes: <ExternalLink textData={{ text: "Joplin Notes", showIcon: true, location: "left" }} linkData={{ href: "https://joplinapp.org/", ariaLabel: "Joplin Notes" }} />
|
}}
|
||||||
</li>
|
/>
|
||||||
<li>
|
</li>
|
||||||
VPN: <ExternalLink textData={{ text: "ProtonVPN", showIcon: true, location: "left" }} linkData={{ href: "https://protonvpn.com", ariaLabel: "ProtonVPN" }} />
|
<li>
|
||||||
</li>
|
Anonymous Email Forwarding: <ExternalLink
|
||||||
</ul>
|
textData={{
|
||||||
|
text: "SimpleLogin (Now owned by Proton AG)",
|
||||||
|
showIcon: true,
|
||||||
|
location: "left",
|
||||||
|
}}
|
||||||
|
linkData={{ href: "https://simplelogin.io", ariaLabel: "SimpleLogin" }}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Email: <ExternalLink
|
||||||
|
textData={{ text: "ProtonMail", showIcon: true, location: "left" }}
|
||||||
|
linkData={{ href: "https://protonmail.com/", ariaLabel: "ProtonMail" }}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Notes: <ExternalLink
|
||||||
|
textData={{ text: "Joplin Notes", showIcon: true, location: "left" }}
|
||||||
|
linkData={{ href: "https://joplinapp.org/", ariaLabel: "Joplin Notes" }}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
VPN: <ExternalLink
|
||||||
|
textData={{ text: "ProtonVPN", showIcon: true, location: "left" }}
|
||||||
|
linkData={{ href: "https://protonvpn.com", ariaLabel: "ProtonVPN" }}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3>NAS Servers for Self Hosting:</h3>
|
<h3>NAS Servers for Self Hosting:</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<ExternalLink textData={{ text: "Synology NAS", showIcon: true, location: "left" }} linkData={{ href: "https://synology.com/", ariaLabel: "Synology NAS" }} />
|
<ExternalLink
|
||||||
: An easy, not cheap, local solution for Google Services like Drive, Photos, Calendar, other services using Docker, etc. (Yes I should use <ExternalLink textData={{ text: "NextCloud Local Hosting Service", showIcon: true, location: "left" }} linkData={{ href: "https://nextcloud.com/", ariaLabel: "NextCloud Local Hosting Service" }} />...maybe eventually)
|
textData={{ text: "Synology NAS", showIcon: true, location: "left" }}
|
||||||
</li>
|
linkData={{ href: "https://synology.com/", ariaLabel: "Synology NAS" }}
|
||||||
<li>
|
/>
|
||||||
Custom NAS Server used as a <ExternalLink textData={{ text: "Plex Machine", showIcon: true, location: "left" }} linkData={{ href: "https://www.plex.tv/", ariaLabel: "Plex" }} />
|
: An easy, not cheap, local solution for Google Services like Drive, Photos,
|
||||||
</li>
|
Calendar, other services using Docker, etc. (Yes I should use <ExternalLink
|
||||||
</ul>
|
textData={{
|
||||||
|
text: "NextCloud Local Hosting Service",
|
||||||
|
showIcon: true,
|
||||||
|
location: "left",
|
||||||
|
}}
|
||||||
|
linkData={{
|
||||||
|
href: "https://nextcloud.com/",
|
||||||
|
ariaLabel: "NextCloud Local Hosting Service",
|
||||||
|
}}
|
||||||
|
/>...maybe eventually)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Custom NAS Server used as a <ExternalLink
|
||||||
|
textData={{ text: "Plex Machine", showIcon: true, location: "left" }}
|
||||||
|
linkData={{ href: "https://www.plex.tv/", ariaLabel: "Plex" }}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
|
|
@ -78,4 +155,4 @@ import ExternalLink from '$lib/components/ExternalLink.svelte';
|
||||||
list-style-type: square;
|
list-style-type: square;
|
||||||
padding-inline-start: 4rem;
|
padding-inline-start: 4rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -5,39 +5,39 @@ import { PUBLIC_SITE_URL } from '$env/static/public';
|
||||||
import type { PageLoad } from './$types';
|
import type { PageLoad } from './$types';
|
||||||
|
|
||||||
export const load: PageLoad = async ({ url }) => {
|
export const load: PageLoad = async ({ url }) => {
|
||||||
const baseUrl = new URL(url.origin).href || PUBLIC_SITE_URL || 'https://bradleyshellnut.com';
|
const baseUrl = new URL(url.origin).href || PUBLIC_SITE_URL || 'https://bradleyshellnut.com';
|
||||||
const currentPageUrl = new URL(url.pathname, url.origin).href;
|
const currentPageUrl = new URL(url.pathname, url.origin).href;
|
||||||
|
|
||||||
const metaTags: MetaTagsProps = Object.freeze({
|
const metaTags: MetaTagsProps = Object.freeze({
|
||||||
title: 'Privacy Blog',
|
title: 'Privacy Blog',
|
||||||
description: 'My thoughts on personal internet privacy.',
|
description: 'My thoughts on personal internet privacy.',
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: 'Privacy Blog',
|
title: 'Privacy Blog',
|
||||||
description: 'My thoughts on personal internet privacy.',
|
description: 'My thoughts on personal internet privacy.',
|
||||||
url: new URL(url.pathname, url.origin).href,
|
url: new URL(url.pathname, url.origin).href,
|
||||||
siteName: 'Bradley Shellnut Personal Website',
|
siteName: 'Bradley Shellnut Personal Website',
|
||||||
type: 'website',
|
type: 'website',
|
||||||
locale: 'en_US',
|
locale: 'en_US',
|
||||||
images: [
|
images: [
|
||||||
{
|
{
|
||||||
url: `${baseUrl}og?header=Privacy Blog | bradleyshellnut.com&page=My thoughts on personal internet privacy.`,
|
url: `${baseUrl}og?header=Privacy Blog | bradleyshellnut.com&page=My thoughts on personal internet privacy.`,
|
||||||
alt: 'Bradley Shellnut Privacy Blog',
|
alt: 'Bradley Shellnut Privacy Blog',
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 630
|
height: 630,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
title: 'Privacy Blog',
|
title: 'Privacy Blog',
|
||||||
description: 'My thoughts on personal internet privacy.',
|
description: 'My thoughts on personal internet privacy.',
|
||||||
card: 'summary_large_image',
|
card: 'summary_large_image',
|
||||||
image: `${baseUrl}og?header=Privacy Blog | bradleyshellnut.com&page=My thoughts on personal internet privacy.`,
|
image: `${baseUrl}og?header=Privacy Blog | bradleyshellnut.com&page=My thoughts on personal internet privacy.`,
|
||||||
imageAlt: 'Bradley Shellnut Website Logo'
|
imageAlt: 'Bradley Shellnut Website Logo',
|
||||||
},
|
},
|
||||||
url: currentPageUrl
|
url: currentPageUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
metaTagsChild: metaTags
|
metaTagsChild: metaTags,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
@import 'reset.pcss';
|
@import 'reset.pcss';
|
||||||
@import 'global.pcss';
|
@import 'global.pcss';
|
||||||
/* @import '$root/styles/theme.pcss'; */
|
|
||||||
@import 'typeography.pcss';
|
@import 'typeography.pcss';
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ const config = {
|
||||||
kit: {
|
kit: {
|
||||||
adapter: adapter(),
|
adapter: adapter(),
|
||||||
alias: {
|
alias: {
|
||||||
$root: './src'
|
$: './src',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
{
|
{
|
||||||
"extends": "./.svelte-kit/tsconfig.json",
|
"extends": "./.svelte-kit/tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"useUnknownInCatchVariables": true,
|
"useUnknownInCatchVariables": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
||||||
//
|
//
|
||||||
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||||
// from the referenced tsconfig.json - TypeScript does not merge them in
|
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,28 @@
|
||||||
import { sveltekit } from "@sveltejs/kit/vite";
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from 'vite';
|
||||||
import { enhancedImages } from "@sveltejs/enhanced-img";
|
import { enhancedImages } from '@sveltejs/enhanced-img';
|
||||||
import { imagetools } from "@zerodevx/svelte-img/vite";
|
import { imagetools } from '@zerodevx/svelte-img/vite';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
enhancedImages(),
|
enhancedImages(),
|
||||||
sveltekit(),
|
sveltekit(),
|
||||||
imagetools({
|
imagetools({
|
||||||
// By default, directives are `?width=480;1024;1920&format=avif;webp;jpg`
|
// By default, directives are `?width=480;1024;1920&format=avif;webp;jpg`
|
||||||
// Now we change it to generate 5 variants instead - `avif/jpg` formats at `640/1280` + LQIP (Now as:run)
|
// Now we change it to generate 5 variants instead - `avif/jpg` formats at `640/1280` + LQIP (Now as:run)
|
||||||
profiles: {
|
profiles: {
|
||||||
run: new URLSearchParams(
|
run: new URLSearchParams('?w=300;480;640;1024;1920&format=avif;webp;jpg&as=run:64'),
|
||||||
"?w=300;480;640;1024;1920&format=avif;webp;jpg&as=run:64"
|
},
|
||||||
),
|
}),
|
||||||
},
|
],
|
||||||
}),
|
esbuild: {
|
||||||
],
|
target: 'es2022',
|
||||||
esbuild: {
|
},
|
||||||
target: "es2022",
|
test: {
|
||||||
},
|
include: ['src/**/*.{test,spec}.{js,ts}'],
|
||||||
test: {
|
mockReset: true,
|
||||||
include: ["src/**/*.{test,spec}.{js,ts}"],
|
},
|
||||||
mockReset: true,
|
css: {
|
||||||
},
|
devSourcemap: true,
|
||||||
css: {
|
},
|
||||||
devSourcemap: true,
|
|
||||||
preprocessorOptions: {
|
|
||||||
postcss: {
|
|
||||||
additionalData: `
|
|
||||||
@custom-media --below_small (width < 400px);
|
|
||||||
@custom-media --below_med (width < 700px);
|
|
||||||
@custom-media --below_large (width < 900px);
|
|
||||||
@custom-media --below_xlarge (width < 1200px);
|
|
||||||
|
|
||||||
@custom-media --above_small (width > 400px);
|
|
||||||
@custom-media --above_med (width > 700px);
|
|
||||||
@custom-media --above_large (width > 900px);
|
|
||||||
@custom-media --above_xlarge (width > 1200px);
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue