From 86708b47f029758060271332bf75fc65cef5dead Mon Sep 17 00:00:00 2001 From: zaidmukaddam Date: Wed, 18 Sep 2024 00:53:40 +0530 Subject: [PATCH] feat: Results Overview, Default Search Engine, o1-mini and more. --- README.md | 6 + app/actions.ts | 81 ++- app/api/chat/route.ts | 71 ++- app/globals.css | 16 + app/search/page.tsx | 1268 ++++++++++++++++++++++++++++----------- components/ui/badge.tsx | 2 + components/ui/table.tsx | 120 ++++ next.config.mjs | 9 +- package.json | 14 +- pnpm-lock.yaml | 390 +++++++++--- 10 files changed, 1523 insertions(+), 454 deletions(-) create mode 100644 components/ui/table.tsx diff --git a/README.md b/README.md index 49f7eec..51ca2b3 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,9 @@ Upvote MiniPerplx on ProductHunt to show your support! - **URL Specific search**: Get information from a specific URL. - **Weather**: Get the current weather for any location using OpenWeather's API. - **Programming**: Run code snippets in multiple languages using E2B's API. +- **Maps**: Get the location of any place using Google Maps API. +- **Results Overview**: Get a quick overview of the results from different providers. +- **Translation**: Translate text to different languages using Microsoft's Translator API. ## Built with - [Next.js](https://nextjs.org/) @@ -26,9 +29,12 @@ Upvote MiniPerplx on ProductHunt to show your support! - [Tavily](https://tavily.com/) - [OpenWeather](https://openweathermap.org/) - [E2B](https://e2b.dev/) +- [Google Maps](https://developers.google.com/maps) ## LLM used - [OpenAI's GPT 4o mini](https://openai.com/index/gpt-4o-mini-advancing-cost-efficient-intelligence/) +- [Anthropic's Claude 3.5 Sonnet](https://www.anthropic.com/news/claude-3-5-sonnet/) +- [OpenAI's o1-mini](https://openai.com/index/openai-o1-mini-advancing-cost-efficient-reasoning/) powered by [OpenRouter](https://openrouter.ai/models/openai/o1-mini) ### Deploy your own diff --git a/app/actions.ts b/app/actions.ts index 8cb114a..6c919d1 100644 --- a/app/actions.ts +++ b/app/actions.ts @@ -1,9 +1,14 @@ 'use server'; -import { OpenAI, AzureOpenAI } from 'openai'; -import { generateObject } from 'ai'; +import { OpenAI } from 'openai'; +import { Redis } from '@upstash/redis'; +import { Ratelimit } from '@upstash/ratelimit'; +import { generateObject, generateText } from 'ai'; +import { createOpenAI } from '@ai-sdk/openai' import { createOpenAI as createGroq } from '@ai-sdk/openai'; import { z } from 'zod'; +import { headers } from 'next/headers'; +import { load } from 'cheerio'; const groq = createGroq({ baseURL: 'https://api.groq.com/openai/v1', @@ -99,4 +104,76 @@ export async function generateSpeech(text: string, voice: 'alloy' | 'echo' | 'fa audio: `data:audio/mp3;base64,${base64Audio}`, }; } +} + +const openai = createOpenAI({ + baseURL: "https://openrouter.ai/api/v1/", + apiKey: process.env.OPENROUTER_API_KEY, + headers: { + "HTTP-Referer": "https://mplx.run/search", + "X-Title": "MiniPerplx" + } +}); + +const redis = new Redis({ + url: process.env.UPSTASH_REDIS_REST_URL!, + token: process.env.UPSTASH_REDIS_REST_TOKEN!, +}); + +const ratelimit = new Ratelimit({ + redis: redis, + limiter: Ratelimit.fixedWindow(10, '24 h'), + analytics: true, + prefix: 'mplx', +}); + +export interface Message { + role: 'user' | 'assistant'; + content: string; +} + +export async function continueConversation(history: Message[]) { + 'use server'; + + const headersList = headers(); + const ip = headersList.get('x-forwarded-for') ?? 'unknown'; + const { success, limit, reset, remaining } = await ratelimit.limit(ip); + + if (!success) { + const resetDate = new Date(reset); + throw new Error(`Daily rate limit exceeded. Try again after ${resetDate.toLocaleTimeString()}.`); + } + + const { text } = await generateText({ + model: openai('openai/o1-mini'), + messages: history, + }); + + return { + messages: [ + ...history, + { + role: 'assistant' as const, + content: text, + }, + ], + remaining, + reset, + }; +} + +export async function fetchMetadata(url: string) { + try { + const response = await fetch(url, { next: { revalidate: 3600 } }); // Cache for 1 hour + const html = await response.text(); + const $ = load(html); + + const title = $('head title').text() || $('meta[property="og:title"]').attr('content') || ''; + const description = $('meta[name="description"]').attr('content') || $('meta[property="og:description"]').attr('content') || ''; + + return { title, description }; + } catch (error) { + console.error('Error fetching metadata:', error); + return null; + } } \ No newline at end of file diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 853ba4b..dde9752 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -1,10 +1,10 @@ import { z } from "zod"; import { createAzure } from '@ai-sdk/azure'; -import { - convertToCoreMessages, - streamText, - tool, - experimental_createProviderRegistry +import { + convertToCoreMessages, + streamText, + tool, + experimental_createProviderRegistry } from "ai"; import { createAnthropicVertex } from 'anthropic-vertex-ai'; import { BlobRequestAbortedError, put } from '@vercel/blob'; @@ -89,7 +89,7 @@ Make sure keep your responses long and informative, but also clear and concise. Here are the tools available to you: -web_search, retrieve, get_weather_data, programming, nearby_search, find_place, text_search, text_translate +web_search, results_overview, retrieve, get_weather_data, programming, nearby_search, find_place, text_search, text_translate ## Basic Guidelines: @@ -104,6 +104,7 @@ Please use the '$' latex format in equations instead of \( ones, same for comple DO's: - Use the web_search tool to gather relevant information. The query should only be the word that need's context for search. Then write the response based on the information gathered. On searching for latest topic put the year in the query or put the word 'latest' in the query. +- If the user query was about a celebrity, call the results_overview tool to generate an overview of the information retrieved using the web_search tool. Not all web search results need an overview, use them only and only if it is about a celebrity. Use all the sources from the 'web_search' tool's response to compose your response after running the 'results_overview' tool. No images should be included in the composed response at all costs. - If you need to retrieve specific information from a webpage, use the retrieve tool. Analyze the user's query to set the topic type either normal or news. Then, compose your response based on the retrieved information. - For weather-related queries, use the get_weather_data tool. The weather results are 5 days weather forecast data with 3-hour step. Then, provide the weather information in your response. - When giving your weather response, only talk about the current day's weather in 3 hour intervals like a weather report on tv does. Do not provide the weather for the next 5 days. @@ -119,6 +120,9 @@ DO's: - Assume the stock name from the user query and use it in the code to get the stock data and plot the stock chart. This will help in getting the stock chart for the user query. ALWAYS REMEMBER TO INSTALL YFINANCE USING !pip install yfinance AT ALL COSTS!! DON'Ts and IMPORTANT GUIDELINES: +- DO NOT RUN THE 'results_overview' if the user query is 'not about a celebrity' at all costs!!!! +- Do not run the 'results_overview' tool without running the 'web_search' tool first at all costs!! 'results_overview' tool should only be called after the 'web_search' tool, no other tool works with it. +- No images should be included in the composed response at all costs, except for the programming tool. - DO NOT TALK BEFORE RUNNING THE TOOL AT ALL COSTS!! JUST RUN THE TOOL AND THEN WRITE YOUR RESPONSE AT ALL COSTS!!!!! - Do not call the same tool twice in a single response at all costs!! - Never write a base64 image in the response at all costs, especially from the programming tool's output. @@ -181,7 +185,6 @@ When asked a "What is" question, maintain the same format as the question and an ), exclude_domains: z .array(z.string()) - .describe( "A list of domains to specifically exclude from the search results. Default is None, which doesn't exclude any domains.", ), @@ -290,17 +293,17 @@ When asked a "What is" question, maintain the same format as the question and an const app = new FirecrawlApp({ apiKey: process.env.FIRECRAWL_API_KEY }); try { const content = await app.scrapeUrl(url); - if (!content.data) { + if (!content.success || !content.metadata) { return { error: "Failed to retrieve content" }; } return { results: [ { - title: content.data.metadata.title, - content: content.data.markdown, - url: content.data.metadata.sourceURL, - description: content.data.metadata.description, - language: content.data.metadata.language, + title: content.metadata.title, + content: content.markdown, + url: content.metadata.sourceURL, + description: content.metadata.description, + language: content.metadata.language, }, ], }; @@ -325,6 +328,48 @@ When asked a "What is" question, maintain the same format as the question and an return data; }, }), + results_overview: tool({ + description: "Generate an overview of the celebrity from the wikipedia url retrieved from the web_search tool.", + parameters: z.object({ + website_url: z.string().describe("The Website URL to generate the overview from like personal website, wikipedia or official website of the thing/person/place to generate overview of."), + }), + execute: async ({ website_url }: { website_url: string }) => { + const app = new FirecrawlApp({ apiKey: process.env.FIRECRAWL_API_KEY }); + console.log("website_url", website_url); + + const schema = z.object({ + image: z.string().describe("The URL of the image put https:// before the URL."), + title: z.string().describe("The title of the overview.").max(30), + description: z.string().describe("The description of the overview.").max(100), + table_data: z.array( + z.object({ + title: z.string().describe("The title of the data.").max(30), + content: z.string().describe("The content of the data.").max(100), + }) + ), + }); + + const scrapeResult = await app.scrapeUrl(website_url, { + formats: ["extract"], + extract: { schema: schema } + }); + + if (!scrapeResult.success || scrapeResult.error) { + console.error("Failed to scrape:", scrapeResult.error); + throw new Error(`Failed to scrape: ${scrapeResult.error}`) + } + const object = scrapeResult.extract; + if (!object) { + throw new Error("Failed to extract overview"); + } + return { + title: object.title, + description: object.description, + table_data: object.table_data, + image: object.image + }; + }, + }), programming: tool({ description: "Write and execute Python code.", parameters: z.object({ diff --git a/app/globals.css b/app/globals.css index 7d982a8..3761638 100644 --- a/app/globals.css +++ b/app/globals.css @@ -19,6 +19,22 @@ body { inset 0 1px 0 0 #ffffff52; } +@layer utilities { + + /* Hide scrollbar for Chrome, Safari and Opera */ + .no-scrollbar::-webkit-scrollbar { + display: none; + } + + /* Hide scrollbar for IE, Edge and Firefox */ + .no-scrollbar { + -ms-overflow-style: none; + /* IE and Edge */ + scrollbar-width: none; + /* Firefox */ + } +} + .tweet-container { display: flex; flex-direction: column; diff --git a/app/search/page.tsx b/app/search/page.tsx index ddf0953..17971a4 100644 --- a/app/search/page.tsx +++ b/app/search/page.tsx @@ -12,17 +12,23 @@ React, memo } from 'react'; import ReactMarkdown, { Components } from 'react-markdown'; -import remarkGfm from 'remark-gfm'; -import remarkMath from 'remark-math'; -import rehypeKatex from 'rehype-katex'; +import Marked, { ReactRenderer } from 'marked-react'; +import katex from 'katex'; import { track } from '@vercel/analytics'; +import { useSearchParams } from 'next/navigation'; import 'katex/dist/katex.min.css'; import { useChat } from 'ai/react'; import { ToolInvocation } from 'ai'; import { toast } from 'sonner'; import { motion, AnimatePresence } from 'framer-motion'; import Image from 'next/image'; -import { generateSpeech, suggestQuestions } from '../actions'; +import { + continueConversation, + fetchMetadata, + generateSpeech, + Message, + suggestQuestions +} from '../actions'; import { Wave } from "@foobar404/wave"; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism'; @@ -56,8 +62,14 @@ import { ImageIcon, Paperclip, ChevronDown, - Zap -} from 'lucide-react'; + Zap, + Edit2, + ChevronUp, + Battery, + Clock, + Cpu, + Network, + ExternalLink} from 'lucide-react'; import { HoverCard, HoverCardContent, @@ -106,6 +118,13 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { cn } from '@/lib/utils'; +import { + Table, + TableBody, + TableCell, + TableRow, +} from "@/components/ui/table"; +import Autoplay from 'embla-carousel-autoplay'; export const maxDuration = 60; @@ -126,20 +145,33 @@ interface Attachment { } export default function Home() { - const [lastSubmittedQuery, setLastSubmittedQuery] = useState(""); - const [hasSubmitted, setHasSubmitted] = useState(false); + const searchParams = useSearchParams(); + const initialQuery = searchParams.get('query') || ''; + const initialModel = searchParams.get('model') || 'azure:gpt4o-mini'; + + const [lastSubmittedQuery, setLastSubmittedQuery] = useState(initialQuery); + const [hasSubmitted, setHasSubmitted] = useState(!!initialQuery); + const [selectedModel, setSelectedModel] = useState(initialModel); const bottomRef = useRef(null); const [suggestedQuestions, setSuggestedQuestions] = useState([]); const [isEditingMessage, setIsEditingMessage] = useState(false); const [editingMessageIndex, setEditingMessageIndex] = useState(-1); const [attachments, setAttachments] = useState([]); - const [selectedModel, setSelectedModel] = useState("azure:gpt4o-mini"); const fileInputRef = useRef(null); const inputRef = useRef(null); - const { isLoading, input, messages, setInput, append, handleSubmit, setMessages, reload } = useChat({ - api: '/api/chat', - maxToolRoundtrips: 1, + const [isInitialQueryProcessed, setIsInitialQueryProcessed] = useState(false); + + const [o1Conversation, setO1Conversation] = useState([]); + const [o1Input, setO1Input] = useState(''); + const [isO1Loading, setIsO1Loading] = useState(false); + const [remainingRequests, setRemainingRequests] = useState(null); + const [resetTime, setResetTime] = useState(null); + + const [openChangelog, setOpenChangelog] = useState(false); + + const { isLoading, input, messages, setInput, handleInputChange, append, handleSubmit, setMessages, reload } = useChat({ + maxToolRoundtrips: 2, body: { model: selectedModel, }, @@ -163,6 +195,93 @@ export default function Home() { }, }); + const handleO1InputChange = (e: React.ChangeEvent) => { + setO1Input(e.target.value); + }; + + const handleO1Submit = useCallback(async () => { + if (o1Input.trim()) { + setIsO1Loading(true); + const newUserMessage = { role: 'user' as const, content: o1Input }; + setLastSubmittedQuery(o1Input); + setO1Input(''); + setO1Conversation(prev => [...prev, newUserMessage]); + + try { + const { messages: newMessages, remaining, reset } = await continueConversation([...o1Conversation, newUserMessage]); + setO1Conversation(newMessages); + // make suggestion questions + const { questions } = await suggestQuestions(newMessages); + setSuggestedQuestions(questions); + setRemainingRequests(remaining); + setResetTime(reset); + if (remaining !== null && remaining <= 3) { + toast.warning(`You have ${remaining} requests remaining today.`); + } + } catch (error) { + console.error("Error in O1 conversation:", error); + toast.error(error instanceof Error ? error.message : "An error occurred while processing your request."); + } finally { + setIsO1Loading(false); + } + } + }, [o1Input, o1Conversation]); + + interface RateLimitInfoProps { + remainingRequests: number; + resetTime: number; + } + + const RateLimitInfo: React.FC = ({ remainingRequests, resetTime }) => { + const formatResetTime = (resetTimestamp: number) => { + const resetDate = new Date(resetTimestamp); + return resetDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + }; + + const getBatteryColor = (remaining: number) => { + if (remaining <= 2) return "text-red-500"; + if (remaining <= 5) return "text-yellow-500"; + return "text-green-500"; + }; + + return ( + + + +
+ + {remainingRequests} + +
+
+ +

Daily limit: {remainingRequests} requests remaining

+

Resets at: {formatResetTime(resetTime)}

+
+
+
+ ); + }; + + const processInitialQuery = useCallback(async () => { + if (initialQuery && !isInitialQueryProcessed) { + setHasSubmitted(true); + setIsInitialQueryProcessed(true); + track('search with url params', { query: initialQuery }); + + if (selectedModel === 'openai/o1-mini') { + setO1Input(initialQuery); + handleO1Submit(); + } else { + await append({ content: initialQuery, role: 'user' }); + } + } + }, [initialQuery, isInitialQueryProcessed, selectedModel, handleO1Submit, append]); + + if (!isInitialQueryProcessed) { + processInitialQuery(); + } + const CopyButton = ({ text }: { text: string }) => { const [isCopied, setIsCopied] = useState(false); @@ -190,6 +309,98 @@ export default function Home() { ); }; + type Changelog = { + id: string; + images: string[]; + content: string; + title: string; + }; + + const changelogs: Changelog[] = [ + { + id: "1", + title: "Results Overview, Default Search Engine, and o1-mini!", + images: [ + "https://metwm7frkvew6tn1.public.blob.vercel-storage.com/mplx-changelogs/results-overview.png", + "https://metwm7frkvew6tn1.public.blob.vercel-storage.com/mplx-changelogs/default-search-engine-mplx.png", + "https://metwm7frkvew6tn1.public.blob.vercel-storage.com/mplx-changelogs/o1-mini-mplx.png" + ], + content: +`## **Results Overview** + +The new Results Overview tool provides a summary of the search results, including images, descriptions, and other relevant information. It also includes a table with additional details. + +## **Default Search Engine** + +You can know set MiniPerplx as your default search engine using the URL parameters. Just add \`?query=%s\` to the URL to set the default search query. The model can also be set using the \`model\` parameter. + +## **o1-mini** + +The o1-mini is a new OpenAI model that is optimized for reasoning tasks. Currently, it is available as a beta feature and doesn't support all features like web search.`, + } + ]; + + const ChangeLogs: React.FC<{ open: boolean; setOpen: (open: boolean) => void }> = ({ open, setOpen }) => { + return ( + + +
+

+ What's new +

+
+
+ {changelogs.map((changelog) => ( +
+ + + {changelog.images.map((image, index) => ( + + {changelog.title} + + ))} + + +
+

{changelog.title}

+

, + p: ({ node, className, ...props }) =>

, + } as Components + } + className="text-sm text-neutral-900" + > + {changelog.content} + +

+
+ ))} +
+
+
+ ); + }; + // Weather chart components interface WeatherDataPoint { @@ -779,6 +990,82 @@ export default function Home() { ); }; + interface TableData { + title: string; + content: string; + } + + interface ResultsOverviewProps { + result: { + image: string; + title: string; + description: string; + table_data: TableData[]; + }; + } + + const ResultsOverview: React.FC = React.memo(({ result }) => { + const [showAll, setShowAll] = useState(false); + + const visibleData = useMemo(() => { + return showAll ? result.table_data : result.table_data.slice(0, 3); + }, [showAll, result.table_data]); + + return ( + + +
+ {result.image && ( +
+ {result.title} +
+ )} +
+ {result.title} +

{result.description}

+
+
+
+ + + + {visibleData.map((item, index) => ( + + {item.title} + {item.content} + + ))} + +
+ {result.table_data.length > 3 && ( + + )} +
+
+ ); + }); + + ResultsOverview.displayName = 'ResultsOverview'; + + const renderToolInvocation = (toolInvocation: ToolInvocation, index: number) => { const args = JSON.parse(JSON.stringify(toolInvocation.args)); const result = 'result' in toolInvocation ? JSON.parse(JSON.stringify(toolInvocation.result)) : null; @@ -1027,6 +1314,13 @@ export default function Home() { > {args.code} +
@@ -1265,101 +1559,259 @@ export default function Home() { return ; } + if (toolInvocation.toolName === 'results_overview') { + if (!result) { + return ( +
+
+ + Generating overview... +
+
+ ); + } + + return ; + } + return null; }; - interface CitationComponentProps { - href: string; - children: React.ReactNode; - index: number; - citationText: string; - } - - const CitationComponent: React.FC = React.memo(({ href, index, citationText }) => { - const { hostname } = new URL(href); - const faviconUrl = `https://www.google.com/s2/favicons?sz=128&domain=${hostname}`; - - return ( - - - - - {index + 1} - - - - - -

{citationText}

-
-
- ); - }); - - CitationComponent.displayName = "CitationComponent"; - interface MarkdownRendererProps { content: string; } - const MarkdownRenderer: React.FC = React.memo(({ content }) => { - // Escape dollar signs that are likely to be currency - const escapedContent = content.replace(/\$(\d+(\.\d{1,2})?)/g, '\\$$1'); + interface CitationLink { + text: string; + link: string; + } - const citationLinks = useMemo(() => { - return [...escapedContent.matchAll(/\[([^\]]+)\]\(([^)]+)\)/g)].map(([_, text, link]) => ({ + interface LinkMetadata { + title: string; + description: string; + } + + const isValidUrl = (str: string) => { + try { + new URL(str); + return true; + } catch { + return false; + } + }; + + const MarkdownRenderer: React.FC = ({ content }) => { + const [metadataCache, setMetadataCache] = useState>({}); + + const citationLinks = useMemo(() => { + return Array.from(content.matchAll(/\[([^\]]+)\]\(([^)]+)\)/g)).map(([_, text, link]) => ({ text, link, })); - }, [escapedContent]); + }, [content]); - const components: Partial = useMemo(() => ({ - a: ({ href, children }) => { - if (!href) return null; - const index = citationLinks.findIndex((link) => link.link === href); - return index !== -1 ? ( - { + if (metadataCache[url]) { + return metadataCache[url]; + } + + const metadata = await fetchMetadata(url); + if (metadata) { + setMetadataCache(prev => ({ ...prev, [url]: metadata })); + } + return metadata; + }, [metadataCache]); + + const renderEquation = (equation: string): string => { + try { + return katex.renderToString(equation, { + throwOnError: false, + displayMode: true, + strict: false, + trust: true, + macros: { + "\\,": "\\:" + } + }); + } catch (error) { + console.error("KaTeX rendering error:", error); + return equation; + } + }; + + const CodeBlock = ({ language, children }: { language: string | undefined; children: string }) => { + const [isCopied, setIsCopied] = useState(false); + + const handleCopy = async () => { + await navigator.clipboard.writeText(children); + setIsCopied(true); + setTimeout(() => setIsCopied(false), 2000); + }; + + return ( +
+ {children} - - ) : ( - - {children} - + + +
+ ); + }; + + const LinkPreview = ({ href }: { href: string }) => { + const [metadata, setMetadata] = useState(null); + const [isLoading, setIsLoading] = useState(false); + + React.useEffect(() => { + setIsLoading(true); + fetchMetadataWithCache(href).then((data) => { + setMetadata(data); + setIsLoading(false); + }); + }, [href]); + + if (isLoading) { + return ( +
+ +
); + } + + const domain = new URL(href).hostname; + + return ( +
+
+ Favicon + {domain} +
+
+

+ {metadata?.title || "Untitled"} +

+ {metadata?.description && ( +

+ {metadata.description} +

+ )} +
+
+ {href} + +
+
+ ); + }; + + const renderHoverCard = (href: string, text: React.ReactNode, isCitation: boolean = false) => { + return ( + + + + {text} + + + + + + + ); + }; + + const renderer: Partial = { + paragraph(children) { + if (typeof children === 'string') { + const parts = children.split(/(\[.*?\])/g); + return ( +

+ {parts.map((part, index) => { + if (part.startsWith('[') && part.endsWith(']')) { + const equation = part.slice(1, -1); + return ( + + ); + } + return {part}; + })} +

+ ); + } + return

{children}

; }, - }), [citationLinks]); + code(children, language) { + return {String(children)}; + }, + link(href, text) { + const citationIndex = citationLinks.findIndex(link => link.link === href); + if (citationIndex !== -1) { + return ( + + {renderHoverCard(href, citationIndex + 1, true)} + + ); + } + return isValidUrl(href) ? renderHoverCard(href, text) : {text}; + }, + heading(children, level) { + const HeadingTag = `h${level}` as keyof JSX.IntrinsicElements; + const className = `text-${4 - level}xl font-bold my-4`; + return {children}; + }, + list(children, ordered) { + const ListTag = ordered ? 'ol' : 'ul'; + return {children}; + }, + listItem(children) { + return
  • {children}
  • ; + }, + blockquote(children) { + return
    {children}
    ; + }, + }; return ( - - {escapedContent} - +
    + {content} +
    ); - }); + }; + - MarkdownRenderer.displayName = "MarkdownRenderer"; const lastUserMessageIndex = useMemo(() => { for (let i = messages.length - 1; i >= 0; i--) { @@ -1377,26 +1829,69 @@ export default function Home() { }, [messages, suggestedQuestions]); const handleExampleClick = useCallback(async (card: typeof suggestionCards[number]) => { - track("search example", { query: card.text }); - setLastSubmittedQuery(card.text.trim()); + const exampleText = selectedModel === 'openai/o1-mini' ? card.o1Examples : card.text; + track("search example", { query: exampleText }); + setLastSubmittedQuery(exampleText.trim()); setHasSubmitted(true); setSuggestedQuestions([]); - await append({ - content: card.text.trim(), - role: 'user', - experimental_attachments: card.attachment ? [card.attachment] : undefined, - }); - }, [append, setLastSubmittedQuery, setHasSubmitted, setSuggestedQuestions]); + + if (selectedModel === 'openai/o1-mini') { + setO1Input(exampleText.trim()); + setIsO1Loading(true); + const newUserMessage = { role: 'user' as const, content: exampleText.trim() }; + setO1Conversation(prev => [...prev, newUserMessage]); + setO1Input(""); + try { + const { messages: newMessages, remaining, reset } = await continueConversation([...o1Conversation, newUserMessage]); + setO1Conversation(newMessages); + // make suggestions for the next user message + const { questions } = await suggestQuestions(newMessages); + setSuggestedQuestions(questions); + setRemainingRequests(remaining); + setResetTime(reset); + } catch (error) { + console.error("Error in O1 conversation:", error); + toast.error(error instanceof Error ? error.message : "An error occurred while processing your request."); + } finally { + setIsO1Loading(false); + } + } else { + await append({ + content: exampleText.trim(), + role: 'user', + }); + } + }, [append, setLastSubmittedQuery, setHasSubmitted, setSuggestedQuestions, selectedModel, setO1Input, o1Conversation]); const handleSuggestedQuestionClick = useCallback(async (question: string) => { setHasSubmitted(true); setSuggestedQuestions([]); - setInput(question.trim()); - await append({ - content: question.trim(), - role: 'user' - }); - }, [setInput, append]); + + if (selectedModel === 'openai/o1-mini') { + setO1Input(question.trim()); + setIsO1Loading(true); + const newUserMessage = { role: 'user' as const, content: question.trim() }; + setO1Conversation(prev => [...prev, newUserMessage]); + setO1Input(""); + try { + const { messages: newMessages, remaining, reset } = await continueConversation([...o1Conversation, newUserMessage]); + setO1Conversation(newMessages); + setRemainingRequests(remaining); + setResetTime(reset); + } catch (error) { + console.error("Error in O1 conversation:", error); + toast.error(error instanceof Error ? error.message : "An error occurred while processing your request."); + } finally { + setIsO1Loading(false); + } + } else { + setInput(question.trim()); + await append({ + content: question.trim(), + role: 'user' + }); + } + }, [setInput, append, selectedModel, setO1Input, o1Conversation]); const handleMessageEdit = useCallback((index: number) => { setIsEditingMessage(true); @@ -1420,66 +1915,83 @@ export default function Home() { const suggestionCards = [ { - icon: , - text: "Where is this place?", - attachment: { - name: 'taj_mahal.jpg', - contentType: 'image/jpeg', - url: 'https://metwm7frkvew6tn1.public.blob.vercel-storage.com/taj-mahal.jpg', - } + icon: , + o1Icon: , + text: "Shah Rukh Khan", + o1Examples: "How many GPUs does it take to fill up Mars?" + }, + { + icon: , + o1Icon: , + text: "Weather in Doha", + o1Examples: "Dijkstra algorithm" + }, + { + icon: , + o1Icon: , + text: "Count the no. of r's in strawberry?", + o1Examples: "Count the no. of r's in strawberry?" }, - { icon: , text: "What's new with XAI's Grok?" }, - { icon: , text: "Latest updates on OpenAI" }, - { icon: , text: "Weather in Doha" }, - { icon: , text: "Count the no. of r's in strawberry?" }, ]; - const Navbar = () => ( -
    - - - -
    - - - - - - - -

    Sponsor this project on GitHub

    -
    -
    -
    + interface NavbarProps { + selectedModel: string; + remainingRequests: number | null; + resetTime: number | null; + } + + const Navbar: React.FC = ({ selectedModel, remainingRequests, resetTime }) => { + return ( +
    + + + +
    + {selectedModel === 'openai/o1-mini' && remainingRequests !== null && resetTime !== null && ( + + )} + + + + + + + +

    Sponsor this project on GitHub

    +
    +
    +
    +
    -
    - ); + ); + }; interface UploadingAttachment { file: File; @@ -1620,7 +2132,6 @@ export default function Home() { inputRef, }) => { const [uploadingAttachments, setUploadingAttachments] = useState([]); - const cursorPositionRef = useRef(null); const uploadFile = async (file: File): Promise => { const formData = new FormData(); @@ -1676,10 +2187,6 @@ export default function Home() { setUploadingAttachments(prev => prev.filter((_, i) => i !== index)); }; - const handleInputChange = useCallback((e: React.ChangeEvent) => { - setInput(e.target.value); - }, [setInput]); - useEffect(() => { if (inputRef.current) { inputRef.current.focus(); @@ -1690,11 +2197,16 @@ export default function Home() { event.preventDefault(); event.stopPropagation(); - if (input.trim() || attachments.length > 0) { + if ((selectedModel === 'openai/o1-mini' ? o1Input : input).trim() || (selectedModel !== 'openai/o1-mini' && attachments.length > 0)) { + track("search enter", { query: (selectedModel === 'openai/o1-mini' ? o1Input : input).trim() }); setHasSubmitted(true); - handleSubmit(event, { - experimental_attachments: attachments, - }); + if (selectedModel === 'openai/o1-mini') { + handleO1Submit(); + } else { + handleSubmit(event, { + experimental_attachments: attachments, + }); + } setAttachments([]); setUploadingAttachments([]); setSuggestedQuestions([]); @@ -1706,44 +2218,6 @@ export default function Home() { } }; - const handlePaste = async (e: React.ClipboardEvent) => { - e.preventDefault(); - - // Handle text paste - const text = e.clipboardData.getData('text'); - if (text) { - setInput(text); - } - - // Handle image paste - const items = Array.from(e.clipboardData.items); - const imageItems = items.filter(item => item.type.indexOf('image') !== -1); - - if (imageItems.length > 0) { - if (attachments.length + uploadingAttachments.length + imageItems.length > MAX_IMAGES) { - toast.error(`You can only attach up to ${MAX_IMAGES} images.`); - return; - } - - for (const item of imageItems) { - const file = item.getAsFile(); - if (file) { - const newUploadingAttachment = { file, progress: 0 }; - setUploadingAttachments(prev => [...prev, newUploadingAttachment]); - - try { - const uploadedFile = await uploadFile(file); - setAttachments(prev => [...prev, uploadedFile]); - setUploadingAttachments(prev => prev.filter(ua => ua.file !== file)); - } catch (error) { - console.error("Error uploading file:", error); - toast.error(`Failed to upload pasted image`); - setUploadingAttachments(prev => prev.filter(ua => ua.file !== file)); - } - } - } - } - }; return ( ); }} - onPaste={handlePaste} className={` ${hasSubmitted ? 'fixed bottom-4 left-1/2 -translate-x-1/2 max-w-[90%] sm:max-w-2xl' : 'max-w-full'} ${attachments.length > 0 || uploadingAttachments.length > 0 ? 'rounded-2xl' : 'rounded-full'} @@ -1771,9 +2244,9 @@ export default function Home() { z-50 `} > -
    0 || uploadingAttachments.length > 0 ? 'p-2' : 'p-0'}`}> +
    0 || uploadingAttachments.length > 0 ? 'p-2' : 'p-0')}> - {(attachments.length > 0 || uploadingAttachments.length > 0) && ( + {selectedModel !== 'openai/o1-mini' && (attachments.length > 0 || uploadingAttachments.length > 0) && ( )} -
    +
    { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + onSubmit(e as any); + } + }} /> - + {selectedModel !== 'openai/o1-mini' && ( + + )} @@ -1843,39 +2330,21 @@ export default function Home() { }; - const SuggestionCards: React.FC = () => { + const SuggestionCards: React.FC<{ selectedModel: string }> = ({ selectedModel }) => { return ( -
    -
    - -
    -
    - {suggestionCards.slice(1).map((card, index) => ( +
    +
    + {suggestionCards.map((card, index) => ( ))} @@ -1887,6 +2356,7 @@ export default function Home() { const models = [ { value: "azure:gpt4o-mini", label: "OpenAI", icon: Zap, description: "High speed, lower quality", color: "emerald" }, { value: "anthropicVertex:claude-3-5-sonnet@20240620", label: "Claude", icon: Sparkles, description: "High quality, lower speed", color: "indigo" }, + { value: "openai/o1-mini", label: "Reasoning", icon: Flame, description: "Experimental model", color: "orange" }, ] interface ModelSwitcherProps { @@ -1909,6 +2379,10 @@ export default function Home() { return isSelected ? '!bg-indigo-500 !text-white hover:!bg-indigo-600' : '!text-indigo-700 hover:!bg-indigo-100'; + case 'orange': + return isSelected + ? '!bg-orange-500 !text-white hover:!bg-orange-600' + : '!text-orange-700 hover:!bg-orange-100'; default: return isSelected ? 'bg-gray-500 text-white hover:bg-gray-600' @@ -1976,22 +2450,31 @@ export default function Home() { const handleModelChange = useCallback((newModel: string) => { setSelectedModel(newModel); setSuggestedQuestions([]); - if (messages.length > 0) { - reload({ - body: { - model: newModel, - }, - }); + if (newModel === 'openai/o1-mini') { + setO1Conversation([]); + } else if (messages.length > 0) { + reload({ body: { model: newModel } }); } }, [messages, reload]); return (
    - +
    {!hasSubmitted && (
    + setOpenChangelog(true)} + className="cursor-pointer gap-1 mb-2" + variant="green" + > + What's new +

    MiniPerplx

    In search for minimalism and simplicity @@ -2011,127 +2494,187 @@ export default function Home() { transition={{ duration: 0.5 }} > - + )}
    - {messages.map((message, index) => ( -
    - {message.role === 'user' && ( + {selectedModel === 'openai/o1-mini' ? ( + <> + {o1Conversation.map((message, index) => ( +
    + {message.role === 'user' && ( + + +
    +

    + {message.content} +

    +
    +
    + )} + {message.role === 'assistant' && ( +
    +
    +
    + +

    Answer

    +
    + +
    +
    + +
    +
    + )} +
    + ))} + {isO1Loading && ( - +
    - {isEditingMessage && editingMessageIndex === index ? ( -
    - setInput(e.target.value)} - className="flex-grow" - /> - - -
    - ) : ( -
    -

    - {message.content} -

    -
    - {message.experimental_attachments?.map((attachment, attachmentIndex) => ( -
    - {attachment.contentType!.startsWith('image/') && ( - {attachment.name - )} -
    - ))} -
    -
    - )} -
    - - {/* {!isEditingMessage && index === lastUserMessageIndex && ( -
    - - +
    + +

    Thinking...

    - - )} */} +
    +
    +
    +
    +
    )} - {message.role === 'assistant' && message.content && ( -
    -
    -
    - -

    Answer

    + + ) : ( + messages.map((message, index) => ( +
    + {message.role === 'user' && ( + + +
    + {isEditingMessage && editingMessageIndex === index ? ( +
    + setInput(e.target.value)} + className="flex-grow" + /> + + +
    + ) : ( +
    +

    + {message.content} +

    +
    + {message.experimental_attachments?.map((attachment, attachmentIndex) => ( +
    + {attachment.contentType!.startsWith('image/') && ( + {attachment.name + )} +
    + ))} +
    +
    + )}
    - -
    + + {!isEditingMessage && index === lastUserMessageIndex && ( +
    + +
    + )} + + )} + {message.role === 'assistant' && message.content && (
    - +
    +
    + +

    Answer

    +
    +
    + + +
    +
    +
    + +
    -
    - )} - {message.toolInvocations?.map((toolInvocation: ToolInvocation, toolIndex: number) => ( -
    - {renderToolInvocation(toolInvocation, toolIndex)} -
    - ))} -
    - ))} + )} + {message.toolInvocations?.map((toolInvocation: ToolInvocation, toolIndex: number) => ( +
    + {renderToolInvocation(toolInvocation, toolIndex)} +
    + ))} +
    + )))} {suggestedQuestions.length > 0 && ( {hasSubmitted && ( )} +
    ); } \ No newline at end of file diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx index e87d62b..883cd34 100644 --- a/components/ui/badge.tsx +++ b/components/ui/badge.tsx @@ -12,6 +12,8 @@ const badgeVariants = cva( "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + green: + "border-transparent bg-green-500/20 hover:bg-green-500/30 text-green-800 rounded-full", destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", outline: "text-foreground", diff --git a/components/ui/table.tsx b/components/ui/table.tsx new file mode 100644 index 0000000..c0df655 --- /dev/null +++ b/components/ui/table.tsx @@ -0,0 +1,120 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Table = React.forwardRef< + HTMLTableElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
    + + +)) +Table.displayName = "Table" + +const TableHeader = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableHeader.displayName = "TableHeader" + +const TableBody = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableBody.displayName = "TableBody" + +const TableFooter = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + tr]:last:border-b-0", + className + )} + {...props} + /> +)) +TableFooter.displayName = "TableFooter" + +const TableRow = React.forwardRef< + HTMLTableRowElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableRow.displayName = "TableRow" + +const TableHead = React.forwardRef< + HTMLTableCellElement, + React.ThHTMLAttributes +>(({ className, ...props }, ref) => ( +
    [role=checkbox]]:translate-y-[2px]", + className + )} + {...props} + /> +)) +TableHead.displayName = "TableHead" + +const TableCell = React.forwardRef< + HTMLTableCellElement, + React.TdHTMLAttributes +>(({ className, ...props }, ref) => ( + [role=checkbox]]:translate-y-[2px]", + className + )} + {...props} + /> +)) +TableCell.displayName = "TableCell" + +const TableCaption = React.forwardRef< + HTMLTableCaptionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
    +)) +TableCaption.displayName = "TableCaption" + +export { + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption, +} diff --git a/next.config.mjs b/next.config.mjs index cf3f647..00d6a30 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -41,7 +41,14 @@ const nextConfig = { hostname: 'metwm7frkvew6tn1.public.blob.vercel-storage.com', port: '', pathname: "**" - } + }, + // upload.wikimedia.org + { + protocol: 'https', + hostname: 'upload.wikimedia.org', + port: '', + pathname: '**' + }, ] } }; diff --git a/package.json b/package.json index 04b3d94..48ad5ba 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@ai-sdk/openai": "^0.0.58", "@e2b/code-interpreter": "^0.0.8", "@foobar404/wave": "^2.0.5", - "@mendable/firecrawl-js": "^0.0.36", + "@mendable/firecrawl-js": "^1.4.3", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1", @@ -28,19 +28,27 @@ "@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.2", "@tailwindcss/typography": "^0.5.13", + "@types/katex": "^0.16.7", + "@types/unist": "^3.0.3", + "@upstash/ratelimit": "^2.0.3", + "@upstash/redis": "^1.34.0", "@vercel/analytics": "^1.3.1", "@vercel/blob": "^0.23.4", "@vercel/functions": "^1.4.0", "ai": "latest", "anthropic-vertex-ai": "^1.0.0", + "cheerio": "^1.0.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "date-fns": "^3.6.0", - "embla-carousel-react": "^8.2.0", + "embla-carousel-autoplay": "^8.3.0", + "embla-carousel-react": "^8.3.0", "framer-motion": "^11.3.19", "google-auth-library": "^9.14.1", + "highlight.js": "^11.10.0", "katex": "^0.16.11", "lucide-react": "^0.424.0", + "marked-react": "^2.0.0", "next": "^14.2.5", "openai": "^4.56.0", "react": "^18", @@ -55,6 +63,8 @@ "sonner": "^1.5.0", "tailwind-merge": "^2.4.0", "tailwindcss-animate": "^1.0.7", + "unified": "^11.0.5", + "unist-util-visit": "^5.0.0", "zod": "^3.23.8" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be1f889..32818e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ dependencies: version: 0.0.31(zod@3.23.8) '@ai-sdk/cohere': specifier: latest - version: 0.0.23(zod@3.23.8) + version: 0.0.24(zod@3.23.8) '@ai-sdk/google': specifier: ^0.0.46 version: 0.0.46(zod@3.23.8) @@ -24,8 +24,8 @@ dependencies: specifier: ^2.0.5 version: 2.0.5 '@mendable/firecrawl-js': - specifier: ^0.0.36 - version: 0.0.36 + specifier: ^1.4.3 + version: 1.4.3(ws@8.18.0) '@radix-ui/react-accordion': specifier: ^1.2.0 version: 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) @@ -62,6 +62,18 @@ dependencies: '@tailwindcss/typography': specifier: ^0.5.13 version: 0.5.13(tailwindcss@3.4.7) + '@types/katex': + specifier: ^0.16.7 + version: 0.16.7 + '@types/unist': + specifier: ^3.0.3 + version: 3.0.3 + '@upstash/ratelimit': + specifier: ^2.0.3 + version: 2.0.3 + '@upstash/redis': + specifier: ^1.34.0 + version: 1.34.0 '@vercel/analytics': specifier: ^1.3.1 version: 1.3.1(next@14.2.5)(react@18.3.1) @@ -73,10 +85,13 @@ dependencies: version: 1.4.0 ai: specifier: latest - version: 3.3.28(openai@4.56.0)(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8) + version: 3.3.40(openai@4.56.0)(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8) anthropic-vertex-ai: specifier: ^1.0.0 version: 1.0.0(zod@3.23.8) + cheerio: + specifier: ^1.0.0 + version: 1.0.0 class-variance-authority: specifier: ^0.7.0 version: 0.7.0 @@ -86,21 +101,30 @@ dependencies: date-fns: specifier: ^3.6.0 version: 3.6.0 + embla-carousel-autoplay: + specifier: ^8.3.0 + version: 8.3.0(embla-carousel@8.3.0) embla-carousel-react: - specifier: ^8.2.0 - version: 8.2.0(react@18.3.1) + specifier: ^8.3.0 + version: 8.3.0(react@18.3.1) framer-motion: specifier: ^11.3.19 version: 11.3.20(react-dom@18.3.1)(react@18.3.1) google-auth-library: specifier: ^9.14.1 version: 9.14.1 + highlight.js: + specifier: ^11.10.0 + version: 11.10.0 katex: specifier: ^0.16.11 version: 0.16.11 lucide-react: specifier: ^0.424.0 version: 0.424.0(react@18.3.1) + marked-react: + specifier: ^2.0.0 + version: 2.0.0(react@18.3.1) next: specifier: ^14.2.5 version: 14.2.5(react-dom@18.3.1)(react@18.3.1) @@ -143,6 +167,12 @@ dependencies: tailwindcss-animate: specifier: ^1.0.7 version: 1.0.7(tailwindcss@3.4.7) + unified: + specifier: ^11.0.5 + version: 11.0.5 + unist-util-visit: + specifier: ^5.0.0 + version: 5.0.0 zod: specifier: ^3.23.8 version: 3.23.8 @@ -196,14 +226,14 @@ packages: zod: 3.23.8 dev: false - /@ai-sdk/cohere@0.0.23(zod@3.23.8): - resolution: {integrity: sha512-NJxntRnbY6bmWpYlYz1d0q6spx2OZ2YhniPfXfGTXZOwQPlze0rqkYePWpr7J2BJ+Qr0/XhpZTG9Dcf5qgOyig==} + /@ai-sdk/cohere@0.0.24(zod@3.23.8): + resolution: {integrity: sha512-2BDe6hSp3N6lRW9qS6/knjZVAUk0oo/oGzANar0XrENrFeMiMGh0tr081otATyZLxVeFJjksI029hW9QwWZNeg==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 dependencies: '@ai-sdk/provider': 0.0.23 - '@ai-sdk/provider-utils': 1.0.18(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.19(zod@3.23.8) zod: 3.23.8 dev: false @@ -289,6 +319,22 @@ packages: zod: 3.23.8 dev: false + /@ai-sdk/provider-utils@1.0.19(zod@3.23.8): + resolution: {integrity: sha512-p02Fq5Mnc8T6nwRBN1Iaou8YXvN1sDS6hbmJaD5UaRbXjizbh+8rpFS/o7jqAHTwf3uHCDitP3pnODyHdc/CDQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + peerDependenciesMeta: + zod: + optional: true + dependencies: + '@ai-sdk/provider': 0.0.23 + eventsource-parser: 1.1.2 + nanoid: 3.3.6 + secure-json-parse: 2.7.0 + zod: 3.23.8 + dev: false + /@ai-sdk/provider@0.0.21: resolution: {integrity: sha512-9j95uaPRxwYkzQdkl4XO/MmWWW5c5vcVSXtqvALpD9SMB9fzH46dO3UN4VbOJR2J3Z84CZAqgZu5tNlkptT9qQ==} engines: {node: '>=18'} @@ -310,8 +356,8 @@ packages: json-schema: 0.4.0 dev: false - /@ai-sdk/react@0.0.55(react@18.3.1)(zod@3.23.8): - resolution: {integrity: sha512-9fUUEEEoH01M6ZhvyZ/2v0DI6tiYnSldBg6RaKoy+qx2tSeKvOpFNZhT/iOvQ7oqAyyp0Ocg5Rj7L/jcLXSMxw==} + /@ai-sdk/react@0.0.59(react@18.3.1)(zod@3.23.8): + resolution: {integrity: sha512-1WbgO3J2/OoheMuNMxy5itJ3NVqOpqpAQxFNp7AoXgnDv4wDF4kTif61rTlKh7dCPvBHj2HXLmob+TrVFaWhYw==} engines: {node: '>=18'} peerDependencies: react: ^18 || ^19 @@ -322,15 +368,15 @@ packages: zod: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.18(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.41(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.19(zod@3.23.8) + '@ai-sdk/ui-utils': 0.0.44(zod@3.23.8) react: 18.3.1 swr: 2.2.5(react@18.3.1) zod: 3.23.8 dev: false - /@ai-sdk/solid@0.0.44(zod@3.23.8): - resolution: {integrity: sha512-3kMhxalepc78jWr2Qg1BAHbY04JKYxp8wRu3TACrRUdokxzwD5sbZYtTb7vu9tw2wx78rfu0DH44CESFWpSfZg==} + /@ai-sdk/solid@0.0.47(zod@3.23.8): + resolution: {integrity: sha512-lVMxIxtuNqoo/TObSFGflEP2dUeJv7bfPQbS4jHTZGBNlyhgBRY2Xc19yNjA3QKRfvQNDVoQusqxn+18MiHJJQ==} engines: {node: '>=18'} peerDependencies: solid-js: ^1.7.7 @@ -338,14 +384,14 @@ packages: solid-js: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.18(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.41(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.19(zod@3.23.8) + '@ai-sdk/ui-utils': 0.0.44(zod@3.23.8) transitivePeerDependencies: - zod dev: false - /@ai-sdk/svelte@0.0.46(svelte@4.2.18)(zod@3.23.8): - resolution: {integrity: sha512-cokqS91vQkpqiRgf8xKwOONFb/RwkIbRg9jYVRb+z5NR9OsWXKMEfoCAf8+VgURfVbp8nqA+ddRXvtgYCwqQjQ==} + /@ai-sdk/svelte@0.0.49(svelte@4.2.18)(zod@3.23.8): + resolution: {integrity: sha512-gV0MhaWxkatjf7uJrCAHO3bWrihokNUwGhuMCgyG+y53lwJKAYhR0zCoDRM2HnTJ89fdnx/PVe3R9fOWEVY5qA==} engines: {node: '>=18'} peerDependencies: svelte: ^3.0.0 || ^4.0.0 @@ -353,16 +399,16 @@ packages: svelte: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.18(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.41(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.19(zod@3.23.8) + '@ai-sdk/ui-utils': 0.0.44(zod@3.23.8) sswr: 2.1.0(svelte@4.2.18) svelte: 4.2.18 transitivePeerDependencies: - zod dev: false - /@ai-sdk/ui-utils@0.0.41(zod@3.23.8): - resolution: {integrity: sha512-I0trJKWxVG8hXeG0MvKqLG54fZjdeGjXvcVZocaSnWMBhl9lpTQxrqAR6ZsQMFDXs5DbvXoKtQs488qu2Bzaiw==} + /@ai-sdk/ui-utils@0.0.44(zod@3.23.8): + resolution: {integrity: sha512-0qiyun/n5zqJzQs/WfQT86dZE5DiDhSHJc7b7ZGLYvNMztHkRQmak2zUCZP4IyGVZEicyEPQK6NEEpBgkmd3Dg==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -371,15 +417,15 @@ packages: optional: true dependencies: '@ai-sdk/provider': 0.0.23 - '@ai-sdk/provider-utils': 1.0.18(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.19(zod@3.23.8) json-schema: 0.4.0 secure-json-parse: 2.7.0 zod: 3.23.8 zod-to-json-schema: 3.23.2(zod@3.23.8) dev: false - /@ai-sdk/vue@0.0.46(vue@3.4.35)(zod@3.23.8): - resolution: {integrity: sha512-H366ydskPbZP8uRs4sm3SAi97P3JVTRI5Q8xYTI6uTaY4UFBA6aOWdDxniYZNa67ebemfe11m7ksX4wHW6Wl8g==} + /@ai-sdk/vue@0.0.49(vue@3.4.35)(zod@3.23.8): + resolution: {integrity: sha512-GLjk5uhn0dA8iXpqdF91NyOw+VCgDIo22zrdkRtDg+nLaqkFSjgdDLAp7CL+ihW4F0/IkpZym3j0lFi9LiCjZA==} engines: {node: '>=18'} peerDependencies: vue: ^3.3.4 @@ -387,8 +433,8 @@ packages: vue: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.18(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.41(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.19(zod@3.23.8) + '@ai-sdk/ui-utils': 0.0.44(zod@3.23.8) swrv: 1.0.4(vue@3.4.35) vue: 3.4.35(typescript@5.5.4) transitivePeerDependencies: @@ -585,16 +631,17 @@ packages: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - /@mendable/firecrawl-js@0.0.36: - resolution: {integrity: sha512-5zQMWUD49r6Q7cxj+QBthQ964Bm9fMooW4E8E4nIca3BMXCeEuQFVf5C3OEWwZf0SjJvR+5Yx2wUbXJWd1wCOA==} + /@mendable/firecrawl-js@1.4.3(ws@8.18.0): + resolution: {integrity: sha512-bqyIGAqEa1LIxdCZ6qsKz++z7nOqsSjrMKRP9d2gvCQKQiM0nbqG/Abx6C1v1oEERU0mAsOapureBf/dtf7gDw==} dependencies: axios: 1.7.4 - dotenv: 16.4.5 - uuid: 9.0.1 + isows: 1.0.4(ws@8.18.0) + typescript-event-target: 1.1.1 zod: 3.23.8 zod-to-json-schema: 3.23.2(zod@3.23.8) transitivePeerDependencies: - debug + - ws dev: false /@next/env@14.2.5: @@ -1574,7 +1621,7 @@ packages: /@types/hast@3.0.4: resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 dev: false /@types/json5@0.0.29: @@ -1588,7 +1635,7 @@ packages: /@types/mdast@4.0.4: resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 dev: false /@types/ms@0.7.34: @@ -1637,8 +1684,8 @@ packages: resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} dev: false - /@types/unist@3.0.2: - resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} + /@types/unist@3.0.3: + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} dev: false /@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4): @@ -1708,6 +1755,25 @@ packages: /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + /@upstash/core-analytics@0.0.10: + resolution: {integrity: sha512-7qJHGxpQgQr9/vmeS1PktEwvNAF7TI4iJDi8Pu2CFZ9YUGHZH4fOP5TfYlZ4aVxfopnELiE4BS4FBjyK7V1/xQ==} + engines: {node: '>=16.0.0'} + dependencies: + '@upstash/redis': 1.34.0 + dev: false + + /@upstash/ratelimit@2.0.3: + resolution: {integrity: sha512-BMUpZPZ9IMwrUwohw0HoVAwjBRo5SDb0riAxfCGrLbutuZTPiVagh017Cm3GfhMqwUWLOp0xJQxTCXp812UJVQ==} + dependencies: + '@upstash/core-analytics': 0.0.10 + dev: false + + /@upstash/redis@1.34.0: + resolution: {integrity: sha512-TrXNoJLkysIl8SBc4u9bNnyoFYoILpCcFJcLyWCccb/QSUmaVKdvY0m5diZqc3btExsapcMbaw/s/wh9Sf1pJw==} + dependencies: + crypto-js: 4.2.0 + dev: false + /@vercel/analytics@1.3.1(next@14.2.5)(react@18.3.1): resolution: {integrity: sha512-xhSlYgAuJ6Q4WQGkzYTLmXwhYl39sWjoMA3nHxfkvG+WdBT25c563a7QhwwKivEOZtPJXifYHR1m2ihoisbWyA==} peerDependencies: @@ -1855,8 +1921,8 @@ packages: humanize-ms: 1.2.1 dev: false - /ai@3.3.28(openai@4.56.0)(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8): - resolution: {integrity: sha512-ogrsMscar8oXa4nTEcnjvb37cs0UJ7AxVga/642BQGkGBevnKhS0hbnXEOUKmlWcny/xRuWQ3GaXA3u9CxhfhQ==} + /ai@3.3.40(openai@4.56.0)(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8): + resolution: {integrity: sha512-EqCrKHKO0TIsZANJEO3QY9uNXiZC9owhpE/jn9Yt83ET3qPrdIi3zbx/MXX8F6ivmKt2vzoYzR4a5Z7l9kJLJQ==} engines: {node: '>=18'} peerDependencies: openai: ^4.42.0 @@ -1877,12 +1943,12 @@ packages: optional: true dependencies: '@ai-sdk/provider': 0.0.23 - '@ai-sdk/provider-utils': 1.0.18(zod@3.23.8) - '@ai-sdk/react': 0.0.55(react@18.3.1)(zod@3.23.8) - '@ai-sdk/solid': 0.0.44(zod@3.23.8) - '@ai-sdk/svelte': 0.0.46(svelte@4.2.18)(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.41(zod@3.23.8) - '@ai-sdk/vue': 0.0.46(vue@3.4.35)(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.19(zod@3.23.8) + '@ai-sdk/react': 0.0.59(react@18.3.1)(zod@3.23.8) + '@ai-sdk/solid': 0.0.47(zod@3.23.8) + '@ai-sdk/svelte': 0.0.49(svelte@4.2.18)(zod@3.23.8) + '@ai-sdk/ui-utils': 0.0.44(zod@3.23.8) + '@ai-sdk/vue': 0.0.49(vue@3.4.35)(zod@3.23.8) '@opentelemetry/api': 1.9.0 eventsource-parser: 1.1.2 json-schema: 0.4.0 @@ -2137,6 +2203,10 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + /boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + dev: false + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -2248,6 +2318,34 @@ packages: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} dev: false + /cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + 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.1.0 + dev: false + + /cheerio@1.0.0: + resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==} + engines: {node: '>=18.17'} + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.1.0 + encoding-sniffer: 0.2.0 + htmlparser2: 9.1.0 + parse5: 7.1.2 + parse5-htmlparser2-tree-adapter: 7.0.0 + parse5-parser-stream: 7.1.2 + undici: 6.19.8 + whatwg-mimetype: 4.0.0 + dev: false + /chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -2337,6 +2435,20 @@ packages: shebang-command: 2.0.0 which: 2.0.2 + /crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + dev: false + + /css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.1.0 + nth-check: 2.1.1 + dev: false + /css-tree@2.3.1: resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} @@ -2345,6 +2457,11 @@ packages: source-map-js: 1.2.0 dev: false + /css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + dev: false + /cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -2595,9 +2712,31 @@ packages: csstype: 3.1.3 dev: false - /dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} - engines: {node: '>=12'} + /dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + dev: false + + /domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + dev: false + + /domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + dev: false + + /domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 dev: false /e2b@0.16.2: @@ -2624,26 +2763,34 @@ packages: safe-buffer: 5.2.1 dev: false - /embla-carousel-react@8.2.0(react@18.3.1): - resolution: {integrity: sha512-dWqbmaEBQjeAcy/EKrcAX37beVr0ubXuHPuLZkx27z58V1FIvRbbMb4/c3cLZx0PAv/ofngX2QFrwUB+62SPnw==} + /embla-carousel-autoplay@8.3.0(embla-carousel@8.3.0): + resolution: {integrity: sha512-h7DFJLf9uQD+XDxr1NwA3/oFIjsnj/iED2RjET5u6/svMec46IbF1CYPhmB5Q/1Fc0WkcvhPpsEsrtVXQLxNzA==} + peerDependencies: + embla-carousel: 8.3.0 + dependencies: + embla-carousel: 8.3.0 + dev: false + + /embla-carousel-react@8.3.0(react@18.3.1): + resolution: {integrity: sha512-P1FlinFDcIvggcErRjNuVqnUR8anyo8vLMIH8Rthgofw7Nj8qTguCa2QjFAbzxAUTQTPNNjNL7yt0BGGinVdFw==} peerDependencies: react: ^16.8.0 || ^17.0.1 || ^18.0.0 dependencies: - embla-carousel: 8.2.0 - embla-carousel-reactive-utils: 8.2.0(embla-carousel@8.2.0) + embla-carousel: 8.3.0 + embla-carousel-reactive-utils: 8.3.0(embla-carousel@8.3.0) react: 18.3.1 dev: false - /embla-carousel-reactive-utils@8.2.0(embla-carousel@8.2.0): - resolution: {integrity: sha512-ZdaPNgMydkPBiDRUv+wRIz3hpZJ3LKrTyz+XWi286qlwPyZFJDjbzPBiXnC3czF9N/nsabSc7LTRvGauUzwKEg==} + /embla-carousel-reactive-utils@8.3.0(embla-carousel@8.3.0): + resolution: {integrity: sha512-EYdhhJ302SC4Lmkx8GRsp0sjUhEN4WyFXPOk0kGu9OXZSRMmcBlRgTvHcq8eKJE1bXWBsOi1T83B+BSSVZSmwQ==} peerDependencies: - embla-carousel: 8.2.0 + embla-carousel: 8.3.0 dependencies: - embla-carousel: 8.2.0 + embla-carousel: 8.3.0 dev: false - /embla-carousel@8.2.0: - resolution: {integrity: sha512-rf2GIX8rab9E6ZZN0Uhz05746qu2KrDje9IfFyHzjwxLwhvGjUt6y9+uaY1Sf+B0OPSa3sgas7BE2hWZCtopTA==} + /embla-carousel@8.3.0: + resolution: {integrity: sha512-Ve8dhI4w28qBqR8J+aMtv7rLK89r1ZA5HocwFz6uMB/i5EiC7bGI7y+AM80yAVUJw3qqaZYK7clmZMUR8kM3UA==} dev: false /emoji-regex@8.0.0: @@ -2652,6 +2799,13 @@ packages: /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + /encoding-sniffer@0.2.0: + resolution: {integrity: sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==} + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + dev: false + /enhanced-resolve@5.17.1: resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} engines: {node: '>=10.13.0'} @@ -3520,7 +3674,7 @@ packages: resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==} dependencies: '@types/hast': 3.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 devlop: 1.1.0 hastscript: 8.0.0 property-information: 6.5.0 @@ -3550,7 +3704,7 @@ packages: dependencies: '@types/estree': 1.0.5 '@types/hast': 3.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 comma-separated-tokens: 2.0.3 devlop: 1.1.0 estree-util-is-identifier-name: 3.0.0 @@ -3571,7 +3725,7 @@ packages: resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} dependencies: '@types/hast': 3.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 hast-util-is-element: 3.0.0 unist-util-find-after: 5.0.0 dev: false @@ -3606,10 +3760,24 @@ packages: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} dev: false + /highlight.js@11.10.0: + resolution: {integrity: sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==} + engines: {node: '>=12.0.0'} + dev: false + /html-url-attributes@3.0.0: resolution: {integrity: sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow==} dev: false + /htmlparser2@9.1.0: + resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + entities: 4.5.0 + dev: false + /https-proxy-agent@7.0.5: resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} engines: {node: '>= 14'} @@ -3626,6 +3794,13 @@ packages: ms: 2.1.3 dev: false + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: false + /ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} @@ -3940,6 +4115,14 @@ packages: ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) dev: false + /isows@1.0.4(ws@8.18.0): + resolution: {integrity: sha512-hEzjY+x9u9hPmBom9IIAqdJCwNLax+xrPb51vEPpERoFlIxgmZcHzsT5jKG06nvInKOBGvReAVz80Umed5CczQ==} + peerDependencies: + ws: '*' + dependencies: + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) + dev: false + /iterator.prototype@1.1.2: resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} dependencies: @@ -4151,6 +4334,21 @@ packages: resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} dev: false + /marked-react@2.0.0(react@18.3.1): + resolution: {integrity: sha512-Mp5HqfONf/RDqFtA+6xw2EjKkSbA8/xNPwyJ8ewLy/q3v21lRsPA7h+HUndVAW/yEIoebvcyzzSDpbjzL/xjZg==} + peerDependencies: + react: ^16.8.0 || >=17.0.0 + dependencies: + marked: 6.0.0 + react: 18.3.1 + dev: false + + /marked@6.0.0: + resolution: {integrity: sha512-7E3m/xIlymrFL5gWswIT4CheIE3fDeh51NV09M4x8iOc7NDYlyERcQMLAIHcSlrvwliwbPQ4OGD+MpPSYiQcqw==} + engines: {node: '>= 16'} + hasBin: true + dev: false + /mdast-util-find-and-replace@3.0.1: resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==} dependencies: @@ -4164,7 +4362,7 @@ packages: resolution: {integrity: sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==} dependencies: '@types/mdast': 4.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 decode-named-character-reference: 1.0.2 devlop: 1.1.0 mdast-util-to-string: 4.0.0 @@ -4281,7 +4479,7 @@ packages: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 ccount: 2.0.1 devlop: 1.1.0 mdast-util-from-markdown: 2.0.1 @@ -4333,7 +4531,7 @@ packages: resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==} dependencies: '@types/mdast': 4.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 longest-streak: 3.1.0 mdast-util-phrasing: 4.1.0 mdast-util-to-string: 4.0.0 @@ -4757,6 +4955,12 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + /nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + dependencies: + boolbase: 1.0.0 + dev: false + /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -4922,6 +5126,19 @@ packages: is-hexadecimal: 2.0.1 dev: false + /parse5-htmlparser2-tree-adapter@7.0.0: + resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} + dependencies: + domhandler: 5.0.3 + parse5: 7.1.2 + dev: false + + /parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + dependencies: + parse5: 7.1.2 + dev: false + /parse5@7.1.2: resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} dependencies: @@ -5471,6 +5688,10 @@ packages: is-regex: 1.1.4 dev: true + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: false + /scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} dependencies: @@ -5953,6 +6174,10 @@ packages: possible-typed-array-names: 1.0.0 dev: true + /typescript-event-target@1.1.1: + resolution: {integrity: sha512-dFSOFBKV6uwaloBCCUhxlD3Pr/P1a/tJdcmPrTXCHlEFD3faj0mztjcGn6VBAhQ0/Bdy8K3VWrrqwbt/ffsYsg==} + dev: false + /typescript@5.5.4: resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} engines: {node: '>=14.17'} @@ -5977,10 +6202,15 @@ packages: '@fastify/busboy': 2.1.1 dev: false + /undici@6.19.8: + resolution: {integrity: sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==} + engines: {node: '>=18.17'} + dev: false + /unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 bail: 2.0.2 devlop: 1.1.0 extend: 3.0.2 @@ -5992,46 +6222,46 @@ packages: /unist-util-find-after@5.0.0: resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-is: 6.0.0 dev: false /unist-util-is@6.0.0: resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 dev: false /unist-util-position@5.0.0: resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 dev: false /unist-util-remove-position@5.0.0: resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-visit: 5.0.0 dev: false /unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 dev: false /unist-util-visit-parents@6.0.1: resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-is: 6.0.0 dev: false /unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 dev: false @@ -6100,21 +6330,21 @@ packages: /vfile-location@5.0.3: resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 vfile: 6.0.2 dev: false /vfile-message@4.0.2: resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-stringify-position: 4.0.0 dev: false /vfile@6.0.2: resolution: {integrity: sha512-zND7NlS8rJYb/sPqkb13ZvbbUoExdbi4w3SfRrMq6R3FvnLQmmfpajJNITuuYm6AZ5uao9vy4BAos3EXBPf2rg==} dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 dev: false @@ -6167,6 +6397,18 @@ packages: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: false + /whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + dependencies: + iconv-lite: 0.6.3 + dev: false + + /whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + dev: false + /whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: