mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
web: Dynamic OpenGraph images (#8773)
Co-authored-by: Roo Code <roomote@roocode.com>
This commit is contained in:
parent
9ac1d6ea14
commit
c74e42bde4
16 changed files with 430 additions and 68 deletions
|
|
@ -17,6 +17,7 @@
|
|||
"@roo-code/evals": "workspace:^",
|
||||
"@roo-code/types": "workspace:^",
|
||||
"@tanstack/react-query": "^5.79.0",
|
||||
"@vercel/og": "^0.6.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"embla-carousel-auto-scroll": "^8.6.0",
|
||||
|
|
|
|||
BIN
apps/web-roo-code/public/og/base_a.png
Normal file
BIN
apps/web-roo-code/public/og/base_a.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 137 KiB |
BIN
apps/web-roo-code/public/og/base_b.png
Normal file
BIN
apps/web-roo-code/public/og/base_b.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 144 KiB |
165
apps/web-roo-code/src/app/api/og/route.tsx
Normal file
165
apps/web-roo-code/src/app/api/og/route.tsx
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
import { ImageResponse } from "next/og"
|
||||
import { NextRequest } from "next/server"
|
||||
|
||||
export const runtime = "edge"
|
||||
|
||||
async function fetchWithTimeout(url: string, init?: RequestInit, timeoutMs = 3000) {
|
||||
const controller = new AbortController()
|
||||
const id = setTimeout(() => controller.abort(), timeoutMs)
|
||||
try {
|
||||
return await fetch(url, { ...init, signal: controller.signal })
|
||||
} finally {
|
||||
clearTimeout(id)
|
||||
}
|
||||
}
|
||||
|
||||
async function loadGoogleFont(font: string, text: string): Promise<ArrayBuffer | null> {
|
||||
try {
|
||||
const url = `https://fonts.googleapis.com/css2?family=${font}&text=${encodeURIComponent(text)}`
|
||||
const cssRes = await fetchWithTimeout(url)
|
||||
if (!cssRes.ok) return null
|
||||
const css = await cssRes.text()
|
||||
|
||||
const match =
|
||||
css.match(/src:\s*url\(([^)]+)\)\s*format\('(?:woff2|woff|opentype|truetype)'\)/i) ||
|
||||
css.match(/url\(([^)]+)\)/i)
|
||||
|
||||
const fontUrl = match && match[1] ? match[1].replace(/^['"]|['"]$/g, "") : null
|
||||
if (!fontUrl) return null
|
||||
|
||||
const res = await fetchWithTimeout(fontUrl, undefined, 5000)
|
||||
if (!res.ok) return null
|
||||
return await res.arrayBuffer()
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const requestUrl = new URL(request.url)
|
||||
const { searchParams } = requestUrl
|
||||
|
||||
// Get title and description from query params
|
||||
const title = searchParams.get("title") || "Roo Code"
|
||||
const description = searchParams.get("description") || ""
|
||||
|
||||
// Combine all text that will be displayed for font loading
|
||||
const displayText = title + description
|
||||
|
||||
// Check if we should try to use the background image
|
||||
const useBackgroundImage = searchParams.get("bg") !== "false"
|
||||
|
||||
// Dynamically get the base URL from the current request
|
||||
// This ensures it works correctly in development, preview, and production environments
|
||||
const baseUrl = `${requestUrl.protocol}//${requestUrl.host}`
|
||||
const variant = title.length % 2 === 0 ? "a" : "b"
|
||||
const backgroundUrl = `${baseUrl}/og/base_${variant}.png`
|
||||
|
||||
// Preload fonts with graceful fallbacks
|
||||
const regularFont = await loadGoogleFont("Inter", displayText)
|
||||
const boldFont = await loadGoogleFont("Inter:wght@700", displayText)
|
||||
const fonts: { name: string; data: ArrayBuffer; style?: "normal" | "italic"; weight?: 400 | 700 }[] = []
|
||||
if (regularFont) {
|
||||
fonts.push({ name: "Inter", data: regularFont, style: "normal", weight: 400 })
|
||||
}
|
||||
if (boldFont) {
|
||||
fonts.push({ name: "Inter", data: boldFont, style: "normal", weight: 700 })
|
||||
}
|
||||
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
position: "relative",
|
||||
// Use gradient background as default/fallback
|
||||
background: "linear-gradient(135deg, #1e3a5f 0%, #0f1922 50%, #1a2332 100%)",
|
||||
}}>
|
||||
{/* Optional Background Image - only render if explicitly requested */}
|
||||
{useBackgroundImage && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
}}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={backgroundUrl}
|
||||
alt=""
|
||||
width={1200}
|
||||
height={630}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "cover",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Text Content */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-end",
|
||||
top: "220px",
|
||||
left: "80px",
|
||||
right: "80px",
|
||||
bottom: "80px",
|
||||
}}>
|
||||
{/* Main Title */}
|
||||
<h1
|
||||
style={{
|
||||
fontSize: 70,
|
||||
fontWeight: 700,
|
||||
fontFamily: "Inter, Helvetica Neue, Helvetica, sans-serif",
|
||||
color: "white",
|
||||
lineHeight: 1.2,
|
||||
margin: 0,
|
||||
maxHeight: "2.4em",
|
||||
overflow: "hidden",
|
||||
}}>
|
||||
{title}
|
||||
</h1>
|
||||
|
||||
{/* Secondary Description */}
|
||||
{description && (
|
||||
<h2
|
||||
style={{
|
||||
fontSize: 70,
|
||||
fontWeight: 400,
|
||||
fontFamily: "Inter, Helvetica Neue, Helvetica, Arial, sans-serif",
|
||||
color: "rgba(255, 255, 255, 0.9)",
|
||||
lineHeight: 1.2,
|
||||
margin: 0,
|
||||
maxHeight: "2.4em",
|
||||
overflow: "hidden",
|
||||
}}>
|
||||
{description}
|
||||
</h2>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
fonts: fonts.length ? fonts : undefined,
|
||||
// Cache for 7 days in production, 3 seconds in development
|
||||
headers: {
|
||||
"Cache-Control":
|
||||
process.env.NODE_ENV === "production"
|
||||
? "public, max-age=604800, s-maxage=604800, stale-while-revalidate=86400"
|
||||
: "public, max-age=3, s-maxage=3",
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -16,14 +16,15 @@ import type { Metadata } from "next"
|
|||
import { Button } from "@/components/ui"
|
||||
import { AnimatedBackground } from "@/components/homepage"
|
||||
import { SEO } from "@/lib/seo"
|
||||
import { ogImageUrl } from "@/lib/og"
|
||||
import { EXTERNAL_LINKS } from "@/lib/constants"
|
||||
import Image from "next/image"
|
||||
|
||||
const TITLE = "Roo Code Cloud"
|
||||
const DESCRIPTION =
|
||||
"Roo Code Cloud gives you and your team the tools to take AI-coding to the next level with cloud agents, remote control, and more."
|
||||
const OG_DESCRIPTION = "Go way beyond the IDE"
|
||||
const PATH = "/cloud"
|
||||
const OG_IMAGE = SEO.ogImage
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: TITLE,
|
||||
|
|
@ -38,10 +39,10 @@ export const metadata: Metadata = {
|
|||
siteName: SEO.name,
|
||||
images: [
|
||||
{
|
||||
url: OG_IMAGE.url,
|
||||
width: OG_IMAGE.width,
|
||||
height: OG_IMAGE.height,
|
||||
alt: OG_IMAGE.alt,
|
||||
url: ogImageUrl(TITLE, OG_DESCRIPTION),
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: TITLE,
|
||||
},
|
||||
],
|
||||
locale: SEO.locale,
|
||||
|
|
@ -51,7 +52,7 @@ export const metadata: Metadata = {
|
|||
card: SEO.twitterCard,
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
images: [OG_IMAGE.url],
|
||||
images: [ogImageUrl(TITLE, OG_DESCRIPTION)],
|
||||
},
|
||||
keywords: [...SEO.keywords, "cloud", "subscription", "cloud agents", "AI cloud development"],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,13 @@ import { ContactForm } from "@/components/enterprise/contact-form"
|
|||
import { EXTERNAL_LINKS } from "@/lib/constants"
|
||||
import type { Metadata } from "next"
|
||||
import { SEO } from "@/lib/seo"
|
||||
import { ogImageUrl } from "@/lib/og"
|
||||
|
||||
const TITLE = "Enterprise Solution"
|
||||
const TITLE = "Roo Code Cloud Enterprise"
|
||||
const DESCRIPTION =
|
||||
"The control-plane for AI-powered software development. Gain visibility, governance, and control over your AI coding initiatives."
|
||||
const OG_DESCRIPTION = "The control-plane for AI-powered software development"
|
||||
const PATH = "/enterprise"
|
||||
const OG_IMAGE = SEO.ogImage
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: TITLE,
|
||||
|
|
@ -27,10 +28,10 @@ export const metadata: Metadata = {
|
|||
siteName: SEO.name,
|
||||
images: [
|
||||
{
|
||||
url: OG_IMAGE.url,
|
||||
width: OG_IMAGE.width,
|
||||
height: OG_IMAGE.height,
|
||||
alt: OG_IMAGE.alt,
|
||||
url: ogImageUrl(TITLE, OG_DESCRIPTION),
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: TITLE,
|
||||
},
|
||||
],
|
||||
locale: SEO.locale,
|
||||
|
|
@ -40,7 +41,7 @@ export const metadata: Metadata = {
|
|||
card: SEO.twitterCard,
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
images: [OG_IMAGE.url],
|
||||
images: [ogImageUrl(TITLE, OG_DESCRIPTION)],
|
||||
},
|
||||
keywords: [
|
||||
...SEO.keywords,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import type { Metadata } from "next"
|
|||
|
||||
import { getEvalRuns } from "@/actions/evals"
|
||||
import { SEO } from "@/lib/seo"
|
||||
import { ogImageUrl } from "@/lib/og"
|
||||
|
||||
import { Evals } from "./evals"
|
||||
|
||||
|
|
@ -10,13 +11,8 @@ export const dynamic = "force-dynamic"
|
|||
|
||||
const TITLE = "Evals"
|
||||
const DESCRIPTION = "Explore quantitative evals of LLM coding skills across tasks and providers."
|
||||
const OG_DESCRIPTION = "Quantitative evals of LLM coding skills"
|
||||
const PATH = "/evals"
|
||||
const IMAGE = {
|
||||
url: "https://i.imgur.com/ijP7aZm.png",
|
||||
width: 1954,
|
||||
height: 1088,
|
||||
alt: "Roo Code Evals – LLM coding benchmarks",
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: TITLE,
|
||||
|
|
@ -29,7 +25,14 @@ export const metadata: Metadata = {
|
|||
description: DESCRIPTION,
|
||||
url: `${SEO.url}${PATH}`,
|
||||
siteName: SEO.name,
|
||||
images: [IMAGE],
|
||||
images: [
|
||||
{
|
||||
url: ogImageUrl(TITLE, OG_DESCRIPTION),
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: TITLE,
|
||||
},
|
||||
],
|
||||
locale: SEO.locale,
|
||||
type: "website",
|
||||
},
|
||||
|
|
@ -37,7 +40,7 @@ export const metadata: Metadata = {
|
|||
card: SEO.twitterCard,
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
images: [IMAGE.url],
|
||||
images: [ogImageUrl(TITLE, OG_DESCRIPTION)],
|
||||
},
|
||||
keywords: [...SEO.keywords, "benchmarks", "LLM evals", "coding evaluations", "model comparison"],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from "react"
|
|||
import type { Metadata } from "next"
|
||||
import { Inter } from "next/font/google"
|
||||
import { SEO } from "@/lib/seo"
|
||||
import { ogImageUrl } from "@/lib/og"
|
||||
import { CookieConsentWrapper } from "@/components/CookieConsentWrapper"
|
||||
|
||||
import { Providers } from "@/components/providers"
|
||||
|
|
@ -12,6 +13,9 @@ import "./globals.css"
|
|||
|
||||
const inter = Inter({ subsets: ["latin"] })
|
||||
|
||||
const OG_TITLE = "Meet Roo Code"
|
||||
const OG_DESCRIPTION = "The AI dev team that gets things done."
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL(SEO.url),
|
||||
title: {
|
||||
|
|
@ -51,10 +55,10 @@ export const metadata: Metadata = {
|
|||
siteName: SEO.name,
|
||||
images: [
|
||||
{
|
||||
url: SEO.ogImage.url,
|
||||
width: SEO.ogImage.width,
|
||||
height: SEO.ogImage.height,
|
||||
alt: SEO.ogImage.alt,
|
||||
url: ogImageUrl(OG_TITLE, OG_DESCRIPTION),
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: OG_TITLE,
|
||||
},
|
||||
],
|
||||
locale: SEO.locale,
|
||||
|
|
@ -64,7 +68,7 @@ export const metadata: Metadata = {
|
|||
card: SEO.twitterCard,
|
||||
title: SEO.title,
|
||||
description: SEO.description,
|
||||
images: [SEO.ogImage.url],
|
||||
images: [ogImageUrl(OG_TITLE, OG_DESCRIPTION)],
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import type { Metadata } from "next"
|
||||
import { SEO } from "@/lib/seo"
|
||||
import { ogImageUrl } from "@/lib/og"
|
||||
|
||||
const TITLE = "Cookie Policy"
|
||||
const TITLE = "Our Cookie Policy"
|
||||
const DESCRIPTION = "Learn about how Roo Code uses cookies to enhance your experience and provide our services."
|
||||
const OG_DESCRIPTION = ""
|
||||
const PATH = "/legal/cookies"
|
||||
const OG_IMAGE = SEO.ogImage
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: TITLE,
|
||||
|
|
@ -19,10 +20,10 @@ export const metadata: Metadata = {
|
|||
siteName: SEO.name,
|
||||
images: [
|
||||
{
|
||||
url: OG_IMAGE.url,
|
||||
width: OG_IMAGE.width,
|
||||
height: OG_IMAGE.height,
|
||||
alt: OG_IMAGE.alt,
|
||||
url: ogImageUrl(TITLE, OG_DESCRIPTION),
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: TITLE,
|
||||
},
|
||||
],
|
||||
locale: SEO.locale,
|
||||
|
|
@ -32,7 +33,7 @@ export const metadata: Metadata = {
|
|||
card: SEO.twitterCard,
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
images: [OG_IMAGE.url],
|
||||
images: [ogImageUrl(TITLE, OG_DESCRIPTION)],
|
||||
},
|
||||
keywords: [...SEO.keywords, "cookies", "privacy", "tracking", "analytics"],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import type { Metadata } from "next"
|
||||
import { SEO } from "@/lib/seo"
|
||||
import { ogImageUrl } from "@/lib/og"
|
||||
|
||||
const TITLE = "Subprocessors"
|
||||
const DESCRIPTION = "List of third-party subprocessors used by Roo Code to process customer data."
|
||||
const OG_DESCRIPTION = ""
|
||||
const PATH = "/legal/subprocessors"
|
||||
const OG_IMAGE = SEO.ogImage
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: TITLE,
|
||||
|
|
@ -19,10 +20,10 @@ export const metadata: Metadata = {
|
|||
siteName: SEO.name,
|
||||
images: [
|
||||
{
|
||||
url: OG_IMAGE.url,
|
||||
width: OG_IMAGE.width,
|
||||
height: OG_IMAGE.height,
|
||||
alt: OG_IMAGE.alt,
|
||||
url: ogImageUrl(TITLE, OG_DESCRIPTION),
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: TITLE,
|
||||
},
|
||||
],
|
||||
locale: SEO.locale,
|
||||
|
|
@ -32,7 +33,7 @@ export const metadata: Metadata = {
|
|||
card: SEO.twitterCard,
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
images: [OG_IMAGE.url],
|
||||
images: [ogImageUrl(TITLE, OG_DESCRIPTION)],
|
||||
},
|
||||
keywords: [...SEO.keywords, "subprocessors", "data processing", "GDPR", "privacy", "third-party services"],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,14 @@ import { Button } from "@/components/ui"
|
|||
import { AnimatedBackground } from "@/components/homepage"
|
||||
import { ContactForm } from "@/components/enterprise/contact-form"
|
||||
import { SEO } from "@/lib/seo"
|
||||
import { ogImageUrl } from "@/lib/og"
|
||||
import { EXTERNAL_LINKS } from "@/lib/constants"
|
||||
|
||||
const TITLE = "Pricing - Roo Code Cloud"
|
||||
const TITLE = "Roo Code Cloud Pricing"
|
||||
const DESCRIPTION =
|
||||
"Simple, transparent pricing for Roo Code Cloud. The VS Code extension is free forever. Choose the cloud plan that fits your needs."
|
||||
const OG_DESCRIPTION = ""
|
||||
const PATH = "/pricing"
|
||||
const OG_IMAGE = SEO.ogImage
|
||||
|
||||
const PRICE_CREDITS = 5
|
||||
|
||||
|
|
@ -29,10 +30,10 @@ export const metadata: Metadata = {
|
|||
siteName: SEO.name,
|
||||
images: [
|
||||
{
|
||||
url: OG_IMAGE.url,
|
||||
width: OG_IMAGE.width,
|
||||
height: OG_IMAGE.height,
|
||||
alt: OG_IMAGE.alt,
|
||||
url: ogImageUrl(TITLE, OG_DESCRIPTION),
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: TITLE,
|
||||
},
|
||||
],
|
||||
locale: SEO.locale,
|
||||
|
|
@ -42,7 +43,7 @@ export const metadata: Metadata = {
|
|||
card: SEO.twitterCard,
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
images: [OG_IMAGE.url],
|
||||
images: [ogImageUrl(TITLE, OG_DESCRIPTION)],
|
||||
},
|
||||
keywords: [
|
||||
...SEO.keywords,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import type { Metadata } from "next"
|
||||
import { SEO } from "@/lib/seo"
|
||||
import { ogImageUrl } from "@/lib/og"
|
||||
|
||||
const TITLE = "Privacy Policy"
|
||||
const TITLE = "Our Privacy Policy"
|
||||
const DESCRIPTION =
|
||||
"Privacy policy for Roo Code Cloud and marketing website. Learn how we handle your data and protect your privacy."
|
||||
const OG_DESCRIPTION = ""
|
||||
const PATH = "/privacy"
|
||||
const OG_IMAGE = SEO.ogImage
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: TITLE,
|
||||
|
|
@ -20,10 +21,10 @@ export const metadata: Metadata = {
|
|||
siteName: SEO.name,
|
||||
images: [
|
||||
{
|
||||
url: OG_IMAGE.url,
|
||||
width: OG_IMAGE.width,
|
||||
height: OG_IMAGE.height,
|
||||
alt: OG_IMAGE.alt,
|
||||
url: ogImageUrl(TITLE, OG_DESCRIPTION),
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: TITLE,
|
||||
},
|
||||
],
|
||||
locale: SEO.locale,
|
||||
|
|
@ -33,7 +34,7 @@ export const metadata: Metadata = {
|
|||
card: SEO.twitterCard,
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
images: [OG_IMAGE.url],
|
||||
images: [ogImageUrl(TITLE, OG_DESCRIPTION)],
|
||||
},
|
||||
keywords: [...SEO.keywords, "privacy", "data protection", "GDPR", "security"],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,14 +5,15 @@ import { Button } from "@/components/ui"
|
|||
import { AnimatedBackground } from "@/components/homepage"
|
||||
import { AgentCarousel } from "@/components/reviewer/agent-carousel"
|
||||
import { SEO } from "@/lib/seo"
|
||||
import { ogImageUrl } from "@/lib/og"
|
||||
import { EXTERNAL_LINKS } from "@/lib/constants"
|
||||
import Image from "next/image"
|
||||
|
||||
const TITLE = "PR Reviewer · Roo Code Cloud"
|
||||
const TITLE = "PR Reviewer"
|
||||
const DESCRIPTION =
|
||||
"Get comprehensive AI-powered PR reviews that save you time, not tokens. Bring your own API key and leverage advanced reasoning, repository-aware analysis, and actionable feedback to keep your PR queue moving."
|
||||
const OG_DESCRIPTION = "AI-powered PR reviews that save you time, not tokens"
|
||||
const PATH = "/reviewer"
|
||||
const OG_IMAGE = SEO.ogImage
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: TITLE,
|
||||
|
|
@ -27,10 +28,10 @@ export const metadata: Metadata = {
|
|||
siteName: SEO.name,
|
||||
images: [
|
||||
{
|
||||
url: OG_IMAGE.url,
|
||||
width: OG_IMAGE.width,
|
||||
height: OG_IMAGE.height,
|
||||
alt: OG_IMAGE.alt,
|
||||
url: ogImageUrl(TITLE, OG_DESCRIPTION),
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: TITLE,
|
||||
},
|
||||
],
|
||||
locale: SEO.locale,
|
||||
|
|
@ -40,7 +41,7 @@ export const metadata: Metadata = {
|
|||
card: SEO.twitterCard,
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
images: [OG_IMAGE.url],
|
||||
images: [ogImageUrl(TITLE, OG_DESCRIPTION)],
|
||||
},
|
||||
keywords: [
|
||||
...SEO.keywords,
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
import type { Metadata } from "next"
|
||||
import { SEO } from "@/lib/seo"
|
||||
import { ogImageUrl } from "@/lib/og"
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import ReactMarkdown from "react-markdown"
|
||||
import remarkGfm from "remark-gfm"
|
||||
import rehypeRaw from "rehype-raw"
|
||||
|
||||
const TITLE = "Terms of Service"
|
||||
const TITLE = "Our Terms of Service"
|
||||
const DESCRIPTION =
|
||||
"Terms of Service for Roo Code Cloud. Learn about our service terms, commercial conditions, and legal framework."
|
||||
const OG_DESCRIPTION = ""
|
||||
const PATH = "/terms"
|
||||
const OG_IMAGE = SEO.ogImage
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: TITLE,
|
||||
|
|
@ -25,10 +26,10 @@ export const metadata: Metadata = {
|
|||
siteName: SEO.name,
|
||||
images: [
|
||||
{
|
||||
url: OG_IMAGE.url,
|
||||
width: OG_IMAGE.width,
|
||||
height: OG_IMAGE.height,
|
||||
alt: OG_IMAGE.alt,
|
||||
url: ogImageUrl(TITLE, OG_DESCRIPTION),
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: TITLE,
|
||||
},
|
||||
],
|
||||
locale: SEO.locale,
|
||||
|
|
@ -38,7 +39,7 @@ export const metadata: Metadata = {
|
|||
card: SEO.twitterCard,
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
images: [OG_IMAGE.url],
|
||||
images: [ogImageUrl(TITLE, OG_DESCRIPTION)],
|
||||
},
|
||||
keywords: [...SEO.keywords, "terms of service", "legal", "agreement", "subscription"],
|
||||
}
|
||||
|
|
|
|||
57
apps/web-roo-code/src/lib/og.ts
Normal file
57
apps/web-roo-code/src/lib/og.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* Generate a dynamic OpenGraph image URL
|
||||
* @param title - The title to display on the OG image
|
||||
* @param description - Optional description to display (will be truncated to ~140 chars)
|
||||
* @returns Absolute URL to the dynamic OG image endpoint
|
||||
*/
|
||||
export function ogImageUrl(title: string, description?: string): string {
|
||||
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || "https://roocode.com"
|
||||
const params = new URLSearchParams()
|
||||
|
||||
params.set("title", title)
|
||||
if (description) {
|
||||
params.set("description", description)
|
||||
}
|
||||
|
||||
return `${baseUrl}/api/og?${params.toString()}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate OpenGraph metadata for a page with dynamic image
|
||||
* @param title - The page title
|
||||
* @param description - The page description
|
||||
* @returns OpenGraph metadata object with dynamic image
|
||||
*/
|
||||
export function getOgMetadata(title: string, description: string) {
|
||||
const imageUrl = ogImageUrl(title, description)
|
||||
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
images: [
|
||||
{
|
||||
url: imageUrl,
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: title,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Twitter metadata for a page with dynamic image
|
||||
* @param title - The page title
|
||||
* @param description - The page description
|
||||
* @returns Twitter metadata object with dynamic image
|
||||
*/
|
||||
export function getTwitterMetadata(title: string, description: string) {
|
||||
const imageUrl = ogImageUrl(title, description)
|
||||
|
||||
return {
|
||||
card: "summary_large_image" as const,
|
||||
title,
|
||||
description,
|
||||
images: [imageUrl],
|
||||
}
|
||||
}
|
||||
126
pnpm-lock.yaml
generated
126
pnpm-lock.yaml
generated
|
|
@ -269,6 +269,9 @@ importers:
|
|||
'@tanstack/react-query':
|
||||
specifier: ^5.79.0
|
||||
version: 5.80.2(react@18.3.1)
|
||||
'@vercel/og':
|
||||
specifier: ^0.6.2
|
||||
version: 0.6.8
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1
|
||||
|
|
@ -3166,6 +3169,10 @@ packages:
|
|||
peerDependencies:
|
||||
'@redis/client': ^5.5.5
|
||||
|
||||
'@resvg/resvg-wasm@2.4.0':
|
||||
resolution: {integrity: sha512-C7c51Nn4yTxXFKvgh2txJFNweaVcfUPQxwEUFw4aWsCmfiBDJsTSwviIF8EcwjQ6k8bPyMWCl1vw4BdxE569Cg==}
|
||||
engines: {node: '>= 10'}
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.40.2':
|
||||
resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==}
|
||||
cpu: [arm]
|
||||
|
|
@ -3293,6 +3300,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
|
||||
|
||||
'@sinclair/typebox@0.27.8':
|
||||
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
||||
|
||||
|
|
@ -4211,6 +4223,10 @@ packages:
|
|||
'@ungap/structured-clone@1.3.0':
|
||||
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
|
||||
|
||||
'@vercel/og@0.6.8':
|
||||
resolution: {integrity: sha512-e4kQK9mP8ntpo3dACWirGod/hHv4qO5JMj9a/0a2AZto7b4persj5YP7t1Er372gTtYFTYxNhMx34jRvHooglw==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
'@vitejs/plugin-react@4.4.1':
|
||||
resolution: {integrity: sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
|
|
@ -4558,6 +4574,10 @@ packages:
|
|||
bare-events:
|
||||
optional: true
|
||||
|
||||
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==}
|
||||
|
||||
|
|
@ -4997,10 +5017,20 @@ packages:
|
|||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
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-in-js-utils@3.1.0:
|
||||
resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==}
|
||||
|
||||
|
|
@ -5983,6 +6013,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==}
|
||||
|
||||
|
|
@ -6382,6 +6415,10 @@ packages:
|
|||
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
|
||||
hasBin: true
|
||||
|
||||
hex-rgb@4.3.0:
|
||||
resolution: {integrity: sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
highlight.js@11.11.1:
|
||||
resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
|
@ -7145,6 +7182,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==}
|
||||
|
||||
|
|
@ -7997,6 +8037,9 @@ packages:
|
|||
package-manager-detector@1.3.0:
|
||||
resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==}
|
||||
|
||||
pako@0.2.9:
|
||||
resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
|
||||
|
||||
pako@1.0.11:
|
||||
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
|
||||
|
||||
|
|
@ -8004,6 +8047,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@2.0.0:
|
||||
resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==}
|
||||
|
||||
|
|
@ -8735,6 +8781,10 @@ packages:
|
|||
sanitize-filename@1.6.3:
|
||||
resolution: {integrity: sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==}
|
||||
|
||||
satori@0.12.2:
|
||||
resolution: {integrity: sha512-3C/laIeE6UUe9A+iQ0A48ywPVCCMKCNSTU5Os101Vhgsjd3AAxGNjyq0uAA8kulMPK5n0csn8JlxPN9riXEjLA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
sax@1.4.1:
|
||||
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
|
||||
|
||||
|
|
@ -9052,6 +9102,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.matchall@4.0.12:
|
||||
resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -9280,6 +9333,9 @@ packages:
|
|||
tiktoken@1.0.21:
|
||||
resolution: {integrity: sha512-/kqtlepLMptX0OgbYD9aMYbM7EFrMZCL7EoHM8Psmg2FuhXoo/bH64KqOiZGGwa6oS9TPdSEDKBnV2LuB8+5vQ==}
|
||||
|
||||
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==}
|
||||
|
||||
|
|
@ -9546,6 +9602,9 @@ packages:
|
|||
resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==}
|
||||
engines: {node: '>=18.17'}
|
||||
|
||||
unicode-trie@2.0.0:
|
||||
resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==}
|
||||
|
||||
unicorn-magic@0.3.0:
|
||||
resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
|
||||
engines: {node: '>=18'}
|
||||
|
|
@ -10155,6 +10214,9 @@ packages:
|
|||
resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
yoga-wasm-web@0.3.3:
|
||||
resolution: {integrity: sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==}
|
||||
|
||||
zip-stream@4.1.1:
|
||||
resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==}
|
||||
engines: {node: '>= 10'}
|
||||
|
|
@ -12628,6 +12690,8 @@ snapshots:
|
|||
dependencies:
|
||||
'@redis/client': 5.5.5
|
||||
|
||||
'@resvg/resvg-wasm@2.4.0': {}
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.40.2':
|
||||
optional: true
|
||||
|
||||
|
|
@ -12725,6 +12789,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
|
||||
|
||||
'@sinclair/typebox@0.27.8': {}
|
||||
|
||||
'@sindresorhus/merge-streams@4.0.0': {}
|
||||
|
|
@ -13847,6 +13916,12 @@ snapshots:
|
|||
|
||||
'@ungap/structured-clone@1.3.0': {}
|
||||
|
||||
'@vercel/og@0.6.8':
|
||||
dependencies:
|
||||
'@resvg/resvg-wasm': 2.4.0
|
||||
satori: 0.12.2
|
||||
yoga-wasm-web: 0.3.3
|
||||
|
||||
'@vitejs/plugin-react@4.4.1(vite@6.3.6(@types/node@20.17.57)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0))':
|
||||
dependencies:
|
||||
'@babel/core': 7.27.1
|
||||
|
|
@ -13919,7 +13994,7 @@ snapshots:
|
|||
sirv: 3.0.1
|
||||
tinyglobby: 0.2.14
|
||||
tinyrainbow: 2.0.0
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@20.17.57)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0)
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.2.1)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0)
|
||||
|
||||
'@vitest/utils@3.2.4':
|
||||
dependencies:
|
||||
|
|
@ -14290,6 +14365,8 @@ snapshots:
|
|||
bare-events: 2.5.4
|
||||
optional: true
|
||||
|
||||
base64-js@0.0.8: {}
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
basic-ftp@5.0.5: {}
|
||||
|
|
@ -14758,8 +14835,14 @@ 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-in-js-utils@3.1.0:
|
||||
dependencies:
|
||||
hyphenate-style-name: 1.1.0
|
||||
|
|
@ -15865,6 +15948,8 @@ snapshots:
|
|||
|
||||
fflate@0.4.8: {}
|
||||
|
||||
fflate@0.7.4: {}
|
||||
|
||||
fflate@0.8.2: {}
|
||||
|
||||
figures@6.1.0:
|
||||
|
|
@ -16381,6 +16466,8 @@ snapshots:
|
|||
|
||||
he@1.2.0: {}
|
||||
|
||||
hex-rgb@4.3.0: {}
|
||||
|
||||
highlight.js@11.11.1: {}
|
||||
|
||||
hosted-git-info@4.1.0:
|
||||
|
|
@ -17133,6 +17220,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: {}
|
||||
|
||||
linkify-it@5.0.0:
|
||||
|
|
@ -18276,12 +18368,19 @@ snapshots:
|
|||
|
||||
package-manager-detector@1.3.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-entities@2.0.0:
|
||||
dependencies:
|
||||
character-entities: 1.2.4
|
||||
|
|
@ -19154,6 +19253,20 @@ snapshots:
|
|||
dependencies:
|
||||
truncate-utf8-bytes: 1.0.2
|
||||
|
||||
satori@0.12.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: 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
|
||||
|
||||
sax@1.4.1: {}
|
||||
|
||||
saxes@5.0.1:
|
||||
|
|
@ -19532,6 +19645,8 @@ snapshots:
|
|||
get-east-asian-width: 1.3.0
|
||||
strip-ansi: 7.1.0
|
||||
|
||||
string.prototype.codepointat@0.2.1: {}
|
||||
|
||||
string.prototype.matchall@4.0.12:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
|
|
@ -19804,6 +19919,8 @@ snapshots:
|
|||
|
||||
tiktoken@1.0.21: {}
|
||||
|
||||
tiny-inflate@1.0.3: {}
|
||||
|
||||
tiny-invariant@1.3.3: {}
|
||||
|
||||
tinybench@2.9.0: {}
|
||||
|
|
@ -20058,6 +20175,11 @@ snapshots:
|
|||
|
||||
undici@6.21.3: {}
|
||||
|
||||
unicode-trie@2.0.0:
|
||||
dependencies:
|
||||
pako: 0.2.9
|
||||
tiny-inflate: 1.0.3
|
||||
|
||||
unicorn-magic@0.3.0: {}
|
||||
|
||||
unified@11.0.5:
|
||||
|
|
@ -20862,6 +20984,8 @@ snapshots:
|
|||
|
||||
yoctocolors@2.1.1: {}
|
||||
|
||||
yoga-wasm-web@0.3.3: {}
|
||||
|
||||
zip-stream@4.1.1:
|
||||
dependencies:
|
||||
archiver-utils: 3.0.4
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue