From 5882f7e7df4375c126d65da5132023467c9eaf4c Mon Sep 17 00:00:00 2001
From: adamsoffer
Date: Tue, 7 Apr 2026 13:08:05 -0400
Subject: [PATCH 1/6] Add ecosystem detail pages with markdown-driven content
Migrate ecosystem data from data/ecosystem.json to per-app markdown files
under content/ecosystem/, mirroring the existing blog pipeline. Each app
now has a dedicated detail page at /ecosystem/[slug] with a Linear-inspired
two-column layout: an overview rendered from markdown alongside a sticky
metadata sidebar grouped into Details, Connect, and Resources.
Listing cards on /ecosystem and the four bento cards on the homepage now
link to the internal detail pages instead of opening external URLs, and
the Daydream CTA in the Start Building section points to /ecosystem/daydream.
Co-Authored-By: Claude Opus 4.6 (1M context)
---
app/ecosystem/[slug]/page.tsx | 54 +++
app/ecosystem/page.tsx | 335 +---------------
app/ecosystem/submit/page.tsx | 171 +-------
components/ecosystem/EcosystemDetail.tsx | 365 ++++++++++++++++++
.../ecosystem/EcosystemListingClient.tsx | 338 ++++++++++++++++
components/ecosystem/SubmitAppForm.tsx | 171 ++++++++
components/home/BuiltOnLivepeer.tsx | 27 +-
components/home/StartBuilding.tsx | 11 +-
content/ecosystem/blueclaw.md | 44 +++
content/ecosystem/daydream.md | 43 +++
content/ecosystem/embody.md | 38 ++
content/ecosystem/frameworks.md | 45 +++
content/ecosystem/higher.md | 30 ++
content/ecosystem/livepeer-studio.md | 42 ++
content/ecosystem/nytv.md | 22 ++
content/ecosystem/spritz.md | 37 ++
content/ecosystem/streamplace.md | 42 ++
content/ecosystem/thelotradio.md | 27 ++
content/ecosystem/tribesocial.md | 45 +++
content/ecosystem/ufo.md | 37 ++
data/ecosystem.json | 99 -----
lib/ecosystem-data.ts | 22 --
lib/ecosystem.ts | 110 ++++++
23 files changed, 1535 insertions(+), 620 deletions(-)
create mode 100644 app/ecosystem/[slug]/page.tsx
create mode 100644 components/ecosystem/EcosystemDetail.tsx
create mode 100644 components/ecosystem/EcosystemListingClient.tsx
create mode 100644 components/ecosystem/SubmitAppForm.tsx
create mode 100644 content/ecosystem/blueclaw.md
create mode 100644 content/ecosystem/daydream.md
create mode 100644 content/ecosystem/embody.md
create mode 100644 content/ecosystem/frameworks.md
create mode 100644 content/ecosystem/higher.md
create mode 100644 content/ecosystem/livepeer-studio.md
create mode 100644 content/ecosystem/nytv.md
create mode 100644 content/ecosystem/spritz.md
create mode 100644 content/ecosystem/streamplace.md
create mode 100644 content/ecosystem/thelotradio.md
create mode 100644 content/ecosystem/tribesocial.md
create mode 100644 content/ecosystem/ufo.md
delete mode 100644 data/ecosystem.json
delete mode 100644 lib/ecosystem-data.ts
create mode 100644 lib/ecosystem.ts
diff --git a/app/ecosystem/[slug]/page.tsx b/app/ecosystem/[slug]/page.tsx
new file mode 100644
index 0000000..5af9dc9
--- /dev/null
+++ b/app/ecosystem/[slug]/page.tsx
@@ -0,0 +1,54 @@
+import type { Metadata } from "next";
+import { notFound } from "next/navigation";
+import EcosystemDetail from "@/components/ecosystem/EcosystemDetail";
+import {
+ getAppBySlug,
+ getAppSlugs,
+ renderEcosystemMarkdown,
+} from "@/lib/ecosystem";
+
+type Props = {
+ params: Promise<{ slug: string }>;
+};
+
+export async function generateStaticParams() {
+ return getAppSlugs().map((slug) => ({ slug }));
+}
+
+export async function generateMetadata({ params }: Props): Promise {
+ const { slug } = await params;
+ try {
+ const app = getAppBySlug(slug);
+ return {
+ title: `${app.name} | Livepeer Ecosystem`,
+ description: app.description,
+ openGraph: {
+ title: `${app.name} | Livepeer Ecosystem`,
+ description: app.description,
+ type: "website",
+ },
+ twitter: {
+ card: "summary_large_image",
+ title: `${app.name} | Livepeer Ecosystem`,
+ description: app.description,
+ },
+ };
+ } catch {
+ return { title: "App Not Found — Livepeer Ecosystem" };
+ }
+}
+
+export default async function EcosystemAppPage({ params }: Props) {
+ const { slug } = await params;
+
+ let app;
+ try {
+ app = getAppBySlug(slug);
+ } catch {
+ notFound();
+ }
+
+ const html = await renderEcosystemMarkdown(app.content);
+
+ return ;
+}
diff --git a/app/ecosystem/page.tsx b/app/ecosystem/page.tsx
index b06fc67..9a5dd51 100644
--- a/app/ecosystem/page.tsx
+++ b/app/ecosystem/page.tsx
@@ -1,321 +1,20 @@
-"use client";
-
-import {
- Suspense,
- useState,
- useMemo,
- useEffect,
- useRef,
- useCallback,
-} from "react";
-import { Search, Plus, ArrowUpRight } from "lucide-react";
-import { motion, AnimatePresence } from "framer-motion";
-import { useSearchParams, useRouter, usePathname } from "next/navigation";
-import { ECOSYSTEM_APPS, ECOSYSTEM_CATEGORIES } from "@/lib/ecosystem-data";
-import PageHero from "@/components/ui/PageHero";
-import Container from "@/components/ui/Container";
-import SectionHeader from "@/components/ui/SectionHeader";
-import Badge from "@/components/ui/Badge";
-import Button from "@/components/ui/Button";
-import FilterPill from "@/components/ui/FilterPill";
-
-const BATCH_SIZE = 12;
-
-function EcosystemPageInner() {
- const searchParams = useSearchParams();
- const router = useRouter();
- const pathname = usePathname();
-
- const [activeCategories, setActiveCategories] = useState(() => {
- const param = searchParams.get("categories");
- return param ? param.split(",").map(decodeURIComponent) : [];
- });
- const [search, setSearch] = useState(() => searchParams.get("q") ?? "");
- const [visible, setVisible] = useState(BATCH_SIZE);
- const [buttonBatch, setButtonBatch] = useState(0);
- const sentinelRef = useRef(null);
-
- const isAllActive = activeCategories.length === 0;
- const loadMore = useCallback(() => setVisible((v) => v + BATCH_SIZE), []);
- const handleButtonLoad = useCallback(() => {
- setButtonBatch(visible);
- loadMore();
- }, [visible, loadMore]);
-
- /* Sync filter state → URL query params */
- useEffect(() => {
- const params = new URLSearchParams();
- if (activeCategories.length > 0)
- params.set("categories", activeCategories.join(","));
- if (search) params.set("q", search);
-
- const qs = params.toString();
- const url = qs ? `${pathname}?${qs}` : pathname;
- router.replace(url, { scroll: false });
- }, [activeCategories, search, pathname, router]);
-
- useEffect(() => {
- setVisible(BATCH_SIZE);
- }, [activeCategories, search]);
-
- const handleCategoryToggle = (cat: string) => {
- if (cat === "All") {
- setActiveCategories([]);
- return;
- }
- setActiveCategories((prev) =>
- prev.includes(cat) ? prev.filter((c) => c !== cat) : [...prev, cat]
- );
- };
-
- const filtered = useMemo(() => {
- return ECOSYSTEM_APPS.filter((app) => {
- const matchesCategory =
- isAllActive || activeCategories.some((c) => app.categories.includes(c));
- const matchesSearch =
- !search ||
- app.name.toLowerCase().includes(search.toLowerCase()) ||
- app.description.toLowerCase().includes(search.toLowerCase());
- return matchesCategory && matchesSearch;
- });
- }, [activeCategories, search, isAllActive]);
-
- const shown = filtered.slice(0, visible);
- const hasMore = visible < filtered.length;
-
- // Infinite scroll with IntersectionObserver on mobile, "View more" button on desktop.
- useEffect(() => {
- const el = sentinelRef.current;
- if (!el || !hasMore) return;
-
- const observer = new IntersectionObserver(
- ([entry]) => {
- if (entry.isIntersecting) loadMore();
- },
- { rootMargin: "200px" }
- );
- observer.observe(el);
- return () => observer.disconnect();
- }, [hasMore, loadMore]);
-
- return (
-
-
-
-
-
- Submit App
-
- }
- />
-
- {/* Filter bar */}
-
-
- handleCategoryToggle("All")}
- />
- 0}
- onToggle={() => handleCategoryToggle("All")}
- dropdown={{
- items: ECOSYSTEM_CATEGORIES.filter((c) => c !== "All"),
- activeItems: activeCategories,
- onItemToggle: handleCategoryToggle,
- }}
- />
-
-
-
-
-
setSearch(e.target.value)}
- className="w-full rounded-md border border-white/[0.12] bg-white/[0.03] backdrop-blur-sm py-1.5 pl-9 pr-8 text-sm text-white/60 placeholder:text-white/30 transition-colors duration-200 focus:bg-white/[0.05] focus:border-white/20 focus:outline-none sm:w-56 select-none"
- />
-
- {search && (
- setSearch("")}
- className="absolute right-2.5 top-1/2 -translate-y-1/2 cursor-pointer text-white/50 transition-colors hover:text-white/80"
- aria-label="Clear search"
- >
-
-
-
-
- )}
-
-
-
-
- {/* App grid */}
- {shown.length > 0 ? (
-
- {shown.map((app, index) => {
- const inButtonBatch =
- buttonBatch >= 0 &&
- index >= buttonBatch &&
- index < buttonBatch + BATCH_SIZE;
- return (
-
-
-
- {app.logo ? (
-
- ) : (
-
- {app.name.charAt(0)}
-
- )}
-
-
-
-
- {app.name}
-
-
- {app.hostname}
-
-
- {app.description}
-
-
- {app.categories.map((cat) => (
-
- {cat}
-
- ))}
-
-
- );
- })}
-
- ) : (
-
-
- No results found{search ? ` for \u201c${search}\u201d` : ""}
-
-
- Try searching for another term.
- {
- setSearch("");
- setActiveCategories([]);
- }}
- className="cursor-pointer rounded border border-white/10 px-3 py-1 text-xs font-medium text-white/50 transition-colors hover:border-white/20 hover:text-white/80"
- >
- Clear search
-
-
-
-
- )}
-
- {hasMore && (
-
- {/* Infinite scroll on mobile, "View more" button on desktop */}
-
-
- View more
-
-
- )}
-
-
-
- );
-}
+import EcosystemListingClient, {
+ type EcosystemListingApp,
+} from "@/components/ecosystem/EcosystemListingClient";
+import { getAllApps, getEcosystemCategories } from "@/lib/ecosystem";
export default function EcosystemPage() {
- return (
-
-
-
- );
+ const apps: EcosystemListingApp[] = getAllApps().map((app) => ({
+ slug: app.slug,
+ name: app.name,
+ url: app.url,
+ hostname: app.hostname,
+ description: app.description,
+ categories: app.categories,
+ logo: app.logo,
+ logoBg: app.logoBg,
+ }));
+ const categories = getEcosystemCategories();
+
+ return ;
}
diff --git a/app/ecosystem/submit/page.tsx b/app/ecosystem/submit/page.tsx
index 7e15be4..291fce2 100644
--- a/app/ecosystem/submit/page.tsx
+++ b/app/ecosystem/submit/page.tsx
@@ -1,170 +1,7 @@
-"use client";
-
-import { useState } from "react";
-import Link from "next/link";
-import { ECOSYSTEM_CATEGORIES } from "@/lib/ecosystem-data";
-import PageHero from "@/components/ui/PageHero";
-import Container from "@/components/ui/Container";
-import Button from "@/components/ui/Button";
-import FilterPill from "@/components/ui/FilterPill";
-
-const CATEGORIES = ECOSYSTEM_CATEGORIES.filter((c) => c !== "All");
+import SubmitAppForm from "@/components/ecosystem/SubmitAppForm";
+import { getEcosystemCategories } from "@/lib/ecosystem";
export default function SubmitAppPage() {
- const [name, setName] = useState("");
- const [url, setUrl] = useState("");
- const [description, setDescription] = useState("");
- const [categories, setCategories] = useState([]);
- const [email, setEmail] = useState("");
-
- const handleSubmit = (e: React.SyntheticEvent) => {
- e.preventDefault();
-
- const issueUrl = new URL("https://github.com/livepeer/naap/issues/new");
- issueUrl.searchParams.set("template", "ecosystem-submission.yml");
- issueUrl.searchParams.set("title", `Add ${name}`);
- issueUrl.searchParams.set("app-name", name);
- issueUrl.searchParams.set("website", url);
- issueUrl.searchParams.set("description", description);
- issueUrl.searchParams.set("categories", categories.join(", "));
- issueUrl.searchParams.set("contact", email);
-
- window.open(issueUrl.toString(), "_blank");
- };
-
- const isValid = name && url && description && categories.length > 0 && email;
-
- return (
-
-
-
-
- Ecosystem
-
- ›
- Submit
-
-
- Submit your app
-
-
- Built something on Livepeer? Submit your app to be featured in the
- ecosystem directory. This will open a GitHub issue for review.
-
-
-
-
-
- );
+ const categories = getEcosystemCategories().filter((c) => c !== "All");
+ return ;
}
diff --git a/components/ecosystem/EcosystemDetail.tsx b/components/ecosystem/EcosystemDetail.tsx
new file mode 100644
index 0000000..392ffb8
--- /dev/null
+++ b/components/ecosystem/EcosystemDetail.tsx
@@ -0,0 +1,365 @@
+import Link from "next/link";
+import { ArrowUpRight } from "lucide-react";
+import Container from "@/components/ui/Container";
+import Button from "@/components/ui/Button";
+import Badge from "@/components/ui/Badge";
+import PageHero from "@/components/ui/PageHero";
+import type { EcosystemApp } from "@/lib/ecosystem";
+
+const XIcon = ({ className }: { className?: string }) => (
+
+
+
+);
+
+const GitHubIcon = ({ className }: { className?: string }) => (
+
+
+
+);
+
+const MailIcon = ({ className }: { className?: string }) => (
+
+
+
+);
+
+function ConnectButton({
+ href,
+ label,
+ children,
+}: {
+ href: string;
+ label: string;
+ children: React.ReactNode;
+}) {
+ return (
+
+ {children}
+
+ );
+}
+
+type Props = {
+ app: EcosystemApp;
+ html: string;
+};
+
+const EM_DASH = "—";
+
+function isEmail(value: string): boolean {
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
+}
+
+function handleFromUrl(url: string): string {
+ try {
+ const segments = new URL(url).pathname.split("/").filter(Boolean);
+ return segments[0] ?? url;
+ } catch {
+ return url;
+ }
+}
+
+function ExternalLink({
+ href,
+ children,
+}: {
+ href: string;
+ children: React.ReactNode;
+}) {
+ return (
+
+ {children}
+
+
+ );
+}
+
+function MetaRow({
+ label,
+ children,
+}: {
+ label: string;
+ children: React.ReactNode;
+}) {
+ return (
+
+
+ {label}
+
+
+ {children}
+
+
+ );
+}
+
+function MetaGroup({
+ title,
+ children,
+ isFirst = false,
+}: {
+ title: string;
+ children: React.ReactNode;
+ isFirst?: boolean;
+}) {
+ return (
+
+
+ {title}
+
+
{children}
+
+ );
+}
+
+function LinkOrDash({
+ value,
+ display,
+ showPath = false,
+}: {
+ value: string | undefined;
+ display?: string;
+ showPath?: boolean;
+}) {
+ if (!value) return {EM_DASH} ;
+ if (isEmail(value)) {
+ return (
+
+ {display ?? value}
+
+ );
+ }
+ let host = display;
+ if (!host) {
+ try {
+ const u = new URL(value);
+ const cleanHost = u.hostname.replace(/^www\./, "");
+ const cleanPath = u.pathname.replace(/\/$/, "");
+ // Short-link hosts (Discord invites, Telegram, etc.) are unrecognizable
+ // without their path, so always include it.
+ const isShortLink = /^(discord\.gg|t\.me|bit\.ly|tinyurl\.com)$/i.test(
+ cleanHost
+ );
+ host =
+ showPath || isShortLink ? `${cleanHost}${cleanPath}` : cleanHost;
+ } catch {
+ host = value;
+ }
+ }
+ return {host} ;
+}
+
+export default function EcosystemDetail({ app, html }: Props) {
+ return (
+ <>
+
+
+ {/* Breadcrumb */}
+
+
+ Ecosystem
+
+ /
+ {app.name}
+
+
+ {/* Header */}
+
+
+ {app.logo ? (
+
+ ) : (
+
+ {app.name.charAt(0)}
+
+ )}
+
+
+
+ {app.name}
+
+
+ {app.description}
+
+
+
+ Visit site
+
+
+ {app.twitter && (
+
+
+
+ )}
+ {app.github && (
+
+
+
+ )}
+ {app.contact &&
+ (isEmail(app.contact) ? (
+
+
+
+ ) : (
+
+
+
+ ))}
+
+
+
+
+
+
+ {/* Two-column body lives outside PageHero so the sidebar can use
+ position: sticky — PageHero's overflow-hidden would otherwise
+ break sticky positioning for any descendant. */}
+
+
+
+ {/* Main column */}
+
+
+ {/* Sidebar */}
+
+
+
+
+ {app.madeBy ? (
+ {app.madeBy}
+ ) : (
+ {EM_DASH}
+ )}
+
+
+ {app.categories.length > 0 ? (
+
+ {app.categories.map((cat) => (
+
+ {cat}
+
+ ))}
+
+ ) : (
+ {EM_DASH}
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ← Back to ecosystem
+
+
+
+ >
+ );
+}
diff --git a/components/ecosystem/EcosystemListingClient.tsx b/components/ecosystem/EcosystemListingClient.tsx
new file mode 100644
index 0000000..e2ce2f3
--- /dev/null
+++ b/components/ecosystem/EcosystemListingClient.tsx
@@ -0,0 +1,338 @@
+"use client";
+
+import {
+ Suspense,
+ useState,
+ useMemo,
+ useEffect,
+ useRef,
+ useCallback,
+} from "react";
+import Link from "next/link";
+import { Search, Plus, ArrowUpRight } from "lucide-react";
+import { motion, AnimatePresence } from "framer-motion";
+import { useSearchParams, useRouter, usePathname } from "next/navigation";
+import PageHero from "@/components/ui/PageHero";
+import Container from "@/components/ui/Container";
+import SectionHeader from "@/components/ui/SectionHeader";
+import Badge from "@/components/ui/Badge";
+import Button from "@/components/ui/Button";
+import FilterPill from "@/components/ui/FilterPill";
+
+const BATCH_SIZE = 12;
+
+export type EcosystemListingApp = {
+ slug: string;
+ name: string;
+ url: string;
+ hostname: string;
+ description: string;
+ categories: string[];
+ logo?: string;
+ logoBg?: string;
+};
+
+type Props = {
+ apps: EcosystemListingApp[];
+ categories: string[];
+};
+
+function EcosystemListingInner({ apps, categories }: Props) {
+ const searchParams = useSearchParams();
+ const router = useRouter();
+ const pathname = usePathname();
+
+ const [activeCategories, setActiveCategories] = useState(() => {
+ const param = searchParams.get("categories");
+ return param ? param.split(",").map(decodeURIComponent) : [];
+ });
+ const [search, setSearch] = useState(() => searchParams.get("q") ?? "");
+ const [visible, setVisible] = useState(BATCH_SIZE);
+ const [buttonBatch, setButtonBatch] = useState(0);
+ const sentinelRef = useRef(null);
+
+ const isAllActive = activeCategories.length === 0;
+ const loadMore = useCallback(() => setVisible((v) => v + BATCH_SIZE), []);
+ const handleButtonLoad = useCallback(() => {
+ setButtonBatch(visible);
+ loadMore();
+ }, [visible, loadMore]);
+
+ /* Sync filter state → URL query params */
+ useEffect(() => {
+ const params = new URLSearchParams();
+ if (activeCategories.length > 0)
+ params.set("categories", activeCategories.join(","));
+ if (search) params.set("q", search);
+
+ const qs = params.toString();
+ const url = qs ? `${pathname}?${qs}` : pathname;
+ router.replace(url, { scroll: false });
+ }, [activeCategories, search, pathname, router]);
+
+ useEffect(() => {
+ setVisible(BATCH_SIZE);
+ }, [activeCategories, search]);
+
+ const handleCategoryToggle = (cat: string) => {
+ if (cat === "All") {
+ setActiveCategories([]);
+ return;
+ }
+ setActiveCategories((prev) =>
+ prev.includes(cat) ? prev.filter((c) => c !== cat) : [...prev, cat]
+ );
+ };
+
+ const filtered = useMemo(() => {
+ return apps.filter((app) => {
+ const matchesCategory =
+ isAllActive || activeCategories.some((c) => app.categories.includes(c));
+ const matchesSearch =
+ !search ||
+ app.name.toLowerCase().includes(search.toLowerCase()) ||
+ app.description.toLowerCase().includes(search.toLowerCase());
+ return matchesCategory && matchesSearch;
+ });
+ }, [apps, activeCategories, search, isAllActive]);
+
+ const shown = filtered.slice(0, visible);
+ const hasMore = visible < filtered.length;
+
+ // Infinite scroll with IntersectionObserver on mobile, "View more" button on desktop.
+ useEffect(() => {
+ const el = sentinelRef.current;
+ if (!el || !hasMore) return;
+
+ const observer = new IntersectionObserver(
+ ([entry]) => {
+ if (entry.isIntersecting) loadMore();
+ },
+ { rootMargin: "200px" }
+ );
+ observer.observe(el);
+ return () => observer.disconnect();
+ }, [hasMore, loadMore]);
+
+ return (
+
+
+
+
+
+ Submit App
+
+ }
+ />
+
+ {/* Filter bar */}
+
+
+ handleCategoryToggle("All")}
+ />
+ 0}
+ onToggle={() => handleCategoryToggle("All")}
+ dropdown={{
+ items: categories.filter((c) => c !== "All"),
+ activeItems: activeCategories,
+ onItemToggle: handleCategoryToggle,
+ }}
+ />
+
+
+
+
+
setSearch(e.target.value)}
+ className="w-full rounded-md border border-white/[0.12] bg-white/[0.03] backdrop-blur-sm py-1.5 pl-9 pr-8 text-sm text-white/60 placeholder:text-white/30 transition-colors duration-200 focus:bg-white/[0.05] focus:border-white/20 focus:outline-none sm:w-56 select-none"
+ />
+
+ {search && (
+ setSearch("")}
+ className="absolute right-2.5 top-1/2 -translate-y-1/2 cursor-pointer text-white/50 transition-colors hover:text-white/80"
+ aria-label="Clear search"
+ >
+
+
+
+
+ )}
+
+
+
+
+ {/* App grid */}
+ {shown.length > 0 ? (
+
+ {shown.map((app, index) => {
+ const inButtonBatch =
+ buttonBatch >= 0 &&
+ index >= buttonBatch &&
+ index < buttonBatch + BATCH_SIZE;
+ return (
+
+
+
+
+ {app.logo ? (
+
+ ) : (
+
+ {app.name.charAt(0)}
+
+ )}
+
+
+
+
+ {app.name}
+
+
+ {app.hostname}
+
+
+ {app.description}
+
+
+ {app.categories.map((cat) => (
+
+ {cat}
+
+ ))}
+
+
+
+ );
+ })}
+
+ ) : (
+
+
+ No results found{search ? ` for \u201c${search}\u201d` : ""}
+
+
+ Try searching for another term.
+ {
+ setSearch("");
+ setActiveCategories([]);
+ }}
+ className="cursor-pointer rounded border border-white/10 px-3 py-1 text-xs font-medium text-white/50 transition-colors hover:border-white/20 hover:text-white/80"
+ >
+ Clear search
+
+
+
+
+ )}
+
+ {hasMore && (
+
+ {/* Infinite scroll on mobile, "View more" button on desktop */}
+
+
+ View more
+
+
+ )}
+
+
+
+ );
+}
+
+export default function EcosystemListingClient(props: Props) {
+ return (
+
+
+
+ );
+}
diff --git a/components/ecosystem/SubmitAppForm.tsx b/components/ecosystem/SubmitAppForm.tsx
new file mode 100644
index 0000000..9c8a9e9
--- /dev/null
+++ b/components/ecosystem/SubmitAppForm.tsx
@@ -0,0 +1,171 @@
+"use client";
+
+import { useState } from "react";
+import Link from "next/link";
+import PageHero from "@/components/ui/PageHero";
+import Container from "@/components/ui/Container";
+import Button from "@/components/ui/Button";
+import FilterPill from "@/components/ui/FilterPill";
+
+type Props = {
+ categories: string[];
+};
+
+export default function SubmitAppForm({ categories: allCategories }: Props) {
+ const [name, setName] = useState("");
+ const [url, setUrl] = useState("");
+ const [description, setDescription] = useState("");
+ const [categories, setCategories] = useState([]);
+ const [email, setEmail] = useState("");
+
+ const handleSubmit = (e: React.SyntheticEvent) => {
+ e.preventDefault();
+
+ const issueUrl = new URL("https://github.com/livepeer/naap/issues/new");
+ issueUrl.searchParams.set("template", "ecosystem-submission.yml");
+ issueUrl.searchParams.set("title", `Add ${name}`);
+ issueUrl.searchParams.set("app-name", name);
+ issueUrl.searchParams.set("website", url);
+ issueUrl.searchParams.set("description", description);
+ issueUrl.searchParams.set("categories", categories.join(", "));
+ issueUrl.searchParams.set("contact", email);
+
+ window.open(issueUrl.toString(), "_blank");
+ };
+
+ const isValid = name && url && description && categories.length > 0 && email;
+
+ return (
+
+
+
+
+ Ecosystem
+
+ ›
+ Submit
+
+
+ Submit your app
+
+
+ Built something on Livepeer? Submit your app to be featured in the
+ ecosystem directory. This will open a GitHub issue for review.
+
+
+
+
+
+ App name
+
+ setName(e.target.value)}
+ placeholder="My Livepeer App"
+ className="w-full rounded-lg border border-dark-border bg-dark-card px-4 py-3 text-sm text-white placeholder:text-white/20 focus:border-white/20 focus:outline-none"
+ />
+
+
+
+
+ Website URL
+
+ setUrl(e.target.value)}
+ placeholder="https://myapp.com"
+ className="w-full rounded-lg border border-dark-border bg-dark-card px-4 py-3 text-sm text-white placeholder:text-white/20 focus:border-white/20 focus:outline-none"
+ />
+
+
+
+
+ Description
+
+ setDescription(e.target.value)}
+ placeholder="A short description of what your app does and how it uses Livepeer..."
+ rows={4}
+ className="w-full resize-none rounded-lg border border-dark-border bg-dark-card px-4 py-3 text-sm text-white placeholder:text-white/20 focus:border-white/20 focus:outline-none"
+ />
+
+
+
+
+ Categories
+
+
+ {allCategories.map((cat) => (
+
+ setCategories((prev) =>
+ prev.includes(cat)
+ ? prev.filter((c) => c !== cat)
+ : [...prev, cat]
+ )
+ }
+ />
+ ))}
+
+
+
+
+
+ Contact email
+
+ setEmail(e.target.value)}
+ placeholder="you@example.com"
+ className="w-full rounded-lg border border-dark-border bg-dark-card px-4 py-3 text-sm text-white placeholder:text-white/20 focus:border-white/20 focus:outline-none"
+ />
+
+
+
+ Submit via GitHub
+
+
+
+
+ );
+}
diff --git a/components/home/BuiltOnLivepeer.tsx b/components/home/BuiltOnLivepeer.tsx
index d2b352a..8533d6e 100644
--- a/components/home/BuiltOnLivepeer.tsx
+++ b/components/home/BuiltOnLivepeer.tsx
@@ -1,5 +1,6 @@
"use client";
+import Link from "next/link";
import { motion } from "framer-motion";
import Container from "@/components/ui/Container";
import SectionHeader from "@/components/ui/SectionHeader";
@@ -628,6 +629,7 @@ function EmbodyVisual() {
const projects = [
{
+ slug: "daydream",
Visual: DaydreamVisual,
Logo: DaydreamLogo,
description:
@@ -635,6 +637,7 @@ const projects = [
domain: "daydream.live",
},
{
+ slug: "frameworks",
Visual: StudioVisual,
Logo: FrameworksLogo,
description:
@@ -642,6 +645,7 @@ const projects = [
domain: "frameworks.network",
},
{
+ slug: "streamplace",
Visual: StreamplaceVisual,
Logo: StreamplaceLogo,
description:
@@ -649,6 +653,7 @@ const projects = [
domain: "stream.place",
},
{
+ slug: "embody",
Visual: EmbodyVisual,
Logo: EmbodyLogo,
description: "Deploy AI avatars that see, speak, and respond in real time.",
@@ -687,41 +692,47 @@ export default function BuiltOnLivepeer() {
transition={{ duration: 0.4 }}
className="lg:row-span-3"
>
-
+
-
+
{featured.description}
{featured.domain}
-
+
{/* 3 smaller cards stacked on the right */}
- {rest.map((project, i) => (
+ {rest.map((project) => (
-
+
-
+
{project.description}
{project.domain}
-
+
))}
diff --git a/components/home/StartBuilding.tsx b/components/home/StartBuilding.tsx
index faa43e6..9838d4b 100644
--- a/components/home/StartBuilding.tsx
+++ b/components/home/StartBuilding.tsx
@@ -1,6 +1,7 @@
"use client";
import { useRef, useEffect, useCallback, useState } from "react";
+import Link from "next/link";
import { motion } from "framer-motion";
import Container from "@/components/ui/Container";
import { EXTERNAL_LINKS } from "@/lib/constants";
@@ -290,10 +291,8 @@ export default function StartBuilding() {
generative video, style transfer, and more.
Direct API access to the Livepeer network is coming.{" "}
diff --git a/content/ecosystem/blueclaw.md b/content/ecosystem/blueclaw.md
new file mode 100644
index 0000000..d650125
--- /dev/null
+++ b/content/ecosystem/blueclaw.md
@@ -0,0 +1,44 @@
+---
+name: Blue Claw
+url: https://blueclaw.network
+description: OpenAI-compatible inference for autonomous agents — flat-rate pricing, no rate limits, no token caps.
+categories:
+ - Agents
+ - API
+logo: blueclaw.webp
+order: 5
+madeBy: OpenClaw
+twitter:
+github: https://github.com/blueclaw-network
+contact:
+docs:
+support: https://discord.gg/blueclaw
+terms:
+privacy: https://blueclaw.network/privacy.html
+---
+
+## Overview
+
+Blue Claw is an inference network built specifically for autonomous agents. It exposes an **OpenAI-compatible API** with no rate limits, no token caps, and flat-rate billing — built by agent developers who got tired of unpredictable backpressure from shared inference endpoints.
+
+If your agents need to think continuously without metering you out, Blue Claw is designed to be a drop-in replacement that doesn't punish you for usage.
+
+## What you can build
+
+- **Always-on autonomous agents** — long-running loops that don't get throttled mid-task
+- **Multi-agent systems** — swarms of agents calling inference in parallel without quota juggling
+- **Agent backends with predictable cost** — flat monthly pricing instead of per-token billing
+- **Migrations from OpenAI** — point your existing OpenAI SDK code at Blue Claw with a base URL change
+
+## Developer surface
+
+- **OpenAI-compatible endpoints**
+ - `/v1/chat/completions` — chat
+ - `/v1/embeddings` — text embeddings
+ - `/v1/images/generations` — image generation
+- **Drop-in SDK support** — works with existing Python, JavaScript, and `curl` integrations
+- **Distributed GPU backend** — global redundancy across the inference network
+
+## Pricing
+
+Free during beta. Post-launch: flat-rate $10–$15 per month, no per-token charges.
diff --git a/content/ecosystem/daydream.md b/content/ecosystem/daydream.md
new file mode 100644
index 0000000..d9cfbdd
--- /dev/null
+++ b/content/ecosystem/daydream.md
@@ -0,0 +1,43 @@
+---
+name: Daydream
+url: https://daydream.live
+description: Open-source, local-first platform for running real-time interactive generative AI video pipelines.
+categories:
+ - AI Video
+ - Generative
+ - API
+logo: daydream.svg
+order: 1
+madeBy: Livepeer Inc
+twitter: https://x.com/DaydreamLiveAI
+github: https://github.com/daydreamlive/scope
+contact: hello@daydream.live
+docs: https://docs.daydream.live/
+support:
+terms: https://daydream.live/terms
+privacy: https://daydream.live/privacy-policy
+---
+
+## Overview
+
+Daydream is an open-source, local-first platform for real-time generative AI video. Its flagship project, **Daydream Scope**, runs autoregressive video diffusion models on your own GPU — or in the cloud — and exposes a node-based workflow for composing live, interactive pipelines from text, video, and live camera inputs.
+
+It's built for developers and creative technologists who want continuous, real-time AI video instead of asynchronous batch generation.
+
+## What you can build
+
+- **Real-time generative video pipelines** — compose text-to-video, video-to-video, and live camera workflows from a node-based graph
+- **Interactive installations and live visuals** — drive output reactively with audio, MIDI, OSC, or DMX
+- **Custom nodes and models** — extend Scope with your own pipelines, LoRAs, and inference logic in Python
+- **Tooling integrations** — share output to TouchDesigner, OBS, Resolume, Unity, Ableton Live, and other creative tools via Spout, Syphon, and NDI
+
+## Developer surface
+
+- **Self-hosted runtime** — Scope runs locally as a server on `localhost:8000` with a WebRTC-based API for programmatic, low-latency control
+- **Bundled diffusion models** — ships with StreamDiffusion V2, LongLive, Krea Realtime, RewardForcing, and MemFlow
+- **Open-source Python codebase** — fork it, build custom nodes, contribute pipelines back upstream
+- **Cloud inference** — run pipelines on remote GPUs when local hardware isn't available
+
+## Powered by Livepeer
+
+Daydream's cloud inference is powered by Livepeer's GPU network. Workloads are routed to independent orchestrators, giving builders elastic, cost-efficient real-time video inference without a centralized provider.
diff --git a/content/ecosystem/embody.md b/content/ecosystem/embody.md
new file mode 100644
index 0000000..5979699
--- /dev/null
+++ b/content/ecosystem/embody.md
@@ -0,0 +1,38 @@
+---
+name: Embody
+url: https://embody.zone
+description: Open-source network for embodied AI avatars — real-time tutoring, telepresence, and on-demand branded content.
+categories:
+ - AI Video
+ - Agents
+logo: embody.svg
+order: 4
+madeBy: DeFine + Dane
+twitter:
+github:
+contact:
+docs:
+support:
+terms:
+privacy:
+---
+
+## Overview
+
+Embody is an open-source network for embodied AI avatars. It provides real-time agent avatars for tutoring, telepresence, and branded content — built so developers can drop expressive, low-latency characters into learning platforms, agent products, and live experiences without building the underlying video pipeline themselves.
+
+## What you can build
+
+- **Real-time AI tutors and tutoring platforms** — embodied agents that see, listen, and respond in live conversations
+- **Telepresence experiences** — agent avatars that represent humans or AI personalities in real time
+- **Branded on-demand avatars** — personalized characters for marketing, customer experience, and education
+- **Custom agent integrations** — drop avatars into your own product instead of building the rendering and inference stack from scratch
+
+## Developer surface
+
+- **Multiple rendering backends** — implementation packages for **Unreal Engine**, **Live2D**, and **Three.js**
+- **Open-source pipeline** — extend or fork the core network for your own avatar workloads
+
+## Powered by Livepeer
+
+Embody runs on Livepeer's GPU infrastructure as a Special Purpose Entity, using the Agent SPE pipeline to generate real-time agent avatars. Workloads are routed across Livepeer orchestrators, giving builders elastic real-time inference for character rendering and agent video without standing up their own GPU fleet.
diff --git a/content/ecosystem/frameworks.md b/content/ecosystem/frameworks.md
new file mode 100644
index 0000000..8cbe165
--- /dev/null
+++ b/content/ecosystem/frameworks.md
@@ -0,0 +1,45 @@
+---
+name: Frameworks
+url: https://frameworks.network
+description: Open streaming stack for live video — full media pipeline from ingest to delivery, self-hosted or cloud.
+categories:
+ - Streaming
+ - Self-hosted
+ - API
+logo: frameworks.svg
+order: 2
+madeBy: DDVTech / MistServer team
+twitter: https://x.com/GetFrames
+github: https://github.com/Livepeer-FrameWorks
+contact: developers@mistserver.org
+docs:
+support:
+terms:
+privacy:
+---
+
+## Overview
+
+Frameworks is an open streaming stack for live video, built on **MistServer** and the **Livepeer** transcoding network. It handles the full media pipeline — ingest, transcoding, processing, storage, and delivery — and ships as a self-hosted platform, a managed "Sovereign Video Cloud," or a hybrid of the two.
+
+It's built for teams that want production-grade live video infrastructure without surrendering ownership of their data or pipeline.
+
+## What you can build
+
+- **Live streaming products** — ingest via RTMP, SRT, or WHIP and deliver to any device or format
+- **VOD workflows** — recordings, clipping, and on-demand playback alongside live
+- **Live AI experiences** — pipe streams through Livepeer's AI inference for real-time transformations
+- **Custom video apps** — embed playback with the Frameworks player packages (React, Svelte, Web Components)
+- **Browser-based production tools** — use StreamCrafter for in-browser stream production
+
+## Developer surface
+
+- **GraphQL and gRPC APIs** — programmatic control over streams, recordings, and accounts
+- **Player SDKs** — drop-in React, Svelte, and Web Component packages
+- **CLI** — manage streams and analytics from the terminal (installable via Homebrew)
+- **Edge + control plane architecture** — edge nodes handle media operations while control and analytics services scale independently
+- **Open source** — the entire monorepo, gitops manifests, and MistServer fork are public on [GitHub](https://github.com/Livepeer-FrameWorks)
+
+## Powered by Livepeer
+
+Frameworks uses Livepeer's decentralized network for transcoding and AI inference, wrapping it in the operational layer that production video applications need. The result is a path to running real, sovereign video infrastructure on top of community-operated GPUs — without rebuilding the pipeline yourself.
diff --git a/content/ecosystem/higher.md b/content/ecosystem/higher.md
new file mode 100644
index 0000000..1baf08c
--- /dev/null
+++ b/content/ecosystem/higher.md
@@ -0,0 +1,30 @@
+---
+name: Higher
+url: https://higher.zip
+description: An onchain creative collective with a manifesto, missions, a shared treasury, and a Farcaster-native experience.
+categories:
+ - Streaming
+ - Community
+ - Decentralized
+logo: higher-zip.svg
+order: 9
+madeBy:
+twitter:
+github:
+contact:
+docs:
+support:
+terms:
+privacy:
+---
+
+## Overview
+
+Higher is an onchain creative collective. It brings together a manifesto, a "Permapool" treasury, ongoing missions, and a shop into a single Farcaster-native experience — a new model for how creators and collectors rally around a shared cultural idea.
+
+## What's inside
+
+- **Manifesto** — the collective's shared philosophy
+- **Permapool** — a permanent shared creative treasury
+- **Missions** — open community quests and contributions
+- **Shop** — collectible drops and merch tied to the collective
diff --git a/content/ecosystem/livepeer-studio.md b/content/ecosystem/livepeer-studio.md
new file mode 100644
index 0000000..d0997ed
--- /dev/null
+++ b/content/ecosystem/livepeer-studio.md
@@ -0,0 +1,42 @@
+---
+name: Livepeer Studio
+url: https://livepeer.studio
+description: Real-time interactive streaming platform — live video, VOD, and transcoding APIs powered by the Livepeer network.
+categories:
+ - Streaming
+ - API
+logo: livepeer-studio.png
+order: 7
+madeBy: Livepeer Inc
+twitter: https://twitter.com/livepeerstudio
+github: https://github.com/livepeer/studio
+contact: https://livepeer.typeform.com/to/HTuUHdDR
+docs: https://docs.livepeer.org/developers/introduction
+support: https://livepeer.statuspage.io/
+terms: https://livepeer.studio/terms-of-service
+privacy: https://livepeer.studio/privacy-policy
+---
+
+## Overview
+
+Livepeer Studio is a real-time interactive streaming platform built on the Livepeer network. It exposes live video, video-on-demand, and transcoding as developer APIs — an open video stack designed for flexibility, with up to 80% cost savings compared to traditional cloud video providers.
+
+It's the fastest path for teams that want to tap Livepeer's network economics without running orchestration infrastructure themselves.
+
+## What you can build
+
+- **Live streaming products** — low-latency live video over WebRTC and HLS
+- **Video-on-demand** — uploads, playback, AI tools, and content integrity features
+- **Custom transcoding pipelines** — scale across modern codecs including AV1
+- **Real-time interactive video** — apps that need sub-second round-trips, not just broadcast
+
+## Developer surface
+
+- **Live Streaming API** — WebRTC and HLS ingest and playback with low-latency delivery
+- **Video On Demand API** — uploads, transcoding, AI tools, and content integrity
+- **Transcode API** — codec coverage including AV1, scales with your traffic
+- **Open source** — the platform itself is on [GitHub](https://github.com/livepeer/studio), and the API is documented for direct integration
+
+## Powered by Livepeer
+
+Every workload routes through the Livepeer network — independent orchestrators contributing GPU and bandwidth — which is what makes Studio's economics and resiliency possible.
diff --git a/content/ecosystem/nytv.md b/content/ecosystem/nytv.md
new file mode 100644
index 0000000..e5fc7f0
--- /dev/null
+++ b/content/ecosystem/nytv.md
@@ -0,0 +1,22 @@
+---
+name: NYTV
+url: https://nytv.live
+description: Independent 24/7 live television station streaming culture and programming from New York.
+categories:
+ - Streaming
+ - Community
+logo: nytv-live.jpg
+order: 10
+madeBy:
+twitter: https://twitter.com/NYTV_
+github:
+contact:
+docs:
+support:
+terms:
+privacy:
+---
+
+## Overview
+
+NYTV (New York Television) is an independent 24/7 live television station streaming culture and programming from New York City. It's a modern take on broadcast television — continuous, live, and built natively for the internet.
diff --git a/content/ecosystem/spritz.md b/content/ecosystem/spritz.md
new file mode 100644
index 0000000..605a8e9
--- /dev/null
+++ b/content/ecosystem/spritz.md
@@ -0,0 +1,37 @@
+---
+name: Spritz
+url: https://app.spritz.chat
+description: Censorship-resistant Web3 chat with HD video calls, livestreaming, and AI agents. Sign in with passkeys or wallets.
+categories:
+ - Streaming
+ - Community
+ - Decentralized
+logo: spritz.svg
+order: 12
+madeBy: Spritz
+twitter:
+github:
+contact:
+docs:
+support:
+terms:
+privacy:
+---
+
+## Overview
+
+Spritz is a censorship-resistant chat app for Web3. Connect with friends using passkeys or wallets, make HD video calls, go live with livestreaming, create AI agents, and chat freely — all on infrastructure that doesn't depend on a central operator.
+
+It's built for communities that want identity, conversations, and live video without surrendering control to a platform.
+
+## What you can do
+
+- **Chat freely** — censorship-resistant peer-to-peer messaging
+- **HD video calls** — real-time video without a central server
+- **Livestreaming** — go live to your community
+- **AI agents** — create and deploy agents inside your conversations
+- **Sign in your way** — passkeys or any supported wallet
+
+## Multi-chain
+
+Spritz works across **Ethereum**, **Base**, and **Solana**, with native apps on web, iOS, and Android. Free to use.
diff --git a/content/ecosystem/streamplace.md b/content/ecosystem/streamplace.md
new file mode 100644
index 0000000..8200f2d
--- /dev/null
+++ b/content/ecosystem/streamplace.md
@@ -0,0 +1,42 @@
+---
+name: Streamplace
+url: https://stream.place
+description: The video layer for decentralized social networks. Open-source, self-hostable, and built on the AT Protocol.
+categories:
+ - Streaming
+ - Decentralized
+logo: stream-place.png
+order: 3
+madeBy: Streamplace
+twitter:
+github: https://github.com/streamplace/streamplace
+contact: eli@stream.place
+docs:
+support:
+terms:
+privacy:
+---
+
+## Overview
+
+Streamplace is the video layer for decentralized social networks. It's a self-hostable, open-source platform that brings high-quality livestreaming, clips, and video uploads to the **AT Protocol** — the same protocol powering Bluesky — and is designed around user sovereignty and content authenticity.
+
+It powers partner platforms like Skylight Social, a TikTok-style app on the AT Protocol.
+
+## What you can build
+
+- **Live video for AT Protocol social apps** — add streaming, clips, and uploads to any Bluesky-compatible client
+- **Sovereign creator tools** — every video is cryptographically signed by its creator and respects consent preferences, built on the same public-key infrastructure as decentralized social
+- **Federated video experiences** — Streamplace nodes connect to any compatible social network to index and serve video content
+- **Self-hosted streaming infrastructure** — run your own node and own your full pipeline
+
+## Developer surface
+
+- **Single-binary node software** — get a Streamplace node running with one command, no complex configuration or deep video expertise required
+- **Open-source monorepo** — hybrid Go + TypeScript codebase, fully public on [GitHub](https://github.com/streamplace/streamplace)
+- **Native client SDKs** — iOS, Android, and web apps for livestreaming, clips, and uploads
+- **AT Protocol integration** — federate with the rest of the AT Protocol ecosystem out of the box
+
+## Powered by Livepeer
+
+Streamplace transcodes every livestream through the Livepeer network for decentralized, low-cost processing and global delivery. The result is open, sovereign video infrastructure that scales on community-operated GPUs instead of a centralized cloud.
diff --git a/content/ecosystem/thelotradio.md b/content/ecosystem/thelotradio.md
new file mode 100644
index 0000000..0529011
--- /dev/null
+++ b/content/ecosystem/thelotradio.md
@@ -0,0 +1,27 @@
+---
+name: The Lot Radio
+url: https://www.thelotradio.com
+description: Independent 24/7 online radio broadcasting live DJ sets from a shipping container in Brooklyn.
+categories:
+ - Streaming
+ - Music
+logo: thelotradio.svg
+logoBg: "#FFFFFF"
+order: 6
+madeBy:
+twitter:
+github:
+contact:
+docs:
+support:
+terms:
+privacy:
+---
+
+## Overview
+
+The Lot Radio is an independent 24/7 online radio station broadcasting from a repurposed shipping container on an empty lot in Brooklyn. It streams a continuous mix of DJ-hosted shows — house, techno, jazz, ambient, experimental electronic, drum & bass, hip-hop, and more — from New York's underground music scene to a global audience.
+
+## Programming
+
+Notable recurring shows include Love Injection, Summer School Radio, and Sorry Records, alongside a rotating schedule of local and international DJs and collectives. Live video and audio run continuously around the clock.
diff --git a/content/ecosystem/tribesocial.md b/content/ecosystem/tribesocial.md
new file mode 100644
index 0000000..c384a09
--- /dev/null
+++ b/content/ecosystem/tribesocial.md
@@ -0,0 +1,45 @@
+---
+name: Tribe Social
+url: https://www.tribesocial.io
+description: Custom branded community apps with courses, live calls, and payments — owned by you, not rented.
+categories:
+ - Streaming
+ - Community
+logo: tribesocial.webp
+order: 8
+madeBy: Tribe Social
+twitter: https://twitter.com/TribeSocialApp
+github:
+contact:
+docs:
+support: https://www.tribesocial.io/help-center
+terms:
+privacy: https://www.tribesocial.io/privacy-policy
+---
+
+## Overview
+
+Tribe Social lets creators and operators launch a custom-branded mobile community app — one they own, not rent. It consolidates community, courses, and live content into a single distraction-free app, and the team helps customers go from zero to launched in 30 days or less.
+
+It's built for community builders who have outgrown generic social platforms and want a branded home for their audience without paying platform fees on every transaction.
+
+## What you can build
+
+- **Branded community apps** — your own iOS and Android apps with your name, logo, and rules
+- **Courses and learning experiences** — exclusive learning modules with audio learning optimized for mobile
+- **Live calls and events** — real-time engagement with replays available on demand
+- **Monetized communities** — integrated payments without platform fees
+
+## Features
+
+- **Community feed** — dynamic interaction space
+- **Courses** — exclusive learning modules
+- **Replays** — on-demand access to recorded calls
+- **Events** — unified calendar
+- **Live calls** — real-time engagement
+- **Audio learning** — mobile-optimized content
+- **Push notifications** — keep members engaged
+
+## Integrations
+
+Zoom, ActiveCampaign, Keap / InfusionSoft, Kajabi, Salesforce, and AddEvent.
diff --git a/content/ecosystem/ufo.md b/content/ecosystem/ufo.md
new file mode 100644
index 0000000..e791f32
--- /dev/null
+++ b/content/ecosystem/ufo.md
@@ -0,0 +1,37 @@
+---
+name: UFO
+url: https://ufo.fm
+description: A home for independent culture — radio, editorial, and weekly mixes from contributors around the world.
+categories:
+ - Streaming
+ - Music
+ - Community
+logo: ufo-fm.svg
+order: 11
+madeBy:
+twitter:
+github:
+contact:
+docs:
+support:
+terms:
+privacy:
+---
+
+## Overview
+
+UFO is a home for independent culture. It publishes radio shows, editorial, and weekly mixes from a network of contributors across London, Copenhagen, Amsterdam, Barcelona, and New York — a distributed alternative to algorithmic audio platforms, with sign-in via Farcaster.
+
+## Programming
+
+A rotating schedule of contributor-led shows, including:
+
+- **Acid Test** — music and conversation on Farcaster
+- **co-tone** — ideas exchanged through music, art, and discussion
+- **FERMI** — underground music from Amsterdam and Bali
+- **Stereo** — exploration of music genres
+- **Sunday 7s** — weekly mixes
+
+## Built on open infrastructure
+
+UFO integrates with a stack of open networks and protocols: **Base**, **Ethereum**, **Farcaster**, **Lens**, **Livepeer**, **Neynar**, **Optimism**, **Paragraph**, **Pods**, **Privy**, **XMTP**, and **Zora**. Livepeer powers the live and on-demand video layer.
diff --git a/data/ecosystem.json b/data/ecosystem.json
deleted file mode 100644
index cb704c7..0000000
--- a/data/ecosystem.json
+++ /dev/null
@@ -1,99 +0,0 @@
-[
- {
- "id": "daydream",
- "name": "Daydream",
- "url": "https://daydream.live",
- "description": "Open-source engine for real-time generative video, audio, and live visuals.",
- "categories": ["AI Video", "Generative", "API"],
- "logo": "daydream.svg"
- },
- {
- "id": "frameworks",
- "name": "Frameworks",
- "url": "https://frameworks.network",
- "description": "Sovereign live streaming platform with SaaS, hybrid, and fully self-hosted modes. No cloud lock-in.",
- "categories": ["Streaming", "Self-hosted", "API"],
- "logo": "frameworks.svg"
- },
- {
- "id": "streamplace",
- "name": "Streamplace",
- "url": "https://stream.place",
- "description": "Streaming video infrastructure for decentralized social networks. Built on the AT Protocol powering Bluesky.",
- "categories": ["Streaming", "Decentralized"],
- "logo": "stream-place.png"
- },
- {
- "id": "embody",
- "name": "Embody",
- "url": "https://embody.zone",
- "description": "Embodied AI avatars for real-time tutoring and telepresence.",
- "categories": ["AI Video", "Agents"],
- "logo": "embody.svg"
- },
- {
- "id": "blueclaw",
- "name": "Blue Claw",
- "url": "https://blueclaw.network",
- "description": "Always-on AI inference for autonomous agents with an OpenAI-compatible API. No rate limits, no throttling.",
- "categories": ["AI Video", "Agents", "API"],
- "logo": "blueclaw.webp"
- },
- {
- "id": "thelotradio",
- "name": "The Lot Radio",
- "url": "https://www.thelotradio.com",
- "description": "Independent 24/7 online radio station broadcasting live DJ sets and music programming from Brooklyn, New York.",
- "categories": ["Streaming", "Music"],
- "logo": "thelotradio.svg",
- "logoBg": "#FFFFFF"
- },
- {
- "id": "livepeer-studio",
- "name": "Livepeer Studio",
- "url": "https://livepeer.studio",
- "description": "Developer APIs for live streaming, video-on-demand, and transcoding powered by the Livepeer network.",
- "categories": ["Streaming", "API"],
- "logo": "livepeer-studio.png"
- },
- {
- "id": "tribesocial",
- "name": "Tribe Social",
- "url": "https://www.tribesocial.io",
- "description": "Branded community apps with live video calls, courses, and integrated payments. Launch in 30 days.",
- "categories": ["Streaming", "Community"],
- "logo": "tribesocial.webp"
- },
- {
- "id": "higher",
- "name": "Higher",
- "url": "https://higher.zip",
- "description": "Onchain community collective on Base with live streaming, missions, and a shared creative treasury.",
- "categories": ["Streaming", "Community", "Decentralized"],
- "logo": "higher-zip.svg"
- },
- {
- "id": "nytv",
- "name": "NYTV",
- "url": "https://nytv.live",
- "description": "Independent 24/7 live television station streaming culture and news programming from New York.",
- "categories": ["Streaming", "Community"],
- "logo": "nytv-live.jpg"
- },
- {
- "id": "ufo",
- "name": "UFO",
- "url": "https://ufo.fm",
- "description": "Broadcast platform for independent culture — radio shows, mixes, and long-form conversations from contributors worldwide.",
- "categories": ["Streaming", "Music", "Community"],
- "logo": "ufo-fm.svg"
- },
- {
- "id": "spritz",
- "name": "Spritz",
- "url": "https://app.spritz.chat",
- "description": "Decentralized social platform combining Web3 messaging, AI agents, livestreaming, and peer-to-peer communication.",
- "categories": ["Streaming", "Community", "Decentralized"],
- "logo": "spritz.svg"
- }
-]
diff --git a/lib/ecosystem-data.ts b/lib/ecosystem-data.ts
deleted file mode 100644
index b38fdbb..0000000
--- a/lib/ecosystem-data.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import ecosystemJson from "@/data/ecosystem.json";
-
-export interface EcosystemApp {
- id: string;
- name: string;
- url: string;
- description: string;
- categories: string[];
- logo?: string;
- logoBg?: string;
-}
-
-export const ECOSYSTEM_CATEGORIES = [
- "All",
- ...Array.from(new Set(ecosystemJson.flatMap((app) => app.categories))).sort(),
-];
-
-export const ECOSYSTEM_APPS: (EcosystemApp & { hostname: string })[] =
- ecosystemJson.map((app) => ({
- ...app,
- hostname: new URL(app.url).hostname.replace("www.", ""),
- }));
diff --git a/lib/ecosystem.ts b/lib/ecosystem.ts
new file mode 100644
index 0000000..97abd9a
--- /dev/null
+++ b/lib/ecosystem.ts
@@ -0,0 +1,110 @@
+import fs from "fs";
+import path from "path";
+import matter from "gray-matter";
+import { unified } from "unified";
+import remarkParse from "remark-parse";
+import remarkRehype from "remark-rehype";
+import rehypeStringify from "rehype-stringify";
+
+const ECOSYSTEM_DIR = path.join(process.cwd(), "content/ecosystem");
+
+export type EcosystemApp = {
+ slug: string;
+ name: string;
+ url: string;
+ hostname: string;
+ description: string;
+ categories: string[];
+ logo?: string;
+ logoBg?: string;
+ madeBy?: string;
+ twitter?: string;
+ github?: string;
+ contact?: string;
+ docs?: string;
+ support?: string;
+ terms?: string;
+ privacy?: string;
+ order?: number;
+ content: string;
+};
+
+function normalize(value: unknown): string | undefined {
+ if (typeof value !== "string") return undefined;
+ const trimmed = value.trim();
+ return trimmed.length > 0 ? trimmed : undefined;
+}
+
+export function getAppSlugs(): string[] {
+ return fs
+ .readdirSync(ECOSYSTEM_DIR)
+ .filter((file) => file.endsWith(".md"))
+ .map((file) => file.replace(/\.md$/, ""));
+}
+
+export function getAppBySlug(slug: string): EcosystemApp {
+ const filePath = path.join(ECOSYSTEM_DIR, `${slug}.md`);
+ const fileContents = fs.readFileSync(filePath, "utf8");
+ const { data, content } = matter(fileContents);
+
+ const url: string = data.url ?? "";
+ let hostname = "";
+ try {
+ hostname = new URL(url).hostname.replace(/^www\./, "");
+ } catch {
+ hostname = url;
+ }
+
+ return {
+ slug,
+ name: data.name ?? slug,
+ url,
+ hostname,
+ description: data.description ?? "",
+ categories: Array.isArray(data.categories) ? data.categories : [],
+ logo: normalize(data.logo),
+ logoBg: normalize(data.logoBg),
+ madeBy: normalize(data.madeBy),
+ twitter: normalize(data.twitter),
+ github: normalize(data.github),
+ contact: normalize(data.contact),
+ docs: normalize(data.docs),
+ support: normalize(data.support),
+ terms: normalize(data.terms),
+ privacy: normalize(data.privacy),
+ order: typeof data.order === "number" ? data.order : undefined,
+ content,
+ };
+}
+
+export function getAllApps(): EcosystemApp[] {
+ const slugs = getAppSlugs();
+ const apps = slugs.map((slug) => getAppBySlug(slug));
+ return apps.sort((a, b) => {
+ const ao = a.order ?? Number.MAX_SAFE_INTEGER;
+ const bo = b.order ?? Number.MAX_SAFE_INTEGER;
+ if (ao !== bo) return ao - bo;
+ return a.name.localeCompare(b.name);
+ });
+}
+
+export function getEcosystemCategories(): string[] {
+ const apps = getAllApps();
+ const cats = new Set();
+ for (const app of apps) {
+ for (const c of app.categories) cats.add(c);
+ }
+ return ["All", ...Array.from(cats).sort()];
+}
+
+export async function renderEcosystemMarkdown(
+ content: string
+): Promise {
+ const result = await unified()
+ .use(remarkParse)
+ .use(remarkRehype, { allowDangerousHtml: true })
+ .use(rehypeStringify, { allowDangerousHtml: true })
+ .process(content);
+
+ return result.toString();
+}
From b20ef2ceac14f5f2ae431a0c28a4fbc055bb9a77 Mon Sep 17 00:00:00 2001
From: adamsoffer
Date: Tue, 7 Apr 2026 13:11:16 -0400
Subject: [PATCH 2/6] Resolve pnpm-lock.yaml merge conflict markers
The lockfile shipped with 66 unresolved conflict markers from the dev
container migration commit (6c5ab2a), which prevented installs and broke
lucide-react resolution at build time. Resolved in favor of HEAD to keep
lucide-react@1.6.0 and next@15.5.14, both of which the codebase actually
imports.
Co-Authored-By: Claude Opus 4.6 (1M context)
---
pnpm-lock.yaml | 600 -------------------------------------------------
1 file changed, 600 deletions(-)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 615ace2..0b5c72e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -14,18 +14,12 @@ importers:
gray-matter:
specifier: ^4.0.3
version: 4.0.3
-<<<<<<< HEAD
lucide-react:
specifier: ^1.6.0
version: 1.6.0(react@19.2.4)
next:
specifier: ^15.1.0
version: 15.5.14(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
-=======
- next:
- specifier: ^15.1.0
- version: 15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
react:
specifier: ^19.0.0
version: 19.2.4
@@ -53,7 +47,6 @@ importers:
devDependencies:
'@eslint/eslintrc':
specifier: ^3
-<<<<<<< HEAD
version: 3.3.5
'@tailwindcss/postcss':
specifier: ^4.0.0
@@ -85,33 +78,6 @@ importers:
tailwindcss:
specifier: ^4.0.0
version: 4.2.2
-=======
- version: 3.3.3
- '@tailwindcss/postcss':
- specifier: ^4.0.0
- version: 4.1.18
- '@types/node':
- specifier: ^22.0.0
- version: 22.19.10
- '@types/react':
- specifier: ^19.0.0
- version: 19.2.13
- '@types/react-dom':
- specifier: ^19.0.0
- version: 19.2.3(@types/react@19.2.13)
- eslint:
- specifier: ^9
- version: 9.39.2(jiti@2.6.1)
- eslint-config-next:
- specifier: ^15.1.0
- version: 15.5.12(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
- postcss:
- specifier: ^8.5.0
- version: 8.5.6
- tailwindcss:
- specifier: ^4.0.0
- version: 4.1.18
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
typescript:
specifier: ^5.7.0
version: 5.9.3
@@ -153,21 +119,12 @@ packages:
resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-<<<<<<< HEAD
'@eslint/eslintrc@3.3.5':
resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/js@9.39.4':
resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==}
-=======
- '@eslint/eslintrc@3.3.3':
- resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
- '@eslint/js@9.39.2':
- resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/object-schema@2.1.7':
@@ -366,7 +323,6 @@ packages:
'@napi-rs/wasm-runtime@0.2.12':
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
-<<<<<<< HEAD
'@next/env@15.5.14':
resolution: {integrity: sha512-aXeirLYuASxEgi4X4WhfXsShCFxWDfNn/8ZeC5YXAS2BB4A8FJi1kwwGL6nvMVboE7fZCzmJPNdMvVHc8JpaiA==}
@@ -375,97 +331,52 @@ packages:
'@next/swc-darwin-arm64@15.5.14':
resolution: {integrity: sha512-Y9K6SPzobnZvrRDPO2s0grgzC+Egf0CqfbdvYmQVaztV890zicw8Z8+4Vqw8oPck8r1TjUHxVh8299Cg4TrxXg==}
-=======
- '@next/env@15.5.12':
- resolution: {integrity: sha512-pUvdJN1on574wQHjaBfNGDt9Mz5utDSZFsIIQkMzPgNS8ZvT4H2mwOrOIClwsQOb6EGx5M76/CZr6G8i6pSpLg==}
-
- '@next/eslint-plugin-next@15.5.12':
- resolution: {integrity: sha512-+ZRSDFTv4aC96aMb5E41rMjysx8ApkryevnvEYZvPZO52KvkqP5rNExLUXJFr9P4s0f3oqNQR6vopCZsPWKDcQ==}
-
- '@next/swc-darwin-arm64@15.5.12':
- resolution: {integrity: sha512-RnRjBtH8S8eXCpUNkQ+543DUc7ys8y15VxmFU9HRqlo9BG3CcBUiwNtF8SNoi2xvGCVJq1vl2yYq+3oISBS0Zg==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
-<<<<<<< HEAD
'@next/swc-darwin-x64@15.5.14':
resolution: {integrity: sha512-aNnkSMjSFRTOmkd7qoNI2/rETQm/vKD6c/Ac9BZGa9CtoOzy3c2njgz7LvebQJ8iPxdeTuGnAjagyis8a9ifBw==}
-=======
- '@next/swc-darwin-x64@15.5.12':
- resolution: {integrity: sha512-nqa9/7iQlboF1EFtNhWxQA0rQstmYRSBGxSM6g3GxvxHxcoeqVXfGNr9stJOme674m2V7r4E3+jEhhGvSQhJRA==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
-<<<<<<< HEAD
'@next/swc-linux-arm64-gnu@15.5.14':
resolution: {integrity: sha512-tjlpia+yStPRS//6sdmlVwuO1Rioern4u2onafa5n+h2hCS9MAvMXqpVbSrjgiEOoCs0nJy7oPOmWgtRRNSM5Q==}
-=======
- '@next/swc-linux-arm64-gnu@15.5.12':
- resolution: {integrity: sha512-dCzAjqhDHwmoB2M4eYfVKqXs99QdQxNQVpftvP1eGVppamXh/OkDAwV737Zr0KPXEqRUMN4uCjh6mjO+XtF3Mw==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
-<<<<<<< HEAD
'@next/swc-linux-arm64-musl@15.5.14':
resolution: {integrity: sha512-8B8cngBaLadl5lbDRdxGCP1Lef8ipD6KlxS3v0ElDAGil6lafrAM3B258p1KJOglInCVFUjk751IXMr2ixeQOQ==}
-=======
- '@next/swc-linux-arm64-musl@15.5.12':
- resolution: {integrity: sha512-+fpGWvQiITgf7PUtbWY1H7qUSnBZsPPLyyq03QuAKpVoTy/QUx1JptEDTQMVvQhvizCEuNLEeghrQUyXQOekuw==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
-<<<<<<< HEAD
'@next/swc-linux-x64-gnu@15.5.14':
resolution: {integrity: sha512-bAS6tIAg8u4Gn3Nz7fCPpSoKAexEt2d5vn1mzokcqdqyov6ZJ6gu6GdF9l8ORFrBuRHgv3go/RfzYz5BkZ6YSQ==}
-=======
- '@next/swc-linux-x64-gnu@15.5.12':
- resolution: {integrity: sha512-jSLvgdRRL/hrFAPqEjJf1fFguC719kmcptjNVDJl26BnJIpjL3KH5h6mzR4mAweociLQaqvt4UyzfbFjgAdDcw==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
-<<<<<<< HEAD
'@next/swc-linux-x64-musl@15.5.14':
resolution: {integrity: sha512-mMxv/FcrT7Gfaq4tsR22l17oKWXZmH/lVqcvjX0kfp5I0lKodHYLICKPoX1KRnnE+ci6oIUdriUhuA3rBCDiSw==}
-=======
- '@next/swc-linux-x64-musl@15.5.12':
- resolution: {integrity: sha512-/uaF0WfmYqQgLfPmN6BvULwxY0dufI2mlN2JbOKqqceZh1G4hjREyi7pg03zjfyS6eqNemHAZPSoP84x17vo6w==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
-<<<<<<< HEAD
'@next/swc-win32-arm64-msvc@15.5.14':
resolution: {integrity: sha512-OTmiBlYThppnvnsqx0rBqjDRemlmIeZ8/o4zI7veaXoeO1PVHoyj2lfTfXTiiGjCyRDhA10y4h6ZvZvBiynr2g==}
-=======
- '@next/swc-win32-arm64-msvc@15.5.12':
- resolution: {integrity: sha512-xhsL1OvQSfGmlL5RbOmU+FV120urrgFpYLq+6U8C6KIym32gZT6XF/SDE92jKzzlPWskkbjOKCpqk5m4i8PEfg==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
-<<<<<<< HEAD
'@next/swc-win32-x64-msvc@15.5.14':
resolution: {integrity: sha512-+W7eFf3RS7m4G6tppVTOSyP9Y6FsJXfOuKzav1qKniiFm3KFByQfPEcouHdjlZmysl4zJGuGLQ/M9XyVeyeNEg==}
-=======
- '@next/swc-win32-x64-msvc@15.5.12':
- resolution: {integrity: sha512-Z1Dh6lhFkxvBDH1FoW6OU/L6prYwPSlwjLiZkExIAh8fbP6iI/M7iGTQAJPYJ9YFlWobCZ1PHbchFhFYb2ADkw==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -495,7 +406,6 @@ packages:
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
-<<<<<<< HEAD
'@tailwindcss/node@4.2.2':
resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==}
@@ -532,94 +442,33 @@ packages:
'@tailwindcss/oxide-linux-arm64-gnu@4.2.2':
resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==}
engines: {node: '>= 20'}
-=======
- '@tailwindcss/node@4.1.18':
- resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==}
-
- '@tailwindcss/oxide-android-arm64@4.1.18':
- resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==}
- engines: {node: '>= 10'}
- cpu: [arm64]
- os: [android]
-
- '@tailwindcss/oxide-darwin-arm64@4.1.18':
- resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==}
- engines: {node: '>= 10'}
- cpu: [arm64]
- os: [darwin]
-
- '@tailwindcss/oxide-darwin-x64@4.1.18':
- resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==}
- engines: {node: '>= 10'}
- cpu: [x64]
- os: [darwin]
-
- '@tailwindcss/oxide-freebsd-x64@4.1.18':
- resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==}
- engines: {node: '>= 10'}
- cpu: [x64]
- os: [freebsd]
-
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18':
- resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==}
- engines: {node: '>= 10'}
- cpu: [arm]
- os: [linux]
-
- '@tailwindcss/oxide-linux-arm64-gnu@4.1.18':
- resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==}
- engines: {node: '>= 10'}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
cpu: [arm64]
os: [linux]
libc: [glibc]
-<<<<<<< HEAD
'@tailwindcss/oxide-linux-arm64-musl@4.2.2':
resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==}
engines: {node: '>= 20'}
-=======
- '@tailwindcss/oxide-linux-arm64-musl@4.1.18':
- resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==}
- engines: {node: '>= 10'}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
cpu: [arm64]
os: [linux]
libc: [musl]
-<<<<<<< HEAD
'@tailwindcss/oxide-linux-x64-gnu@4.2.2':
resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==}
engines: {node: '>= 20'}
-=======
- '@tailwindcss/oxide-linux-x64-gnu@4.1.18':
- resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==}
- engines: {node: '>= 10'}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
cpu: [x64]
os: [linux]
libc: [glibc]
-<<<<<<< HEAD
'@tailwindcss/oxide-linux-x64-musl@4.2.2':
resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==}
engines: {node: '>= 20'}
-=======
- '@tailwindcss/oxide-linux-x64-musl@4.1.18':
- resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==}
- engines: {node: '>= 10'}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
cpu: [x64]
os: [linux]
libc: [musl]
-<<<<<<< HEAD
'@tailwindcss/oxide-wasm32-wasi@4.2.2':
resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==}
-=======
- '@tailwindcss/oxide-wasm32-wasi@4.1.18':
- resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>=14.0.0'}
cpu: [wasm32]
bundledDependencies:
@@ -630,7 +479,6 @@ packages:
- '@emnapi/wasi-threads'
- tslib
-<<<<<<< HEAD
'@tailwindcss/oxide-win32-arm64-msvc@4.2.2':
resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==}
engines: {node: '>= 20'}
@@ -649,26 +497,6 @@ packages:
'@tailwindcss/postcss@4.2.2':
resolution: {integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==}
-=======
- '@tailwindcss/oxide-win32-arm64-msvc@4.1.18':
- resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==}
- engines: {node: '>= 10'}
- cpu: [arm64]
- os: [win32]
-
- '@tailwindcss/oxide-win32-x64-msvc@4.1.18':
- resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==}
- engines: {node: '>= 10'}
- cpu: [x64]
- os: [win32]
-
- '@tailwindcss/oxide@4.1.18':
- resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==}
- engines: {node: '>= 10'}
-
- '@tailwindcss/postcss@4.1.18':
- resolution: {integrity: sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
'@tybys/wasm-util@0.10.1':
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
@@ -694,26 +522,16 @@ packages:
'@types/ms@2.1.0':
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
-<<<<<<< HEAD
'@types/node@22.19.15':
resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==}
-=======
- '@types/node@22.19.10':
- resolution: {integrity: sha512-tF5VOugLS/EuDlTBijk0MqABfP8UxgYazTLo3uIn3b4yJgg26QRbVYJYsDtHrjdDUIRfP70+VfhTTc+CE1yskw==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
'@types/react-dom@19.2.3':
resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
peerDependencies:
'@types/react': ^19.2.0
-<<<<<<< HEAD
'@types/react@19.2.14':
resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==}
-=======
- '@types/react@19.2.13':
- resolution: {integrity: sha512-KkiJeU6VbYbUOp5ITMIc7kBfqlYkKA5KhEHVrGMmUUMt7NeaZg65ojdPk+FtNrBAOXNVM5QM72jnADjM+XVRAQ==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
@@ -1148,13 +966,8 @@ packages:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
-<<<<<<< HEAD
eslint-config-next@15.5.14:
resolution: {integrity: sha512-lmJ5F8ZgOYogq0qtH4L5SpxuASY2SPdOzqUprN2/56+P3GPsIpXaUWIJC66kYIH+yZdsM4nkHE5MIBP6s1NiBw==}
-=======
- eslint-config-next@15.5.12:
- resolution: {integrity: sha512-ktW3XLfd+ztEltY5scJNjxjHwtKWk6vU2iwzZqSN09UsbBmMeE/cVlJ1yESg6Yx5LW7p/Z8WzUAgYXGLEmGIpg==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
peerDependencies:
eslint: ^7.23.0 || ^8.0.0 || ^9.0.0
typescript: '>=3.3.1'
@@ -1249,13 +1062,8 @@ packages:
resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
-<<<<<<< HEAD
eslint@9.39.4:
resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==}
-=======
- eslint@9.39.2:
- resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true
peerDependencies:
@@ -1651,138 +1459,78 @@ packages:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
-<<<<<<< HEAD
lightningcss-android-arm64@1.32.0:
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
-=======
- lightningcss-android-arm64@1.30.2:
- resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [android]
-<<<<<<< HEAD
lightningcss-darwin-arm64@1.32.0:
resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
-=======
- lightningcss-darwin-arm64@1.30.2:
- resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [darwin]
-<<<<<<< HEAD
lightningcss-darwin-x64@1.32.0:
resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
-=======
- lightningcss-darwin-x64@1.30.2:
- resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [darwin]
-<<<<<<< HEAD
lightningcss-freebsd-x64@1.32.0:
resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
-=======
- lightningcss-freebsd-x64@1.30.2:
- resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [freebsd]
-<<<<<<< HEAD
lightningcss-linux-arm-gnueabihf@1.32.0:
resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
-=======
- lightningcss-linux-arm-gnueabihf@1.30.2:
- resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 12.0.0'}
cpu: [arm]
os: [linux]
-<<<<<<< HEAD
lightningcss-linux-arm64-gnu@1.32.0:
resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
-=======
- lightningcss-linux-arm64-gnu@1.30.2:
- resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
-<<<<<<< HEAD
lightningcss-linux-arm64-musl@1.32.0:
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
-=======
- lightningcss-linux-arm64-musl@1.30.2:
- resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
-<<<<<<< HEAD
lightningcss-linux-x64-gnu@1.32.0:
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
-=======
- lightningcss-linux-x64-gnu@1.30.2:
- resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
-<<<<<<< HEAD
lightningcss-linux-x64-musl@1.32.0:
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
-=======
- lightningcss-linux-x64-musl@1.30.2:
- resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
-<<<<<<< HEAD
lightningcss-win32-arm64-msvc@1.32.0:
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
-=======
- lightningcss-win32-arm64-msvc@1.30.2:
- resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [win32]
-<<<<<<< HEAD
lightningcss-win32-x64-msvc@1.32.0:
resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
-=======
- lightningcss-win32-x64-msvc@1.30.2:
- resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [win32]
-<<<<<<< HEAD
lightningcss@1.32.0:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
-=======
- lightningcss@1.30.2:
- resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: '>= 12.0.0'}
locate-path@6.0.0:
@@ -1799,14 +1547,11 @@ packages:
lowlight@3.3.0:
resolution: {integrity: sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==}
-<<<<<<< HEAD
lucide-react@1.6.0:
resolution: {integrity: sha512-YxLKVCOF5ZDI1AhKQE5IBYMY9y/Nr4NT15+7QEWpsTSVCdn4vmZhww+6BP76jWYjQx8rSz1Z+gGme1f+UycWEw==}
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
-=======
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
@@ -1926,13 +1671,8 @@ packages:
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
-<<<<<<< HEAD
next@15.5.14:
resolution: {integrity: sha512-M6S+4JyRjmKic2Ssm7jHUPkE6YUJ6lv4507jprsSZLulubz0ihO2E+S4zmQK3JZ2ov81JrugukKU4Tz0ivgqqQ==}
-=======
- next@15.5.12:
- resolution: {integrity: sha512-Fi/wQ4Etlrn60rz78bebG1i1SR20QxvV8tVp6iJspjLUSHcZoeUXCt+vmWoEcza85ElZzExK/jJ/F6SvtGktjA==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
hasBin: true
peerDependencies:
@@ -2038,13 +1778,8 @@ packages:
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
engines: {node: ^10 || ^12 || >=14}
-<<<<<<< HEAD
postcss@8.5.8:
resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==}
-=======
- postcss@8.5.6:
- resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
engines: {node: ^10 || ^12 || >=14}
prelude-ls@1.2.1:
@@ -2272,13 +2007,8 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
-<<<<<<< HEAD
tailwindcss@4.2.2:
resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==}
-=======
- tailwindcss@4.1.18:
- resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
tapable@2.3.0:
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
@@ -2427,15 +2157,9 @@ snapshots:
tslib: 2.8.1
optional: true
-<<<<<<< HEAD
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))':
dependencies:
eslint: 9.39.4(jiti@2.6.1)
-=======
- '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.6.1))':
- dependencies:
- eslint: 9.39.2(jiti@2.6.1)
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {}
@@ -2456,11 +2180,7 @@ snapshots:
dependencies:
'@types/json-schema': 7.0.15
-<<<<<<< HEAD
'@eslint/eslintrc@3.3.5':
-=======
- '@eslint/eslintrc@3.3.3':
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
dependencies:
ajv: 6.14.0
debug: 4.4.3
@@ -2474,11 +2194,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
-<<<<<<< HEAD
'@eslint/js@9.39.4': {}
-=======
- '@eslint/js@9.39.2': {}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
'@eslint/object-schema@2.1.7': {}
@@ -2621,7 +2337,6 @@ snapshots:
'@tybys/wasm-util': 0.10.1
optional: true
-<<<<<<< HEAD
'@next/env@15.5.14': {}
'@next/eslint-plugin-next@15.5.14':
@@ -2650,36 +2365,6 @@ snapshots:
optional: true
'@next/swc-win32-x64-msvc@15.5.14':
-=======
- '@next/env@15.5.12': {}
-
- '@next/eslint-plugin-next@15.5.12':
- dependencies:
- fast-glob: 3.3.1
-
- '@next/swc-darwin-arm64@15.5.12':
- optional: true
-
- '@next/swc-darwin-x64@15.5.12':
- optional: true
-
- '@next/swc-linux-arm64-gnu@15.5.12':
- optional: true
-
- '@next/swc-linux-arm64-musl@15.5.12':
- optional: true
-
- '@next/swc-linux-x64-gnu@15.5.12':
- optional: true
-
- '@next/swc-linux-x64-musl@15.5.12':
- optional: true
-
- '@next/swc-win32-arm64-msvc@15.5.12':
- optional: true
-
- '@next/swc-win32-x64-msvc@15.5.12':
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
optional: true
'@nodelib/fs.scandir@2.1.5':
@@ -2704,16 +2389,11 @@ snapshots:
dependencies:
tslib: 2.8.1
-<<<<<<< HEAD
'@tailwindcss/node@4.2.2':
-=======
- '@tailwindcss/node@4.1.18':
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
dependencies:
'@jridgewell/remapping': 2.3.5
enhanced-resolve: 5.20.1
jiti: 2.6.1
-<<<<<<< HEAD
lightningcss: 1.32.0
magic-string: 0.30.21
source-map-js: 1.2.1
@@ -2777,71 +2457,6 @@ snapshots:
'@tailwindcss/oxide': 4.2.2
postcss: 8.5.8
tailwindcss: 4.2.2
-=======
- lightningcss: 1.30.2
- magic-string: 0.30.21
- source-map-js: 1.2.1
- tailwindcss: 4.1.18
-
- '@tailwindcss/oxide-android-arm64@4.1.18':
- optional: true
-
- '@tailwindcss/oxide-darwin-arm64@4.1.18':
- optional: true
-
- '@tailwindcss/oxide-darwin-x64@4.1.18':
- optional: true
-
- '@tailwindcss/oxide-freebsd-x64@4.1.18':
- optional: true
-
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18':
- optional: true
-
- '@tailwindcss/oxide-linux-arm64-gnu@4.1.18':
- optional: true
-
- '@tailwindcss/oxide-linux-arm64-musl@4.1.18':
- optional: true
-
- '@tailwindcss/oxide-linux-x64-gnu@4.1.18':
- optional: true
-
- '@tailwindcss/oxide-linux-x64-musl@4.1.18':
- optional: true
-
- '@tailwindcss/oxide-wasm32-wasi@4.1.18':
- optional: true
-
- '@tailwindcss/oxide-win32-arm64-msvc@4.1.18':
- optional: true
-
- '@tailwindcss/oxide-win32-x64-msvc@4.1.18':
- optional: true
-
- '@tailwindcss/oxide@4.1.18':
- optionalDependencies:
- '@tailwindcss/oxide-android-arm64': 4.1.18
- '@tailwindcss/oxide-darwin-arm64': 4.1.18
- '@tailwindcss/oxide-darwin-x64': 4.1.18
- '@tailwindcss/oxide-freebsd-x64': 4.1.18
- '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18
- '@tailwindcss/oxide-linux-arm64-gnu': 4.1.18
- '@tailwindcss/oxide-linux-arm64-musl': 4.1.18
- '@tailwindcss/oxide-linux-x64-gnu': 4.1.18
- '@tailwindcss/oxide-linux-x64-musl': 4.1.18
- '@tailwindcss/oxide-wasm32-wasi': 4.1.18
- '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18
- '@tailwindcss/oxide-win32-x64-msvc': 4.1.18
-
- '@tailwindcss/postcss@4.1.18':
- dependencies:
- '@alloc/quick-lru': 5.2.0
- '@tailwindcss/node': 4.1.18
- '@tailwindcss/oxide': 4.1.18
- postcss: 8.5.6
- tailwindcss: 4.1.18
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
'@tybys/wasm-util@0.10.1':
dependencies:
@@ -2868,7 +2483,6 @@ snapshots:
'@types/ms@2.1.0': {}
-<<<<<<< HEAD
'@types/node@22.19.15':
dependencies:
undici-types: 6.21.0
@@ -2878,23 +2492,11 @@ snapshots:
'@types/react': 19.2.14
'@types/react@19.2.14':
-=======
- '@types/node@22.19.10':
- dependencies:
- undici-types: 6.21.0
-
- '@types/react-dom@19.2.3(@types/react@19.2.13)':
- dependencies:
- '@types/react': 19.2.13
-
- '@types/react@19.2.13':
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
dependencies:
csstype: 3.2.3
'@types/unist@3.0.3': {}
-<<<<<<< HEAD
'@typescript-eslint/eslint-plugin@8.57.1(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
@@ -2904,17 +2506,6 @@ snapshots:
'@typescript-eslint/utils': 8.57.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.57.1
eslint: 9.39.4(jiti@2.6.1)
-=======
- '@typescript-eslint/eslint-plugin@8.57.1(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
- dependencies:
- '@eslint-community/regexpp': 4.12.2
- '@typescript-eslint/parser': 8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
- '@typescript-eslint/scope-manager': 8.57.1
- '@typescript-eslint/type-utils': 8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
- '@typescript-eslint/utils': 8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
- '@typescript-eslint/visitor-keys': 8.57.1
- eslint: 9.39.2(jiti@2.6.1)
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
ignore: 7.0.5
natural-compare: 1.4.0
ts-api-utils: 2.5.0(typescript@5.9.3)
@@ -2922,22 +2513,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
-<<<<<<< HEAD
'@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)':
-=======
- '@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
dependencies:
'@typescript-eslint/scope-manager': 8.57.1
'@typescript-eslint/types': 8.57.1
'@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.57.1
debug: 4.4.3
-<<<<<<< HEAD
eslint: 9.39.4(jiti@2.6.1)
-=======
- eslint: 9.39.2(jiti@2.6.1)
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -2960,7 +2543,6 @@ snapshots:
dependencies:
typescript: 5.9.3
-<<<<<<< HEAD
'@typescript-eslint/type-utils@8.57.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@typescript-eslint/types': 8.57.1
@@ -2968,15 +2550,6 @@ snapshots:
'@typescript-eslint/utils': 8.57.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
debug: 4.4.3
eslint: 9.39.4(jiti@2.6.1)
-=======
- '@typescript-eslint/type-utils@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
- dependencies:
- '@typescript-eslint/types': 8.57.1
- '@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3)
- '@typescript-eslint/utils': 8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
- debug: 4.4.3
- eslint: 9.39.2(jiti@2.6.1)
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
ts-api-utils: 2.5.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
@@ -2999,7 +2572,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
-<<<<<<< HEAD
'@typescript-eslint/utils@8.57.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1))
@@ -3007,15 +2579,6 @@ snapshots:
'@typescript-eslint/types': 8.57.1
'@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3)
eslint: 9.39.4(jiti@2.6.1)
-=======
- '@typescript-eslint/utils@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
- dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
- '@typescript-eslint/scope-manager': 8.57.1
- '@typescript-eslint/types': 8.57.1
- '@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3)
- eslint: 9.39.2(jiti@2.6.1)
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -3438,7 +3001,6 @@ snapshots:
escape-string-regexp@4.0.0: {}
-<<<<<<< HEAD
eslint-config-next@15.5.14(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3):
dependencies:
'@next/eslint-plugin-next': 15.5.14
@@ -3452,21 +3014,6 @@ snapshots:
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-react-hooks: 5.2.0(eslint@9.39.4(jiti@2.6.1))
-=======
- eslint-config-next@15.5.12(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3):
- dependencies:
- '@next/eslint-plugin-next': 15.5.12
- '@rushstack/eslint-patch': 1.16.1
- '@typescript-eslint/eslint-plugin': 8.57.1(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
- '@typescript-eslint/parser': 8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
- eslint: 9.39.2(jiti@2.6.1)
- eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
- eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
- eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1))
- eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1))
- eslint-plugin-react-hooks: 5.2.0(eslint@9.39.2(jiti@2.6.1))
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
@@ -3486,26 +3033,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
-<<<<<<< HEAD
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.4.3
eslint: 9.39.4(jiti@2.6.1)
-=======
- eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)):
- dependencies:
- '@nolyfill/is-core-module': 1.0.39
- debug: 4.4.3
- eslint: 9.39.2(jiti@2.6.1)
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
get-tsconfig: 4.13.7
is-bun-module: 2.0.0
stable-hash: 0.0.5
tinyglobby: 0.2.15
unrs-resolver: 1.11.1
optionalDependencies:
-<<<<<<< HEAD
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1))
transitivePeerDependencies:
- supports-color
@@ -3522,24 +3060,6 @@ snapshots:
- supports-color
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)):
-=======
- eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
- transitivePeerDependencies:
- - supports-color
-
- eslint-module-utils@2.12.1(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)):
- dependencies:
- debug: 3.2.7
- optionalDependencies:
- '@typescript-eslint/parser': 8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
- eslint: 9.39.2(jiti@2.6.1)
- eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
- transitivePeerDependencies:
- - supports-color
-
- eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)):
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -3548,15 +3068,9 @@ snapshots:
array.prototype.flatmap: 1.3.3
debug: 3.2.7
doctrine: 2.1.0
-<<<<<<< HEAD
eslint: 9.39.4(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1))
-=======
- eslint: 9.39.2(jiti@2.6.1)
- eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -3568,21 +3082,13 @@ snapshots:
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
-<<<<<<< HEAD
'@typescript-eslint/parser': 8.57.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
-=======
- '@typescript-eslint/parser': 8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
-<<<<<<< HEAD
eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.4(jiti@2.6.1)):
-=======
- eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.2(jiti@2.6.1)):
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
dependencies:
aria-query: 5.3.2
array-includes: 3.1.9
@@ -3592,11 +3098,7 @@ snapshots:
axobject-query: 4.1.0
damerau-levenshtein: 1.0.8
emoji-regex: 9.2.2
-<<<<<<< HEAD
eslint: 9.39.4(jiti@2.6.1)
-=======
- eslint: 9.39.2(jiti@2.6.1)
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
hasown: 2.0.2
jsx-ast-utils: 3.3.5
language-tags: 1.0.9
@@ -3605,19 +3107,11 @@ snapshots:
safe-regex-test: 1.1.0
string.prototype.includes: 2.0.1
-<<<<<<< HEAD
eslint-plugin-react-hooks@5.2.0(eslint@9.39.4(jiti@2.6.1)):
dependencies:
eslint: 9.39.4(jiti@2.6.1)
eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.6.1)):
-=======
- eslint-plugin-react-hooks@5.2.0(eslint@9.39.2(jiti@2.6.1)):
- dependencies:
- eslint: 9.39.2(jiti@2.6.1)
-
- eslint-plugin-react@7.37.5(eslint@9.39.2(jiti@2.6.1)):
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
dependencies:
array-includes: 3.1.9
array.prototype.findlast: 1.2.5
@@ -3625,11 +3119,7 @@ snapshots:
array.prototype.tosorted: 1.1.4
doctrine: 2.1.0
es-iterator-helpers: 1.3.1
-<<<<<<< HEAD
eslint: 9.39.4(jiti@2.6.1)
-=======
- eslint: 9.39.2(jiti@2.6.1)
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
estraverse: 5.3.0
hasown: 2.0.2
jsx-ast-utils: 3.3.5
@@ -3654,26 +3144,15 @@ snapshots:
eslint-visitor-keys@5.0.1: {}
-<<<<<<< HEAD
eslint@9.39.4(jiti@2.6.1):
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1))
-=======
- eslint@9.39.2(jiti@2.6.1):
- dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
'@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.21.2
'@eslint/config-helpers': 0.4.2
'@eslint/core': 0.17.0
-<<<<<<< HEAD
'@eslint/eslintrc': 3.3.5
'@eslint/js': 9.39.4
-=======
- '@eslint/eslintrc': 3.3.3
- '@eslint/js': 9.39.2
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
'@eslint/plugin-kit': 0.4.1
'@humanfs/node': 0.16.7
'@humanwhocodes/module-importer': 1.0.1
@@ -4104,7 +3583,6 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
-<<<<<<< HEAD
lightningcss-android-arm64@1.32.0:
optional: true
@@ -4153,56 +3631,6 @@ snapshots:
lightningcss-linux-x64-musl: 1.32.0
lightningcss-win32-arm64-msvc: 1.32.0
lightningcss-win32-x64-msvc: 1.32.0
-=======
- lightningcss-android-arm64@1.30.2:
- optional: true
-
- lightningcss-darwin-arm64@1.30.2:
- optional: true
-
- lightningcss-darwin-x64@1.30.2:
- optional: true
-
- lightningcss-freebsd-x64@1.30.2:
- optional: true
-
- lightningcss-linux-arm-gnueabihf@1.30.2:
- optional: true
-
- lightningcss-linux-arm64-gnu@1.30.2:
- optional: true
-
- lightningcss-linux-arm64-musl@1.30.2:
- optional: true
-
- lightningcss-linux-x64-gnu@1.30.2:
- optional: true
-
- lightningcss-linux-x64-musl@1.30.2:
- optional: true
-
- lightningcss-win32-arm64-msvc@1.30.2:
- optional: true
-
- lightningcss-win32-x64-msvc@1.30.2:
- optional: true
-
- lightningcss@1.30.2:
- dependencies:
- detect-libc: 2.1.2
- optionalDependencies:
- lightningcss-android-arm64: 1.30.2
- lightningcss-darwin-arm64: 1.30.2
- lightningcss-darwin-x64: 1.30.2
- lightningcss-freebsd-x64: 1.30.2
- lightningcss-linux-arm-gnueabihf: 1.30.2
- lightningcss-linux-arm64-gnu: 1.30.2
- lightningcss-linux-arm64-musl: 1.30.2
- lightningcss-linux-x64-gnu: 1.30.2
- lightningcss-linux-x64-musl: 1.30.2
- lightningcss-win32-arm64-msvc: 1.30.2
- lightningcss-win32-x64-msvc: 1.30.2
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
locate-path@6.0.0:
dependencies:
@@ -4220,13 +3648,10 @@ snapshots:
devlop: 1.1.0
highlight.js: 11.11.1
-<<<<<<< HEAD
lucide-react@1.6.0(react@19.2.4):
dependencies:
react: 19.2.4
-=======
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -4430,15 +3855,9 @@ snapshots:
natural-compare@1.4.0: {}
-<<<<<<< HEAD
next@15.5.14(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
dependencies:
'@next/env': 15.5.14
-=======
- next@15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
- dependencies:
- '@next/env': 15.5.12
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
'@swc/helpers': 0.5.15
caniuse-lite: 1.0.30001781
postcss: 8.4.31
@@ -4446,7 +3865,6 @@ snapshots:
react-dom: 19.2.4(react@19.2.4)
styled-jsx: 5.1.6(react@19.2.4)
optionalDependencies:
-<<<<<<< HEAD
'@next/swc-darwin-arm64': 15.5.14
'@next/swc-darwin-x64': 15.5.14
'@next/swc-linux-arm64-gnu': 15.5.14
@@ -4455,16 +3873,6 @@ snapshots:
'@next/swc-linux-x64-musl': 15.5.14
'@next/swc-win32-arm64-msvc': 15.5.14
'@next/swc-win32-x64-msvc': 15.5.14
-=======
- '@next/swc-darwin-arm64': 15.5.12
- '@next/swc-darwin-x64': 15.5.12
- '@next/swc-linux-arm64-gnu': 15.5.12
- '@next/swc-linux-arm64-musl': 15.5.12
- '@next/swc-linux-x64-gnu': 15.5.12
- '@next/swc-linux-x64-musl': 15.5.12
- '@next/swc-win32-arm64-msvc': 15.5.12
- '@next/swc-win32-x64-msvc': 15.5.12
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
sharp: 0.34.5
transitivePeerDependencies:
- '@babel/core'
@@ -4566,11 +3974,7 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
-<<<<<<< HEAD
postcss@8.5.8:
-=======
- postcss@8.5.6:
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
dependencies:
nanoid: 3.3.11
picocolors: 1.1.1
@@ -4882,11 +4286,7 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
-<<<<<<< HEAD
tailwindcss@4.2.2: {}
-=======
- tailwindcss@4.1.18: {}
->>>>>>> 6c5ab2a (Add dev container and migrate to pnpm for supply chain security)
tapable@2.3.0: {}
From 1676f306f6c5fe7c08ccccb3e897eb905a5e7078 Mon Sep 17 00:00:00 2001
From: adamsoffer
Date: Tue, 7 Apr 2026 13:12:24 -0400
Subject: [PATCH 3/6] Update ecosystem page description copy
Co-Authored-By: Claude Opus 4.6 (1M context)
---
components/ecosystem/EcosystemListingClient.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/components/ecosystem/EcosystemListingClient.tsx b/components/ecosystem/EcosystemListingClient.tsx
index e2ce2f3..c823edc 100644
--- a/components/ecosystem/EcosystemListingClient.tsx
+++ b/components/ecosystem/EcosystemListingClient.tsx
@@ -121,7 +121,7 @@ function EcosystemListingInner({ apps, categories }: Props) {
Date: Tue, 7 Apr 2026 13:13:35 -0400
Subject: [PATCH 4/6] Use next/link for Explore the ecosystem button
Co-Authored-By: Claude Opus 4.6 (1M context)
---
components/home/BuiltOnLivepeer.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/components/home/BuiltOnLivepeer.tsx b/components/home/BuiltOnLivepeer.tsx
index 8533d6e..959cafc 100644
--- a/components/home/BuiltOnLivepeer.tsx
+++ b/components/home/BuiltOnLivepeer.tsx
@@ -742,7 +742,7 @@ export default function BuiltOnLivepeer() {
transition={{ duration: 0.4 }}
className="mt-12 text-center"
>
-
Explore the ecosystem →
-
+
From 420c3386fd8c18da814ecc11fa6f4220fa8c99e4 Mon Sep 17 00:00:00 2001
From: adamsoffer
Date: Tue, 7 Apr 2026 13:18:42 -0400
Subject: [PATCH 5/6] Update check-ecosystem-urls script to read markdown
frontmatter
The script previously read data/ecosystem.json which has been removed.
Now walks content/ecosystem/*.md and parses the name and url out of each
file's frontmatter using a tiny inline parser.
Co-Authored-By: Claude Opus 4.6 (1M context)
---
scripts/check-ecosystem-urls.mjs | 30 ++++++++++++++++++++++++++----
1 file changed, 26 insertions(+), 4 deletions(-)
diff --git a/scripts/check-ecosystem-urls.mjs b/scripts/check-ecosystem-urls.mjs
index e3c25d7..4c380ae 100644
--- a/scripts/check-ecosystem-urls.mjs
+++ b/scripts/check-ecosystem-urls.mjs
@@ -3,13 +3,35 @@
* Exits with code 1 if any URL is broken.
*/
-import { readFileSync } from "node:fs";
-import { resolve, dirname } from "node:path";
+import { readFileSync, readdirSync } from "node:fs";
+import { resolve, dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
-const dataPath = resolve(__dirname, "../data/ecosystem.json");
-const apps = JSON.parse(readFileSync(dataPath, "utf-8"));
+const ecosystemDir = resolve(__dirname, "../content/ecosystem");
+
+/**
+ * Minimal frontmatter parser — extracts `name` and `url` from each markdown
+ * file. Avoids pulling in gray-matter for a one-file CI script.
+ */
+function parseFrontmatter(source) {
+ const match = source.match(/^---\r?\n([\s\S]*?)\r?\n---/);
+ if (!match) return {};
+ const data = {};
+ for (const line of match[1].split(/\r?\n/)) {
+ const m = line.match(/^([A-Za-z0-9_]+):\s*(.*)$/);
+ if (!m) continue;
+ const value = m[2].trim();
+ if (value === "") continue;
+ data[m[1]] = value.replace(/^["']|["']$/g, "");
+ }
+ return data;
+}
+
+const apps = readdirSync(ecosystemDir)
+ .filter((f) => f.endsWith(".md"))
+ .map((f) => parseFrontmatter(readFileSync(join(ecosystemDir, f), "utf-8")))
+ .filter((app) => app.url);
const TIMEOUT_MS = 15_000;
const MAX_RETRIES = 2;
From d6c056f078f67d852c5e8a19de913e93d6fe5872 Mon Sep 17 00:00:00 2001
From: adamsoffer
Date: Tue, 7 Apr 2026 13:36:56 -0400
Subject: [PATCH 6/6] Improve Daydream homepage description and add Discord
support link
Co-Authored-By: Claude Opus 4.6 (1M context)
---
components/home/BuiltOnLivepeer.tsx | 2 +-
content/ecosystem/daydream.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/components/home/BuiltOnLivepeer.tsx b/components/home/BuiltOnLivepeer.tsx
index 959cafc..092fce3 100644
--- a/components/home/BuiltOnLivepeer.tsx
+++ b/components/home/BuiltOnLivepeer.tsx
@@ -633,7 +633,7 @@ const projects = [
Visual: DaydreamVisual,
Logo: DaydreamLogo,
description:
- "Turn a live camera feed into AI-generated video, in real time.",
+ "Transform any video into AI-generated visuals in real-time — from a live camera, a clip, or a text prompt.",
domain: "daydream.live",
},
{
diff --git a/content/ecosystem/daydream.md b/content/ecosystem/daydream.md
index d9cfbdd..9be64ad 100644
--- a/content/ecosystem/daydream.md
+++ b/content/ecosystem/daydream.md
@@ -13,7 +13,7 @@ twitter: https://x.com/DaydreamLiveAI
github: https://github.com/daydreamlive/scope
contact: hello@daydream.live
docs: https://docs.daydream.live/
-support:
+support: https://discord.com/invite/mnfGR4Fjhp
terms: https://daydream.live/terms
privacy: https://daydream.live/privacy-policy
---