diff --git a/apps/web/package.json b/apps/web/package.json
index fff4cd3aa7..d9c4f1d69a 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -25,16 +25,20 @@
"@types/node": "^22.3.0",
"svelte": "catalog:svelte",
"svelte-check": "catalog:svelte",
- "vite": "catalog:",
- "svelte-french-toast": "^1.2.0"
+ "svelte-french-toast": "^1.2.0",
+ "vite": "catalog:"
},
"dependencies": {
+ "@ethercorps/sveltekit-og": "^3.0.0",
"@fontsource-variable/spline-sans-mono": "^5.1.0",
+ "@resvg/resvg-js": "^2.6.2",
"@sentry/sveltekit": "^8.48.0",
"@tryghost/content-api": "^1.11.21",
"@types/tryghost__content-api": "^1.3.17",
"dayjs": "^1.11.13",
"highlight.js": "^11.10.0",
- "marked": "^10.0.0"
+ "marked": "^10.0.0",
+ "satori": "^0.12.1",
+ "satori-html": "^0.3.2"
}
}
diff --git a/apps/web/src/lib/fonts/NotoSans-Regular.ttf b/apps/web/src/lib/fonts/NotoSans-Regular.ttf
new file mode 100644
index 0000000000..ebd7703f69
Binary files /dev/null and b/apps/web/src/lib/fonts/NotoSans-Regular.ttf differ
diff --git a/apps/web/src/routes/[ownerSlug]/[projectSlug]/reviews/[branchId]/+page.svelte b/apps/web/src/routes/[ownerSlug]/[projectSlug]/reviews/[branchId]/+page.svelte
index f3a7f7ea2c..370b0122dd 100644
--- a/apps/web/src/routes/[ownerSlug]/[projectSlug]/reviews/[branchId]/+page.svelte
+++ b/apps/web/src/routes/[ownerSlug]/[projectSlug]/reviews/[branchId]/+page.svelte
@@ -27,6 +27,7 @@
import relativeTime from 'dayjs/plugin/relativeTime';
import type { Branch } from '@gitbutler/shared/branches/types';
import { goto } from '$app/navigation';
+ import { PUBLIC_APP_HOST } from '$env/static/public';
dayjs.extend(relativeTime);
@@ -106,7 +107,6 @@
}
function copyLocation() {
- console.log('hi');
copyToClipboard(location.href);
}
@@ -117,6 +117,16 @@
{/if}
{/snippet}
+
+ Review: {data.ownerSlug}/{data.projectSlug}
+
+
+
+
+
Review page: {data.ownerSlug}/{data.projectSlug} {data.branchId}
diff --git a/apps/web/src/routes/[ownerSlug]/[projectSlug]/reviews/[branchId]/og/+server.ts b/apps/web/src/routes/[ownerSlug]/[projectSlug]/reviews/[branchId]/og/+server.ts
new file mode 100644
index 0000000000..02a2d9f4c7
--- /dev/null
+++ b/apps/web/src/routes/[ownerSlug]/[projectSlug]/reviews/[branchId]/og/+server.ts
@@ -0,0 +1,94 @@
+import Card from './Card.svelte';
+import { HttpClient } from '@gitbutler/shared/network/httpClient';
+import { gravatarUrlFromEmail } from '@gitbutler/ui/avatar/gravatar';
+import { Resvg } from '@resvg/resvg-js';
+import satori from 'satori';
+import { html as satoriHtml } from 'satori-html';
+import { render } from 'svelte/server';
+import { readable } from 'svelte/store';
+import { readFileSync } from 'fs';
+import path from 'path';
+import type { RequestHandler } from './$types';
+import type { ApiBranch } from '@gitbutler/shared/branches/types';
+import type { ApiUser } from '@gitbutler/shared/users/types';
+import { env } from '$env/dynamic/public';
+
+// Load the font data
+const fontFilePath = path.resolve('src/lib/fonts/NotoSans-Regular.ttf');
+const fontData = readFileSync(fontFilePath);
+
+// eslint-disable-next-line func-style
+export const GET: RequestHandler = async ({ params }) => {
+ const httpClient = new HttpClient(fetch, env.PUBLIC_APP_HOST, readable(undefined));
+
+ let branch: ApiBranch;
+ let date: string;
+
+ try {
+ branch = await httpClient.get(
+ `patch_stack/${params.ownerSlug}/${params.projectSlug}/branch/${params.branchId}`
+ );
+ const dateString = branch.created_at;
+ const dateObj = new Date(dateString);
+ // format date as Jun 30, 2021
+ date = dateObj.toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric'
+ });
+ } catch (e) {
+ console.error(e);
+ return new Response();
+ }
+
+ let user: ApiUser | undefined;
+ try {
+ user = await httpClient.get(`user/${branch.owner_login}`);
+ } catch (_) {
+ /* empty */
+ }
+
+ const slug = params.ownerSlug + '/' + params.projectSlug;
+ const title = branch.title || 'unknown';
+ const picture =
+ user?.picture || (await gravatarUrlFromEmail(user?.email || 'example@example.com'));
+
+ const commit_titles = branch.patches.map((patch) => patch.title || '') || [];
+ const commits = branch.stack_size || 1;
+ const files = branch.patches.reduce((acc, patch) => acc + patch.statistics.file_count, 0);
+ const additions = branch.patches.reduce(
+ (acc, patch) => acc + (patch.statistics.lines - patch.statistics.deletions),
+ 0
+ );
+ const subtractions = branch.patches.reduce((acc, patch) => acc + patch.statistics.deletions, 0);
+
+ const { body } = render(Card, {
+ props: { title, slug, picture, date, commit_titles, commits, files, additions, subtractions }
+ });
+
+ // Convert HTML string to VDOM
+ const vdom = satoriHtml(body);
+
+ // Generate the SVG using Satori
+ // @ts-expect-error The satori-html library is fine
+ const svg = await satori(vdom, {
+ width: 1200,
+ height: 630,
+ fonts: [
+ {
+ name: 'Noto Sans',
+ data: fontData,
+ weight: 400,
+ style: 'normal'
+ }
+ ]
+ });
+
+ // Render the SVG to PNG using Resvg
+ const resvg = new Resvg(svg);
+ const pngData = resvg.render();
+
+ return new Response(pngData.asPng(), {
+ headers: { 'Content-Type': 'image/png' }
+ });
+};
diff --git a/apps/web/src/routes/[ownerSlug]/[projectSlug]/reviews/[branchId]/og/Card.svelte b/apps/web/src/routes/[ownerSlug]/[projectSlug]/reviews/[branchId]/og/Card.svelte
new file mode 100644
index 0000000000..dc15182cd7
--- /dev/null
+++ b/apps/web/src/routes/[ownerSlug]/[projectSlug]/reviews/[branchId]/og/Card.svelte
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+ {#each commit_titles as commit_title}
+
+ - {commit_title.substring(0, 70)}
+
+ {/each}
+
+
+
+ {date}
+ {#if commits > 1}
+ {commits} commits
+ {:else}
+ {commits} commit
+ {/if}
+ {files} files
+
+
+ +{additions}
+ -{subtractions}
+
+
+
diff --git a/packages/shared/src/lib/branches/types.ts b/packages/shared/src/lib/branches/types.ts
index 220ddc0fc4..f5e815e139 100644
--- a/packages/shared/src/lib/branches/types.ts
+++ b/packages/shared/src/lib/branches/types.ts
@@ -269,6 +269,7 @@ export type ApiBranch = {
branch_stack_id?: string;
branch_stack_order?: number;
permissions: ApiPermissions;
+ owner_login?: string;
};
export type Branch = {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4173f2c8c3..64f4d29d9b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -332,9 +332,15 @@ importers:
apps/web:
dependencies:
+ '@ethercorps/sveltekit-og':
+ specifier: ^3.0.0
+ version: 3.0.0(svelte@5.18.0)
'@fontsource-variable/spline-sans-mono':
specifier: ^5.1.0
version: 5.1.0
+ '@resvg/resvg-js':
+ specifier: ^2.6.2
+ version: 2.6.2
'@sentry/sveltekit':
specifier: ^8.48.0
version: 8.48.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.28.0)(@sveltejs/kit@2.15.2(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.18.0)(vite@5.4.11(@types/node@22.3.0)(sass-embedded@1.82.0)))(svelte@5.18.0)(vite@5.4.11(@types/node@22.3.0)(sass-embedded@1.82.0)))(svelte@5.18.0)(vite@5.4.11(@types/node@22.3.0)(sass-embedded@1.82.0))
@@ -353,6 +359,12 @@ importers:
marked:
specifier: ^10.0.0
version: 10.0.0
+ satori:
+ specifier: ^0.12.1
+ version: 0.12.1
+ satori-html:
+ specifier: ^0.3.2
+ version: 0.3.2
devDependencies:
'@fontsource/fira-mono':
specifier: ^4.5.10
@@ -1020,6 +1032,16 @@ packages:
resolution: {integrity: sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ '@ethercorps/svelte-h2j@0.1.0':
+ resolution: {integrity: sha512-0cs5FgGgiJRgHr2paR3/49Fv/KsOjqbLEPbgZjJtdBiqUD3awZwgoK6ynwTA3zBq+KfHqAJjJU1XWbCwQMih3Q==}
+ peerDependencies:
+ svelte: ^4.0.0
+
+ '@ethercorps/sveltekit-og@3.0.0':
+ resolution: {integrity: sha512-CQRmcfCPnTEqP+EeHBE7wWp1sNxpBaT4Fb5Js5fGnWEgFFIDbJEEcFiT0Uwc1CteolXY+CyTOg7mokcOtu3d7w==}
+ peerDependencies:
+ svelte: ^4.0.0
+
'@fontsource-variable/spline-sans-mono@5.1.0':
resolution: {integrity: sha512-B9rWuTg1/G9/sKTxWb7CHVabv0tSFw5ETEmMpY8RU10nZq09JzWdMJO7Fj4cx61S0DvAOaM1F/uyTpFRmd+KWQ==}
@@ -1529,6 +1551,82 @@ packages:
'@lezer/javascript': ^1.2.0
'@lezer/lr': ^1.0.0
+ '@resvg/resvg-js-android-arm-eabi@2.6.2':
+ resolution: {integrity: sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [android]
+
+ '@resvg/resvg-js-android-arm64@2.6.2':
+ resolution: {integrity: sha512-VcOKezEhm2VqzXpcIJoITuvUS/fcjIw5NA/w3tjzWyzmvoCdd+QXIqy3FBGulWdClvp4g+IfUemigrkLThSjAQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [android]
+
+ '@resvg/resvg-js-darwin-arm64@2.6.2':
+ resolution: {integrity: sha512-nmok2LnAd6nLUKI16aEB9ydMC6Lidiiq2m1nEBDR1LaaP7FGs4AJ90qDraxX+CWlVuRlvNjyYJTNv8qFjtL9+A==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@resvg/resvg-js-darwin-x64@2.6.2':
+ resolution: {integrity: sha512-GInyZLjgWDfsVT6+SHxQVRwNzV0AuA1uqGsOAW+0th56J7Nh6bHHKXHBWzUrihxMetcFDmQMAX1tZ1fZDYSRsw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@resvg/resvg-js-linux-arm-gnueabihf@2.6.2':
+ resolution: {integrity: sha512-YIV3u/R9zJbpqTTNwTZM5/ocWetDKGsro0SWp70eGEM9eV2MerWyBRZnQIgzU3YBnSBQ1RcxRZvY/UxwESfZIw==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@resvg/resvg-js-linux-arm64-gnu@2.6.2':
+ resolution: {integrity: sha512-zc2BlJSim7YR4FZDQ8OUoJg5holYzdiYMeobb9pJuGDidGL9KZUv7SbiD4E8oZogtYY42UZEap7dqkkYuA91pg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@resvg/resvg-js-linux-arm64-musl@2.6.2':
+ resolution: {integrity: sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@resvg/resvg-js-linux-x64-gnu@2.6.2':
+ resolution: {integrity: sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@resvg/resvg-js-linux-x64-musl@2.6.2':
+ resolution: {integrity: sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@resvg/resvg-js-win32-arm64-msvc@2.6.2':
+ resolution: {integrity: sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@resvg/resvg-js-win32-ia32-msvc@2.6.2':
+ resolution: {integrity: sha512-har4aPAlvjnLcil40AC77YDIk6loMawuJwFINEM7n0pZviwMkMvjb2W5ZirsNOZY4aDbo5tLx0wNMREp5Brk+w==}
+ engines: {node: '>= 10'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@resvg/resvg-js-win32-x64-msvc@2.6.2':
+ resolution: {integrity: sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@resvg/resvg-js@2.6.2':
+ resolution: {integrity: sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q==}
+ engines: {node: '>= 10'}
+
'@rollup/rollup-android-arm-eabi@4.27.3':
resolution: {integrity: sha512-EzxVSkIvCFxUd4Mgm4xR9YXrcp976qVaHnqom/Tgm+vU79k4vV4eYTjmRvGfeoW8m9LVcsAy/lGjcgVegKEhLQ==}
cpu: [arm]
@@ -1731,6 +1829,11 @@ packages:
resolution: {integrity: sha512-zIieP1VLWQb3wUjFJlwOAoaaJygJhXeUoGd0e/Ha2RLb2eW2S+4gjf6y6NqyY71tZ74LYVZKg/4prB6FAZSMXQ==}
engines: {node: '>= 14'}
+ '@shuding/opentype.js@1.4.0-beta.0':
+ resolution: {integrity: sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==}
+ engines: {node: '>= 8.0.0'}
+ hasBin: true
+
'@sinclair/typebox@0.27.8':
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
@@ -2849,6 +2952,10 @@ packages:
bare-stream@2.1.3:
resolution: {integrity: sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==}
+ base64-js@0.0.8:
+ resolution: {integrity: sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==}
+ engines: {node: '>= 0.4'}
+
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@@ -2936,6 +3043,9 @@ packages:
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
engines: {node: '>=10'}
+ camelize@1.0.1:
+ resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
+
caniuse-lite@1.0.30001640:
resolution: {integrity: sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==}
@@ -3107,9 +3217,26 @@ packages:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
+ css-background-parser@0.1.0:
+ resolution: {integrity: sha512-2EZLisiZQ+7m4wwur/qiYJRniHX4K5Tc9w93MT3AS0WS1u5kaZ4FKXlOTBhOjc+CgEgPiGY+fX1yWD8UwpEqUA==}
+
+ css-box-shadow@1.0.0-3:
+ resolution: {integrity: sha512-9jaqR6e7Ohds+aWwmhe6wILJ99xYQbfmK9QQB9CcMjDbTxPZjwEmUQpU91OG05Xgm8BahT5fW+svbsQGjS/zPg==}
+
+ css-color-keywords@1.0.0:
+ resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==}
+ engines: {node: '>=4'}
+
+ css-gradient-parser@0.0.16:
+ resolution: {integrity: sha512-3O5QdqgFRUbXvK1x5INf1YkBz1UKSWqrd63vWsum8MNHDBYD5urm3QtxZbKU259OrEXNM26lP/MPY3d1IGkBgA==}
+ engines: {node: '>=16'}
+
css-shorthand-properties@1.1.1:
resolution: {integrity: sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A==}
+ css-to-react-native@3.2.0:
+ resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==}
+
css-value@0.0.1:
resolution: {integrity: sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==}
@@ -3378,6 +3505,9 @@ packages:
electron-to-chromium@1.5.65:
resolution: {integrity: sha512-PWVzBjghx7/wop6n22vS2MLU8tKGd4Q91aCEGhG/TYmW6PP5OcSXcdnxTe1NNt0T66N8D6jxh4kC8UsdzOGaIw==}
+ emoji-regex@10.4.0:
+ resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
+
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -3462,6 +3592,9 @@ packages:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
+ escape-html@1.0.3:
+ resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+
escape-string-regexp@1.0.5:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
@@ -3743,6 +3876,9 @@ packages:
fflate@0.4.8:
resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
+ fflate@0.7.4:
+ resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==}
+
fflate@0.8.2:
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
@@ -4065,6 +4201,10 @@ packages:
headers-polyfill@4.0.3:
resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==}
+ hex-rgb@4.3.0:
+ resolution: {integrity: sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==}
+ engines: {node: '>=6'}
+
highlight.js@11.10.0:
resolution: {integrity: sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==}
engines: {node: '>=12.0.0'}
@@ -4493,6 +4633,9 @@ packages:
resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==}
engines: {node: '>=14'}
+ linebreak@1.1.0:
+ resolution: {integrity: sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==}
+
lines-and-columns@2.0.4:
resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -4933,6 +5076,9 @@ packages:
package-json-from-dist@1.0.0:
resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==}
+ pako@0.2.9:
+ resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
+
pako@1.0.11:
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
@@ -4940,6 +5086,9 @@ packages:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
+ parse-css-color@0.2.1:
+ resolution: {integrity: sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==}
+
parse-json@7.1.1:
resolution: {integrity: sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==}
engines: {node: '>=16'}
@@ -5597,6 +5746,17 @@ packages:
engines: {node: '>=16.0.0'}
hasBin: true
+ satori-html@0.3.2:
+ resolution: {integrity: sha512-wjTh14iqADFKDK80e51/98MplTGfxz2RmIzh0GqShlf4a67+BooLywF17TvJPD6phO0Hxm7Mf1N5LtRYvdkYRA==}
+
+ satori@0.10.14:
+ resolution: {integrity: sha512-abovcqmwl97WKioxpkfuMeZmndB1TuDFY/R+FymrZyiGP+pMYomvgSzVPnbNMWHHESOPosVHGL352oFbdAnJcA==}
+ engines: {node: '>=16'}
+
+ satori@0.12.1:
+ resolution: {integrity: sha512-0SbjchvDrDbeXeQgxWVtSWxww7qcFgk3DtSE2/blHOSlLsSHwIqO2fCrtVa/EudJ7Eqno8A33QNx56rUyGbLuw==}
+ engines: {node: '>=16'}
+
saxes@6.0.0:
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
engines: {node: '>=v12.22.7'}
@@ -5790,6 +5950,9 @@ packages:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'}
+ string.prototype.codepointat@0.2.1:
+ resolution: {integrity: sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==}
+
string.prototype.trim@1.2.9:
resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==}
engines: {node: '>= 0.4'}
@@ -5988,6 +6151,9 @@ packages:
tiny-glob@0.2.9:
resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==}
+ tiny-inflate@1.0.3:
+ resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==}
+
tiny-invariant@1.3.3:
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
@@ -6165,6 +6331,9 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
+ ultrahtml@1.5.3:
+ resolution: {integrity: sha512-GykOvZwgDWZlTQMtp5jrD4BVL+gNn2NVlVafjcFUJ7taY20tqYdwdoWBFy6GBJsNTZe1GkGPkSl5knQAjtgceg==}
+
unbox-primitive@1.0.2:
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
@@ -6177,6 +6346,9 @@ packages:
undici-types@6.18.2:
resolution: {integrity: sha512-5ruQbENj95yDYJNS3TvcaxPMshV7aizdv/hWYjGIKoANWKjhWNBsr2YEuYZKodQulB1b8l7ILOuDQep3afowQQ==}
+ unicode-trie@2.0.0:
+ resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==}
+
unicorn-magic@0.1.0:
resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==}
engines: {node: '>=18'}
@@ -6571,6 +6743,9 @@ packages:
resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==}
engines: {node: '>=18'}
+ yoga-wasm-web@0.3.3:
+ resolution: {integrity: sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==}
+
zimmerframe@1.1.2:
resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
@@ -7075,6 +7250,17 @@ snapshots:
dependencies:
levn: 0.4.1
+ '@ethercorps/svelte-h2j@0.1.0(svelte@5.18.0)':
+ dependencies:
+ svelte: 5.18.0
+
+ '@ethercorps/sveltekit-og@3.0.0(svelte@5.18.0)':
+ dependencies:
+ '@ethercorps/svelte-h2j': 0.1.0(svelte@5.18.0)
+ '@resvg/resvg-js': 2.6.2
+ satori: 0.10.14
+ svelte: 5.18.0
+
'@fontsource-variable/spline-sans-mono@5.1.0': {}
'@fontsource/fira-mono@4.5.10': {}
@@ -7700,6 +7886,57 @@ snapshots:
'@lezer/javascript': 1.4.16
'@lezer/lr': 1.4.1
+ '@resvg/resvg-js-android-arm-eabi@2.6.2':
+ optional: true
+
+ '@resvg/resvg-js-android-arm64@2.6.2':
+ optional: true
+
+ '@resvg/resvg-js-darwin-arm64@2.6.2':
+ optional: true
+
+ '@resvg/resvg-js-darwin-x64@2.6.2':
+ optional: true
+
+ '@resvg/resvg-js-linux-arm-gnueabihf@2.6.2':
+ optional: true
+
+ '@resvg/resvg-js-linux-arm64-gnu@2.6.2':
+ optional: true
+
+ '@resvg/resvg-js-linux-arm64-musl@2.6.2':
+ optional: true
+
+ '@resvg/resvg-js-linux-x64-gnu@2.6.2':
+ optional: true
+
+ '@resvg/resvg-js-linux-x64-musl@2.6.2':
+ optional: true
+
+ '@resvg/resvg-js-win32-arm64-msvc@2.6.2':
+ optional: true
+
+ '@resvg/resvg-js-win32-ia32-msvc@2.6.2':
+ optional: true
+
+ '@resvg/resvg-js-win32-x64-msvc@2.6.2':
+ optional: true
+
+ '@resvg/resvg-js@2.6.2':
+ optionalDependencies:
+ '@resvg/resvg-js-android-arm-eabi': 2.6.2
+ '@resvg/resvg-js-android-arm64': 2.6.2
+ '@resvg/resvg-js-darwin-arm64': 2.6.2
+ '@resvg/resvg-js-darwin-x64': 2.6.2
+ '@resvg/resvg-js-linux-arm-gnueabihf': 2.6.2
+ '@resvg/resvg-js-linux-arm64-gnu': 2.6.2
+ '@resvg/resvg-js-linux-arm64-musl': 2.6.2
+ '@resvg/resvg-js-linux-x64-gnu': 2.6.2
+ '@resvg/resvg-js-linux-x64-musl': 2.6.2
+ '@resvg/resvg-js-win32-arm64-msvc': 2.6.2
+ '@resvg/resvg-js-win32-ia32-msvc': 2.6.2
+ '@resvg/resvg-js-win32-x64-msvc': 2.6.2
+
'@rollup/rollup-android-arm-eabi@4.27.3':
optional: true
@@ -7925,6 +8162,11 @@ snapshots:
- encoding
- supports-color
+ '@shuding/opentype.js@1.4.0-beta.0':
+ dependencies:
+ fflate: 0.7.4
+ string.prototype.codepointat: 0.2.1
+
'@sinclair/typebox@0.27.8': {}
'@sindresorhus/is@5.6.0': {}
@@ -9452,6 +9694,8 @@ snapshots:
streamx: 2.18.0
optional: true
+ base64-js@0.0.8: {}
+
base64-js@1.5.1: {}
basic-ftp@5.0.5: {}
@@ -9544,6 +9788,8 @@ snapshots:
camelcase@6.3.0: {}
+ camelize@1.0.1: {}
+
caniuse-lite@1.0.30001640: {}
caniuse-lite@1.0.30001684: {}
@@ -9726,8 +9972,22 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
+ css-background-parser@0.1.0: {}
+
+ css-box-shadow@1.0.0-3: {}
+
+ css-color-keywords@1.0.0: {}
+
+ css-gradient-parser@0.0.16: {}
+
css-shorthand-properties@1.1.1: {}
+ css-to-react-native@3.2.0:
+ dependencies:
+ camelize: 1.0.1
+ css-color-keywords: 1.0.0
+ postcss-value-parser: 4.2.0
+
css-value@0.0.1: {}
css.escape@1.5.1: {}
@@ -9956,6 +10216,8 @@ snapshots:
electron-to-chromium@1.5.65: {}
+ emoji-regex@10.4.0: {}
+
emoji-regex@8.0.0: {}
emoji-regex@9.2.2: {}
@@ -10112,6 +10374,8 @@ snapshots:
escalade@3.2.0: {}
+ escape-html@1.0.3: {}
+
escape-string-regexp@1.0.5: {}
escape-string-regexp@2.0.0: {}
@@ -10508,6 +10772,8 @@ snapshots:
fflate@0.4.8: {}
+ fflate@0.7.4: {}
+
fflate@0.8.2: {}
figures@5.0.0:
@@ -10863,6 +11129,8 @@ snapshots:
headers-polyfill@4.0.3: {}
+ hex-rgb@4.3.0: {}
+
highlight.js@11.10.0: {}
hosted-git-info@7.0.2:
@@ -11303,6 +11571,11 @@ snapshots:
lilconfig@3.1.1: {}
+ linebreak@1.1.0:
+ dependencies:
+ base64-js: 0.0.8
+ unicode-trie: 2.0.0
+
lines-and-columns@2.0.4: {}
locate-app@2.4.21:
@@ -11739,12 +12012,19 @@ snapshots:
package-json-from-dist@1.0.0: {}
+ pako@0.2.9: {}
+
pako@1.0.11: {}
parent-module@1.0.1:
dependencies:
callsites: 3.1.0
+ parse-css-color@0.2.1:
+ dependencies:
+ color-name: 1.1.4
+ hex-rgb: 4.3.0
+
parse-json@7.1.1:
dependencies:
'@babel/code-frame': 7.24.7
@@ -12436,6 +12716,37 @@ snapshots:
sass-embedded-win32-x64: 1.82.0
optional: true
+ satori-html@0.3.2:
+ dependencies:
+ ultrahtml: 1.5.3
+
+ satori@0.10.14:
+ dependencies:
+ '@shuding/opentype.js': 1.4.0-beta.0
+ css-background-parser: 0.1.0
+ css-box-shadow: 1.0.0-3
+ css-to-react-native: 3.2.0
+ emoji-regex: 10.4.0
+ escape-html: 1.0.3
+ linebreak: 1.1.0
+ parse-css-color: 0.2.1
+ postcss-value-parser: 4.2.0
+ yoga-wasm-web: 0.3.3
+
+ satori@0.12.1:
+ dependencies:
+ '@shuding/opentype.js': 1.4.0-beta.0
+ css-background-parser: 0.1.0
+ css-box-shadow: 1.0.0-3
+ css-gradient-parser: 0.0.16
+ css-to-react-native: 3.2.0
+ emoji-regex: 10.4.0
+ escape-html: 1.0.3
+ linebreak: 1.1.0
+ parse-css-color: 0.2.1
+ postcss-value-parser: 4.2.0
+ yoga-wasm-web: 0.3.3
+
saxes@6.0.0:
dependencies:
xmlchars: 2.2.0
@@ -12638,6 +12949,8 @@ snapshots:
emoji-regex: 9.2.2
strip-ansi: 7.1.0
+ string.prototype.codepointat@0.2.1: {}
+
string.prototype.trim@1.2.9:
dependencies:
call-bind: 1.0.7
@@ -12846,6 +13159,8 @@ snapshots:
globalyzer: 0.1.0
globrex: 0.1.2
+ tiny-inflate@1.0.3: {}
+
tiny-invariant@1.3.3: {}
tinybench@2.9.0: {}
@@ -13009,6 +13324,8 @@ snapshots:
typescript@5.4.5: {}
+ ultrahtml@1.5.3: {}
+
unbox-primitive@1.0.2:
dependencies:
call-bind: 1.0.7
@@ -13025,6 +13342,11 @@ snapshots:
undici-types@6.18.2: {}
+ unicode-trie@2.0.0:
+ dependencies:
+ pako: 0.2.9
+ tiny-inflate: 1.0.3
+
unicorn-magic@0.1.0: {}
universal-user-agent@6.0.0: {}
@@ -13508,6 +13830,8 @@ snapshots:
yoctocolors-cjs@2.1.2: {}
+ yoga-wasm-web@0.3.3: {}
+
zimmerframe@1.1.2: {}
zip-stream@6.0.1: