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 @@ + + +
+
+
+
+ + + +
+ {slug} +
+
+
+ {title} +
+
+
+ avatar +
+
+
+ {#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: