diff --git a/apps/www/astro.config.ts b/apps/www/astro.config.ts index ada6d82c..de686ddd 100644 --- a/apps/www/astro.config.ts +++ b/apps/www/astro.config.ts @@ -1,22 +1,46 @@ import cloudflare from "@astrojs/cloudflare" import mdx from "@astrojs/mdx" import react from "@astrojs/react" +import sitemap from "@astrojs/sitemap" import tailwindcss from "@tailwindcss/vite" import { defineConfig } from "astro/config" import inspect from "vite-plugin-inspect" +import { siteConfig } from "./src/config/site" import { remarkPluginCodeWrapper } from "./src/lib/remark-code-wrapper" -// https://astro.build/configo export default defineConfig({ - adapter: cloudflare(), + site: siteConfig.url, - integrations: [mdx({ remarkPlugins: [remarkPluginCodeWrapper] }), react()], + adapter: cloudflare({ + imageService: "compile", + }), + + integrations: [ + mdx({ remarkPlugins: [remarkPluginCodeWrapper] }), + react(), + sitemap(), + ], vite: { plugins: [inspect(), tailwindcss()], + ssr: { + external: [ + "node:path", + "node:fs", + "node:module", + "node:fs/promises", + "node:v8", + "node:url", + "node:process", + "node:assert", + "node:util", + ], + }, }, + output: "server", + trailingSlash: "ignore", redirects: { diff --git a/apps/www/package.json b/apps/www/package.json index 9b6d16d8..f9c78460 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -7,7 +7,8 @@ "dev": "astro dev", "clean": "git clean -xdf node_modules .astro .turbo dist", "build": "astro build", - "preview": "astro preview", + "preview": "wrangler dev", + "upload": "wrangler versions upload", "typecheck": "astro check", "test": "vitest run" }, @@ -18,10 +19,12 @@ "@astrojs/cloudflare": "catalog:", "@astrojs/mdx": "catalog:", "@astrojs/react": "catalog:", + "@astrojs/sitemap": "^3.5.1", "@astrojs/vue": "catalog:", "@fontsource-variable/geist": "catalog:", "@fontsource-variable/geist-mono": "catalog:", "@lucide/astro": "catalog:", + "@resvg/resvg-wasm": "^2.6.2", "@tailwindcss/vite": "catalog:", "@tanstack/react-table": "catalog:", "@ui/lib": "workspace:*", @@ -39,11 +42,13 @@ "react-dom": "catalog:react18", "react-scan": "catalog:", "recharts": "catalog:", + "satori": "0.15.2", "scule": "catalog:", - "sharp": "catalog:", "tailwind-merge": "catalog:", "tailwindcss": "catalog:", - "tw-animate-css": "catalog:" + "tiny-invariant": "^1.3.3", + "tw-animate-css": "catalog:", + "yoga-wasm-web": "^0.3.3" }, "devDependencies": { "@astrojs/ts-plugin": "catalog:", diff --git a/apps/www/public/fonts/Geist-Regular.ttf b/apps/www/public/fonts/Geist-Regular.ttf new file mode 100644 index 00000000..d399691f Binary files /dev/null and b/apps/www/public/fonts/Geist-Regular.ttf differ diff --git a/apps/www/public/fonts/Geist-SemiBold.ttf b/apps/www/public/fonts/Geist-SemiBold.ttf new file mode 100644 index 00000000..a3241080 Binary files /dev/null and b/apps/www/public/fonts/Geist-SemiBold.ttf differ diff --git a/apps/www/public/fonts/GeistMono-Regular.ttf b/apps/www/public/fonts/GeistMono-Regular.ttf new file mode 100644 index 00000000..221b74c4 Binary files /dev/null and b/apps/www/public/fonts/GeistMono-Regular.ttf differ diff --git a/apps/www/public/logo-dark.png b/apps/www/public/logo-dark.png new file mode 100644 index 00000000..b980761d Binary files /dev/null and b/apps/www/public/logo-dark.png differ diff --git a/apps/www/public/logo-dark.svg b/apps/www/public/logo-dark.svg index d9bca8f2..df5e1c59 100644 --- a/apps/www/public/logo-dark.svg +++ b/apps/www/public/logo-dark.svg @@ -1,7 +1,7 @@ - - shipbase logo + + shipbase logo (dark) - \ No newline at end of file + diff --git a/apps/www/public/logo.png b/apps/www/public/logo.png new file mode 100644 index 00000000..7ea58b7b Binary files /dev/null and b/apps/www/public/logo.png differ diff --git a/apps/www/public/og-image.png b/apps/www/public/og-image.png index d7cc5b21..257e80d5 100644 Binary files a/apps/www/public/og-image.png and b/apps/www/public/og-image.png differ diff --git a/apps/www/public/og-image.svg b/apps/www/public/og-image.svg deleted file mode 100644 index 061f7a95..00000000 --- a/apps/www/public/og-image.svg +++ /dev/null @@ -1,11 +0,0 @@ - - Open Graph Image – Shipbase S Logo - 1200×630 image with dark background and the Shipbase S logo centered. - - - - - - - - \ No newline at end of file diff --git a/apps/www/public/site.webmanifest b/apps/www/public/site.webmanifest index b3b8728e..e2c1b7b9 100644 --- a/apps/www/public/site.webmanifest +++ b/apps/www/public/site.webmanifest @@ -1 +1,19 @@ -{"name":"Shipbase","short_name":"Shipbase","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#111111","background_color":"#111111","display":"standalone"} \ No newline at end of file +{ + "name": "shipbase", + "short_name": "shipbase", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#09090b", + "background_color": "#09090b", + "display": "standalone" +} diff --git a/apps/www/src/components/landing/cta.astro b/apps/www/src/components/landing/cta.astro index 6686ea16..35c8228d 100644 --- a/apps/www/src/components/landing/cta.astro +++ b/apps/www/src/components/landing/cta.astro @@ -1,7 +1,6 @@ --- import { siteConfig } from "@/config/site" import { Button } from "@ui/react/button" -import { Input } from "@ui/react/input" import ArkUI from "@/assets/logos/ark-ui.svg" import CVA from "@/assets/logos/cva.svg" @@ -19,12 +18,11 @@ import Vue from "@/assets/logos/vue.svg"

- Beautiful Components for
- Multi Framework + Shadcn UI Components for
+ Multi-Framework

- A set of beautiful designed components you can customize, extend, and make - your own. Multi Framework Support, Open Source. + {siteConfig.description}

diff --git a/apps/www/src/components/mobile-nav/sheet.tsx b/apps/www/src/components/mobile-nav/sheet.tsx index 4f1a9d54..ba59c0ef 100644 --- a/apps/www/src/components/mobile-nav/sheet.tsx +++ b/apps/www/src/components/mobile-nav/sheet.tsx @@ -14,7 +14,7 @@ export function MobileNavSheet({ children }: { children: React.ReactNode }) { Toggle Menu - + {children} diff --git a/apps/www/src/components/open-graph.tsx b/apps/www/src/components/open-graph.tsx new file mode 100644 index 00000000..1cab635f --- /dev/null +++ b/apps/www/src/components/open-graph.tsx @@ -0,0 +1,241 @@ +type OpenGraphProps = { + width: number + height: number + title: string + description: string +} + +const DEFAULT_COLORS = { + background: "#000000", + text: "#ffffff", + guide: "#1a1a1a", + cross: "#515356", + description: "#a8a29e", +} as const + +export function OpenGraph({ + width, + height, + title, + description, +}: OpenGraphProps) { + const TITLE_FONT_SIZE = title && title.length > 20 ? 64 : 80 + + const GUIDE_INSET = 64 + const GUIDE_THICKNESS = 3 + const CROSS_LENGTH = 48 + const CROSS_THICKNESS = 3 + const CROSS_HALF = CROSS_LENGTH / 2 + const CROSS_HALF_THICKNESS = Math.floor(CROSS_THICKNESS / 2) + const GUIDE_HALF_THICKNESS = Math.floor(GUIDE_THICKNESS / 2) + + return ( +
+ {/* Vertical guide left */} +
+ + {/* Vertical guide right */} +
+ + {/* Horizontal guide top */} +
+ + {/* Horizontal guide bottom */} +
+ + {/* Cross at top-left intersection */} +
+
+ + {/* Cross at bottom-right intersection */} +
+
+ + {/* Content */} +
+
+ {/* Logo */} + + + {/* Title */} +
+ {title} +
+
+ +
+ {description} +
+
+
+ ) +} + +function Logo() { + const LOGO_SIZE = 88 + return ( + /** biome-ignore lint/a11y/noSvgWithoutTitle: svg logo */ + + + + + + ) +} + +export type { OpenGraphProps } diff --git a/apps/www/src/config/site.ts b/apps/www/src/config/site.ts index 80bdda3d..1f2ccb88 100644 --- a/apps/www/src/config/site.ts +++ b/apps/www/src/config/site.ts @@ -1,13 +1,22 @@ export const siteConfig = { name: "shipbase/ui", - url: "https://ui.shipbase.cc", - ogImage: "", + url: "https://ui.shipbase.xyz", + author: "shipbase", + creator: "shipbase", + ogImage: "/og-image.png", + twitterImage: "/og-image.png", + title: "Shadcn UI Components for Multi-Framework", description: - "Beautifully designed components that you can copy and paste into your apps. Accessible. Customizable. Open Source.", + "A set of beautiful designed components you can customize, extend, and make your own. Multi-Framework Support, Open Source.", links: { - twitter: "https://x.com", + twitter: "https://x.com/shipba_se", github: "https://github.com/shipbase/ui", }, } export type SiteConfig = typeof siteConfig + +export const META_THEME_COLORS = { + light: "#ffffff", + dark: "#09090b", +} diff --git a/apps/www/src/layouts/root-layout.astro b/apps/www/src/layouts/root-layout.astro index fb519a13..c5c5c6c3 100644 --- a/apps/www/src/layouts/root-layout.astro +++ b/apps/www/src/layouts/root-layout.astro @@ -4,37 +4,105 @@ import { ClientRouter } from "astro:transitions" import SiteFooter from "@/components/side-footer.astro" import SiteHeader from "@/components/site-header.astro" import TailwindIndicator from "@/components/tailwind-indicator.astro" +import { META_THEME_COLORS, siteConfig } from "@/config/site" import "@/styles/global.css" - import "@fontsource-variable/geist" import "@fontsource-variable/geist-mono" + +export interface Props { + metadata: { + title: string + description: string + openGraph: { + type: "website" | "article" | "book" | "profile" | (string & {}) + title: string + description: string + image: { + url: string + } + } + twitter: { + card: "summary" | "summary_large_image" | "app" | "player" + title: string + description: string + image: { + url: string + } + } + } +} + +const { metadata } = Astro.props as Props +const url = Astro.url --- - shipbase/ui + {metadata.title} – {siteConfig.name} - + + + + + + + + + + + + + + + + + + + + + + + + - - + - + + +
+

404 – Page not found

+

+ The page you’re looking for doesn’t exist. +

+
+ diff --git a/apps/www/src/pages/docs/[...slug].astro b/apps/www/src/pages/docs/[...slug].astro index d57471e3..c8b3f23a 100644 --- a/apps/www/src/pages/docs/[...slug].astro +++ b/apps/www/src/pages/docs/[...slug].astro @@ -6,6 +6,8 @@ import { MDXComponents } from "@/components/content" import DocLayout from "@/layouts/doc-layout.astro" import RootLayout from "@/layouts/root-layout.astro" +export const prerender = true + export const getStaticPaths = (async () => { const overview = await getCollection("overview") return overview.map((entry) => ({ @@ -16,9 +18,32 @@ export const getStaticPaths = (async () => { const { entry } = Astro.props const { Content, headings } = await render(entry) + +const url = Astro.url +const imageURL = new URL( + `/og?title=${encodeURIComponent(entry.data.title)}&description=${encodeURIComponent(entry.data.description)}`, + url.origin +).toString() --- - + { const components = await getCollection("components") @@ -22,11 +24,33 @@ export const getStaticPaths = (async () => { }) satisfies GetStaticPaths const { entry } = Astro.props - const { Content, headings } = await render(entry) + +const url = Astro.url +const imageURL = new URL( + `/og?title=${encodeURIComponent(entry.data.title)}&description=${encodeURIComponent(entry.data.description)}`, + url.origin +).toString() --- - + { const collections = await getCollection("components") return collections.flat(1).map((entry) => ({ @@ -11,9 +12,33 @@ export const getStaticPaths = (async () => { props: { entry }, })) }) satisfies GetStaticPaths +const { entry } = Astro.props + +const url = Astro.url +const imageURL = new URL( + `/og?title=${encodeURIComponent(entry.data.title)}&description=${encodeURIComponent(entry.data.description)}`, + url.origin +).toString() --- - +
diff --git a/apps/www/src/pages/examples/index.astro b/apps/www/src/pages/examples/index.astro index d684c36d..d894f237 100644 --- a/apps/www/src/pages/examples/index.astro +++ b/apps/www/src/pages/examples/index.astro @@ -1,8 +1,29 @@ --- +import { siteConfig } from "@/config/site" import RootLayout from "@/layouts/root-layout.astro" + +const url = Astro.url +const imageUrl = new URL(siteConfig.ogImage, url.origin).toString() --- - +
diff --git a/apps/www/src/pages/index.astro b/apps/www/src/pages/index.astro index 1efb80e8..ae898128 100644 --- a/apps/www/src/pages/index.astro +++ b/apps/www/src/pages/index.astro @@ -4,26 +4,47 @@ import CTA from "@/components/landing/cta.astro" import FeaturesSection from "@/components/landing/features.astro" import Newsletter from "@/components/landing/newsletter.astro" import Shadcn from "@/components/landing/shadcn.astro" +import { siteConfig } from "@/config/site" import RootLayout from "@/layouts/root-layout.astro" import { cn } from "@/lib/utils" +const url = Astro.url const separatorClass = cn( "relative py-5", "before:bg-[linear-gradient(to_right,--theme(--color-border/.3),--theme(--color-border)_200px,--theme(--color-border)_calc(100%-200px),--theme(--color-border/.3))] before:absolute before:-inset-x-32 before:top-0 before:h-px", "after:bg-[linear-gradient(to_right,--theme(--color-border/.3),--theme(--color-border)_200px,--theme(--color-border)_calc(100%-200px),--theme(--color-border/.3))] after:absolute after:-inset-x-32 after:bottom-0 after:h-px" ) + +const imageUrl = new URL(siteConfig.ogImage, url.origin).toString() --- - +
-
+
-
+
-
+
-
+
diff --git a/apps/www/src/pages/og/index.ts b/apps/www/src/pages/og/index.ts new file mode 100644 index 00000000..3f89ed57 --- /dev/null +++ b/apps/www/src/pages/og/index.ts @@ -0,0 +1,124 @@ +import { OpenGraph } from "@/components/open-graph" +import { siteConfig } from "@/config/site" +import { Resvg, initWasm } from "@resvg/resvg-wasm" +// @ts-ignore - resvg.wasm is a module +import resvgwasm from "@resvg/resvg-wasm/index_bg.wasm" +import type { APIRoute } from "astro" +// use satori standalone version after https://github.com/vercel/satori/issues/693 +import satori, { init } from "satori/wasm" +import initYoga from "yoga-wasm-web" +// @ts-ignore +import yogaWasm from "yoga-wasm-web/dist/yoga.wasm" + +export const prerender = false + +const WIDTH = 1200 +const HEIGHT = 630 +const FONT_TEXT = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/,-." + +const initResvgPromise: Promise | null = initWasm(resvgwasm) +const initYogaPromise = initYoga(yogaWasm).then((yoga) => init(yoga)) + +async function loadGoogleFont(font: string, text?: string) { + if (!font || !text) return + const API = `https://fonts.googleapis.com/css2?family=${font}&text=${encodeURIComponent( + text + )}` + const css = await ( + await fetch(API, { + headers: { + // Make sure it returns TTF. + "User-Agent": + "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1", + }, + }) + ).text() + const resource = css.match(/src: url\((.+)\) format\('(opentype|truetype)'\)/) + if (!resource || !resource[1]) + throw new Error("Failed to download dynamic font") + const res = await fetch(resource[1]) + if (!res.ok) { + throw new Error(`Failed to download dynamic font. Status: ${res.status}`) + } + return res.arrayBuffer() +} + +async function loadFonts() { + const [geistRegularData, geistSemiBoldData, geistMonoData] = + await Promise.all([ + loadGoogleFont("Geist", FONT_TEXT), + loadGoogleFont("Geist", FONT_TEXT), + loadGoogleFont("Geist Mono", FONT_TEXT), + ]) + + if (!geistRegularData || !geistSemiBoldData || !geistMonoData) { + throw new Error("Failed to load fonts") + } + + return [ + { + name: "Geist", + data: geistRegularData, + weight: 400 as const, + style: "normal" as const, + }, + { + name: "Geist", + data: geistSemiBoldData, + weight: 600 as const, + style: "normal" as const, + }, + { + name: "Geist Mono", + data: geistMonoData, + weight: 400 as const, + style: "normal" as const, + }, + ] +} + +export const GET: APIRoute = async ({ url }) => { + try { + const fonts = await loadFonts() + const searchParams = url.searchParams + + await Promise.all([initResvgPromise, initYogaPromise]) + + const title = searchParams.get("title") ?? siteConfig.name + const description = + searchParams.get("description") ?? siteConfig.description + + const svg = await satori( + OpenGraph({ width: WIDTH, height: HEIGHT, title, description }), + { + width: WIDTH, + height: HEIGHT, + fonts, + } + ) + + const renderer = new Resvg(svg, { + fitTo: { + mode: "width", + value: WIDTH, + }, + }) + const image = renderer.render() + const pngBuffer = image.asPng() + + return new Response(pngBuffer, { + status: 200, + headers: { + "content-type": "image/png", + "cache-control": "public, immutable, no-transform, max-age=31536000", + }, + }) + } catch (error) { + const message = + error instanceof Error ? error.message : "OG image generation failed" + return new Response(`Error ${message}`, { + status: 500, + headers: { "Content-Type": "text/plain; charset=utf-8" }, + }) + } +} diff --git a/apps/www/src/pages/robots.txt.ts b/apps/www/src/pages/robots.txt.ts new file mode 100644 index 00000000..3a0eba16 --- /dev/null +++ b/apps/www/src/pages/robots.txt.ts @@ -0,0 +1,13 @@ +import type { APIRoute } from "astro" + +const getRobotsTxt = (sitemapURL: URL) => `\ +User-agent: * +Allow: / + +Sitemap: ${sitemapURL.href} +` + +export const GET: APIRoute = ({ site }) => { + const sitemapURL = new URL("sitemap-index.xml", site) + return new Response(getRobotsTxt(sitemapURL)) +} diff --git a/apps/www/wrangler.jsonc b/apps/www/wrangler.jsonc index 768f06c8..acea4a68 100644 --- a/apps/www/wrangler.jsonc +++ b/apps/www/wrangler.jsonc @@ -1,7 +1,7 @@ { "name": "shipbase-ui", "main": "./dist/_worker.js/index.js", - "compatibility_date": "2025-03-25", + "compatibility_date": "2025-08-15", "compatibility_flags": ["nodejs_compat"], "assets": { "directory": "./dist" diff --git a/packages/lib/utils/url.ts b/packages/lib/utils/url.ts new file mode 100644 index 00000000..20b6f12b --- /dev/null +++ b/packages/lib/utils/url.ts @@ -0,0 +1,10 @@ +/** + * Remove a single trailing slash from the end of a URL/string, if present. + * - Preserves protocol separators (e.g. keeps `https://` intact). + * - Leaves the root path "/" unchanged. + */ +export function removeTrailingSlash(input: string | URL): string { + const value = typeof input === "string" ? input : input.toString() + if (value === "/") return "/" + return value.replace(/\/$/, "") +} diff --git a/packages/react/package.json b/packages/react/package.json index d822dc65..e1b4037e 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -61,6 +61,7 @@ "tinyglobby": "catalog:", "tsdown": "^0.14.1", "tw-animate-css": "catalog:", + "typescript": "catalog:", "vite": "catalog:", "vite-plugin-inspect": "catalog:", "vite-tsconfig-paths": "catalog:" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2401c258..6e0e6530 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -189,9 +189,6 @@ catalogs: shadcn: specifier: ^2.10.0 version: 2.9.3 - sharp: - specifier: ^0.34.3 - version: 0.34.3 sherif: specifier: ^1.6.1 version: 1.6.1 @@ -351,6 +348,9 @@ importers: '@astrojs/react': specifier: 'catalog:' version: 4.3.0(@types/node@22.17.2)(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(jiti@2.5.1)(lightningcss@1.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tsx@4.20.5)(yaml@2.8.1) + '@astrojs/sitemap': + specifier: ^3.5.1 + version: 3.5.1 '@astrojs/vue': specifier: 'catalog:' version: 5.1.0(@types/node@22.17.2)(astro@5.13.3(@types/node@22.17.2)(jiti@2.5.1)(lightningcss@1.30.1)(rollup@4.48.0)(tsx@4.20.5)(typescript@5.9.2)(yaml@2.8.1))(jiti@2.5.1)(lightningcss@1.30.1)(rollup@4.48.0)(tsx@4.20.5)(vue@3.5.19(typescript@5.9.2))(yaml@2.8.1) @@ -363,6 +363,9 @@ importers: '@lucide/astro': specifier: 'catalog:' version: 0.539.0(astro@5.13.3(@types/node@22.17.2)(jiti@2.5.1)(lightningcss@1.30.1)(rollup@4.48.0)(tsx@4.20.5)(typescript@5.9.2)(yaml@2.8.1)) + '@resvg/resvg-wasm': + specifier: ^2.6.2 + version: 2.6.2 '@tailwindcss/vite': specifier: 'catalog:' version: 4.1.11(vite@6.3.5(@types/node@22.17.2)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) @@ -414,21 +417,27 @@ importers: recharts: specifier: 'catalog:' version: 2.15.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + satori: + specifier: 0.15.2 + version: 0.15.2 scule: specifier: 'catalog:' version: 1.3.0 - sharp: - specifier: 'catalog:' - version: 0.34.3 tailwind-merge: specifier: 'catalog:' version: 3.3.1 tailwindcss: specifier: 'catalog:' version: 4.1.11 + tiny-invariant: + specifier: ^1.3.3 + version: 1.3.3 tw-animate-css: specifier: 'catalog:' version: 1.3.7 + yoga-wasm-web: + specifier: ^0.3.3 + version: 0.3.3 devDependencies: '@astrojs/ts-plugin': specifier: 'catalog:' @@ -703,6 +712,9 @@ importers: tw-animate-css: specifier: 'catalog:' version: 1.3.7 + typescript: + specifier: 'catalog:' + version: 5.9.2 vite: specifier: 'catalog:' version: 7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) @@ -923,6 +935,9 @@ packages: react: ^17.0.2 || ^18.0.0 || ^19.0.0 react-dom: ^17.0.2 || ^18.0.0 || ^19.0.0 + '@astrojs/sitemap@3.5.1': + resolution: {integrity: sha512-uX5z52GLtQTgOe8r3jeGmFRYrFe52mdpLYJzqjvL1cdy5Kg3MLOZEvaZ/OCH0fSq0t7e50uJQ6oBMZG0ffszBg==} + '@astrojs/telemetry@3.3.0': resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==} engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} @@ -2421,6 +2436,10 @@ packages: '@types/react': optional: true + '@resvg/resvg-wasm@2.6.2': + resolution: {integrity: sha512-FqALmHI8D4o6lk/LRWDnhw95z5eO+eAa6ORjVg09YRR7BkcM6oPHU9uyC0gtQG5vpFLvgpeU4+zEAz2H8APHNw==} + engines: {node: '>= 10'} + '@rolldown/binding-android-arm64@1.0.0-beta.34': resolution: {integrity: sha512-jf5GNe5jP3Sr1Tih0WKvg2bzvh5T/1TA0fn1u32xSH7ca/p5t+/QRr4VRFCV/na5vjwKEhwWrChsL2AWlY+eoA==} cpu: [arm64] @@ -2754,6 +2773,11 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@shuding/opentype.js@1.4.0-beta.0': + resolution: {integrity: sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==} + engines: {node: '>= 8.0.0'} + hasBin: true + '@sindresorhus/is@7.0.2': resolution: {integrity: sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw==} engines: {node: '>=18'} @@ -3348,6 +3372,9 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + '@types/node@17.0.45': + resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + '@types/node@20.19.11': resolution: {integrity: sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==} @@ -3379,6 +3406,9 @@ packages: '@types/resolve@1.20.6': resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==} + '@types/sax@1.2.7': + resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + '@types/statuses@2.0.6': resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} @@ -4374,6 +4404,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -4496,6 +4529,10 @@ packages: base-64@1.0.0: resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} + 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==} @@ -4610,6 +4647,9 @@ packages: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} + camelize@1.0.1: + resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} + caniuse-lite@1.0.30001718: resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==} @@ -4868,6 +4908,23 @@ packages: crossws@0.3.5: resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + 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-to-react-native@3.2.0: + resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} + css-tree@3.1.0: resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} @@ -5126,6 +5183,10 @@ packages: emmet@2.4.11: resolution: {integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==} + emoji-regex-xs@2.0.1: + resolution: {integrity: sha512-1QFuh8l7LqUcKe24LsPUNzjrzJQ7pgRwp1QMcZ5MX6mFplk2zQ08NVCM84++1cveaUUYtcCYHmeFEuNg16sU4g==} + engines: {node: '>=10.0.0'} + emoji-regex@10.3.0: resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} @@ -5500,6 +5561,9 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} + fflate@0.7.4: + resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==} + figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} @@ -5803,6 +5867,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'} + hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} @@ -6316,6 +6384,9 @@ packages: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} + linebreak@1.1.0: + resolution: {integrity: sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -6981,6 +7052,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-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} @@ -7627,6 +7701,13 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + satori@0.15.2: + resolution: {integrity: sha512-vu/49vdc8MzV5jUchs3TIRDCOkOvMc1iJ11MrZvhg9tE4ziKIEIBjBZvies6a9sfM2vQ2gc3dXeu6rCK7AztHA==} + engines: {node: '>=16'} + + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} @@ -7769,6 +7850,11 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + sitemap@8.0.0: + resolution: {integrity: sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A==} + engines: {node: '>=14.0.0', npm: '>=6.0.0'} + hasBin: true + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -7857,6 +7943,9 @@ packages: prettier: optional: true + stream-replace-string@2.0.0: + resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==} + strict-event-emitter@0.5.1: resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} @@ -7876,6 +7965,9 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string.prototype.codepointat@0.2.1: + resolution: {integrity: sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==} + string.prototype.includes@2.0.1: resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} engines: {node: '>= 0.4'} @@ -9006,6 +9098,9 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} + yoga-wasm-web@0.3.3: + resolution: {integrity: sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==} + youch-core@0.3.3: resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} @@ -9323,6 +9418,12 @@ snapshots: - tsx - yaml + '@astrojs/sitemap@3.5.1': + dependencies: + sitemap: 8.0.0 + stream-replace-string: 2.0.0 + zod: 3.25.76 + '@astrojs/telemetry@3.3.0': dependencies: ci-info: 4.3.0 @@ -10859,6 +10960,8 @@ snapshots: optionalDependencies: '@types/react': 18.3.23 + '@resvg/resvg-wasm@2.6.2': {} + '@rolldown/binding-android-arm64@1.0.0-beta.34': optional: true @@ -11091,6 +11194,11 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@shuding/opentype.js@1.4.0-beta.0': + dependencies: + fflate: 0.7.4 + string.prototype.codepointat: 0.2.1 + '@sindresorhus/is@7.0.2': {} '@sindresorhus/merge-streams@4.0.0': {} @@ -11736,6 +11844,8 @@ snapshots: '@types/node@12.20.55': {} + '@types/node@17.0.45': {} + '@types/node@20.19.11': dependencies: undici-types: 6.21.0 @@ -11770,6 +11880,10 @@ snapshots: '@types/resolve@1.20.6': {} + '@types/sax@1.2.7': + dependencies: + '@types/node': 24.3.0 + '@types/statuses@2.0.6': {} '@types/tough-cookie@4.0.5': {} @@ -13448,6 +13562,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + arg@5.0.2: {} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -13686,6 +13802,8 @@ snapshots: base-64@1.0.0: {} + base64-js@0.0.8: {} + base64-js@1.5.1: {} better-opn@3.0.2: @@ -13826,6 +13944,8 @@ snapshots: camelcase@8.0.0: {} + camelize@1.0.1: {} + caniuse-lite@1.0.30001718: {} caniuse-lite@1.0.30001737: {} @@ -14054,6 +14174,20 @@ snapshots: dependencies: uncrypto: 0.1.3 + 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-to-react-native@3.2.0: + dependencies: + camelize: 1.0.1 + css-color-keywords: 1.0.0 + postcss-value-parser: 4.2.0 + css-tree@3.1.0: dependencies: mdn-data: 2.12.2 @@ -14258,6 +14392,8 @@ snapshots: '@emmetio/abbreviation': 2.3.3 '@emmetio/css-abbreviation': 2.1.8 + emoji-regex-xs@2.0.1: {} + emoji-regex@10.3.0: {} emoji-regex@8.0.0: {} @@ -14851,6 +14987,8 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 + fflate@0.7.4: {} + figures@6.1.0: dependencies: is-unicode-supported: 2.1.0 @@ -15270,6 +15408,8 @@ snapshots: headers-polyfill@4.0.3: {} + hex-rgb@4.3.0: {} + hookable@5.5.3: {} html-escaper@3.0.3: {} @@ -15706,6 +15846,11 @@ snapshots: lilconfig@3.1.3: {} + linebreak@1.1.0: + dependencies: + base64-js: 0.0.8 + unicode-trie: 2.0.0 + lines-and-columns@1.2.4: {} lint-staged@16.1.5: @@ -16728,6 +16873,11 @@ snapshots: dependencies: callsites: 3.1.0 + parse-css-color@0.2.1: + dependencies: + color-name: 1.1.4 + hex-rgb: 4.3.0 + parse-entities@4.0.2: dependencies: '@types/unist': 2.0.11 @@ -17556,6 +17706,22 @@ snapshots: safer-buffer@2.1.2: {} + satori@0.15.2: + 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-xs: 2.0.1 + 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 + + sax@1.4.1: {} + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 @@ -17740,6 +17906,7 @@ snapshots: '@img/sharp-win32-arm64': 0.34.3 '@img/sharp-win32-ia32': 0.34.3 '@img/sharp-win32-x64': 0.34.3 + optional: true shebang-command@2.0.0: dependencies: @@ -17833,6 +18000,13 @@ snapshots: sisteransi@1.0.5: {} + sitemap@8.0.0: + dependencies: + '@types/node': 17.0.45 + '@types/sax': 1.2.7 + arg: 5.0.2 + sax: 1.4.1 + slash@3.0.0: {} slice-ansi@5.0.0: @@ -17901,6 +18075,8 @@ snapshots: - supports-color - utf-8-validate + stream-replace-string@2.0.0: {} + strict-event-emitter@0.5.1: {} string-argv@0.3.2: {} @@ -17923,6 +18099,8 @@ snapshots: get-east-asian-width: 1.2.0 strip-ansi: 7.1.0 + string.prototype.codepointat@0.2.1: {} + string.prototype.includes@2.0.1: dependencies: call-bind: 1.0.7 @@ -19220,6 +19398,8 @@ snapshots: yoctocolors@2.1.2: {} + yoga-wasm-web@0.3.3: {} + youch-core@0.3.3: dependencies: '@poppinss/exception': 1.2.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 6f1b6d85..578a64c5 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -67,7 +67,7 @@ catalog: rimraf: ^6.0.1 scule: ^1.3.0 shadcn: ^2.10.0 - sharp: ^0.34.3 + sharp: 0.34.3 sherif: ^1.6.1 shiki: ^3.11.0 simple-git-hooks: ^2.13.1