+
-
+
- {children}
-
+ {children}
+
+
+
)
}
diff --git a/apps/sim/app/(landing)/blog/og/route.tsx b/apps/sim/app/(landing)/blog/og/route.tsx
new file mode 100644
index 00000000000..23b8465cd44
--- /dev/null
+++ b/apps/sim/app/(landing)/blog/og/route.tsx
@@ -0,0 +1,256 @@
+import fs from 'fs/promises'
+import path from 'path'
+import { ImageResponse } from 'next/og'
+import type { NextRequest } from 'next/server'
+import { getPostBySlug } from '@/lib/blog/registry'
+import { formatDate } from '@/lib/core/utils/formatting'
+import { getPrimaryCategory } from '@/app/(landing)/blog/tag-colors'
+
+function getTitleFontSize(title: string): number {
+ if (title.length > 80) return 36
+ if (title.length > 60) return 40
+ if (title.length > 40) return 48
+ return 56
+}
+
+export async function GET(request: NextRequest) {
+ const slug = request.nextUrl.searchParams.get('slug')
+
+ if (!slug) {
+ return new Response('Missing slug parameter', { status: 400 })
+ }
+
+ let post
+ try {
+ post = await getPostBySlug(slug)
+ } catch {
+ return new Response('Post not found', { status: 404 })
+ }
+
+ const category = getPrimaryCategory(post.tags)
+ const authors = post.authors && post.authors.length > 0 ? post.authors : [post.author]
+ const authorNames = authors.map((a) => a.name).join(', ')
+
+ let fontMedium: Buffer
+ let fontBold: Buffer
+ try {
+ const fontsDirPrimary = path.join(process.cwd(), 'app', '_styles', 'fonts', 'season')
+ const fontsDirFallback = path.join(
+ process.cwd(),
+ 'apps',
+ 'sim',
+ 'app',
+ '_styles',
+ 'fonts',
+ 'season'
+ )
+
+ let fontsDir = fontsDirPrimary
+ try {
+ await fs.access(fontsDirPrimary)
+ } catch {
+ fontsDir = fontsDirFallback
+ }
+
+ ;[fontMedium, fontBold] = await Promise.all([
+ fs.readFile(path.join(fontsDir, 'SeasonSans-Medium.woff')),
+ fs.readFile(path.join(fontsDir, 'SeasonSans-Bold.woff')),
+ ])
+ } catch {
+ return new Response('Font assets not found', { status: 500 })
+ }
+
+ const COLORS = ['#2ABBF8', '#FA4EDF', '#FFCC02', '#00F701'] as const
+
+ return new ImageResponse(
+
+
+
+
+
+ {COLORS.map((color) => (
+
+ ))}
+
+
+ {COLORS.slice(0, 3).map((color) => (
+
+ ))}
+
+
+
+ {[...COLORS].reverse().map((color) => (
+
+ ))}
+
+
+
+ {category.label}
+
+ {post.readingTime && (
+
+ {post.readingTime} min read
+
+ )}
+
+
+
+ {post.title}
+
+
+ {post.description.length > 140
+ ? `${post.description.slice(0, 140)}...`
+ : post.description}
+
+
+
+
+ {authorNames}
+
+
+ {formatDate(new Date(post.date))}
+
+
+
+ sim.ai/blog
+
+
+
,
+ {
+ width: 1200,
+ height: 630,
+ fonts: [
+ {
+ name: 'Season Sans',
+ data: fontMedium,
+ style: 'normal' as const,
+ weight: 500 as const,
+ },
+ {
+ name: 'Season Sans',
+ data: fontBold,
+ style: 'normal' as const,
+ weight: 700 as const,
+ },
+ ],
+ }
+ )
+}
diff --git a/apps/sim/app/(landing)/blog/page.tsx b/apps/sim/app/(landing)/blog/page.tsx
index 359464e3209..153c70c0ff0 100644
--- a/apps/sim/app/(landing)/blog/page.tsx
+++ b/apps/sim/app/(landing)/blog/page.tsx
@@ -1,42 +1,39 @@
import type { Metadata } from 'next'
-import Link from 'next/link'
import { getAllPostMeta } from '@/lib/blog/registry'
-import { PostGrid } from '@/app/(landing)/blog/post-grid'
+import { StudioContent } from '@/app/(landing)/blog/studio-content'
export const metadata: Metadata = {
title: 'Blog',
description: 'Announcements, insights, and guides from the Sim team.',
}
-export const revalidate = 3600
-
-export default async function BlogIndex({
+export default async function StudioIndex({
searchParams,
}: {
- searchParams: Promise<{ page?: string; tag?: string }>
+ searchParams: Promise<{ tag?: string; q?: string }>
}) {
- const { page, tag } = await searchParams
- const pageNum = Math.max(1, Number(page || 1))
- const perPage = 20
-
+ const { tag, q } = await searchParams
const all = await getAllPostMeta()
- const filtered = tag ? all.filter((p) => p.tags.includes(tag)) : all
-
- const sorted =
- pageNum === 1
- ? filtered.sort((a, b) => {
- if (a.featured && !b.featured) return -1
- if (!a.featured && b.featured) return 1
- return new Date(b.date).getTime() - new Date(a.date).getTime()
- })
- : filtered
- const totalPages = Math.max(1, Math.ceil(sorted.length / perPage))
- const start = (pageNum - 1) * perPage
- const posts = sorted.slice(start, start + perPage)
- // Tag filter chips are intentionally disabled for now.
- // const tags = await getAllTags()
- const blogJsonLd = {
+ const pickAuthor = (a: { name: string; avatarUrl?: string }) => ({
+ name: a.name,
+ avatarUrl: a.avatarUrl,
+ })
+
+ const posts = all.map((p) => ({
+ slug: p.slug,
+ title: p.title,
+ description: p.description,
+ date: p.date,
+ ogImage: p.ogImage,
+ readingTime: p.readingTime,
+ tags: p.tags,
+ author: pickAuthor(p.author),
+ authors: p.authors?.map(pickAuthor),
+ featured: p.featured ?? false,
+ }))
+
+ const studioJsonLd = {
'@context': 'https://schema.org',
'@type': 'Blog',
name: 'Sim Blog',
@@ -45,54 +42,12 @@ export default async function BlogIndex({
}
return (
-
+ <>
-
- Blog
-
-
- Announcements, insights, and guides for building AI agent workflows.
-
-
- {/* Tag filter chips hidden until we have more posts */}
- {/*
- All
- {tags.map((t) => (
-
- {t.tag} ({t.count})
-
- ))}
-
*/}
-
- {/* Grid layout for consistent rows */}
-
-
- {totalPages > 1 && (
-
- {pageNum > 1 && (
-
- Previous
-
- )}
-
- Page {pageNum} of {totalPages}
-
- {pageNum < totalPages && (
-
- Next
-
- )}
-
- )}
-
+
+ >
)
}
diff --git a/apps/sim/app/(landing)/blog/post-grid.tsx b/apps/sim/app/(landing)/blog/post-grid.tsx
index 918d6782f53..ad96e30e3ed 100644
--- a/apps/sim/app/(landing)/blog/post-grid.tsx
+++ b/apps/sim/app/(landing)/blog/post-grid.tsx
@@ -1,14 +1,44 @@
'use client'
+import { motion, useReducedMotion } from 'framer-motion'
import Image from 'next/image'
import Link from 'next/link'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/emcn'
+import { formatDate } from '@/lib/core/utils/formatting'
+import { getPrimaryCategory } from '@/app/(landing)/blog/tag-colors'
+
+const EASE_OUT_QUINT = [0.23, 1, 0.32, 1] as const
+const CARD_STAGGER = 0.08
+const CARD_DURATION = 0.35
+const CARD_Y = 16
+const LEAD_Y = 20
+
+const gridVariants = {
+ hidden: {},
+ visible: { transition: { staggerChildren: CARD_STAGGER } },
+}
+
+const cardVariants = {
+ hidden: { opacity: 0, y: CARD_Y },
+ visible: {
+ opacity: 1,
+ y: 0,
+ transition: { duration: CARD_DURATION, ease: EASE_OUT_QUINT },
+ },
+}
+
+const leadVariants = {
+ hidden: { opacity: 0, y: LEAD_Y },
+ visible: {
+ opacity: 1,
+ y: 0,
+ transition: { duration: CARD_DURATION + 0.05, ease: EASE_OUT_QUINT },
+ },
+}
interface Author {
- id: string
name: string
avatarUrl?: string
- url?: string
}
interface Post {
@@ -17,74 +47,191 @@ interface Post {
description: string
date: string
ogImage: string
+ readingTime?: number
+ tags: string[]
author: Author
authors?: Author[]
featured?: boolean
}
-export function PostGrid({ posts }: { posts: Post[] }) {
+interface PostGridProps {
+ posts: Post[]
+}
+
+function PostCard({ post, priority = false }: { post: Post; priority?: boolean }) {
+ const category = getPrimaryCategory(post.tags)
+ const color = category.color
+ const authors = post.authors && post.authors.length > 0 ? post.authors : [post.author]
+
return (
-
- {posts.map((p, index) => (
-
-
- {/* Image container with fixed aspect ratio to prevent layout shift */}
-
-
+
+
+
+
+
+
+
+
+
+ {category.label}
+
+ {post.readingTime && (
+
+ {post.readingTime} min read
+
+ )}
-
-
- {new Date(p.date).toLocaleDateString('en-US', {
- month: 'short',
- day: 'numeric',
- year: 'numeric',
- })}
+
+ {post.title}
+
+
+ {post.description}
+
+
+
+
+ {authors.slice(0, 2).map((a, idx) => (
+
+
+
+ {a?.name.slice(0, 2)}
+
+
+ ))}
+
+
+ {authors
+ .slice(0, 2)
+ .map((a) => a?.name)
+ .join(', ')}
+
-
{p.title}
-
{p.description}
+
+
+
+
+
+
+
+ )
+}
+
+function FeaturedLeadCard({ post }: { post: Post }) {
+ const category = getPrimaryCategory(post.tags)
+ const color = category.color
+ const authors = post.authors && post.authors.length > 0 ? post.authors : [post.author]
+
+ return (
+
+
+
+
+
+
+
+
+
+ {category.label}
+
+ {post.readingTime && (
+
+ {post.readingTime} min read
+
+ )}
+
+
+
+ {post.title}
+
+
{post.description}
+
- {(p.authors && p.authors.length > 0 ? p.authors : [p.author])
- .slice(0, 3)
- .map((author, idx) => (
-
-
-
- {author?.name.slice(0, 2)}
-
-
- ))}
+ {authors.slice(0, 2).map((a, idx) => (
+
+
+
+ {a?.name.slice(0, 2)}
+
+
+ ))}
-
- {(p.authors && p.authors.length > 0 ? p.authors : [p.author])
+
+ {authors
.slice(0, 2)
.map((a) => a?.name)
.join(', ')}
- {(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length > 2 && (
- <>
- {' '}
- and {(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length - 2}{' '}
- other
- {(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length - 2 > 1
- ? 's'
- : ''}
- >
- )}
+
+
-
+
+
+
+ )
+}
+
+export function FeaturedGrid({ posts }: PostGridProps) {
+ const shouldReduceMotion = useReducedMotion()
+ if (posts.length === 0) return null
+ const [lead, ...rest] = posts
+
+ return (
+
+
+ {rest.map((p) => (
+
+ ))}
+
+ )
+}
+
+export function PostGrid({ posts }: PostGridProps) {
+ const shouldReduceMotion = useReducedMotion()
+
+ return (
+
+ {posts.map((p, index) => (
+
))}
-
+
)
}
diff --git a/apps/sim/app/(landing)/blog/search-input.tsx b/apps/sim/app/(landing)/blog/search-input.tsx
new file mode 100644
index 00000000000..461c063fbfd
--- /dev/null
+++ b/apps/sim/app/(landing)/blog/search-input.tsx
@@ -0,0 +1,46 @@
+'use client'
+
+import { useCallback, useRef } from 'react'
+import { Search } from 'lucide-react'
+import { useRouter, useSearchParams } from 'next/navigation'
+
+export function SearchInput() {
+ const router = useRouter()
+ const searchParams = useSearchParams()
+ const inputRef = useRef
(null)
+ const currentQuery = searchParams.get('q') ?? ''
+
+ const handleSubmit = useCallback(
+ (e: React.FormEvent) => {
+ e.preventDefault()
+ const value = inputRef.current?.value.trim() ?? ''
+ if (value) {
+ router.push(`/blog?q=${encodeURIComponent(value)}`)
+ } else {
+ router.push('/blog')
+ }
+ },
+ [router]
+ )
+
+ return (
+
+ )
+}
diff --git a/apps/sim/app/(landing)/blog/studio-content.tsx b/apps/sim/app/(landing)/blog/studio-content.tsx
new file mode 100644
index 00000000000..609b82ea53f
--- /dev/null
+++ b/apps/sim/app/(landing)/blog/studio-content.tsx
@@ -0,0 +1,261 @@
+'use client'
+
+import { useCallback, useEffect, useMemo, useState } from 'react'
+import { StudioHero } from '@/app/(landing)/blog/hero'
+import { FeaturedGrid, PostGrid } from '@/app/(landing)/blog/post-grid'
+import { BlogStudioSidebar } from '@/app/(landing)/blog/studio-sidebar-client'
+import { CATEGORIES, getCategoryById, getPrimaryCategory } from '@/app/(landing)/blog/tag-colors'
+
+interface SerializedPost {
+ slug: string
+ title: string
+ description: string
+ date: string
+ ogImage: string
+ readingTime?: number
+ tags: string[]
+ author: { name: string; avatarUrl?: string }
+ authors?: { name: string; avatarUrl?: string }[]
+ featured?: boolean
+}
+
+interface StudioContentProps {
+ posts: SerializedPost[]
+ initialTag?: string | null
+ initialQuery?: string
+}
+
+const PER_PAGE = 20
+
+export function StudioContent({ posts, initialTag, initialQuery }: StudioContentProps) {
+ const [activeTag, setActiveTag] = useState(initialTag ?? null)
+ const [query, setQuery] = useState(initialQuery ?? '')
+ const [page, setPage] = useState(1)
+
+ const syncUrl = useCallback((tag: string | null, q: string) => {
+ const params = new URLSearchParams()
+ if (tag) params.set('tag', tag)
+ if (q) params.set('q', q)
+ const search = params.toString()
+ window.history.replaceState(null, '', search ? `/blog?${search}` : '/blog')
+ }, [])
+
+ useEffect(() => {
+ const onPopState = () => {
+ const params = new URLSearchParams(window.location.search)
+ setActiveTag(params.get('tag'))
+ setQuery(params.get('q') ?? '')
+ setPage(1)
+ }
+ window.addEventListener('popstate', onPopState)
+ return () => window.removeEventListener('popstate', onPopState)
+ }, [])
+
+ const lowerQ = query.trim().toLowerCase()
+
+ const { sorted, activeCategory } = useMemo(() => {
+ const validTag = activeTag && CATEGORIES.some((c) => c.id === activeTag) ? activeTag : null
+
+ let filtered = posts
+
+ if (validTag) {
+ filtered = posts.filter((p) => getPrimaryCategory(p.tags).id === validTag)
+ }
+
+ if (lowerQ) {
+ filtered = filtered.filter((p) => {
+ const haystack = [
+ p.title,
+ p.description,
+ ...p.tags,
+ p.author.name,
+ ...(p.authors?.map((a) => a.name) ?? []),
+ ]
+ .join(' ')
+ .toLowerCase()
+ return haystack.includes(lowerQ)
+ })
+ }
+
+ const cat = validTag ? getCategoryById(validTag) : null
+ const s = [...filtered].sort((a, b) => {
+ if (a.featured && !b.featured) return -1
+ if (!a.featured && b.featured) return 1
+ return new Date(b.date).getTime() - new Date(a.date).getTime()
+ })
+ return { sorted: s, activeCategory: cat }
+ }, [posts, activeTag, lowerQ])
+
+ const totalPages = Math.max(1, Math.ceil(sorted.length / PER_PAGE))
+ const pagePosts = sorted.slice((page - 1) * PER_PAGE, page * PER_PAGE)
+
+ const isDefaultView = page === 1 && !activeTag && !lowerQ
+ const featured: SerializedPost[] = []
+ const feed: SerializedPost[] = []
+ for (const p of pagePosts) {
+ if (isDefaultView && p.featured) {
+ featured.push(p)
+ } else {
+ feed.push(p)
+ }
+ }
+ const showHero = isDefaultView
+
+ const handleCategorySelect = useCallback(
+ (id: string | null) => {
+ setActiveTag(id)
+ setQuery('')
+ setPage(1)
+ syncUrl(id, '')
+ },
+ [syncUrl]
+ )
+
+ const handleSearch = useCallback(
+ (value: string) => {
+ setQuery(value)
+ setActiveTag(null)
+ setPage(1)
+ syncUrl(null, value.trim())
+ },
+ [syncUrl]
+ )
+
+ const handleClearAll = useCallback(() => {
+ setActiveTag(null)
+ setQuery('')
+ setPage(1)
+ syncUrl(null, '')
+ }, [syncUrl])
+
+ return (
+
+
+
+
+
+ {showHero && (
+
+
+
+ )}
+
+ {lowerQ && (
+
+
+ Results for:
+
+
+ {query.trim()}
+
+
+
+ )}
+
+ {activeCategory && !lowerQ && (
+
+
+ Filtered by:
+
+
+ {activeCategory.label}
+
+
+
+ )}
+
+ {featured.length > 0 && (
+
+
+
+ Featured Content
+
+
+
+ )}
+
+ {feed.length > 0 && (
+
+
+
+ {lowerQ ? 'Search Results' : activeCategory ? activeCategory.label : 'All Posts'}
+
+
+
+ )}
+
+ {pagePosts.length === 0 && (
+
+
+ {lowerQ ? `No posts matching "${query.trim()}".` : 'No posts found.'}
+
+
+
+ )}
+
+ {totalPages > 1 && (
+
+ {page > 1 && (
+
+ )}
+
+ Page {page} of {totalPages}
+
+ {page < totalPages && (
+
+ )}
+
+ )}
+
+
+
+
+ )
+}
diff --git a/apps/sim/app/(landing)/blog/studio-scrollbar.css b/apps/sim/app/(landing)/blog/studio-scrollbar.css
new file mode 100644
index 00000000000..e1df9fe9f1d
--- /dev/null
+++ b/apps/sim/app/(landing)/blog/studio-scrollbar.css
@@ -0,0 +1,48 @@
+/* Firefox — scoped to studio layout root */
+.studio-scroll {
+ scrollbar-width: thin;
+ scrollbar-color: #2a2a2a #1c1c1c;
+}
+
+/* WebKit (Chrome, Safari, Edge) — scoped to studio layout root */
+.studio-scroll::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
+}
+
+.studio-scroll::-webkit-scrollbar-track {
+ background: #1c1c1c;
+}
+
+.studio-scroll::-webkit-scrollbar-thumb {
+ background: #2a2a2a;
+ border-radius: 3px;
+}
+
+.studio-scroll::-webkit-scrollbar-thumb:hover {
+ background: #3d3d3d;
+}
+
+/* Descendant scrollbars */
+.studio-scroll *::-webkit-scrollbar {
+ width: 4px;
+ height: 4px;
+}
+
+.studio-scroll *::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.studio-scroll *::-webkit-scrollbar-thumb {
+ background: #2a2a2a;
+ border-radius: 2px;
+}
+
+.studio-scroll *::-webkit-scrollbar-thumb:hover {
+ background: #3d3d3d;
+}
+
+.studio-scroll * {
+ scrollbar-width: thin;
+ scrollbar-color: #2a2a2a transparent;
+}
diff --git a/apps/sim/app/(landing)/blog/studio-sidebar-client.tsx b/apps/sim/app/(landing)/blog/studio-sidebar-client.tsx
new file mode 100644
index 00000000000..0cfab0748f3
--- /dev/null
+++ b/apps/sim/app/(landing)/blog/studio-sidebar-client.tsx
@@ -0,0 +1,244 @@
+'use client'
+
+import { useMemo } from 'react'
+import { useReducedMotion } from 'framer-motion'
+import { Search } from 'lucide-react'
+import { cn } from '@/lib/core/utils/cn'
+import { CATEGORIES, getPrimaryCategory } from '@/app/(landing)/blog/tag-colors'
+import { hexToRgba } from '@/lib/core/utils/formatting'
+
+const LEFT_WALL_CLIP = 'polygon(0 8px, 100% 0, 100% 100%, 0 100%)'
+const BOTTOM_WALL_CLIP = 'polygon(0 0, 100% 0, calc(100% - 8px) 100%, 0 100%)'
+
+const DEPTH_SEGMENTS = [
+ [0.3, 10],
+ [0.5, 8],
+ [0.8, 6],
+ [1, 5],
+ [0.4, 12],
+ [0.7, 8],
+ [1, 6],
+ [0.5, 10],
+ [0.9, 7],
+ [0.6, 12],
+ [1, 8],
+ [0.35, 8],
+] as const
+
+function buildBottomWallGradient(color: string): string {
+ let pos = 0
+ const stops: string[] = []
+ for (const [opacity, width] of DEPTH_SEGMENTS) {
+ const c = hexToRgba(color, opacity)
+ stops.push(`${c} ${pos}%`, `${c} ${pos + width}%`)
+ pos += width
+ }
+ return `linear-gradient(135deg, ${stops.join(', ')})`
+}
+
+interface BlogSidebarPost {
+ tags: string[]
+}
+
+interface BlogStudioSidebarProps {
+ posts: BlogSidebarPost[]
+ activeTag: string | null
+ query: string
+ onChangeQuery: (value: string) => void
+ onSelectTag: (id: string | null) => void
+ className?: string
+}
+
+export function BlogStudioSidebar({
+ posts,
+ activeTag,
+ query,
+ onChangeQuery,
+ onSelectTag,
+ className,
+}: BlogStudioSidebarProps) {
+ const shouldReduceMotion = useReducedMotion()
+
+ const categoryItems = useMemo(() => {
+ const counts: Record = {}
+ for (const cat of CATEGORIES) counts[cat.id] = 0
+ for (const post of posts) {
+ const catId = getPrimaryCategory(post.tags).id
+ counts[catId] = (counts[catId] ?? 0) + 1
+ }
+ return [
+ { id: null as string | null, label: 'All Posts', count: posts.length, color: '#00F701' },
+ ...CATEGORIES.map((cat) => ({
+ id: cat.id as string | null,
+ label: cat.label,
+ count: counts[cat.id] ?? 0,
+ color: cat.color,
+ })),
+ ]
+ }, [posts])
+
+ return (
+
+ )
+}
+
+interface SidebarSearchProps {
+ value: string
+ onChange: (value: string) => void
+}
+
+function SidebarSearch({ value, onChange }: SidebarSearchProps) {
+ return (
+
+ )
+}
+
+interface SidebarCategoryItem {
+ id: string | null
+ label: string
+ count: number
+ color: string
+}
+
+interface SidebarCategoriesProps {
+ items: SidebarCategoryItem[]
+ activeId: string | null
+ onSelect: (id: string | null) => void
+ shouldReduceMotion: boolean
+}
+
+function SidebarCategories({
+ items,
+ activeId,
+ onSelect,
+ shouldReduceMotion,
+}: SidebarCategoriesProps) {
+ return (
+
+ {items.map((item) => {
+ const isActive = item.id === activeId
+ const key = item.id ?? 'all'
+ return (
+ -
+
+
+ )
+ })}
+
+ )
+}
diff --git a/apps/sim/app/(landing)/blog/tag-colors.ts b/apps/sim/app/(landing)/blog/tag-colors.ts
new file mode 100644
index 00000000000..1afc6f9ae88
--- /dev/null
+++ b/apps/sim/app/(landing)/blog/tag-colors.ts
@@ -0,0 +1,84 @@
+export interface Category {
+ id: string
+ label: string
+ color: string
+}
+
+export const CATEGORIES: Category[] = [
+ { id: 'announcements', label: 'Announcements', color: '#FA4EDF' },
+ { id: 'product', label: 'Product', color: '#00F701' },
+ { id: 'engineering', label: 'Engineering', color: '#2ABBF8' },
+ { id: 'design', label: 'Design', color: '#8B5CF6' },
+ { id: 'insights', label: 'Insights', color: '#FFCC02' },
+] as const
+
+const TAG_TO_CATEGORY: Record = {
+ // Announcements — company news, funding, milestones
+ Announcement: 'announcements',
+ Funding: 'announcements',
+ 'Series A': 'announcements',
+ YCombinator: 'announcements',
+
+ // Product — releases, features, enterprise
+ Release: 'product',
+ Enterprise: 'product',
+ Copilot: 'product',
+ MCP: 'product',
+ Integrations: 'product',
+ Observability: 'product',
+ Security: 'product',
+ 'Self-Hosted': 'product',
+ SSO: 'product',
+ SAML: 'product',
+ Compliance: 'product',
+ BYOK: 'product',
+ 'Access Control': 'product',
+ Whitelabel: 'product',
+ API: 'product',
+ Import: 'product',
+ Export: 'product',
+
+ // Engineering — architecture, internals, deep dives
+ Architecture: 'engineering',
+ Executor: 'engineering',
+ DAG: 'engineering',
+ Orchestration: 'engineering',
+ Multiplayer: 'engineering',
+ Realtime: 'engineering',
+ Collaboration: 'engineering',
+ WebSockets: 'engineering',
+ Benchmarks: 'engineering',
+ 'AI Assistant': 'engineering',
+
+ // Design — UI/UX, design systems, components
+ Design: 'design',
+ Emcn: 'design',
+ UI: 'design',
+ UX: 'design',
+ Components: 'design',
+
+ // Insights — comparisons, analysis, thought leadership
+ 'AI Agents': 'insights',
+ 'Workflow Automation': 'insights',
+ 'OpenAI AgentKit': 'insights',
+ n8n: 'insights',
+}
+
+export function getTagCategory(tag: string): string {
+ return TAG_TO_CATEGORY[tag] ?? 'insights'
+}
+
+export function getCategoryById(id: string): Category {
+ return CATEGORIES.find((c) => c.id === id) ?? CATEGORIES[4]
+}
+
+export function getPrimaryCategory(tags: string[]): Category {
+ if (tags.length === 0) return CATEGORIES[4]
+ const firstCatId = getTagCategory(tags[0])
+ return getCategoryById(firstCatId)
+}
+
+export function getTagColor(tag: string): string {
+ const catId = getTagCategory(tag)
+ return getCategoryById(catId).color
+}
diff --git a/apps/sim/app/(landing)/blog/tags/page.tsx b/apps/sim/app/(landing)/blog/tags/page.tsx
index a36ca97fd05..8e78c6e981b 100644
--- a/apps/sim/app/(landing)/blog/tags/page.tsx
+++ b/apps/sim/app/(landing)/blog/tags/page.tsx
@@ -1,33 +1,72 @@
import type { Metadata } from 'next'
import Link from 'next/link'
-import { getAllTags } from '@/lib/blog/registry'
+import { getAllPostMeta } from '@/lib/blog/registry'
+import { CATEGORIES, getPrimaryCategory } from '@/app/(landing)/blog/tag-colors'
export const metadata: Metadata = {
title: 'Tags',
}
export default async function TagsIndex() {
- const tags = await getAllTags()
+ const allPosts = await getAllPostMeta()
+
+ const categoryCounts: Record = {}
+ for (const cat of CATEGORIES) {
+ categoryCounts[cat.id] = 0
+ }
+ for (const post of allPosts) {
+ const cat = getPrimaryCategory(post.tags)
+ categoryCounts[cat.id] = (categoryCounts[cat.id] ?? 0) + 1
+ }
+
return (
-
- Browse by tag
+
+
+
+ Browse by Category
+
+
+
+ Topics
+
+
+ Filter posts by category to find what interests you.
+
- All
+ All Posts ({allPosts.length})
- {tags.map((t) => (
-
- {t.tag} ({t.count})
-
- ))}
+ {CATEGORIES.map((cat) => {
+ const count = categoryCounts[cat.id] ?? 0
+ return (
+
+
+ {cat.label}
+ ({count})
+
+ )
+ })}
-
+
)
}
diff --git a/apps/sim/app/api/changelog/releases/route.ts b/apps/sim/app/api/changelog/releases/route.ts
new file mode 100644
index 00000000000..303272f867d
--- /dev/null
+++ b/apps/sim/app/api/changelog/releases/route.ts
@@ -0,0 +1,50 @@
+import { NextResponse } from 'next/server'
+import { env } from '@/lib/core/config/env'
+
+const GITHUB_API_URL = 'https://api.github.com/repos/simstudioai/sim/releases'
+const PER_PAGE = 10
+
+export async function GET(request: Request) {
+ const { searchParams } = new URL(request.url)
+ const page = Math.max(1, Number(searchParams.get('page') ?? '1'))
+
+ try {
+ const token = env.GITHUB_TOKEN
+ const response = await fetch(`${GITHUB_API_URL}?per_page=${PER_PAGE}&page=${page}`, {
+ headers: {
+ Accept: 'application/vnd.github+json',
+ 'X-GitHub-Api-Version': '2022-11-28',
+ 'User-Agent': 'Sim/1.0',
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
+ },
+ next: { revalidate: 3600 },
+ })
+
+ if (!response.ok) {
+ return NextResponse.json(
+ { error: `GitHub API returned ${response.status}` },
+ { status: response.status }
+ )
+ }
+
+ const data = await response.json()
+
+ if (!Array.isArray(data)) {
+ return NextResponse.json({ error: 'Unexpected response from GitHub API' }, { status: 502 })
+ }
+
+ const releases = data
+ .filter((r: any) => !r.prerelease)
+ .map((r: any) => ({
+ tag: r.tag_name,
+ title: r.name || r.tag_name,
+ content: String(r.body || ''),
+ date: r.published_at,
+ url: r.html_url,
+ }))
+
+ return NextResponse.json({ releases })
+ } catch (error) {
+ return NextResponse.json({ error: 'Failed to fetch releases' }, { status: 500 })
+ }
+}
diff --git a/apps/sim/app/changelog/components/changelog-content.tsx b/apps/sim/app/changelog/components/changelog-content.tsx
index d8f3e687e93..27a39741b8c 100644
--- a/apps/sim/app/changelog/components/changelog-content.tsx
+++ b/apps/sim/app/changelog/components/changelog-content.tsx
@@ -1,5 +1,5 @@
-import { BookOpen, Github, Rss } from 'lucide-react'
-import Link from 'next/link'
+import { env } from '@/lib/core/config/env'
+import { ChangelogHero } from '@/app/changelog/components/changelog-hero'
import ChangelogList from '@/app/changelog/components/timeline-list'
export interface ChangelogEntry {
@@ -21,76 +21,51 @@ export default async function ChangelogContent() {
let entries: ChangelogEntry[] = []
try {
+ const token = env.GITHUB_TOKEN
const res = await fetch(
'https://api.github.com/repos/simstudioai/sim/releases?per_page=10&page=1',
{
- headers: { Accept: 'application/vnd.github+json' },
+ headers: {
+ Accept: 'application/vnd.github+json',
+ 'X-GitHub-Api-Version': '2022-11-28',
+ 'User-Agent': 'Sim/1.0',
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
+ },
next: { revalidate: 3600 },
}
)
- const releases: any[] = await res.json()
- entries = (releases || [])
- .filter((r) => !r.prerelease)
- .map((r) => ({
- tag: r.tag_name,
- title: r.name || r.tag_name,
- content: String(r.body || ''),
- date: r.published_at,
- url: r.html_url,
- contributors: extractMentions(String(r.body || '')),
- }))
+
+ if (!res.ok) {
+ entries = []
+ } else {
+ const releases = await res.json()
+ if (Array.isArray(releases)) {
+ entries = releases
+ .filter((r) => !r.prerelease)
+ .map((r) => ({
+ tag: r.tag_name,
+ title: r.name || r.tag_name,
+ content: String(r.body || ''),
+ date: r.published_at,
+ url: r.html_url,
+ contributors: extractMentions(String(r.body || '')),
+ }))
+ }
+ }
} catch (err) {
entries = []
}
return (
-
-
- {/* Left intro panel */}
-
-
-
Changelog
-
- Stay up-to-date with the latest features, improvements, and bug fixes in Sim. All
- changes are documented here with detailed release notes.
-
-
-
-
-
-
- View on GitHub
-
-
-
- Documentation
-
-
-
- RSS Feed
-
-
-
-
-
- {/* Right timeline */}
-
-
+
+
+
+
+
+ All Releases
+
+
+
)
}
diff --git a/apps/sim/app/changelog/components/changelog-hero.tsx b/apps/sim/app/changelog/components/changelog-hero.tsx
new file mode 100644
index 00000000000..e581b31670b
--- /dev/null
+++ b/apps/sim/app/changelog/components/changelog-hero.tsx
@@ -0,0 +1,82 @@
+'use client'
+
+import { motion, useReducedMotion } from 'framer-motion'
+import { BookOpen, Github, Rss } from 'lucide-react'
+import Link from 'next/link'
+
+const EASE_OUT_QUINT = [0.23, 1, 0.32, 1] as const
+const STAGGER = 0.2
+const DURATION = 0.4
+const Y_OFFSET = 12
+
+const containerVariants = {
+ hidden: {},
+ visible: {
+ transition: { staggerChildren: STAGGER },
+ },
+}
+
+const itemVariants = {
+ hidden: { opacity: 0, y: Y_OFFSET },
+ visible: {
+ opacity: 1,
+ y: 0,
+ transition: { duration: DURATION, ease: EASE_OUT_QUINT },
+ },
+}
+
+export function ChangelogHero() {
+ const shouldReduceMotion = useReducedMotion()
+
+ return (
+
+
+
+
+
+ What's new in Sim.
+
+
+ Stay up-to-date with the latest features, improvements, and bug fixes.
+
+
+
+
+ View on GitHub
+
+
+
+ Documentation
+
+
+
+ RSS Feed
+
+
+
+
+
+ )
+}
diff --git a/apps/sim/app/changelog/components/timeline-list.tsx b/apps/sim/app/changelog/components/timeline-list.tsx
index 968d80ea087..f327c7ac51f 100644
--- a/apps/sim/app/changelog/components/timeline-list.tsx
+++ b/apps/sim/app/changelog/components/timeline-list.tsx
@@ -1,12 +1,19 @@
'use client'
import React from 'react'
+import { motion, useReducedMotion } from 'framer-motion'
+import { ArrowUpRight } from 'lucide-react'
import ReactMarkdown from 'react-markdown'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/emcn'
+import { formatDate } from '@/lib/core/utils/formatting'
import type { ChangelogEntry } from '@/app/changelog/components/changelog-content'
type Props = { initialEntries: ChangelogEntry[] }
+const EASE_OUT_QUINT = [0.23, 1, 0.32, 1] as const
+const CARD_DURATION = 0.35
+const CARD_Y = 16
+
function sanitizeContent(body: string): string {
return body.replace(/ /g, '')
}
@@ -32,19 +39,33 @@ function stripContributors(body: string): string {
return output
}
-function isContributorsLabel(nodeChildren: React.ReactNode): boolean {
- return /^\s*contributors\s*:?\s*$/i.test(String(nodeChildren))
-}
-
function stripPrReferences(body: string): string {
return body.replace(/\s*\(\s*\[#\d+\]\([^)]*\)\s*\)/g, '').replace(/\s*\(\s*#\d+\s*\)/g, '')
}
+function stripViewOnGitHub(body: string): string {
+ return body.replace(/\n*\[View changes on GitHub\]\([^)]*\)\s*$/gi, '')
+}
+
+function stripCommitPrefix(text: string): string {
+ const cleaned = text.replace(
+ /^(feat|fix|improvement|chore|refactor|docs|test|ci|perf|style|build|revert)\([^)]*\)\s*:\s*/i,
+ ''
+ )
+ if (cleaned.length === 0) return text
+ return cleaned.charAt(0).toUpperCase() + cleaned.slice(1)
+}
+
+function isContributorsLabel(nodeChildren: React.ReactNode): boolean {
+ return /^\s*contributors\s*:?\s*$/i.test(String(nodeChildren))
+}
+
function cleanMarkdown(body: string): string {
const sanitized = sanitizeContent(body)
const withoutContribs = stripContributors(sanitized)
const withoutPrs = stripPrReferences(withoutContribs)
- return withoutPrs
+ const withoutGhLink = stripViewOnGitHub(withoutPrs)
+ return withoutGhLink
}
function extractMentions(body: string): string[] {
@@ -52,6 +73,195 @@ function extractMentions(body: string): string[] {
return Array.from(new Set(matches.map((m) => m.slice(1))))
}
+function ReleaseCard({ entry, index }: { entry: ChangelogEntry; index: number }) {
+ const shouldReduceMotion = useReducedMotion()
+
+ return (
+
+
+
+ {entry.title !== entry.tag && (
+
+ {entry.title}
+
+ )}
+
+
{
+ if (isContributorsLabel(children)) return null
+ return (
+
+
+ {children}
+
+ )
+ },
+ h3: ({ children, ...props }) => {
+ if (isContributorsLabel(children)) return null
+ return (
+
+
+ {children}
+
+ )
+ },
+ ul: ({ children, ...props }) => (
+
+ ),
+ li: ({ children, ...props }) => {
+ const text = String(children)
+ if (/^\s*contributors\s*:?\s*$/i.test(text)) return null
+ return (
+
+
+
+ {children}
+
+
+ )
+ },
+ p: ({ children, ...props }) =>
+ /^\s*contributors\s*:?\s*$/i.test(String(children)) ? null : (
+
+ {children}
+
+ ),
+ strong: ({ children, ...props }) => (
+
+ {children}
+
+ ),
+ code: ({ children, ...props }) => (
+
+ {children}
+
+ ),
+ img: () => null,
+ a: ({ className, ...props }: any) => (
+
+ ),
+ }}
+ >
+ {cleanMarkdown(entry.content)}
+
+
+
+ {entry.contributors && entry.contributors.length > 0 && (
+
+
+
+ {entry.contributors
+ .slice(0, 3)
+ .map((c) => c)
+ .join(', ')}
+ {entry.contributors.length > 3 && ` +${entry.contributors.length - 3}`}
+
+
+ )}
+
+ )
+}
+
+function CleanedListContent({ children }: { children: React.ReactNode }) {
+ let cleaned = false
+ const result = React.Children.map(children, (child) => {
+ if (!cleaned && typeof child === 'string') {
+ cleaned = true
+ return stripCommitPrefix(child)
+ }
+ return child
+ })
+ return <>{result}>
+}
+
export default function ChangelogList({ initialEntries }: Props) {
const [entries, setEntries] = React.useState
(initialEntries)
const [page, setPage] = React.useState(1)
@@ -63,28 +273,32 @@ export default function ChangelogList({ initialEntries }: Props) {
setLoading(true)
try {
const nextPage = page + 1
- const res = await fetch(
- `https://api.github.com/repos/simstudioai/sim/releases?per_page=10&page=${nextPage}`,
- { headers: { Accept: 'application/vnd.github+json' } }
- )
- const releases: any[] = await res.json()
- const mapped: ChangelogEntry[] = (releases || [])
- .filter((r) => !r.prerelease)
- .map((r) => ({
- tag: r.tag_name,
- title: r.name || r.tag_name,
- content: sanitizeContent(String(r.body || '')),
- date: r.published_at,
- url: r.html_url,
- contributors: extractMentions(String(r.body || '')),
- }))
-
- if (mapped.length === 0) {
+ const res = await fetch(`/api/changelog/releases?page=${nextPage}`)
+
+ if (!res.ok) {
+ setDone(true)
+ return
+ }
+
+ const data = await res.json()
+ const releases = data?.releases
+
+ if (!Array.isArray(releases) || releases.length === 0) {
setDone(true)
- } else {
- setEntries((prev) => [...prev, ...mapped])
- setPage(nextPage)
+ return
}
+
+ const mapped: ChangelogEntry[] = releases.map((r: any) => ({
+ tag: r.tag,
+ title: r.title,
+ content: r.content,
+ date: r.date,
+ url: r.url,
+ contributors: extractMentions(String(r.content || '')),
+ }))
+
+ setEntries((prev) => [...prev, ...mapped])
+ setPage(nextPage)
} catch {
setDone(true)
} finally {
@@ -93,133 +307,22 @@ export default function ChangelogList({ initialEntries }: Props) {
}
return (
-
- {entries.map((entry) => (
-
-
-
-
- {entry.tag}
-
- {entry.contributors && entry.contributors.length > 0 && (
-
- )}
-
-
- {new Date(entry.date).toLocaleDateString('en-US', {
- year: 'numeric',
- month: 'short',
- day: 'numeric',
- })}
-
-
-
-
-
- isContributorsLabel(children) ? null : (
-
- {children}
-
- ),
- h3: ({ children, ...props }) =>
- isContributorsLabel(children) ? null : (
-
- {children}
-
- ),
- ul: ({ children, ...props }) => (
-
- ),
- li: ({ children, ...props }) => {
- const text = String(children)
- if (/^\s*contributors\s*:?\s*$/i.test(text)) return null
- return (
-
- {children}
-
- )
- },
- p: ({ children, ...props }) =>
- /^\s*contributors\s*:?\s*$/i.test(String(children)) ? null : (
-
- {children}
-
- ),
- strong: ({ children, ...props }) => (
-
- {children}
-
- ),
- code: ({ children, ...props }) => (
-
- {children}
-
- ),
- img: () => null,
- a: ({ className, ...props }: any) => (
-
- ),
- }}
- >
- {cleanMarkdown(entry.content)}
-
-
-
- ))}
+
+
+ {entries.map((entry, index) => (
+
+ ))}
+
{!done && (
-
+
)}
diff --git a/apps/sim/app/changelog/layout.tsx b/apps/sim/app/changelog/layout.tsx
index e7d7153dfca..7dedb4979b6 100644
--- a/apps/sim/app/changelog/layout.tsx
+++ b/apps/sim/app/changelog/layout.tsx
@@ -1,17 +1,21 @@
import { martianMono } from '@/app/_styles/fonts/martian-mono/martian-mono'
+import { season } from '@/app/_styles/fonts/season/season'
import Footer from '@/app/(home)/components/footer/footer'
import Navbar from '@/app/(home)/components/navbar/navbar'
+import '@/app/(landing)/blog/studio-scrollbar.css'
export default function ChangelogLayout({ children }: { children: React.ReactNode }) {
return (
-
+
- {children}
-
+ {children}
+
+
+
)
}
diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/loading.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/loading.tsx
new file mode 100644
index 00000000000..0b15b0c4f99
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/knowledge/loading.tsx
@@ -0,0 +1,61 @@
+import { Skeleton } from '@/components/emcn'
+
+const SKELETON_ROW_COUNT = 5
+const COLUMN_COUNT = 6
+
+export default function KnowledgeLoading() {
+ return (
+
+
+
+
+
+
+
+ |
+
+ |
+ {Array.from({ length: COLUMN_COUNT }).map((_, i) => (
+
+
+ |
+ ))}
+
+
+
+ {Array.from({ length: SKELETON_ROW_COUNT }).map((_, rowIndex) => (
+
+ |
+
+ |
+ {Array.from({ length: COLUMN_COUNT }).map((_, colIndex) => (
+
+
+ |
+ ))}
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/apps/sim/content/authors/emir.json b/apps/sim/content/authors/emir.json
index eb0e5305d25..38d2fb84fc1 100644
--- a/apps/sim/content/authors/emir.json
+++ b/apps/sim/content/authors/emir.json
@@ -1,7 +1,7 @@
{
"id": "emir",
"name": "Emir Karabeg",
- "url": "https://x.com/karabegemir",
- "xHandle": "karabegemir",
+ "url": "https://x.com/emkara",
+ "xHandle": "emkara",
"avatarUrl": "/blog/authors/emir.jpg"
}
diff --git a/apps/sim/content/blog/multiplayer/index.mdx b/apps/sim/content/blog/multiplayer/index.mdx
index 58b59970815..a5f55e37909 100644
--- a/apps/sim/content/blog/multiplayer/index.mdx
+++ b/apps/sim/content/blog/multiplayer/index.mdx
@@ -178,4 +178,4 @@ Multiplayer workflow building is no longer a technical curiosity—it's how team
---
-*Interested in how Sim's multiplayer system works in practice? [Try building a workflow](https://sim.ai) with a collaborator in real-time.*
+> Interested in how Sim's multiplayer system works in practice? [Try building a workflow](https://sim.ai) with a collaborator in real-time.
diff --git a/apps/sim/lib/blog/code.tsx b/apps/sim/lib/blog/code.tsx
index fd405f7feee..9b39d321eee 100644
--- a/apps/sim/lib/blog/code.tsx
+++ b/apps/sim/lib/blog/code.tsx
@@ -1,21 +1,136 @@
'use client'
-import { Code } from '@/components/emcn'
+import { useCallback, useState } from 'react'
+import { Check, Copy } from 'lucide-react'
+import { highlight, languages } from 'prismjs'
+import 'prismjs/components/prism-typescript'
+import 'prismjs/components/prism-javascript'
+import 'prismjs/components/prism-json'
+import 'prismjs/components/prism-python'
+import 'prismjs/components/prism-bash'
+import 'prismjs/components/prism-css'
+import 'prismjs/components/prism-yaml'
+import 'prismjs/components/prism-markdown'
+import 'prismjs/components/prism-jsx'
+import 'prismjs/components/prism-tsx'
+import 'prismjs/components/prism-sql'
+import 'prismjs/components/prism-graphql'
+import 'prismjs/components/prism-go'
+import 'prismjs/components/prism-rust'
+import 'prismjs/components/prism-toml'
+import 'prismjs/components/prism-diff'
+import 'prismjs/components/prism-docker'
interface CodeBlockProps {
code: string
- language: 'javascript' | 'json' | 'python'
+ language: string
+}
+
+const LANG_MAP: Record
= {
+ js: 'javascript',
+ jsx: 'jsx',
+ ts: 'typescript',
+ tsx: 'tsx',
+ typescript: 'typescript',
+ javascript: 'javascript',
+ json: 'json',
+ python: 'python',
+ py: 'python',
+ bash: 'bash',
+ sh: 'bash',
+ shell: 'bash',
+ css: 'css',
+ yaml: 'yaml',
+ yml: 'yaml',
+ md: 'markdown',
+ markdown: 'markdown',
+ html: 'markup',
+ xml: 'markup',
+ sql: 'sql',
+ graphql: 'graphql',
+ go: 'go',
+ rust: 'rust',
+ toml: 'toml',
+ diff: 'diff',
+ dockerfile: 'docker',
+}
+
+const LANG_LABEL: Record = {
+ javascript: 'JavaScript',
+ typescript: 'TypeScript',
+ jsx: 'JSX',
+ tsx: 'TSX',
+ json: 'JSON',
+ python: 'Python',
+ bash: 'Bash',
+ css: 'CSS',
+ html: 'HTML',
+ yaml: 'YAML',
+ markdown: 'Markdown',
+ sql: 'SQL',
+ graphql: 'GraphQL',
+ go: 'Go',
+ rust: 'Rust',
+ toml: 'TOML',
+ diff: 'Diff',
+ dockerfile: 'Dockerfile',
+ markup: 'HTML',
+ docker: 'Dockerfile',
}
export function CodeBlock({ code, language }: CodeBlockProps) {
+ const [copied, setCopied] = useState(false)
+
+ const lang = LANG_MAP[language.toLowerCase()] || 'javascript'
+ const grammar = languages[lang] || languages.javascript
+ const highlighted = highlight(code, grammar, lang)
+ const label = LANG_LABEL[lang] || LANG_LABEL[language.toLowerCase()] || language || 'Code'
+
+ const handleCopy = useCallback(async () => {
+ try {
+ await navigator.clipboard.writeText(code)
+ setCopied(true)
+ setTimeout(() => setCopied(false), 1500)
+ } catch {
+ /* clipboard unavailable */
+ }
+ }, [code])
+
return (
-
-
+
+
+
+
+
+
+
+
+ {label}
+
+
+
+
)
}
diff --git a/apps/sim/lib/blog/mdx.tsx b/apps/sim/lib/blog/mdx.tsx
index 67302696f58..71b3dbe50e4 100644
--- a/apps/sim/lib/blog/mdx.tsx
+++ b/apps/sim/lib/blog/mdx.tsx
@@ -2,6 +2,7 @@ import clsx from 'clsx'
import Image from 'next/image'
import type { MDXRemoteProps } from 'next-mdx-remote/rsc'
import { CodeBlock } from '@/lib/blog/code'
+import { MermaidDiagram } from '@/lib/blog/mermaid'
export const mdxComponents: MDXRemoteProps['components'] = {
img: (props: any) => (
@@ -102,27 +103,18 @@ export const mdxComponents: MDXRemoteProps['components'] = {
const codeContent = child.props.children || ''
const className = child.props.className || ''
const language = className.replace('language-', '') || 'javascript'
-
- const languageMap: Record
= {
- js: 'javascript',
- jsx: 'javascript',
- ts: 'javascript',
- tsx: 'javascript',
- typescript: 'javascript',
- javascript: 'javascript',
- json: 'json',
- python: 'python',
- py: 'python',
+ const code = typeof codeContent === 'string' ? codeContent.trim() : String(codeContent)
+ if (language.toLowerCase() === 'mermaid') {
+ return (
+
+
+
+ )
}
- const mappedLanguage = languageMap[language.toLowerCase()] || 'javascript'
-
return (
-
+
)
}
diff --git a/apps/sim/lib/blog/mermaid.tsx b/apps/sim/lib/blog/mermaid.tsx
new file mode 100644
index 00000000000..14a624a8882
--- /dev/null
+++ b/apps/sim/lib/blog/mermaid.tsx
@@ -0,0 +1,149 @@
+'use client'
+
+import { useCallback, useEffect, useId, useRef, useState } from 'react'
+
+interface MermaidDiagramProps {
+ chart: string
+}
+
+export function MermaidDiagram({ chart }: MermaidDiagramProps) {
+ const id = useId().replace(/:/g, '-')
+ const containerId = `mermaid-${id}`
+ const containerRef = useRef(null)
+ const [svg, setSvg] = useState('')
+ const [error, setError] = useState(null)
+ const rendered = useRef(false)
+
+ const renderDiagram = useCallback(async () => {
+ if (rendered.current) return
+ rendered.current = true
+
+ try {
+ const mermaid = (await import('mermaid')).default
+
+ mermaid.initialize({
+ startOnLoad: false,
+ theme: 'base',
+ fontFamily: 'var(--font-martian-mono), ui-monospace, monospace',
+ fontSize: 13,
+ themeVariables: {
+ // Background
+ mainBkg: '#232323',
+ secondBkg: '#1C1C1C',
+ tertiaryColor: '#2A2A2A',
+
+ // Text
+ primaryTextColor: '#ECECEC',
+ secondaryTextColor: '#999999',
+ tertiaryTextColor: '#666666',
+
+ // Lines and borders
+ lineColor: '#3d3d3d',
+ primaryBorderColor: '#2A2A2A',
+ secondaryBorderColor: '#3d3d3d',
+
+ // Nodes
+ nodeBorder: '#3d3d3d',
+ clusterBorder: '#2A2A2A',
+ clusterBkg: '#1C1C1C',
+
+ // Brand accent colors for different node types
+ primaryColor: '#232323',
+ secondaryColor: '#1C1C1C',
+
+ // Flowchart-specific
+ edgeLabelBackground: '#1C1C1C',
+
+ // Notes
+ noteBkgColor: '#232323',
+ noteTextColor: '#ECECEC',
+ noteBorderColor: '#2A2A2A',
+
+ // Sequence diagram
+ actorBkg: '#232323',
+ actorTextColor: '#ECECEC',
+ actorBorder: '#3d3d3d',
+ activationBorderColor: '#2ABBF8',
+ activationBkgColor: '#232323',
+ signalColor: '#ECECEC',
+ signalTextColor: '#ECECEC',
+ labelBoxBkgColor: '#232323',
+ labelBoxBorderColor: '#2A2A2A',
+ labelTextColor: '#ECECEC',
+ loopTextColor: '#999999',
+ },
+ })
+
+ const { svg: renderedSvg } = await mermaid.render(containerId, chart.trim())
+ setSvg(renderedSvg)
+ } catch (err) {
+ setError(err instanceof Error ? err.message : 'Failed to render diagram')
+ }
+ }, [chart, containerId])
+
+ useEffect(() => {
+ renderDiagram()
+ }, [renderDiagram])
+
+ if (error) {
+ return (
+
+
+
+
+
+
+
+
+ Mermaid
+
+
+
+
+ {chart}
+
+
+ )
+ }
+
+ if (!svg) {
+ return (
+
+
+
+ Rendering diagram...
+
+
+ )
+ }
+
+ return (
+
+
+
+
+
+
+
+
+ Diagram
+
+
+
+
+
+ )
+}
diff --git a/apps/sim/lib/blog/registry.ts b/apps/sim/lib/blog/registry.ts
index fa6c9f1ee55..658716c16e1 100644
--- a/apps/sim/lib/blog/registry.ts
+++ b/apps/sim/lib/blog/registry.ts
@@ -1,5 +1,6 @@
import fs from 'fs/promises'
import path from 'path'
+import { slug as githubSlug } from 'github-slugger'
import matter from 'gray-matter'
import { compileMDX } from 'next-mdx-remote/rsc'
import rehypeAutolinkHeadings from 'rehype-autolink-headings'
@@ -32,11 +33,7 @@ async function loadAuthors(): Promise> {
}
function slugify(text: string): string {
- return text
- .toLowerCase()
- .trim()
- .replace(/[^a-z0-9\s-]/g, '')
- .replace(/\s+/g, '-')
+ return githubSlug(text)
}
async function scanFrontmatters(): Promise {
@@ -142,13 +139,14 @@ export async function getPostBySlug(slug: string): Promise {
},
},
})
- const headings: { text: string; id: string }[] = []
+ const headings: { text: string; id: string; level: number }[] = []
const lines = content.split('\n')
for (const line of lines) {
- const match = /^##\s+(.+)$/.exec(line.trim())
+ const match = /^(#{2,3})\s+(.+)$/.exec(line.trim())
if (match) {
- const text = match[1].trim()
- headings.push({ text, id: slugify(text) })
+ const level = match[1].length
+ const text = match[2].trim()
+ headings.push({ text, id: slugify(text), level })
}
}
return {
diff --git a/apps/sim/lib/blog/schema.ts b/apps/sim/lib/blog/schema.ts
index 2d49492929c..642271ff551 100644
--- a/apps/sim/lib/blog/schema.ts
+++ b/apps/sim/lib/blog/schema.ts
@@ -65,7 +65,7 @@ export interface BlogMeta {
export interface BlogPost extends BlogMeta {
Content: React.ComponentType
- headings?: { text: string; id: string }[]
+ headings?: { text: string; id: string; level: number }[]
}
export interface TagWithCount {
diff --git a/apps/sim/lib/blog/seo.ts b/apps/sim/lib/blog/seo.ts
index ef314d2385b..fcd8eb1fb52 100644
--- a/apps/sim/lib/blog/seo.ts
+++ b/apps/sim/lib/blog/seo.ts
@@ -33,7 +33,7 @@ export function buildPostMetadata(post: BlogMeta): Metadata {
tags: post.tags,
images: [
{
- url: post.ogImage.startsWith('http') ? post.ogImage : `${baseUrl}${post.ogImage}`,
+ url: `${baseUrl}/blog/og?slug=${encodeURIComponent(post.slug)}`,
width: 1200,
height: 630,
alt: post.ogAlt || post.title,
@@ -44,7 +44,7 @@ export function buildPostMetadata(post: BlogMeta): Metadata {
card: 'summary_large_image',
title: post.title,
description: post.description,
- images: [post.ogImage],
+ images: [`${baseUrl}/blog/og?slug=${encodeURIComponent(post.slug)}`],
creator: post.author.url?.includes('x.com') ? `@${post.author.xHandle || ''}` : undefined,
site: '@simdotai',
},
diff --git a/apps/sim/lib/core/utils/formatting.ts b/apps/sim/lib/core/utils/formatting.ts
index d6985147f68..38a387a2452 100644
--- a/apps/sim/lib/core/utils/formatting.ts
+++ b/apps/sim/lib/core/utils/formatting.ts
@@ -254,3 +254,10 @@ export function formatRelativeTime(dateString: string): string {
const years = Math.floor(diffInSeconds / 31536000)
return `${years}y ago`
}
+
+export function hexToRgba(hex: string, alpha: number): string {
+ const r = Number.parseInt(hex.slice(1, 3), 16)
+ const g = Number.parseInt(hex.slice(3, 5), 16)
+ const b = Number.parseInt(hex.slice(5, 7), 16)
+ return `rgba(${r},${g},${b},${alpha})`
+}
diff --git a/apps/sim/package.json b/apps/sim/package.json
index 8e9fee648e5..147e67de827 100644
--- a/apps/sim/package.json
+++ b/apps/sim/package.json
@@ -126,6 +126,7 @@
"lucide-react": "^0.479.0",
"mammoth": "^1.9.0",
"marked": "17.0.4",
+ "mermaid": "11.13.0",
"mongodb": "6.19.0",
"mysql2": "3.14.3",
"nanoid": "^3.3.7",
diff --git a/bun.lock b/bun.lock
index 61df0c93763..5f65e6265a5 100644
--- a/bun.lock
+++ b/bun.lock
@@ -152,6 +152,7 @@
"lucide-react": "^0.479.0",
"mammoth": "^1.9.0",
"marked": "17.0.4",
+ "mermaid": "11.13.0",
"mongodb": "6.19.0",
"mysql2": "3.14.3",
"nanoid": "^3.3.7",
@@ -374,6 +375,8 @@
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
+ "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
+
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.71.2", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-TGNDEUuEstk/DKu0/TflXAEt+p+p/WhTlFzEnoosvbaDU2LTjm42igSdlL0VijrKpWejtOKxX0b8A7uc+XiSAQ=="],
"@ark/schema": ["@ark/schema@0.56.0", "", { "dependencies": { "@ark/util": "0.56.0" } }, "sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA=="],
@@ -594,6 +597,8 @@
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.0.0-beta.5", "", { "os": "win32", "cpu": "x64" }, "sha512-N7Yby52BJmvEdst1iMbclE5hxxefboaXKRJLm1tLfBYr4FeuoCe6j8HdiQSwhCRdIUGFFqBLaDXh//LLF6EReA=="],
+ "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="],
+
"@browserbasehq/sdk": ["@browserbasehq/sdk@2.6.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-83iXP5D7xMm8Wyn66TUaUrgoByCmAJuoMoZQI3sGg3JAiMlTfnCIMqyVBoNSaItaPIkaCnrsj6LiusmXV2X9YA=="],
"@browserbasehq/stagehand": ["@browserbasehq/stagehand@3.0.8", "", { "dependencies": { "@ai-sdk/provider": "^2.0.0", "@anthropic-ai/sdk": "0.39.0", "@browserbasehq/sdk": "^2.4.0", "@google/genai": "^1.22.0", "@langchain/openai": "^0.4.4", "@modelcontextprotocol/sdk": "^1.17.2", "ai": "^5.0.0", "devtools-protocol": "^0.0.1464554", "fetch-cookie": "^3.1.0", "openai": "^4.87.1", "pino": "^9.6.0", "pino-pretty": "^13.0.0", "uuid": "^11.1.0", "ws": "^8.18.0", "zod-to-json-schema": "^3.25.0" }, "optionalDependencies": { "@ai-sdk/anthropic": "^2.0.34", "@ai-sdk/azure": "^2.0.54", "@ai-sdk/cerebras": "^1.0.25", "@ai-sdk/deepseek": "^1.0.23", "@ai-sdk/google": "^2.0.23", "@ai-sdk/google-vertex": "^3.0.70", "@ai-sdk/groq": "^2.0.24", "@ai-sdk/mistral": "^2.0.19", "@ai-sdk/openai": "^2.0.53", "@ai-sdk/perplexity": "^2.0.13", "@ai-sdk/togetherai": "^1.0.23", "@ai-sdk/xai": "^2.0.26", "@langchain/core": "^0.3.40", "bufferutil": "^4.0.9", "chrome-launcher": "^1.2.0", "ollama-ai-provider-v2": "^1.5.0", "patchright-core": "^1.55.2", "playwright": "^1.52.0", "playwright-core": "^1.54.1", "puppeteer-core": "^22.8.0" }, "peerDependencies": { "deepmerge": "^4.3.1", "dotenv": "^16.4.5", "zod": "^3.25.76 || ^4.2.0" } }, "sha512-ppI4PmqjRnFEpTtaQyRzKZgL4uVzscOQsDjBpQlvZNhhEp3da1wBaP1ml/hMfsuAmY9wOUwdN4V0uyyRbxWAdA=="],
@@ -606,6 +611,16 @@
"@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="],
+ "@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@11.1.2", "", { "dependencies": { "@chevrotain/gast": "11.1.2", "@chevrotain/types": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-XTsjvDVB5nDZBQB8o0o/0ozNelQtn2KrUVteIHSlPd2VAV2utEb6JzyCJaJ8tGxACR4RiBNWy5uYUHX2eji88Q=="],
+
+ "@chevrotain/gast": ["@chevrotain/gast@11.1.2", "", { "dependencies": { "@chevrotain/types": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-Z9zfXR5jNZb1Hlsd/p+4XWeUFugrHirq36bKzPWDSIacV+GPSVXdk+ahVWZTwjhNwofAWg/sZg58fyucKSQx5g=="],
+
+ "@chevrotain/regexp-to-ast": ["@chevrotain/regexp-to-ast@11.1.2", "", {}, "sha512-nMU3Uj8naWer7xpZTYJdxbAs6RIv/dxYzkYU8GSwgUtcAAlzjcPfX1w+RKRcYG8POlzMeayOQ/znfwxEGo5ulw=="],
+
+ "@chevrotain/types": ["@chevrotain/types@11.1.2", "", {}, "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw=="],
+
+ "@chevrotain/utils": ["@chevrotain/utils@11.1.2", "", {}, "sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA=="],
+
"@connectrpc/connect": ["@connectrpc/connect@2.0.0-rc.3", "", { "peerDependencies": { "@bufbuild/protobuf": "^2.2.0" } }, "sha512-ARBt64yEyKbanyRETTjcjJuHr2YXorzQo0etyS5+P6oSeW8xEuzajA9g+zDnMcj1hlX2dQE93foIWQGfpru7gQ=="],
"@connectrpc/connect-web": ["@connectrpc/connect-web@2.0.0-rc.3", "", { "peerDependencies": { "@bufbuild/protobuf": "^2.2.0", "@connectrpc/connect": "2.0.0-rc.3" } }, "sha512-w88P8Lsn5CCsA7MFRl2e6oLY4J/5toiNtJns/YJrlyQaWOy3RO8pDgkz+iIkG98RPMhj2thuBvsd3Cn4DKKCkw=="],
@@ -720,6 +735,10 @@
"@hookform/resolvers": ["@hookform/resolvers@4.1.3", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.0.0" } }, "sha512-Jsv6UOWYTrEFJ/01ZrnwVXs7KDvP8XIo115i++5PWvNkNvkrsTfGiLS6w+eJ57CYtUtDQalUWovCZDHFJ8u1VQ=="],
+ "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
+
+ "@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="],
+
"@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="],
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.0" }, "os": "darwin", "cpu": "arm64" }, "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg=="],
@@ -810,6 +829,8 @@
"@mdx-js/react": ["@mdx-js/react@3.1.1", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw=="],
+ "@mermaid-js/parser": ["@mermaid-js/parser@1.0.1", "", { "dependencies": { "langium": "^4.0.0" } }, "sha512-opmV19kN1JsK0T6HhhokHpcVkqKpF+x2pPDKKM2ThHtZAB5F4PROopk0amuVYK5qMrIA4erzpNm8gmPNJgMDxQ=="],
+
"@microsoft/fetch-event-source": ["@microsoft/fetch-event-source@2.0.1", "", {}, "sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA=="],
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.20.2", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-6rqTdFt67AAAzln3NOKsXRmv5ZzPkgbfaebKBqUbts7vK1GZudqnrun5a8d3M/h955cam9RHZ6Jb4Y1XhnmFPg=="],
@@ -1610,6 +1631,8 @@
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
+ "@upsetjs/venn.js": ["@upsetjs/venn.js@2.0.0", "", { "optionalDependencies": { "d3-selection": "^3.0.0", "d3-transition": "^3.0.1" } }, "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw=="],
+
"@vercel/og": ["@vercel/og@0.6.8", "", { "dependencies": { "@resvg/resvg-wasm": "2.4.0", "satori": "0.12.2", "yoga-wasm-web": "0.3.3" } }, "sha512-e4kQK9mP8ntpo3dACWirGod/hHv4qO5JMj9a/0a2AZto7b4persj5YP7t1Er372gTtYFTYxNhMx34jRvHooglw=="],
"@vercel/oidc": ["@vercel/oidc@3.1.0", "", {}, "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w=="],
@@ -1834,6 +1857,10 @@
"cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="],
+ "chevrotain": ["chevrotain@11.1.2", "", { "dependencies": { "@chevrotain/cst-dts-gen": "11.1.2", "@chevrotain/gast": "11.1.2", "@chevrotain/regexp-to-ast": "11.1.2", "@chevrotain/types": "11.1.2", "@chevrotain/utils": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-opLQzEVriiH1uUQ4Kctsd49bRoFDXGGSC4GUqj7pGyxM3RehRhvTlZJc1FL/Flew2p5uwxa1tUDWKzI4wNM8pg=="],
+
+ "chevrotain-allstar": ["chevrotain-allstar@0.3.1", "", { "dependencies": { "lodash-es": "^4.17.21" }, "peerDependencies": { "chevrotain": "^11.0.0" } }, "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw=="],
+
"chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
@@ -1922,6 +1949,8 @@
"cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="],
+ "cose-base": ["cose-base@1.0.3", "", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="],
+
"cpu-features": ["cpu-features@0.0.10", "", { "dependencies": { "buildcheck": "~0.0.6", "nan": "^2.19.0" } }, "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA=="],
"crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="],
@@ -1958,24 +1987,78 @@
"csv-parse": ["csv-parse@6.1.0", "", {}, "sha512-CEE+jwpgLn+MmtCpVcPtiCZpVtB6Z2OKPTr34pycYYoL7sxdOkXDdQ4lRiw6ioC0q6BLqhc6cKweCVvral8yhw=="],
+ "cytoscape": ["cytoscape@3.33.1", "", {}, "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ=="],
+
+ "cytoscape-cose-bilkent": ["cytoscape-cose-bilkent@4.1.0", "", { "dependencies": { "cose-base": "^1.0.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ=="],
+
+ "cytoscape-fcose": ["cytoscape-fcose@2.2.0", "", { "dependencies": { "cose-base": "^2.2.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ=="],
+
+ "d3": ["d3@7.9.0", "", { "dependencies": { "d3-array": "3", "d3-axis": "3", "d3-brush": "3", "d3-chord": "3", "d3-color": "3", "d3-contour": "4", "d3-delaunay": "6", "d3-dispatch": "3", "d3-drag": "3", "d3-dsv": "3", "d3-ease": "3", "d3-fetch": "3", "d3-force": "3", "d3-format": "3", "d3-geo": "3", "d3-hierarchy": "3", "d3-interpolate": "3", "d3-path": "3", "d3-polygon": "3", "d3-quadtree": "3", "d3-random": "3", "d3-scale": "4", "d3-scale-chromatic": "3", "d3-selection": "3", "d3-shape": "3", "d3-time": "3", "d3-time-format": "4", "d3-timer": "3", "d3-transition": "3", "d3-zoom": "3" } }, "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA=="],
+
+ "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
+
+ "d3-axis": ["d3-axis@3.0.0", "", {}, "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="],
+
+ "d3-brush": ["d3-brush@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "3", "d3-transition": "3" } }, "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ=="],
+
+ "d3-chord": ["d3-chord@3.0.1", "", { "dependencies": { "d3-path": "1 - 3" } }, "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g=="],
+
"d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="],
+ "d3-contour": ["d3-contour@4.0.2", "", { "dependencies": { "d3-array": "^3.2.0" } }, "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA=="],
+
+ "d3-delaunay": ["d3-delaunay@6.0.4", "", { "dependencies": { "delaunator": "5" } }, "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A=="],
+
"d3-dispatch": ["d3-dispatch@3.0.1", "", {}, "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="],
"d3-drag": ["d3-drag@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-selection": "3" } }, "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg=="],
+ "d3-dsv": ["d3-dsv@3.0.1", "", { "dependencies": { "commander": "7", "iconv-lite": "0.6", "rw": "1" }, "bin": { "csv2json": "bin/dsv2json.js", "csv2tsv": "bin/dsv2dsv.js", "dsv2dsv": "bin/dsv2dsv.js", "dsv2json": "bin/dsv2json.js", "json2csv": "bin/json2dsv.js", "json2dsv": "bin/json2dsv.js", "json2tsv": "bin/json2dsv.js", "tsv2csv": "bin/dsv2dsv.js", "tsv2json": "bin/dsv2json.js" } }, "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q=="],
+
"d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="],
+ "d3-fetch": ["d3-fetch@3.0.1", "", { "dependencies": { "d3-dsv": "1 - 3" } }, "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw=="],
+
+ "d3-force": ["d3-force@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-quadtree": "1 - 3", "d3-timer": "1 - 3" } }, "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg=="],
+
+ "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="],
+
+ "d3-geo": ["d3-geo@3.1.1", "", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="],
+
+ "d3-hierarchy": ["d3-hierarchy@3.1.2", "", {}, "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="],
+
"d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="],
+ "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="],
+
+ "d3-polygon": ["d3-polygon@3.0.1", "", {}, "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="],
+
+ "d3-quadtree": ["d3-quadtree@3.0.1", "", {}, "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="],
+
+ "d3-random": ["d3-random@3.0.1", "", {}, "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="],
+
+ "d3-sankey": ["d3-sankey@0.12.3", "", { "dependencies": { "d3-array": "1 - 2", "d3-shape": "^1.2.0" } }, "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ=="],
+
+ "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
+
+ "d3-scale-chromatic": ["d3-scale-chromatic@3.1.0", "", { "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" } }, "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ=="],
+
"d3-selection": ["d3-selection@3.0.0", "", {}, "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="],
+ "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="],
+
+ "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
+
+ "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="],
+
"d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
"d3-transition": ["d3-transition@3.0.1", "", { "dependencies": { "d3-color": "1 - 3", "d3-dispatch": "1 - 3", "d3-ease": "1 - 3", "d3-interpolate": "1 - 3", "d3-timer": "1 - 3" }, "peerDependencies": { "d3-selection": "2 - 3" } }, "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w=="],
"d3-zoom": ["d3-zoom@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "2 - 3", "d3-transition": "2 - 3" } }, "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw=="],
+ "dagre-d3-es": ["dagre-d3-es@7.0.14", "", { "dependencies": { "d3": "^7.9.0", "lodash-es": "^4.17.21" } }, "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg=="],
+
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
"data-urls": ["data-urls@5.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" } }, "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg=="],
@@ -2012,6 +2095,8 @@
"degenerator": ["degenerator@5.0.1", "", { "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", "esprima": "^4.0.1" } }, "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ=="],
+ "delaunator": ["delaunator@5.0.1", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw=="],
+
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
@@ -2328,6 +2413,8 @@
"gtoken": ["gtoken@8.0.0", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="],
+ "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="],
+
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
@@ -2422,6 +2509,8 @@
"inquirer": ["inquirer@8.2.7", "", { "dependencies": { "@inquirer/external-editor": "^1.0.0", "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", "cli-cursor": "^3.1.0", "cli-width": "^3.0.0", "figures": "^3.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", "rxjs": "^7.5.5", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6", "wrap-ansi": "^6.0.1" } }, "sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA=="],
+ "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
+
"ioredis": ["ioredis@5.9.2", "", { "dependencies": { "@ioredis/commands": "1.5.0", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-tAAg/72/VxOUW7RQSX1pIxJVucYKcjFjfvj60L57jrZpYCHC3XN0WCQ3sNYL4Gmvv+7GPvTAjc+KSdeNuE8oWQ=="],
"ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="],
@@ -2534,14 +2623,22 @@
"jwt-decode": ["jwt-decode@4.0.0", "", {}, "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA=="],
+ "katex": ["katex@0.16.38", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-cjHooZUmIAUmDsHBN+1n8LaZdpmbj03LtYeYPyuYB7OuloiaeaV6N4LcfjcnHVzGWjVQmKrxxTrpDcmSzEZQwQ=="],
+
+ "khroma": ["khroma@2.1.0", "", {}, "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="],
+
"kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="],
"kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
"kysely": ["kysely@0.28.11", "", {}, "sha512-zpGIFg0HuoC893rIjYX1BETkVWdDnzTzF5e0kWXJFg5lE0k1/LfNWBejrcnOFu8Q2Rfq/hTDTU7XLUM8QOrpzg=="],
+ "langium": ["langium@4.2.1", "", { "dependencies": { "chevrotain": "~11.1.1", "chevrotain-allstar": "~0.3.1", "vscode-languageserver": "~9.0.1", "vscode-languageserver-textdocument": "~1.0.11", "vscode-uri": "~3.1.0" } }, "sha512-zu9QWmjpzJcomzdJQAHgDVhLGq5bLosVak1KVa40NzQHXfqr4eAHupvnPOVXEoLkg6Ocefvf/93d//SB7du4YQ=="],
+
"langsmith": ["langsmith@0.3.87", "", { "dependencies": { "@types/uuid": "^10.0.0", "chalk": "^4.1.2", "console-table-printer": "^2.12.1", "p-queue": "^6.6.2", "semver": "^7.6.3", "uuid": "^10.0.0" }, "peerDependencies": { "@opentelemetry/api": "*", "@opentelemetry/exporter-trace-otlp-proto": "*", "@opentelemetry/sdk-trace-base": "*", "openai": "*" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/exporter-trace-otlp-proto", "@opentelemetry/sdk-trace-base", "openai"] }, "sha512-XXR1+9INH8YX96FKWc5tie0QixWz6tOqAsAKfcJyPkE0xPep+NDz0IQLR32q4bn10QK3LqD2HN6T3n6z1YLW7Q=="],
+ "layout-base": ["layout-base@1.0.2", "", {}, "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="],
+
"leac": ["leac@0.6.0", "", {}, "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg=="],
"leven": ["leven@4.1.0", "", {}, "sha512-KZ9W9nWDT7rF7Dazg8xyLHGLrmpgq2nVNFUckhqdW3szVP6YhCpp/RAnpmVExA9JvrMynjwSLVrEj3AepHR6ew=="],
@@ -2592,6 +2689,8 @@
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
+ "lodash-es": ["lodash-es@4.17.23", "", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="],
+
"lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="],
"lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="],
@@ -2694,6 +2793,8 @@
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
+ "mermaid": ["mermaid@11.13.0", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.1", "@iconify/utils": "^3.0.2", "@mermaid-js/parser": "^1.0.1", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", "cytoscape": "^3.33.1", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.14", "dayjs": "^1.11.19", "dompurify": "^3.3.1", "katex": "^0.16.25", "khroma": "^2.1.0", "lodash-es": "^4.17.23", "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0" } }, "sha512-fEnci+Immw6lKMFI8sqzjlATTyjLkRa6axrEgLV2yHTfv8r+h1wjFbV6xeRtd4rUV1cS4EpR9rwp3Rci7TRWDw=="],
+
"meshoptimizer": ["meshoptimizer@0.18.1", "", {}, "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw=="],
"methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="],
@@ -2952,6 +3053,8 @@
"package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
+ "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="],
+
"pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
"papaparse": ["papaparse@5.5.3", "", {}, "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A=="],
@@ -2974,6 +3077,8 @@
"patchright-core": ["patchright-core@1.57.0", "", { "bin": { "patchright-core": "cli.js" } }, "sha512-um/9Wue7IFAa9UDLacjNgDn62ub5GJe1b1qouvYpELIF9rsFVMNhRo/rRXYajupLwp5xKJ0sSjOV6sw8/HarBQ=="],
+ "path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="],
+
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
@@ -3028,6 +3133,10 @@
"playwright-core": ["playwright-core@1.58.2", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg=="],
+ "points-on-curve": ["points-on-curve@0.2.0", "", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="],
+
+ "points-on-path": ["points-on-path@0.2.1", "", { "dependencies": { "path-data-parser": "0.1.0", "points-on-curve": "0.2.0" } }, "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g=="],
+
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
"postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="],
@@ -3214,10 +3323,14 @@
"rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="],
+ "robust-predicates": ["robust-predicates@3.0.2", "", {}, "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="],
+
"rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="],
"rou3": ["rou3@0.5.1", "", {}, "sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ=="],
+ "roughjs": ["roughjs@4.6.6", "", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="],
+
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
"rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="],
@@ -3230,6 +3343,8 @@
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
+ "rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="],
+
"rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="],
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
@@ -3408,6 +3523,8 @@
"styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
+ "stylis": ["stylis@4.3.6", "", {}, "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ=="],
+
"sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="],
"superjson": ["superjson@2.2.6", "", { "dependencies": { "copy-anything": "^4" } }, "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA=="],
@@ -3488,6 +3605,8 @@
"ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="],
+ "ts-dedent": ["ts-dedent@2.2.0", "", {}, "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ=="],
+
"ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
"tsafe": ["tsafe@1.8.12", "", {}, "sha512-nFRqW0ttu/2o6XTXsHiVZWJBCOaxhVqZLg7dgs3coZNsCMPXPfwz+zPHAQA+70fNnVJLAPg1EgGIqK9Q84tvAw=="],
@@ -3610,10 +3729,18 @@
"vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="],
+ "vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="],
+
+ "vscode-languageserver": ["vscode-languageserver@9.0.1", "", { "dependencies": { "vscode-languageserver-protocol": "3.17.5" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g=="],
+
+ "vscode-languageserver-protocol": ["vscode-languageserver-protocol@3.17.5", "", { "dependencies": { "vscode-jsonrpc": "8.2.0", "vscode-languageserver-types": "3.17.5" } }, "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg=="],
+
"vscode-languageserver-textdocument": ["vscode-languageserver-textdocument@1.0.12", "", {}, "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="],
"vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="],
+ "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="],
+
"w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="],
"wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="],
@@ -3702,6 +3829,8 @@
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
+ "@antfu/install-pkg/tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
+
"@asamuzakjp/css-color/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"@aws-crypto/crc32/@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="],
@@ -4078,6 +4207,14 @@
"cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
+ "cytoscape-fcose/cose-base": ["cose-base@2.2.0", "", { "dependencies": { "layout-base": "^2.0.0" } }, "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g=="],
+
+ "d3-dsv/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
+
+ "d3-sankey/d3-array": ["d3-array@2.12.1", "", { "dependencies": { "internmap": "^1.0.0" } }, "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ=="],
+
+ "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="],
+
"dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"e2b/glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="],
@@ -4158,6 +4295,8 @@
"jaeger-client/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
+ "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
+
"langsmith/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"langsmith/uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="],
@@ -4184,6 +4323,8 @@
"mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
+ "mermaid/marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="],
+
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"neo4j-driver-bolt-connection/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
@@ -4552,6 +4693,12 @@
"critters/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
+ "cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="],
+
+ "d3-sankey/d3-array/internmap": ["internmap@1.0.1", "", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="],
+
+ "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="],
+
"engine.io/@types/node/undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
"fetch-cookie/tough-cookie/tldts": ["tldts@7.0.23", "", { "dependencies": { "tldts-core": "^7.0.23" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-ASdhgQIBSay0R/eXggAkQ53G4nTJqTXqC2kbaBbdDwM7SkjyZyO0OaaN1/FH7U/yCeqOHDwFO5j8+Os/IS1dXw=="],