feat: implement automatic image optimization with Astro Assets

- Configure content collections to use image() helper for type-safe image references
- Replace all <img> tags with <Image> component from astro:assets
- Migrate images from /public to /src/assets for automatic optimization
- Update blog posts to use relative paths in frontmatter (../../assets/photos/)
- Remove unused images and duplicates from /public folder
- Update Footer component to use optimized SVG icons
- Clean up manifest.json to use favicon.ico instead of deleted photos

Performance improvements:
- remote.jpg: 1.8MB → 128KB (93% reduction)
- me.png: 71KB → 12KB (83% reduction)
- Automatic WebP conversion
- Tree-shaking: only used images are optimized

/public folder reduced from 2.3MB to 32KB (only essential files)
Build output: 2.2MB with fully optimized images ready for S3 deployment
This commit is contained in:
Lorenzo Iovino 2026-01-08 17:40:19 +01:00
parent e3f7a631eb
commit 049c20a4b2
47 changed files with 632 additions and 419 deletions

View file

@ -1,25 +0,0 @@
{
"$ref": "#/definitions/bio",
"definitions": {
"bio": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"$schema": {
"type": "string"
}
},
"required": [
"title",
"description"
],
"additionalProperties": false
}
},
"$schema": "http://json-schema.org/draft-07/schema#"
}

View file

@ -1,76 +0,0 @@
{
"$ref": "#/definitions/blog",
"definitions": {
"blog": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"pubDate": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "string",
"format": "date"
},
{
"type": "integer",
"format": "unix-time"
}
]
},
"updatedDate": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "string",
"format": "date"
},
{
"type": "integer",
"format": "unix-time"
}
]
},
"heroImage": {
"type": "string"
},
"author": {
"type": "string",
"default": "Lorenzo Iovino"
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"draft": {
"type": "boolean",
"default": false
},
"$schema": {
"type": "string"
}
},
"required": [
"title",
"description",
"pubDate"
],
"additionalProperties": false
}
},
"$schema": "http://json-schema.org/draft-07/schema#"
}

View file

@ -1 +0,0 @@
export default new Map();

View file

@ -1 +0,0 @@
export default new Map();

219
.astro/content.d.ts vendored
View file

@ -1,219 +0,0 @@
declare module 'astro:content' {
export interface RenderResult {
Content: import('astro/runtime/server/index.js').AstroComponentFactory;
headings: import('astro').MarkdownHeading[];
remarkPluginFrontmatter: Record<string, any>;
}
interface Render {
'.md': Promise<RenderResult>;
}
export interface RenderedContent {
html: string;
metadata?: {
imagePaths: Array<string>;
[key: string]: unknown;
};
}
}
declare module 'astro:content' {
type Flatten<T> = T extends { [K: string]: infer U } ? U : never;
export type CollectionKey = keyof AnyEntryMap;
export type CollectionEntry<C extends CollectionKey> = Flatten<AnyEntryMap[C]>;
export type ContentCollectionKey = keyof ContentEntryMap;
export type DataCollectionKey = keyof DataEntryMap;
type AllValuesOf<T> = T extends any ? T[keyof T] : never;
type ValidContentEntrySlug<C extends keyof ContentEntryMap> = AllValuesOf<
ContentEntryMap[C]
>['slug'];
export type ReferenceDataEntry<
C extends CollectionKey,
E extends keyof DataEntryMap[C] = string,
> = {
collection: C;
id: E;
};
export type ReferenceContentEntry<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}) = string,
> = {
collection: C;
slug: E;
};
export type ReferenceLiveEntry<C extends keyof LiveContentConfig['collections']> = {
collection: C;
id: string;
};
/** @deprecated Use `getEntry` instead. */
export function getEntryBySlug<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}),
>(
collection: C,
// Note that this has to accept a regular string too, for SSR
entrySlug: E,
): E extends ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
/** @deprecated Use `getEntry` instead. */
export function getDataEntryById<C extends keyof DataEntryMap, E extends keyof DataEntryMap[C]>(
collection: C,
entryId: E,
): Promise<CollectionEntry<C>>;
export function getCollection<C extends keyof AnyEntryMap, E extends CollectionEntry<C>>(
collection: C,
filter?: (entry: CollectionEntry<C>) => entry is E,
): Promise<E[]>;
export function getCollection<C extends keyof AnyEntryMap>(
collection: C,
filter?: (entry: CollectionEntry<C>) => unknown,
): Promise<CollectionEntry<C>[]>;
export function getLiveCollection<C extends keyof LiveContentConfig['collections']>(
collection: C,
filter?: LiveLoaderCollectionFilterType<C>,
): Promise<
import('astro').LiveDataCollectionResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>
>;
export function getEntry<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}),
>(
entry: ReferenceContentEntry<C, E>,
): E extends ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof DataEntryMap,
E extends keyof DataEntryMap[C] | (string & {}),
>(
entry: ReferenceDataEntry<C, E>,
): E extends keyof DataEntryMap[C]
? Promise<DataEntryMap[C][E]>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}),
>(
collection: C,
slug: E,
): E extends ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof DataEntryMap,
E extends keyof DataEntryMap[C] | (string & {}),
>(
collection: C,
id: E,
): E extends keyof DataEntryMap[C]
? string extends keyof DataEntryMap[C]
? Promise<DataEntryMap[C][E]> | undefined
: Promise<DataEntryMap[C][E]>
: Promise<CollectionEntry<C> | undefined>;
export function getLiveEntry<C extends keyof LiveContentConfig['collections']>(
collection: C,
filter: string | LiveLoaderEntryFilterType<C>,
): Promise<import('astro').LiveDataEntryResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>>;
/** Resolve an array of entry references from the same collection */
export function getEntries<C extends keyof ContentEntryMap>(
entries: ReferenceContentEntry<C, ValidContentEntrySlug<C>>[],
): Promise<CollectionEntry<C>[]>;
export function getEntries<C extends keyof DataEntryMap>(
entries: ReferenceDataEntry<C, keyof DataEntryMap[C]>[],
): Promise<CollectionEntry<C>[]>;
export function render<C extends keyof AnyEntryMap>(
entry: AnyEntryMap[C][string],
): Promise<RenderResult>;
export function reference<C extends keyof AnyEntryMap>(
collection: C,
): import('astro/zod').ZodEffects<
import('astro/zod').ZodString,
C extends keyof ContentEntryMap
? ReferenceContentEntry<C, ValidContentEntrySlug<C>>
: ReferenceDataEntry<C, keyof DataEntryMap[C]>
>;
// Allow generic `string` to avoid excessive type errors in the config
// if `dev` is not running to update as you edit.
// Invalid collection names will be caught at build time.
export function reference<C extends string>(
collection: C,
): import('astro/zod').ZodEffects<import('astro/zod').ZodString, never>;
type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
type InferEntrySchema<C extends keyof AnyEntryMap> = import('astro/zod').infer<
ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>
>;
type ContentEntryMap = {
};
type DataEntryMap = {
"bio": Record<string, {
id: string;
render(): Render[".md"];
slug: string;
body: string;
collection: "bio";
data: InferEntrySchema<"bio">;
rendered?: RenderedContent;
filePath?: string;
}>;
"blog": Record<string, {
id: string;
render(): Render[".md"];
slug: string;
body: string;
collection: "blog";
data: InferEntrySchema<"blog">;
rendered?: RenderedContent;
filePath?: string;
}>;
};
type AnyEntryMap = ContentEntryMap & DataEntryMap;
type ExtractLoaderTypes<T> = T extends import('astro/loaders').LiveLoader<
infer TData,
infer TEntryFilter,
infer TCollectionFilter,
infer TError
>
? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError }
: { data: never; entryFilter: never; collectionFilter: never; error: never };
type ExtractDataType<T> = ExtractLoaderTypes<T>['data'];
type ExtractEntryFilterType<T> = ExtractLoaderTypes<T>['entryFilter'];
type ExtractCollectionFilterType<T> = ExtractLoaderTypes<T>['collectionFilter'];
type ExtractErrorType<T> = ExtractLoaderTypes<T>['error'];
type LiveLoaderDataType<C extends keyof LiveContentConfig['collections']> =
LiveContentConfig['collections'][C]['schema'] extends undefined
? ExtractDataType<LiveContentConfig['collections'][C]['loader']>
: import('astro/zod').infer<
Exclude<LiveContentConfig['collections'][C]['schema'], undefined>
>;
type LiveLoaderEntryFilterType<C extends keyof LiveContentConfig['collections']> =
ExtractEntryFilterType<LiveContentConfig['collections'][C]['loader']>;
type LiveLoaderCollectionFilterType<C extends keyof LiveContentConfig['collections']> =
ExtractCollectionFilterType<LiveContentConfig['collections'][C]['loader']>;
type LiveLoaderErrorType<C extends keyof LiveContentConfig['collections']> = ExtractErrorType<
LiveContentConfig['collections'][C]['loader']
>;
export type ContentConfig = typeof import("../src/content/config.js");
export type LiveContentConfig = never;
}

File diff suppressed because one or more lines are too long

View file

@ -1,5 +0,0 @@
{
"_variables": {
"lastUpdateCheck": 1767866746327
}
}

2
.astro/types.d.ts vendored
View file

@ -1,2 +0,0 @@
/// <reference types="astro/client" />
/// <reference path="content.d.ts" />

1
.gitignore vendored
View file

@ -1,6 +1,7 @@
# build output # build output
dist/ dist/
.output/ .output/
.astro/
# dependencies # dependencies
node_modules/ node_modules/

View file

@ -1,11 +1,12 @@
import mdx from "@astrojs/mdx";
import sitemap from "@astrojs/sitemap"; import sitemap from "@astrojs/sitemap";
import tailwind from "@astrojs/tailwind"; import tailwind from "@astrojs/tailwind";
import { defineConfig } from "astro/config"; import { defineConfig } from "astro/config";
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
integrations: [tailwind(), sitemap()], integrations: [tailwind(), sitemap(), mdx()],
site: "https://lorenzoiovino.com", site: "https://www.lorenzoiovino.com",
compressHTML: true, compressHTML: true,
build: { build: {
inlineStylesheets: "auto", inlineStylesheets: "auto",

View file

@ -15,8 +15,10 @@
"type-check": "astro check" "type-check": "astro check"
}, },
"dependencies": { "dependencies": {
"@astrojs/mdx": "^4.3.13",
"@astrojs/tailwind": "^6.0.2", "@astrojs/tailwind": "^6.0.2",
"astro": "^5.16.7", "astro": "^5.16.7",
"sharp": "^0.34.5",
"tailwindcss": "^3.4.0" "tailwindcss": "^3.4.0"
}, },
"devDependencies": { "devDependencies": {

520
pnpm-lock.yaml generated
View file

@ -8,12 +8,18 @@ importers:
.: .:
dependencies: dependencies:
'@astrojs/mdx':
specifier: ^4.3.13
version: 4.3.13(astro@5.16.7(jiti@1.21.7)(rollup@4.55.1)(typescript@5.9.3)(yaml@2.8.2))
'@astrojs/tailwind': '@astrojs/tailwind':
specifier: ^6.0.2 specifier: ^6.0.2
version: 6.0.2(astro@5.16.7(jiti@1.21.7)(rollup@4.55.1)(typescript@5.9.3)(yaml@2.8.2))(tailwindcss@3.4.19(yaml@2.8.2)) version: 6.0.2(astro@5.16.7(jiti@1.21.7)(rollup@4.55.1)(typescript@5.9.3)(yaml@2.8.2))(tailwindcss@3.4.19(yaml@2.8.2))
astro: astro:
specifier: ^5.16.7 specifier: ^5.16.7
version: 5.16.7(jiti@1.21.7)(rollup@4.55.1)(typescript@5.9.3)(yaml@2.8.2) version: 5.16.7(jiti@1.21.7)(rollup@4.55.1)(typescript@5.9.3)(yaml@2.8.2)
sharp:
specifier: ^0.34.5
version: 0.34.5
tailwindcss: tailwindcss:
specifier: ^3.4.0 specifier: ^3.4.0
version: 3.4.19(yaml@2.8.2) version: 3.4.19(yaml@2.8.2)
@ -73,6 +79,12 @@ packages:
'@astrojs/markdown-remark@6.3.10': '@astrojs/markdown-remark@6.3.10':
resolution: {integrity: sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A==} resolution: {integrity: sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A==}
'@astrojs/mdx@4.3.13':
resolution: {integrity: sha512-IHDHVKz0JfKBy3//52JSiyWv089b7GVSChIXLrlUOoTLWowG3wr2/8hkaEgEyd/vysvNQvGk+QhysXpJW5ve6Q==}
engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0}
peerDependencies:
astro: ^5.0.0
'@astrojs/prism@3.3.0': '@astrojs/prism@3.3.0':
resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==} resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==}
engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0}
@ -500,6 +512,9 @@ packages:
'@jridgewell/trace-mapping@0.3.31': '@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
'@mdx-js/mdx@3.1.1':
resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==}
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@ -673,6 +688,9 @@ packages:
'@types/debug@4.1.12': '@types/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
'@types/estree-jsx@1.0.5':
resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
'@types/estree@1.0.8': '@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
@ -682,6 +700,9 @@ packages:
'@types/mdast@4.0.4': '@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
'@types/mdx@2.0.13':
resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==}
'@types/ms@2.1.0': '@types/ms@2.1.0':
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
@ -694,6 +715,9 @@ packages:
'@types/sax@1.2.7': '@types/sax@1.2.7':
resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==}
'@types/unist@2.0.11':
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
'@types/unist@3.0.3': '@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
@ -726,6 +750,11 @@ packages:
'@vscode/l10n@0.0.18': '@vscode/l10n@0.0.18':
resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==}
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
acorn@8.15.0: acorn@8.15.0:
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
@ -781,6 +810,10 @@ packages:
array-iterate@2.0.1: array-iterate@2.0.1:
resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==}
astring@1.9.0:
resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==}
hasBin: true
astro@5.16.7: astro@5.16.7:
resolution: {integrity: sha512-Kfv7FKisFR+THvmojXWtvJGRCvQ4D9przguE9XdeUtS464ned6hvbgmyFDvPzyaNmDtkHGNpPwAQ9tgFcVqp+Q==} resolution: {integrity: sha512-Kfv7FKisFR+THvmojXWtvJGRCvQ4D9przguE9XdeUtS464ned6hvbgmyFDvPzyaNmDtkHGNpPwAQ9tgFcVqp+Q==}
engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'}
@ -854,6 +887,9 @@ packages:
character-entities@2.0.2: character-entities@2.0.2:
resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
character-reference-invalid@2.0.1:
resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
chokidar@3.6.0: chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'} engines: {node: '>= 8.10.0'}
@ -878,6 +914,9 @@ packages:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'} engines: {node: '>=6'}
collapse-white-space@2.1.0:
resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==}
color-convert@2.0.1: color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'} engines: {node: '>=7.0.0'}
@ -1019,6 +1058,12 @@ packages:
es-module-lexer@1.7.0: es-module-lexer@1.7.0:
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
esast-util-from-estree@2.0.0:
resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==}
esast-util-from-js@2.0.1:
resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==}
esbuild@0.25.12: esbuild@0.25.12:
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -1032,6 +1077,24 @@ packages:
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
engines: {node: '>=12'} engines: {node: '>=12'}
estree-util-attach-comments@3.0.0:
resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==}
estree-util-build-jsx@3.0.1:
resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==}
estree-util-is-identifier-name@3.0.0:
resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
estree-util-scope@1.0.0:
resolution: {integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==}
estree-util-to-js@2.0.0:
resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==}
estree-util-visit@2.0.0:
resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==}
estree-walker@2.0.2: estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
@ -1137,9 +1200,15 @@ packages:
hast-util-raw@9.1.0: hast-util-raw@9.1.0:
resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==}
hast-util-to-estree@3.1.3:
resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==}
hast-util-to-html@9.0.5: hast-util-to-html@9.0.5:
resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==}
hast-util-to-jsx-runtime@2.3.6:
resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
hast-util-to-parse5@8.0.1: hast-util-to-parse5@8.0.1:
resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==}
@ -1164,9 +1233,18 @@ packages:
import-meta-resolve@4.2.0: import-meta-resolve@4.2.0:
resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==}
inline-style-parser@0.2.7:
resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==}
iron-webcrypto@1.2.1: iron-webcrypto@1.2.1:
resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==}
is-alphabetical@2.0.1:
resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
is-alphanumerical@2.0.1:
resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==}
is-binary-path@2.1.0: is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -1175,6 +1253,9 @@ packages:
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
is-decimal@2.0.1:
resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==}
is-docker@3.0.0: is-docker@3.0.0:
resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@ -1192,6 +1273,9 @@ packages:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
is-hexadecimal@2.0.1:
resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==}
is-inside-container@1.0.0: is-inside-container@1.0.0:
resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
engines: {node: '>=14.16'} engines: {node: '>=14.16'}
@ -1256,6 +1340,10 @@ packages:
magicast@0.5.1: magicast@0.5.1:
resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==}
markdown-extensions@2.0.0:
resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==}
engines: {node: '>=16'}
markdown-table@3.0.4: markdown-table@3.0.4:
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
@ -1286,6 +1374,18 @@ packages:
mdast-util-gfm@3.1.0: mdast-util-gfm@3.1.0:
resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==}
mdast-util-mdx-expression@2.0.1:
resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==}
mdast-util-mdx-jsx@3.2.0:
resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==}
mdast-util-mdx@3.0.0:
resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==}
mdast-util-mdxjs-esm@2.0.1:
resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==}
mdast-util-phrasing@4.1.0: mdast-util-phrasing@4.1.0:
resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
@ -1332,12 +1432,30 @@ packages:
micromark-extension-gfm@3.0.0: micromark-extension-gfm@3.0.0:
resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
micromark-extension-mdx-expression@3.0.1:
resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==}
micromark-extension-mdx-jsx@3.0.2:
resolution: {integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==}
micromark-extension-mdx-md@2.0.0:
resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==}
micromark-extension-mdxjs-esm@3.0.0:
resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==}
micromark-extension-mdxjs@3.0.0:
resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==}
micromark-factory-destination@2.0.1: micromark-factory-destination@2.0.1:
resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==}
micromark-factory-label@2.0.1: micromark-factory-label@2.0.1:
resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==}
micromark-factory-mdx-expression@2.0.3:
resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==}
micromark-factory-space@2.0.1: micromark-factory-space@2.0.1:
resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==}
@ -1368,6 +1486,9 @@ packages:
micromark-util-encode@2.0.1: micromark-util-encode@2.0.1:
resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
micromark-util-events-to-acorn@2.0.3:
resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==}
micromark-util-html-tag-name@2.0.1: micromark-util-html-tag-name@2.0.1:
resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==}
@ -1472,6 +1593,9 @@ packages:
package-manager-detector@1.6.0: package-manager-detector@1.6.0:
resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==}
parse-entities@4.0.2:
resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==}
parse-latin@7.0.0: parse-latin@7.0.0:
resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==}
@ -1602,6 +1726,20 @@ packages:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'} engines: {node: '>= 14.18.0'}
recma-build-jsx@1.0.0:
resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==}
recma-jsx@1.0.1:
resolution: {integrity: sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==}
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
recma-parse@1.0.0:
resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==}
recma-stringify@1.0.0:
resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==}
regex-recursion@6.0.2: regex-recursion@6.0.2:
resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
@ -1617,6 +1755,9 @@ packages:
rehype-raw@7.0.0: rehype-raw@7.0.0:
resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==}
rehype-recma@1.0.0:
resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==}
rehype-stringify@10.0.1: rehype-stringify@10.0.1:
resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==}
@ -1626,6 +1767,9 @@ packages:
remark-gfm@4.0.1: remark-gfm@4.0.1:
resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
remark-mdx@3.1.1:
resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==}
remark-parse@11.0.0: remark-parse@11.0.0:
resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
@ -1719,6 +1863,10 @@ packages:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
source-map@0.7.6:
resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
engines: {node: '>= 12'}
space-separated-tokens@2.0.2: space-separated-tokens@2.0.2:
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
@ -1747,6 +1895,12 @@ packages:
strnum@2.1.2: strnum@2.1.2:
resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==}
style-to-js@1.1.21:
resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==}
style-to-object@1.0.14:
resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==}
sucrase@3.35.1: sucrase@3.35.1:
resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==}
engines: {node: '>=16 || 14 >=14.17'} engines: {node: '>=16 || 14 >=14.17'}
@ -1852,6 +2006,9 @@ packages:
unist-util-modify-children@4.0.0: unist-util-modify-children@4.0.0:
resolution: {integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==} resolution: {integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==}
unist-util-position-from-estree@2.0.0:
resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==}
unist-util-position@5.0.0: unist-util-position@5.0.0:
resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
@ -2238,6 +2395,25 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@astrojs/mdx@4.3.13(astro@5.16.7(jiti@1.21.7)(rollup@4.55.1)(typescript@5.9.3)(yaml@2.8.2))':
dependencies:
'@astrojs/markdown-remark': 6.3.10
'@mdx-js/mdx': 3.1.1
acorn: 8.15.0
astro: 5.16.7(jiti@1.21.7)(rollup@4.55.1)(typescript@5.9.3)(yaml@2.8.2)
es-module-lexer: 1.7.0
estree-util-visit: 2.0.0
hast-util-to-html: 9.0.5
piccolore: 0.1.3
rehype-raw: 7.0.0
remark-gfm: 4.0.1
remark-smartypants: 3.0.2
source-map: 0.7.6
unist-util-visit: 5.0.0
vfile: 6.0.3
transitivePeerDependencies:
- supports-color
'@astrojs/prism@3.3.0': '@astrojs/prism@3.3.0':
dependencies: dependencies:
prismjs: 1.30.0 prismjs: 1.30.0
@ -2437,8 +2613,7 @@ snapshots:
'@esbuild/win32-x64@0.25.12': '@esbuild/win32-x64@0.25.12':
optional: true optional: true
'@img/colour@1.0.0': '@img/colour@1.0.0': {}
optional: true
'@img/sharp-darwin-arm64@0.34.5': '@img/sharp-darwin-arm64@0.34.5':
optionalDependencies: optionalDependencies:
@ -2548,6 +2723,36 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2 '@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/sourcemap-codec': 1.5.5
'@mdx-js/mdx@3.1.1':
dependencies:
'@types/estree': 1.0.8
'@types/estree-jsx': 1.0.5
'@types/hast': 3.0.4
'@types/mdx': 2.0.13
acorn: 8.15.0
collapse-white-space: 2.1.0
devlop: 1.1.0
estree-util-is-identifier-name: 3.0.0
estree-util-scope: 1.0.0
estree-walker: 3.0.3
hast-util-to-jsx-runtime: 2.3.6
markdown-extensions: 2.0.0
recma-build-jsx: 1.0.0
recma-jsx: 1.0.1(acorn@8.15.0)
recma-stringify: 1.0.0
rehype-recma: 1.0.0
remark-mdx: 3.1.1
remark-parse: 11.0.0
remark-rehype: 11.1.2
source-map: 0.7.6
unified: 11.0.5
unist-util-position-from-estree: 2.0.0
unist-util-stringify-position: 4.0.0
unist-util-visit: 5.0.0
vfile: 6.0.3
transitivePeerDependencies:
- supports-color
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
dependencies: dependencies:
'@nodelib/fs.stat': 2.0.5 '@nodelib/fs.stat': 2.0.5
@ -2682,6 +2887,10 @@ snapshots:
dependencies: dependencies:
'@types/ms': 2.1.0 '@types/ms': 2.1.0
'@types/estree-jsx@1.0.5':
dependencies:
'@types/estree': 1.0.8
'@types/estree@1.0.8': {} '@types/estree@1.0.8': {}
'@types/hast@3.0.4': '@types/hast@3.0.4':
@ -2692,6 +2901,8 @@ snapshots:
dependencies: dependencies:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
'@types/mdx@2.0.13': {}
'@types/ms@2.1.0': {} '@types/ms@2.1.0': {}
'@types/nlcst@2.0.3': '@types/nlcst@2.0.3':
@ -2704,6 +2915,8 @@ snapshots:
dependencies: dependencies:
'@types/node': 17.0.45 '@types/node': 17.0.45
'@types/unist@2.0.11': {}
'@types/unist@3.0.3': {} '@types/unist@3.0.3': {}
'@ungap/structured-clone@1.3.0': {} '@ungap/structured-clone@1.3.0': {}
@ -2758,6 +2971,10 @@ snapshots:
'@vscode/l10n@0.0.18': {} '@vscode/l10n@0.0.18': {}
acorn-jsx@5.3.2(acorn@8.15.0):
dependencies:
acorn: 8.15.0
acorn@8.15.0: {} acorn@8.15.0: {}
ajv-draft-04@1.0.0(ajv@8.17.1): ajv-draft-04@1.0.0(ajv@8.17.1):
@ -2800,6 +3017,8 @@ snapshots:
array-iterate@2.0.1: {} array-iterate@2.0.1: {}
astring@1.9.0: {}
astro@5.16.7(jiti@1.21.7)(rollup@4.55.1)(typescript@5.9.3)(yaml@2.8.2): astro@5.16.7(jiti@1.21.7)(rollup@4.55.1)(typescript@5.9.3)(yaml@2.8.2):
dependencies: dependencies:
'@astrojs/compiler': 2.13.0 '@astrojs/compiler': 2.13.0
@ -2962,6 +3181,8 @@ snapshots:
character-entities@2.0.2: {} character-entities@2.0.2: {}
character-reference-invalid@2.0.1: {}
chokidar@3.6.0: chokidar@3.6.0:
dependencies: dependencies:
anymatch: 3.1.3 anymatch: 3.1.3
@ -2990,6 +3211,8 @@ snapshots:
clsx@2.1.1: {} clsx@2.1.1: {}
collapse-white-space@2.1.0: {}
color-convert@2.0.1: color-convert@2.0.1:
dependencies: dependencies:
color-name: 1.1.4 color-name: 1.1.4
@ -3052,8 +3275,7 @@ snapshots:
destr@2.0.5: {} destr@2.0.5: {}
detect-libc@2.1.2: detect-libc@2.1.2: {}
optional: true
deterministic-object-hash@2.0.2: deterministic-object-hash@2.0.2:
dependencies: dependencies:
@ -3108,6 +3330,20 @@ snapshots:
es-module-lexer@1.7.0: {} es-module-lexer@1.7.0: {}
esast-util-from-estree@2.0.0:
dependencies:
'@types/estree-jsx': 1.0.5
devlop: 1.1.0
estree-util-visit: 2.0.0
unist-util-position-from-estree: 2.0.0
esast-util-from-js@2.0.1:
dependencies:
'@types/estree-jsx': 1.0.5
acorn: 8.15.0
esast-util-from-estree: 2.0.0
vfile-message: 4.0.3
esbuild@0.25.12: esbuild@0.25.12:
optionalDependencies: optionalDependencies:
'@esbuild/aix-ppc64': 0.25.12 '@esbuild/aix-ppc64': 0.25.12
@ -3141,6 +3377,35 @@ snapshots:
escape-string-regexp@5.0.0: {} escape-string-regexp@5.0.0: {}
estree-util-attach-comments@3.0.0:
dependencies:
'@types/estree': 1.0.8
estree-util-build-jsx@3.0.1:
dependencies:
'@types/estree-jsx': 1.0.5
devlop: 1.1.0
estree-util-is-identifier-name: 3.0.0
estree-walker: 3.0.3
estree-util-is-identifier-name@3.0.0: {}
estree-util-scope@1.0.0:
dependencies:
'@types/estree': 1.0.8
devlop: 1.1.0
estree-util-to-js@2.0.0:
dependencies:
'@types/estree-jsx': 1.0.5
astring: 1.9.0
source-map: 0.7.6
estree-util-visit@2.0.0:
dependencies:
'@types/estree-jsx': 1.0.5
'@types/unist': 3.0.3
estree-walker@2.0.2: {} estree-walker@2.0.2: {}
estree-walker@3.0.3: estree-walker@3.0.3:
@ -3270,6 +3535,27 @@ snapshots:
web-namespaces: 2.0.1 web-namespaces: 2.0.1
zwitch: 2.0.4 zwitch: 2.0.4
hast-util-to-estree@3.1.3:
dependencies:
'@types/estree': 1.0.8
'@types/estree-jsx': 1.0.5
'@types/hast': 3.0.4
comma-separated-tokens: 2.0.3
devlop: 1.1.0
estree-util-attach-comments: 3.0.0
estree-util-is-identifier-name: 3.0.0
hast-util-whitespace: 3.0.0
mdast-util-mdx-expression: 2.0.1
mdast-util-mdx-jsx: 3.2.0
mdast-util-mdxjs-esm: 2.0.1
property-information: 7.1.0
space-separated-tokens: 2.0.2
style-to-js: 1.1.21
unist-util-position: 5.0.0
zwitch: 2.0.4
transitivePeerDependencies:
- supports-color
hast-util-to-html@9.0.5: hast-util-to-html@9.0.5:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
@ -3284,6 +3570,26 @@ snapshots:
stringify-entities: 4.0.4 stringify-entities: 4.0.4
zwitch: 2.0.4 zwitch: 2.0.4
hast-util-to-jsx-runtime@2.3.6:
dependencies:
'@types/estree': 1.0.8
'@types/hast': 3.0.4
'@types/unist': 3.0.3
comma-separated-tokens: 2.0.3
devlop: 1.1.0
estree-util-is-identifier-name: 3.0.0
hast-util-whitespace: 3.0.0
mdast-util-mdx-expression: 2.0.1
mdast-util-mdx-jsx: 3.2.0
mdast-util-mdxjs-esm: 2.0.1
property-information: 7.1.0
space-separated-tokens: 2.0.2
style-to-js: 1.1.21
unist-util-position: 5.0.0
vfile-message: 4.0.3
transitivePeerDependencies:
- supports-color
hast-util-to-parse5@8.0.1: hast-util-to-parse5@8.0.1:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
@ -3321,8 +3627,17 @@ snapshots:
import-meta-resolve@4.2.0: {} import-meta-resolve@4.2.0: {}
inline-style-parser@0.2.7: {}
iron-webcrypto@1.2.1: {} iron-webcrypto@1.2.1: {}
is-alphabetical@2.0.1: {}
is-alphanumerical@2.0.1:
dependencies:
is-alphabetical: 2.0.1
is-decimal: 2.0.1
is-binary-path@2.1.0: is-binary-path@2.1.0:
dependencies: dependencies:
binary-extensions: 2.3.0 binary-extensions: 2.3.0
@ -3331,6 +3646,8 @@ snapshots:
dependencies: dependencies:
hasown: 2.0.2 hasown: 2.0.2
is-decimal@2.0.1: {}
is-docker@3.0.0: {} is-docker@3.0.0: {}
is-extglob@2.1.1: {} is-extglob@2.1.1: {}
@ -3341,6 +3658,8 @@ snapshots:
dependencies: dependencies:
is-extglob: 2.1.1 is-extglob: 2.1.1
is-hexadecimal@2.0.1: {}
is-inside-container@1.0.0: is-inside-container@1.0.0:
dependencies: dependencies:
is-docker: 3.0.0 is-docker: 3.0.0
@ -3389,6 +3708,8 @@ snapshots:
'@babel/types': 7.28.5 '@babel/types': 7.28.5
source-map-js: 1.2.1 source-map-js: 1.2.1
markdown-extensions@2.0.0: {}
markdown-table@3.0.4: {} markdown-table@3.0.4: {}
mdast-util-definitions@6.0.0: mdast-util-definitions@6.0.0:
@ -3478,6 +3799,55 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
mdast-util-mdx-expression@2.0.1:
dependencies:
'@types/estree-jsx': 1.0.5
'@types/hast': 3.0.4
'@types/mdast': 4.0.4
devlop: 1.1.0
mdast-util-from-markdown: 2.0.2
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
mdast-util-mdx-jsx@3.2.0:
dependencies:
'@types/estree-jsx': 1.0.5
'@types/hast': 3.0.4
'@types/mdast': 4.0.4
'@types/unist': 3.0.3
ccount: 2.0.1
devlop: 1.1.0
mdast-util-from-markdown: 2.0.2
mdast-util-to-markdown: 2.1.2
parse-entities: 4.0.2
stringify-entities: 4.0.4
unist-util-stringify-position: 4.0.0
vfile-message: 4.0.3
transitivePeerDependencies:
- supports-color
mdast-util-mdx@3.0.0:
dependencies:
mdast-util-from-markdown: 2.0.2
mdast-util-mdx-expression: 2.0.1
mdast-util-mdx-jsx: 3.2.0
mdast-util-mdxjs-esm: 2.0.1
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
mdast-util-mdxjs-esm@2.0.1:
dependencies:
'@types/estree-jsx': 1.0.5
'@types/hast': 3.0.4
'@types/mdast': 4.0.4
devlop: 1.1.0
mdast-util-from-markdown: 2.0.2
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
mdast-util-phrasing@4.1.0: mdast-util-phrasing@4.1.0:
dependencies: dependencies:
'@types/mdast': 4.0.4 '@types/mdast': 4.0.4
@ -3594,6 +3964,57 @@ snapshots:
micromark-util-combine-extensions: 2.0.1 micromark-util-combine-extensions: 2.0.1
micromark-util-types: 2.0.2 micromark-util-types: 2.0.2
micromark-extension-mdx-expression@3.0.1:
dependencies:
'@types/estree': 1.0.8
devlop: 1.1.0
micromark-factory-mdx-expression: 2.0.3
micromark-factory-space: 2.0.1
micromark-util-character: 2.1.1
micromark-util-events-to-acorn: 2.0.3
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
micromark-extension-mdx-jsx@3.0.2:
dependencies:
'@types/estree': 1.0.8
devlop: 1.1.0
estree-util-is-identifier-name: 3.0.0
micromark-factory-mdx-expression: 2.0.3
micromark-factory-space: 2.0.1
micromark-util-character: 2.1.1
micromark-util-events-to-acorn: 2.0.3
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
vfile-message: 4.0.3
micromark-extension-mdx-md@2.0.0:
dependencies:
micromark-util-types: 2.0.2
micromark-extension-mdxjs-esm@3.0.0:
dependencies:
'@types/estree': 1.0.8
devlop: 1.1.0
micromark-core-commonmark: 2.0.3
micromark-util-character: 2.1.1
micromark-util-events-to-acorn: 2.0.3
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
unist-util-position-from-estree: 2.0.0
vfile-message: 4.0.3
micromark-extension-mdxjs@3.0.0:
dependencies:
acorn: 8.15.0
acorn-jsx: 5.3.2(acorn@8.15.0)
micromark-extension-mdx-expression: 3.0.1
micromark-extension-mdx-jsx: 3.0.2
micromark-extension-mdx-md: 2.0.0
micromark-extension-mdxjs-esm: 3.0.0
micromark-util-combine-extensions: 2.0.1
micromark-util-types: 2.0.2
micromark-factory-destination@2.0.1: micromark-factory-destination@2.0.1:
dependencies: dependencies:
micromark-util-character: 2.1.1 micromark-util-character: 2.1.1
@ -3607,6 +4028,18 @@ snapshots:
micromark-util-symbol: 2.0.1 micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2 micromark-util-types: 2.0.2
micromark-factory-mdx-expression@2.0.3:
dependencies:
'@types/estree': 1.0.8
devlop: 1.1.0
micromark-factory-space: 2.0.1
micromark-util-character: 2.1.1
micromark-util-events-to-acorn: 2.0.3
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
unist-util-position-from-estree: 2.0.0
vfile-message: 4.0.3
micromark-factory-space@2.0.1: micromark-factory-space@2.0.1:
dependencies: dependencies:
micromark-util-character: 2.1.1 micromark-util-character: 2.1.1
@ -3659,6 +4092,16 @@ snapshots:
micromark-util-encode@2.0.1: {} micromark-util-encode@2.0.1: {}
micromark-util-events-to-acorn@2.0.3:
dependencies:
'@types/estree': 1.0.8
'@types/unist': 3.0.3
devlop: 1.1.0
estree-util-visit: 2.0.0
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
vfile-message: 4.0.3
micromark-util-html-tag-name@2.0.1: {} micromark-util-html-tag-name@2.0.1: {}
micromark-util-normalize-identifier@2.0.1: micromark-util-normalize-identifier@2.0.1:
@ -3778,6 +4221,16 @@ snapshots:
package-manager-detector@1.6.0: {} package-manager-detector@1.6.0: {}
parse-entities@4.0.2:
dependencies:
'@types/unist': 2.0.11
character-entities-legacy: 3.0.0
character-reference-invalid: 2.0.1
decode-named-character-reference: 1.2.0
is-alphanumerical: 2.0.1
is-decimal: 2.0.1
is-hexadecimal: 2.0.1
parse-latin@7.0.0: parse-latin@7.0.0:
dependencies: dependencies:
'@types/nlcst': 2.0.3 '@types/nlcst': 2.0.3
@ -3883,6 +4336,35 @@ snapshots:
readdirp@4.1.2: {} readdirp@4.1.2: {}
recma-build-jsx@1.0.0:
dependencies:
'@types/estree': 1.0.8
estree-util-build-jsx: 3.0.1
vfile: 6.0.3
recma-jsx@1.0.1(acorn@8.15.0):
dependencies:
acorn: 8.15.0
acorn-jsx: 5.3.2(acorn@8.15.0)
estree-util-to-js: 2.0.0
recma-parse: 1.0.0
recma-stringify: 1.0.0
unified: 11.0.5
recma-parse@1.0.0:
dependencies:
'@types/estree': 1.0.8
esast-util-from-js: 2.0.1
unified: 11.0.5
vfile: 6.0.3
recma-stringify@1.0.0:
dependencies:
'@types/estree': 1.0.8
estree-util-to-js: 2.0.0
unified: 11.0.5
vfile: 6.0.3
regex-recursion@6.0.2: regex-recursion@6.0.2:
dependencies: dependencies:
regex-utilities: 2.3.0 regex-utilities: 2.3.0
@ -3905,6 +4387,14 @@ snapshots:
hast-util-raw: 9.1.0 hast-util-raw: 9.1.0
vfile: 6.0.3 vfile: 6.0.3
rehype-recma@1.0.0:
dependencies:
'@types/estree': 1.0.8
'@types/hast': 3.0.4
hast-util-to-estree: 3.1.3
transitivePeerDependencies:
- supports-color
rehype-stringify@10.0.1: rehype-stringify@10.0.1:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
@ -3929,6 +4419,13 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
remark-mdx@3.1.1:
dependencies:
mdast-util-mdx: 3.0.0
micromark-extension-mdxjs: 3.0.0
transitivePeerDependencies:
- supports-color
remark-parse@11.0.0: remark-parse@11.0.0:
dependencies: dependencies:
'@types/mdast': 4.0.4 '@types/mdast': 4.0.4
@ -4075,7 +4572,6 @@ snapshots:
'@img/sharp-win32-arm64': 0.34.5 '@img/sharp-win32-arm64': 0.34.5
'@img/sharp-win32-ia32': 0.34.5 '@img/sharp-win32-ia32': 0.34.5
'@img/sharp-win32-x64': 0.34.5 '@img/sharp-win32-x64': 0.34.5
optional: true
shiki@3.21.0: shiki@3.21.0:
dependencies: dependencies:
@ -4101,6 +4597,8 @@ snapshots:
source-map-js@1.2.1: {} source-map-js@1.2.1: {}
source-map@0.7.6: {}
space-separated-tokens@2.0.2: {} space-separated-tokens@2.0.2: {}
stream-replace-string@2.0.0: {} stream-replace-string@2.0.0: {}
@ -4132,6 +4630,14 @@ snapshots:
strnum@2.1.2: {} strnum@2.1.2: {}
style-to-js@1.1.21:
dependencies:
style-to-object: 1.0.14
style-to-object@1.0.14:
dependencies:
inline-style-parser: 0.2.7
sucrase@3.35.1: sucrase@3.35.1:
dependencies: dependencies:
'@jridgewell/gen-mapping': 0.3.13 '@jridgewell/gen-mapping': 0.3.13
@ -4266,6 +4772,10 @@ snapshots:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
array-iterate: 2.0.1 array-iterate: 2.0.1
unist-util-position-from-estree@2.0.0:
dependencies:
'@types/unist': 3.0.3
unist-util-position@5.0.0: unist-util-position@5.0.0:
dependencies: dependencies:
'@types/unist': 3.0.3 '@types/unist': 3.0.3

View file

@ -1,7 +1,7 @@
{ {
"name": "Lorenzo Iovino - Software Developer", "name": "Lorenzo Iovino - Software Engineer",
"short_name": "Lorenzo Iovino", "short_name": "Lorenzo Iovino",
"description": "Software Developer based in Sicily. Passionate about technology, remote work, and life balance.", "description": "Software Engineer based in Sicily. Passionate about technology, remote work, and life balance.",
"start_url": "/", "start_url": "/",
"display": "standalone", "display": "standalone",
"background_color": "#1e3a8a", "background_color": "#1e3a8a",
@ -12,18 +12,6 @@
"src": "/favicon.ico", "src": "/favicon.ico",
"sizes": "48x48", "sizes": "48x48",
"type": "image/x-icon" "type": "image/x-icon"
},
{
"src": "/photos/me.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/photos/me.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
} }
], ],
"categories": ["business", "productivity", "lifestyle"], "categories": ["business", "productivity", "lifestyle"],
@ -38,8 +26,8 @@
"url": "/blog", "url": "/blog",
"icons": [ "icons": [
{ {
"src": "/photos/me.png", "src": "/favicon.ico",
"sizes": "192x192" "sizes": "48x48"
} }
] ]
}, },
@ -50,8 +38,8 @@
"url": "/bio", "url": "/bio",
"icons": [ "icons": [
{ {
"src": "/photos/me.png", "src": "/favicon.ico",
"sizes": "192x192" "sizes": "48x48"
} }
] ]
} }

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 705 KiB

After

Width:  |  Height:  |  Size: 705 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 189 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 570 KiB

After

Width:  |  Height:  |  Size: 570 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.5 MiB

After

Width:  |  Height:  |  Size: 3.5 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 414 KiB

After

Width:  |  Height:  |  Size: 414 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3 MiB

After

Width:  |  Height:  |  Size: 3 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 210 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 224 KiB

After

Width:  |  Height:  |  Size: 224 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 939 KiB

After

Width:  |  Height:  |  Size: 939 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 519 KiB

After

Width:  |  Height:  |  Size: 519 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 11 MiB

After

Width:  |  Height:  |  Size: 11 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 421 KiB

After

Width:  |  Height:  |  Size: 421 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 706 KiB

After

Width:  |  Height:  |  Size: 706 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 772 B

After

Width:  |  Height:  |  Size: 772 B

Before After
Before After

View file

@ -1,5 +1,8 @@
--- ---
import { Image } from "astro:assets";
import astroIcon from "../assets/astro.svg";
import tailwindIcon from "../assets/tailwind.svg";
import githubIcon from "../assets/github.svg";
--- ---
<div class="w-full bg-secondary py-2 px-4"> <div class="w-full bg-secondary py-2 px-4">
@ -79,11 +82,11 @@
class="inline-flex items-center hover:text-white transition-all duration-200" class="inline-flex items-center hover:text-white transition-all duration-200"
title="Astro" title="Astro"
> >
<img <Image
class="h-5 w-5" class="h-5 w-5"
src="/astro.svg" src={astroIcon}
height="20" width={20}
width="20" height={20}
loading="lazy" loading="lazy"
alt="Astro Logo" alt="Astro Logo"
/> />
@ -94,11 +97,11 @@
class="inline-flex items-center center hover:text-white transition-all duration-200" class="inline-flex items-center center hover:text-white transition-all duration-200"
title="TailwindCSS" title="TailwindCSS"
> >
<img <Image
class="h-5 w-5" class="h-5 w-5"
src="/tailwind.svg" src={tailwindIcon}
height="20" width={20}
width="20" height={20}
loading="lazy" loading="lazy"
alt="Tailwind Logo" alt="Tailwind Logo"
/> />
@ -109,11 +112,11 @@
class="hover:text-white transition-colors duration-200 inline-flex items-center justify-center sm:justify-end gap-2 font-medium" class="hover:text-white transition-colors duration-200 inline-flex items-center justify-center sm:justify-end gap-2 font-medium"
> >
<span>Check the source code</span> <span>Check the source code</span>
<img <Image
src="/github.svg" src={githubIcon}
class="h-5 w-5" class="h-5 w-5"
height="20" width={20}
width="20" height={20}
loading="lazy" loading="lazy"
alt="Github Logo" alt="Github Logo"
/> />

View file

@ -1,5 +1,8 @@
--- ---
import { Image } from "astro:assets";
import mePhoto from "../assets/photos/me.png";
import linkedinIcon from "../assets/linkedin.svg";
import githubIcon from "../assets/github.svg";
--- ---
<div class="w-full"> <div class="w-full">
@ -12,12 +15,14 @@
<!-- Photo Section --> <!-- Photo Section -->
<div class="w-full md:w-auto flex-shrink-0 order-2 md:order-none"> <div class="w-full md:w-auto flex-shrink-0 order-2 md:order-none">
<div class="relative overflow-hidden md:rounded-l-3xl flex justify-center md:block"> <div class="relative overflow-hidden md:rounded-l-3xl flex justify-center md:block">
<img <Image
src="/photos/me.png" src={mePhoto}
alt="Photo of Lorenzo Iovino" alt="Photo of Lorenzo Iovino"
class="w-48 h-auto md:w-full object-cover object-center" class="w-48 h-auto md:w-full object-cover object-center"
width="300" width={300}
height="300" height={300}
quality={90}
format="webp"
/> />
<div class="absolute inset-0 to-transparent md:bg-gradient-to-r"></div> <div class="absolute inset-0 to-transparent md:bg-gradient-to-r"></div>
<div class="absolute inset-0 to-transparent md:bg-gradient-to-r"></div> <div class="absolute inset-0 to-transparent md:bg-gradient-to-r"></div>
@ -57,12 +62,12 @@
class="group relative p-3 bg-gray-50 hover:bg-secondary/10 rounded-xl transition-all duration-200 transform hover:-translate-y-1" class="group relative p-3 bg-gray-50 hover:bg-secondary/10 rounded-xl transition-all duration-200 transform hover:-translate-y-1"
aria-label="LinkedIn" aria-label="LinkedIn"
> >
<img <Image
src="/linkedin.svg" src={linkedinIcon}
alt="LinkedIn" alt="LinkedIn"
class="h-6 w-6 opacity-70 group-hover:opacity-100 transition-opacity" class="h-6 w-6 opacity-70 group-hover:opacity-100 transition-opacity"
width="24" width={24}
height="24" height={24}
loading="lazy" loading="lazy"
/> />
</a> </a>
@ -71,12 +76,12 @@
class="group relative p-3 bg-gray-50 hover:bg-secondary/10 rounded-xl transition-all duration-200 transform hover:-translate-y-1" class="group relative p-3 bg-gray-50 hover:bg-secondary/10 rounded-xl transition-all duration-200 transform hover:-translate-y-1"
aria-label="GitHub" aria-label="GitHub"
> >
<img <Image
src="/github.svg" src={githubIcon}
alt="GitHub" alt="GitHub"
class="h-6 w-6 opacity-70 group-hover:opacity-100 transition-opacity" class="h-6 w-6 opacity-70 group-hover:opacity-100 transition-opacity"
width="24" width={24}
height="24" height={24}
loading="lazy" loading="lazy"
/> />
</a> </a>

View file

@ -3,6 +3,21 @@ title: "My Story"
description: "A journey through code, curiosity, and creativity" description: "A journey through code, curiosity, and creativity"
--- ---
import { Image } from 'astro:assets';
import meBaby from '../../assets/photos/me-baby.jpg';
import pokemon from '../../assets/photos/pokemon.jpg';
import meGuitar from '../../assets/photos/me-guitar-17.jpg';
import meCC from '../../assets/photos/me-cc.jpg';
import goliardia from '../../assets/photos/goliardia.jpg';
import meMoverio from '../../assets/photos/me-moverio.jpg';
import gameJam from '../../assets/photos/game-jam.jpg';
import valenciaTuria from '../../assets/photos/valencia-turia.jpg';
import remote from '../../assets/photos/remote.jpg';
import dogs from '../../assets/photos/dogs.jpg';
import wine from '../../assets/photos/wine.jpg';
import modica from '../../assets/photos/modica.jpg';
import meAmanda from '../../assets/photos/me-amanda.jpg';
## Hello world! ## Hello world!
Hi! Im Lorenzo Iovino. Hi! Im Lorenzo Iovino.
@ -16,8 +31,8 @@ This page is a small recap of my story. Nothing special, just me.
## Childhood Nostalgia ## Childhood Nostalgia
<div class="float-right img-medium"> <div class="float-right img-medium">
<img src="/photos/me-baby.jpg" alt="Super young software developer with an Apple II" class="float-right img-medium" /> <Image src={meBaby} alt="Super young geek with an Apple II" class="float-right img-medium" width={400} height={300} quality={85} format="webp" />
<em class="text-sm block mt-2">Super young software developer with an Apple II</em> <em class="text-sm block mt-2">Super young software geek with an Apple II</em>
</div> </div>
My first “wow” moment with computers was very early. I was around 4 years old and I was playing Prince of Persia on an Apple II. My first “wow” moment with computers was very early. I was around 4 years old and I was playing Prince of Persia on an Apple II.
@ -29,7 +44,7 @@ I grew up in Ispica, in the south of Sicily. My days were simple: school, videog
Ispica is slow, warm, and beautiful. When I was a kid I didnt see it that way. I wanted to escape. I was dreaming about big cities, more people, more things happening, more opportunities. Ispica is slow, warm, and beautiful. When I was a kid I didnt see it that way. I wanted to escape. I was dreaming about big cities, more people, more things happening, more opportunities.
<div class="float-left img-medium"> <div class="float-left img-medium">
<img src="/photos/pokemon.jpg" alt="Pokemon Yellow and Game Boy Advance" class="float-left img-medium" /> <Image src={pokemon} alt="Pokemon Yellow and Game Boy Advance" class="float-left img-medium" width={400} height={300} quality={85} format="webp" />
<em class="text-sm block mt-2">Pokemon Yellow and Game Boy Advance</em> <em class="text-sm block mt-2">Pokemon Yellow and Game Boy Advance</em>
</div> </div>
@ -38,7 +53,7 @@ Then came Pokemon, Nintendo consoles, and long afternoons with friends. And at s
When I was 15 I discovered rock music and it hit me hard. I bought a guitar and I started learning (very badly at the beginning) but I loved it. When I was 15 I discovered rock music and it hit me hard. I bought a guitar and I started learning (very badly at the beginning) but I loved it.
<div class="float-right img-medium"> <div class="float-right img-medium">
<img src="/photos/me-guitar-17.jpg" alt="My dream guitar Fender stratocaster" class="float-right img-medium" /> <Image src={meGuitar} alt="My dream guitar Fender stratocaster" class="float-right img-medium" width={400} height={300} quality={85} format="webp" />
<em class="text-sm block mt-2">My dream guitar "Fender stratocaster"</em> <em class="text-sm block mt-2">My dream guitar "Fender stratocaster"</em>
</div> </div>
@ -51,7 +66,7 @@ At 17 I also discovered Magic: The Gathering. It became another obsession for a
I studied Computer Science at the University of Pisa. It was not a straight path. I studied Computer Science at the University of Pisa. It was not a straight path.
<div class="float-right img-medium"> <div class="float-right img-medium">
<img src="/photos/me-cc.jpg" alt="Me burning out studying" class="float-right img-medium" /> <Image src={meCC} alt="Me burning out studying" class="float-right img-medium" width={400} height={300} quality={85} format="webp" />
<em class="text-sm block mt-2">Me burning out studying Computability and Complexity exam</em> <em class="text-sm block mt-2">Me burning out studying Computability and Complexity exam</em>
</div> </div>
@ -64,7 +79,7 @@ It took me 12 years to finish my Bachelor. Not proud of the timeline, but Im
Moving to Pisa was not only about studying. It was also my first real “life outside Sicily” experience, and I needed that. Moving to Pisa was not only about studying. It was also my first real “life outside Sicily” experience, and I needed that.
<div class="float-left img-medium"> <div class="float-left img-medium">
<img src="/photos/goliardia.jpg" alt="My student hat goliardo" class="float-left img-medium" /> <Image src={goliardia} alt="My student hat goliardo" class="float-left img-medium" width={400} height={300} quality={85} format="webp" />
<em class="text-sm block mt-2">My student hat (that's not a hat) "goliardo"</em> <em class="text-sm block mt-2">My student hat (that's not a hat) "goliardo"</em>
</div> </div>
@ -75,7 +90,7 @@ That period also started my love for traveling. Seeing different cultures in rea
## Embarking on Hackathon Adventures ## Embarking on Hackathon Adventures
<div class="float-right img-small"> <div class="float-right img-small">
<img src="/photos/me-moverio.jpg" alt="Me wearing moverio smart glasses" class="float-right img-small" /> <Image src={meMoverio} alt="Me wearing moverio smart glasses" class="float-right img-small" width={300} height={300} quality={85} format="webp" />
<em class="text-sm block mt-2">Me wearing moverio smart glasses</em> <em class="text-sm block mt-2">Me wearing moverio smart glasses</em>
</div> </div>
@ -88,7 +103,7 @@ It was made for Epson Moverio Smartglass: the idea was to let people “place”
That experience made me addicted to hackathons. After that I joined other events like Hackaton Toscana (mobility) and also some game jams. Every time you learn something new, and you also learn a lot about teamwork under pressure. That experience made me addicted to hackathons. After that I joined other events like Hackaton Toscana (mobility) and also some game jams. Every time you learn something new, and you also learn a lot about teamwork under pressure.
<div class="w-full"> <div class="w-full">
<img src="/photos/game-jam.jpg" alt="Me and the team presenting the game" class="img-hero" /> <Image src={gameJam} alt="Me and the team presenting the game" class="img-hero" width={1200} height={630} quality={80} format="webp" />
<em class="text-sm block mt-2">Me and the team presenting the game developed</em> <em class="text-sm block mt-2">Me and the team presenting the game developed</em>
</div> </div>
@ -97,7 +112,7 @@ That experience made me addicted to hackathons. After that I joined other events
## Erasmus Project in Valencia ## Erasmus Project in Valencia
<div class="float-right img-large"> <div class="float-right img-large">
<img src="/photos/valencia-turia.jpg" alt="Beautiful sunny day in Valencia" class="float-right img-large" /> <Image src={valenciaTuria} alt="Beautiful sunny day in Valencia" class="float-right img-large" width={600} height={400} quality={80} format="webp" />
<em class="text-sm block mt-2">Beautiful sunny day in Valencia</em> <em class="text-sm block mt-2">Beautiful sunny day in Valencia</em>
</div> </div>
@ -116,7 +131,7 @@ At some point I decided to go back to Sicily.
Not as a “I give up” move. More like: I want a different balance. Not as a “I give up” move. More like: I want a different balance.
<div class="float-left img-medium"> <div class="float-left img-medium">
<img src="/photos/remote.jpg" alt="Working remote watching the sea" class="float-left img-medium" /> <Image src={remote} alt="Working remote watching the sea" class="float-left img-medium" width={400} height={300} quality={80} format="webp" />
<em class="text-sm block mt-2">Working remote watching the sea</em> <em class="text-sm block mt-2">Working remote watching the sea</em>
</div> </div>
@ -127,7 +142,7 @@ I work remote, and Sicily is perfect for that. Life is slower. Sometimes its
Time here feels different. You can actually breathe. You can have “nothing special” days, and those days can still feel good. Time here feels different. You can actually breathe. You can have “nothing special” days, and those days can still feel good.
<div class="w-full"> <div class="w-full">
<img src="/photos/dogs.jpg" alt="My wineyard" class="img-hero" /> <Image src={dogs} alt="My wineyard" class="img-hero" width={1200} height={630} quality={80} format="webp" />
<em class="text-sm block mt-2">My wineyard</em> <em class="text-sm block mt-2">My wineyard</em>
</div> </div>
@ -136,7 +151,7 @@ Family is a big part of my life here. And I also started a side project with my
That project became [www.netum.it](https://netum.it/). Its small (1 hectare), limited bottles, but its something we built together and I love it. That project became [www.netum.it](https://netum.it/). Its small (1 hectare), limited bottles, but its something we built together and I love it.
<div class="float-right img-small"> <div class="float-right img-small">
<img src="/photos/wine.jpg" alt="The wine produced Zia Lina" class="float-right img-small" /> <Image src={wine} alt="The wine produced Zia Lina" class="float-right img-small" width={300} height={300} quality={85} format="webp" />
<em class="text-sm block mt-2">The wine produced "Zia Lina"</em> <em class="text-sm block mt-2">The wine produced "Zia Lina"</em>
</div> </div>
@ -147,7 +162,7 @@ And yes: food. Sicily is crazy for food. Its not even a “food culture”, i
For me, living here is a reminder: success is not only about projects and code. Its also about how you live your days. For me, living here is a reminder: success is not only about projects and code. Its also about how you live your days.
<div class="w-full"> <div class="w-full">
<img src="/photos/modica.jpg" alt="Modica view from my house" class="img-hero" /> <Image src={modica} alt="Modica view from my house" class="img-hero" width={1200} height={630} quality={80} format="webp" />
<em class="text-sm block mt-2">Modica view from my house</em> <em class="text-sm block mt-2">Modica view from my house</em>
</div> </div>
@ -158,6 +173,6 @@ Today Im a husband (to my wife Amanda) and a dad (to our little Leonardo).
Life changed a lot, in a good way. I still love technology, I still build things, but family gives a different meaning to everything. Life changed a lot, in a good way. I still love technology, I still build things, but family gives a different meaning to everything.
<div class="w-full"> <div class="w-full">
<img src="/photos/me-amanda.jpg" alt="Me and my wife Amanda" class="img-hero" /> <Image src={meAmanda} alt="Me and my wife Amanda" class="img-hero" width={1200} height={630} quality={85} format="webp" />
<em class="text-sm block mt-2">Me and my wife Amanda</em> <em class="text-sm block mt-2">Me and my wife Amanda</em>
</div> </div>

View file

@ -2,7 +2,7 @@
title: "Welcome to My Blog!" title: "Welcome to My Blog!"
description: "First commit" description: "First commit"
pubDate: 2024-01-15 pubDate: 2024-01-15
heroImage: "/photos/remote.jpg" heroImage: "../../assets/photos/remote.jpg"
tags: ["personal", "welcome"] tags: ["personal", "welcome"]
--- ---

View file

@ -2,12 +2,12 @@ import { defineCollection, z } from "astro:content";
const blog = defineCollection({ const blog = defineCollection({
type: "content", type: "content",
schema: z.object({ schema: ({ image }) => z.object({
title: z.string(), title: z.string(),
description: z.string(), description: z.string(),
pubDate: z.coerce.date(), pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(), updatedDate: z.coerce.date().optional(),
heroImage: z.string().optional(), heroImage: image().optional(),
author: z.string().default("Lorenzo Iovino"), author: z.string().default("Lorenzo Iovino"),
tags: z.array(z.string()).default([]), tags: z.array(z.string()).default([]),
draft: z.boolean().default(false), draft: z.boolean().default(false),

View file

@ -12,7 +12,7 @@ interface Props {
const { const {
title, title,
description = "Lorenzo Iovino - Software Developer based in Sicily. Passionate about technology, remote work, and life balance.", description = "Lorenzo Iovino - Software Engineer based in Sicily. Passionate about technology, remote work, and life balance.",
canonicalUrl, canonicalUrl,
image = "/photos/me.png", image = "/photos/me.png",
type = "website", type = "website",
@ -21,7 +21,7 @@ const {
tags = [], tags = [],
} = Astro.props; } = Astro.props;
const siteUrl = "https://lorenzoiovino.com"; const siteUrl = "https://www.lorenzoiovino.com";
const fullImageUrl = image.startsWith("http") ? image : `${siteUrl}${image}`; const fullImageUrl = image.startsWith("http") ? image : `${siteUrl}${image}`;
const fullCanonicalUrl = canonicalUrl || `${siteUrl}${Astro.url.pathname}`; const fullCanonicalUrl = canonicalUrl || `${siteUrl}${Astro.url.pathname}`;

View file

@ -1,10 +1,13 @@
--- ---
import type { CollectionEntry } from "astro:content"; import type { CollectionEntry } from "astro:content";
import { getEntry } from "astro:content"; import { getEntry } from "astro:content";
import { Image } from "astro:assets";
import Footer from "../components/Footer.astro"; import Footer from "../components/Footer.astro";
import Navbar from "../components/Navbar.astro"; import Navbar from "../components/Navbar.astro";
import BaseLayout from "../layouts/BaseLayout.astro"; import BaseLayout from "../layouts/BaseLayout.astro";
import "../styles/prose.css"; import "../styles/prose.css";
import linkedinIcon from "../assets/linkedin.svg";
import githubIcon from "../assets/github.svg";
type BioEntry = CollectionEntry<"bio">; type BioEntry = CollectionEntry<"bio">;
@ -20,7 +23,6 @@ const { Content } = await bioEntry.render();
<BaseLayout <BaseLayout
title="Lorenzo Iovino >> Bio" title="Lorenzo Iovino >> Bio"
description="Biography and life story of Lorenzo Iovino" description="Biography and life story of Lorenzo Iovino"
canonicalUrl="https://www.lorenzoiovino.com/bio"
> >
<Navbar /> <Navbar />
<div class="min-h-screen pt-16 bg-secondary"> <div class="min-h-screen pt-16 bg-secondary">
@ -56,12 +58,12 @@ const { Content } = await bioEntry.render();
rel="noopener noreferrer" rel="noopener noreferrer"
class="inline-flex items-center px-10 py-5 bg-white hover:bg-gray-50 text-gray-700 font-semibold text-lg rounded-xl transition-all duration-200 shadow-lg hover:shadow-xl border-2 border-gray-200 hover:border-gray-300 transform hover:-translate-y-1 hover:scale-105" class="inline-flex items-center px-10 py-5 bg-white hover:bg-gray-50 text-gray-700 font-semibold text-lg rounded-xl transition-all duration-200 shadow-lg hover:shadow-xl border-2 border-gray-200 hover:border-gray-300 transform hover:-translate-y-1 hover:scale-105"
> >
<img <Image
src="/linkedin.svg" src={linkedinIcon}
alt="LinkedIn" alt="LinkedIn"
class="h-6 w-6 mr-3 opacity-80" class="h-6 w-6 mr-3 opacity-80"
width="24" width={24}
height="24" height={24}
/> />
LinkedIn LinkedIn
</a> </a>
@ -71,12 +73,12 @@ const { Content } = await bioEntry.render();
rel="noopener noreferrer" rel="noopener noreferrer"
class="inline-flex items-center px-10 py-5 bg-white hover:bg-gray-50 text-gray-700 font-semibold text-lg rounded-xl transition-all duration-200 shadow-lg hover:shadow-xl border-2 border-gray-200 hover:border-gray-300 transform hover:-translate-y-1 hover:scale-105" class="inline-flex items-center px-10 py-5 bg-white hover:bg-gray-50 text-gray-700 font-semibold text-lg rounded-xl transition-all duration-200 shadow-lg hover:shadow-xl border-2 border-gray-200 hover:border-gray-300 transform hover:-translate-y-1 hover:scale-105"
> >
<img <Image
src="/github.svg" src={githubIcon}
alt="GitHub" alt="GitHub"
class="h-6 w-6 mr-3 opacity-80" class="h-6 w-6 mr-3 opacity-80"
width="24" width={24}
height="24" height={24}
/> />
GitHub GitHub
</a> </a>

View file

@ -1,6 +1,7 @@
--- ---
import type { CollectionEntry } from "astro:content"; import type { CollectionEntry } from "astro:content";
import { getCollection } from "astro:content"; import { getCollection } from "astro:content";
import { Image } from "astro:assets";
import Footer from "../components/Footer.astro"; import Footer from "../components/Footer.astro";
import Navbar from "../components/Navbar.astro"; import Navbar from "../components/Navbar.astro";
import BaseLayout from "../layouts/BaseLayout.astro"; import BaseLayout from "../layouts/BaseLayout.astro";
@ -33,7 +34,6 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
<BaseLayout <BaseLayout
title="Lorenzo Iovino >> Blog" title="Lorenzo Iovino >> Blog"
description="Blog posts and articles by Lorenzo Iovino" description="Blog posts and articles by Lorenzo Iovino"
canonicalUrl="https://www.lorenzoiovino.com/blog"
> >
<Navbar /> <Navbar />
<div class="min-h-screen pt-16 bg-secondary"> <div class="min-h-screen pt-16 bg-secondary">
@ -115,10 +115,14 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
{post.data.heroImage && ( {post.data.heroImage && (
<a href={`/blog/${post.slug}`} class="block"> <a href={`/blog/${post.slug}`} class="block">
<div class="relative h-64 overflow-hidden"> <div class="relative h-64 overflow-hidden">
<img <Image
src={post.data.heroImage} src={post.data.heroImage}
alt={post.data.title} alt={post.data.title}
class="w-full h-full object-cover hover:scale-105 transition-transform duration-300" class="w-full h-full object-cover hover:scale-105 transition-transform duration-300"
width={1200}
height={630}
quality={80}
format="webp"
/> />
<div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent" /> <div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent" />
</div> </div>

View file

@ -1,6 +1,7 @@
--- ---
import type { CollectionEntry } from "astro:content"; import type { CollectionEntry } from "astro:content";
import { getCollection } from "astro:content"; import { getCollection } from "astro:content";
import { Image } from "astro:assets";
import BaseLayout from "../../layouts/BaseLayout.astro"; import BaseLayout from "../../layouts/BaseLayout.astro";
import Navbar from "../../components/Navbar.astro"; import Navbar from "../../components/Navbar.astro";
import Footer from "../../components/Footer.astro"; import Footer from "../../components/Footer.astro";
@ -18,13 +19,15 @@ export async function getStaticPaths() {
const { entry }: { entry: BlogPost } = Astro.props; const { entry }: { entry: BlogPost } = Astro.props;
const { Content } = await entry.render(); const { Content } = await entry.render();
// Extract image src for meta tags (Open Graph expects a URL string)
const heroImageSrc = entry.data.heroImage?.src || "/photos/me.png";
--- ---
<BaseLayout <BaseLayout
title={`${entry.data.title} | Lorenzo Iovino`} title={`${entry.data.title} | Lorenzo Iovino`}
description={entry.data.description} description={entry.data.description}
canonicalUrl={`https://www.lorenzoiovino.com/blog/${entry.slug}`} image={heroImageSrc}
image={entry.data.heroImage || "/photos/me.png"}
type="article" type="article"
publishedTime={entry.data.pubDate} publishedTime={entry.data.pubDate}
modifiedTime={entry.data.updatedDate} modifiedTime={entry.data.updatedDate}
@ -69,10 +72,14 @@ const { Content } = await entry.render();
entry.data.heroImage && ( entry.data.heroImage && (
<div class="mb-8 md:mb-12 -mx-4 sm:mx-0"> <div class="mb-8 md:mb-12 -mx-4 sm:mx-0">
<div class="relative h-64 sm:h-80 md:h-96 overflow-hidden sm:rounded-2xl shadow-xl"> <div class="relative h-64 sm:h-80 md:h-96 overflow-hidden sm:rounded-2xl shadow-xl">
<img <Image
src={entry.data.heroImage} src={entry.data.heroImage}
alt={entry.data.title} alt={entry.data.title}
class="w-full h-full object-cover" class="w-full h-full object-cover"
width={1200}
height={630}
quality={80}
format="webp"
/> />
</div> </div>
</div> </div>
@ -91,6 +98,8 @@ const { Content } = await entry.render();
src="/photos/me.png" src="/photos/me.png"
alt="Lorenzo Iovino" alt="Lorenzo Iovino"
class="w-20 h-20 rounded-full object-cover" class="w-20 h-20 rounded-full object-cover"
width="80"
height="80"
/> />
<div> <div>
<h3 class="text-lg md:text-xl font-bold text-gray-900 mb-2">Lorenzo Iovino</h3> <h3 class="text-lg md:text-xl font-bold text-gray-900 mb-2">Lorenzo Iovino</h3>

View file

@ -1,6 +1,7 @@
--- ---
import type { CollectionEntry } from "astro:content"; import type { CollectionEntry } from "astro:content";
import { getCollection } from "astro:content"; import { getCollection } from "astro:content";
import { Image } from "astro:assets";
import Footer from "../../../components/Footer.astro"; import Footer from "../../../components/Footer.astro";
import Navbar from "../../../components/Navbar.astro"; import Navbar from "../../../components/Navbar.astro";
import BaseLayout from "../../../layouts/BaseLayout.astro"; import BaseLayout from "../../../layouts/BaseLayout.astro";
@ -38,7 +39,6 @@ const displayTag: string = tag.charAt(0).toUpperCase() + tag.slice(1);
<BaseLayout <BaseLayout
title={`${displayTag} Posts | Lorenzo Iovino Blog`} title={`${displayTag} Posts | Lorenzo Iovino Blog`}
description={`Browse all blog posts about ${displayTag}. ${posts.length} article${posts.length !== 1 ? "s" : ""} found.`} description={`Browse all blog posts about ${displayTag}. ${posts.length} article${posts.length !== 1 ? "s" : ""} found.`}
canonicalUrl={`https://www.lorenzoiovino.com/blog/tag/${tag}`}
> >
<Navbar /> <Navbar />
<div class="min-h-screen pt-16 bg-secondary"> <div class="min-h-screen pt-16 bg-secondary">
@ -92,10 +92,14 @@ const displayTag: string = tag.charAt(0).toUpperCase() + tag.slice(1);
{post.data.heroImage && ( {post.data.heroImage && (
<a href={`/blog/${post.slug}`} class="block"> <a href={`/blog/${post.slug}`} class="block">
<div class="relative h-64 overflow-hidden"> <div class="relative h-64 overflow-hidden">
<img <Image
src={post.data.heroImage} src={post.data.heroImage}
alt={post.data.title} alt={post.data.title}
class="w-full h-full object-cover hover:scale-105 transition-transform duration-300" class="w-full h-full object-cover hover:scale-105 transition-transform duration-300"
width={1200}
height={630}
quality={80}
format="webp"
/> />
<div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent" /> <div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent" />
</div> </div>

View file

@ -7,9 +7,8 @@ import BaseLayout from "../layouts/BaseLayout.astro";
--- ---
<BaseLayout <BaseLayout
title="Lorenzo Iovino >> Home" title="Lorenzo Iovino >> Homepage "
description="Lorenzo Iovino - Software Developer" description="Lorenzo Iovino - Software Engineer"
canonicalUrl="https://www.lorenzoiovino.com"
> >
<Navbar /> <Navbar />
<div class="bg-secondary min-h-screen flex flex-col relative pt-16"> <div class="bg-secondary min-h-screen flex flex-col relative pt-16">