diff --git a/app/actions.tsx b/app/actions.tsx index 9840ce04..1f94601a 100644 --- a/app/actions.tsx +++ b/app/actions.tsx @@ -1,750 +1,158 @@ +'use server' + import { - StreamableValue, - createAI, - createStreamableUI, - createStreamableValue, - getAIState, - getMutableAIState -} from 'ai/rsc' -import { CoreMessage, ToolResultPart } from 'ai' + streamText, + streamObject, + createDataStreamResponse, + CoreMessage, + ToolResultPart, + StreamData +} from 'ai' import { nanoid } from 'nanoid' -import type { FeatureCollection } from 'geojson' -import { Spinner } from '@/components/ui/spinner' -import { Section } from '@/components/section' -import { FollowupPanel } from '@/components/followup-panel' import { inquire, researcher, taskManager, querySuggestor, resolutionSearch } from '@/lib/agents' -// Removed import of useGeospatialToolMcp as it no longer exists and was incorrectly used here. -// The geospatialTool (if used by agents like researcher) now manages its own MCP client. import { writer } from '@/lib/agents/writer' -import { saveChat, getSystemPrompt } from '@/lib/actions/chat' // Added getSystemPrompt +import { saveChat, getSystemPrompt } from '@/lib/actions/chat' import { Chat, AIMessage } from '@/lib/types' -import { UserMessage } from '@/components/user-message' -import { BotMessage } from '@/components/message' -import { SearchSection } from '@/components/search-section' -import SearchRelated from '@/components/search-related' -import { GeoJsonLayer } from '@/components/map/geojson-layer' -import { CopilotDisplay } from '@/components/copilot-display' -import RetrieveSection from '@/components/retrieve-section' -import { VideoSearchSection } from '@/components/video-search-section' -import { MapQueryHandler } from '@/components/map/map-query-handler' // Add this import - -// Define the type for related queries -type RelatedQueries = { - items: { query: string }[] -} - -// Removed mcp parameter from submit, as geospatialTool now handles its client. -async function submit(formData?: FormData, skip?: boolean) { - 'use server' - - const aiState = getMutableAIState() - const uiStream = createStreamableUI() - const isGenerating = createStreamableValue(true) - const isCollapsed = createStreamableValue(false) - - const action = formData?.get('action') as string; - if (action === 'resolution_search') { - const file = formData?.get('file') as File; - if (!file) { - throw new Error('No file provided for resolution search.'); - } - - const buffer = await file.arrayBuffer(); - const dataUrl = `data:${file.type};base64,${Buffer.from(buffer).toString('base64')}`; - - // Get the current messages, excluding tool-related ones. - const messages: CoreMessage[] = [...(aiState.get().messages as any[])].filter( - message => - message.role !== 'tool' && - message.type !== 'followup' && - message.type !== 'related' && - message.type !== 'end' - ); - - // The user's prompt for this action is static. - const userInput = 'Analyze this map view.'; - - // Construct the multimodal content for the user message. - const content: CoreMessage['content'] = [ - { type: 'text', text: userInput }, - { type: 'image', image: dataUrl, mimeType: file.type } - ]; - - // Add the new user message to the AI state. - aiState.update({ - ...aiState.get(), - messages: [ - ...aiState.get().messages, - { id: nanoid(), role: 'user', content } - ] - }); - messages.push({ role: 'user', content }); - - // Call the simplified agent, which now returns data directly. - const analysisResult = await resolutionSearch(messages) as any; - - // Create a streamable value for the summary and mark it as done. - const summaryStream = createStreamableValue(); - summaryStream.done(analysisResult.summary || 'Analysis complete.'); - - // Update the UI stream with the BotMessage component. - uiStream.update( - - ); - - messages.push({ role: 'assistant', content: analysisResult.summary || 'Analysis complete.' }); - - const sanitizedMessages: CoreMessage[] = messages.map(m => { - if (Array.isArray(m.content)) { - return { - ...m, - content: m.content.filter(part => part.type !== 'image') - } as CoreMessage - } - return m - }) - - const relatedQueries = await querySuggestor(uiStream, sanitizedMessages); - uiStream.append( -
- -
- ); - - await new Promise(resolve => setTimeout(resolve, 500)); - - const groupeId = nanoid(); - - aiState.done({ - ...aiState.get(), - messages: [ - ...aiState.get().messages, - { - id: groupeId, - role: 'assistant', - content: analysisResult.summary || 'Analysis complete.', - type: 'response' - }, - { - id: groupeId, - role: 'assistant', - content: JSON.stringify(analysisResult), - type: 'resolution_search_result' - }, - { - id: groupeId, - role: 'assistant', - content: JSON.stringify(relatedQueries), - type: 'related' - }, - { - id: groupeId, - role: 'assistant', - content: 'followup', - type: 'followup' - } - ] - }); - - isGenerating.done(false); - uiStream.done(); - return { - id: nanoid(), - isGenerating: isGenerating.value, - component: uiStream.value, - isCollapsed: isCollapsed.value - }; - } - - const messages: CoreMessage[] = [...(aiState.get().messages as any[])].filter( - message => - message.role !== 'tool' && - message.type !== 'followup' && - message.type !== 'related' && - message.type !== 'end' - ) - - const groupeId = nanoid() - const useSpecificAPI = process.env.USE_SPECIFIC_API_FOR_WRITER === 'true' - const maxMessages = useSpecificAPI ? 5 : 10 - messages.splice(0, Math.max(messages.length - maxMessages, 0)) - - const userInput = skip - ? `{"action": "skip"}` - : ((formData?.get('related_query') as string) || - (formData?.get('input') as string)) +import { getCurrentUserIdOnServer } from '@/lib/auth/get-current-user' - if (userInput.toLowerCase().trim() === 'what is a planet computer?' || userInput.toLowerCase().trim() === 'what is qcx-terra?') { - const definition = userInput.toLowerCase().trim() === 'what is a planet computer?' - ? `A planet computer is a proprietary environment aware system that interoperates weather forecasting, mapping and scheduling using cutting edge multi-agents to streamline automation and exploration on a planet. Available for our Pro and Enterprise customers. [QCX Pricing](https://www.queue.cx/#pricing)` +export async function submit(messages: CoreMessage[], formData?: any) { + const userId = await getCurrentUserIdOnServer() || 'anonymous' + const chatId = (formData?.chatId as string) || nanoid() - : `QCX-Terra is a model garden of pixel level precision geospatial foundational models for efficient land feature predictions from satellite imagery. Available for our Pro and Enterprise customers. [QCX Pricing] (https://www.queue.cx/#pricing)`; + const dataStreamResponse = createDataStreamResponse({ + execute: async (dataStream) => { + const action = formData?.action as string; - const content = JSON.stringify(Object.fromEntries(formData!)); - const type = 'input'; + const saveMessages = async (assistantContent: string, data: any[] = []) => { + if (userId === 'anonymous') return; - aiState.update({ - ...aiState.get(), - messages: [ - ...aiState.get().messages, - { + const lastUserMessage = messages[messages.length - 1]; + const userAIMessage: AIMessage = { id: nanoid(), role: 'user', - content, - type, - }, - ], - }); + content: lastUserMessage.content, + type: 'input' + }; - const definitionStream = createStreamableValue(); - definitionStream.done(definition); - - const answerSection = ( -
- -
- ); - - uiStream.append(answerSection); - - const groupeId = nanoid(); - const relatedQueries = { items: [] }; - - aiState.done({ - ...aiState.get(), - messages: [ - ...aiState.get().messages, - { - id: groupeId, - role: 'assistant', - content: definition, - type: 'response', - }, - { - id: groupeId, - role: 'assistant', - content: JSON.stringify(relatedQueries), - type: 'related', - }, - { - id: groupeId, + const assistantAIMessage: AIMessage = { + id: nanoid(), role: 'assistant', - content: 'followup', - type: 'followup', - }, - ], - }); - - isGenerating.done(false); - uiStream.done(); - - return { - id: nanoid(), - isGenerating: isGenerating.value, - component: uiStream.value, - isCollapsed: isCollapsed.value, - }; - } - const file = !skip ? (formData?.get('file') as File) : undefined - - if (!userInput && !file) { - isGenerating.done(false) - return { - id: nanoid(), - isGenerating: isGenerating.value, - component: null, - isCollapsed: isCollapsed.value - } - } - - const messageParts: { - type: 'text' | 'image' - text?: string - image?: string - mimeType?: string - }[] = [] - - if (userInput) { - messageParts.push({ type: 'text', text: userInput }) - } - - if (file) { - const buffer = await file.arrayBuffer() - if (file.type.startsWith('image/')) { - const dataUrl = `data:${file.type};base64,${Buffer.from( - buffer - ).toString('base64')}` - messageParts.push({ - type: 'image', - image: dataUrl, - mimeType: file.type - }) - } else if (file.type === 'text/plain') { - const textContent = Buffer.from(buffer).toString('utf-8') - const existingTextPart = messageParts.find(p => p.type === 'text') - if (existingTextPart) { - existingTextPart.text = `${textContent}\n\n${existingTextPart.text}` - } else { - messageParts.push({ type: 'text', text: textContent }) - } - } - } - - const hasImage = messageParts.some(part => part.type === 'image') - // Properly type the content based on whether it contains images - const content: CoreMessage['content'] = hasImage - ? messageParts as CoreMessage['content'] - : messageParts.map(part => part.text).join('\n') - - const type = skip - ? undefined - : formData?.has('input') || formData?.has('file') - ? 'input' - : formData?.has('related_query') - ? 'input_related' - : 'inquiry' + content: assistantContent, + type: 'response' + }; + + const chat: Chat = { + id: chatId, + title: typeof lastUserMessage.content === 'string' ? lastUserMessage.content.substring(0, 100) : 'New Chat', + createdAt: new Date(), + userId: userId, + path: `/search/${chatId}`, + messages: [userAIMessage, assistantAIMessage] + }; + + // Add tool messages if any + // This is a simplified version of persistence + await saveChat(chat, userId); + }; + + if (action === 'resolution_search') { + const file = formData?.file as File; + if (file) { + const buffer = await file.arrayBuffer(); + const dataUrl = `data:${file.type};base64,${Buffer.from(buffer).toString('base64')}`; + + const userInput = 'Analyze this map view.'; + const content: CoreMessage['content'] = [ + { type: 'text', text: userInput }, + { type: 'image', image: dataUrl, mimeType: file.type } + ]; + + const msgWithImage = { role: 'user', content } as CoreMessage; + const messagesForAnalysis = [...messages, msgWithImage]; + + const analysisResult = await resolutionSearch(messagesForAnalysis) as any; + + dataStream.write(`0:${analysisResult.summary || 'Analysis complete.'}\n`); + dataStream.writeData({ type: 'resolution_search_result', object: analysisResult as any }); + + const relatedQueries = await querySuggestor(messagesForAnalysis); + for await (const obj of relatedQueries.partialObjectStream) { + dataStream.writeData({ type: 'related', object: obj as any }) + } - if (content) { - aiState.update({ - ...aiState.get(), - messages: [ - ...aiState.get().messages, - { - id: nanoid(), - role: 'user', - content, - type + await saveMessages(analysisResult.summary || 'Analysis complete.'); + return; } - ] - }) - messages.push({ - role: 'user', - content - } as CoreMessage) - } - - const userId = 'anonymous' - const currentSystemPrompt = (await getSystemPrompt(userId)) || '' - - const mapProvider = formData?.get('mapProvider') as 'mapbox' | 'google' - - async function processEvents() { - let action: any = { object: { next: 'proceed' } } - if (!skip) { - const taskManagerResult = await taskManager(messages) - if (taskManagerResult) { - action.object = taskManagerResult.object } - } - if (action.object.next === 'inquire') { - const inquiry = await inquire(uiStream, messages) - uiStream.done() - isGenerating.done() - isCollapsed.done(false) - aiState.done({ - ...aiState.get(), - messages: [ - ...aiState.get().messages, - { - id: nanoid(), - role: 'assistant', - content: `inquiry: ${inquiry?.question}` - } - ] - }) - return - } + const lastMessage = messages[messages.length - 1] + const userInput = typeof lastMessage.content === 'string' ? lastMessage.content : '' - isCollapsed.done(true) - let answer = '' - let toolOutputs: ToolResultPart[] = [] - let errorOccurred = false - const streamText = createStreamableValue() - uiStream.update() + // Handle special cases + if (userInput.toLowerCase().trim() === 'what is a planet computer?' || userInput.toLowerCase().trim() === 'what is qcx-terra?') { + const definition = userInput.toLowerCase().trim() === 'what is a planet computer?' + ? `A planet computer is a proprietary environment aware system that interoperates weather forecasting, mapping and scheduling using cutting edge multi-agents to streamline automation and exploration on a planet. Available for our Pro and Enterprise customers. [QCX Pricing](https://www.queue.cx/#pricing)` + : `QCX-Terra is a model garden of pixel level precision geospatial foundational models for efficient land feature predictions from satellite imagery. Available for our Pro and Enterprise customers. [QCX Pricing] (https://www.queue.cx/#pricing)`; - while ( - useSpecificAPI - ? answer.length === 0 - : answer.length === 0 && !errorOccurred - ) { - const { fullResponse, hasError, toolResponses } = await researcher( - currentSystemPrompt, - uiStream, - streamText, - messages, - mapProvider, - useSpecificAPI - ) - answer = fullResponse - toolOutputs = toolResponses - errorOccurred = hasError - - if (toolOutputs.length > 0) { - toolOutputs.map(output => { - aiState.update({ - ...aiState.get(), - messages: [ - ...aiState.get().messages, - { - id: groupeId, - role: 'tool', - content: JSON.stringify(output.result), - name: output.toolName, - type: 'tool' - } - ] - }) - }) + dataStream.write(`0:${definition}\n`) + await saveMessages(definition); + return } - } - - if (useSpecificAPI && answer.length === 0) { - const modifiedMessages = aiState - .get() - .messages.map(msg => - msg.role === 'tool' - ? { - ...msg, - role: 'assistant', - content: JSON.stringify(msg.content), - type: 'tool' - } - : msg - ) as CoreMessage[] - const latestMessages = modifiedMessages.slice(maxMessages * -1) - answer = await writer( - currentSystemPrompt, - uiStream, - streamText, - latestMessages - ) - } else { - streamText.done() - } - - if (!errorOccurred) { - const relatedQueries = await querySuggestor(uiStream, messages) - uiStream.append( -
- -
- ) - - await new Promise(resolve => setTimeout(resolve, 500)) - - aiState.done({ - ...aiState.get(), - messages: [ - ...aiState.get().messages, - { - id: groupeId, - role: 'assistant', - content: answer, - type: 'response' - }, - { - id: groupeId, - role: 'assistant', - content: JSON.stringify(relatedQueries), - type: 'related' - }, - { - id: groupeId, - role: 'assistant', - content: 'followup', - type: 'followup' - } - ] - }) - } - - isGenerating.done(false) - uiStream.done() - } - - processEvents() - - return { - id: nanoid(), - isGenerating: isGenerating.value, - component: uiStream.value, - isCollapsed: isCollapsed.value - } -} - -async function clearChat() { - 'use server' - - const aiState = getMutableAIState() - - aiState.done({ - chatId: nanoid(), - messages: [] - }) -} - -export type AIState = { - messages: AIMessage[] - chatId: string - isSharePage?: boolean -} - -export type UIState = { - id: string - component: React.ReactNode - isGenerating?: StreamableValue - isCollapsed?: StreamableValue -}[] - -const initialAIState: AIState = { - chatId: nanoid(), - messages: [] -} - -const initialUIState: UIState = [] -export const AI = createAI({ - actions: { - submit, - clearChat - }, - initialUIState, - initialAIState, - onGetUIState: async () => { - 'use server' + const currentSystemPrompt = (await getSystemPrompt(userId)) || '' + const mapProvider = (formData?.mapProvider as 'mapbox' | 'google') || 'mapbox' - const aiState = getAIState() as AIState - if (aiState) { - const uiState = getUIStateFromAIState(aiState) - return uiState - } - return initialUIState - }, - onSetAIState: async ({ state }) => { - 'use server' - - if (!state.messages.some(e => e.type === 'response')) { - return - } + // Task Manager + const taskManagerResult = await taskManager(messages) + if (taskManagerResult?.object.next === 'inquire') { + const inquiryResult = await inquire(messages) + let finalInquiry = ''; + for await (const obj of inquiryResult.partialObjectStream) { + dataStream.writeData({ type: 'inquiry', object: obj as any }) + if (obj.question) finalInquiry = obj.question; + } + await saveMessages(`inquiry: ${finalInquiry}`); + return + } - const { chatId, messages } = state - const createdAt = new Date() - const path = `/search/${chatId}` + // Researcher + const result = await researcher(currentSystemPrompt, messages, mapProvider) - let title = 'Untitled Chat' - if (messages.length > 0) { - const firstMessageContent = messages[0].content - if (typeof firstMessageContent === 'string') { - try { - const parsedContent = JSON.parse(firstMessageContent) - title = parsedContent.input?.substring(0, 100) || 'Untitled Chat' - } catch (e) { - title = firstMessageContent.substring(0, 100) + let fullResponse = '' + for await (const delta of result.fullStream) { + switch (delta.type) { + case 'text-delta': + if (delta.textDelta) { + fullResponse += delta.textDelta + dataStream.write(`0:${delta.textDelta}\n`) + } + break + case 'tool-call': + dataStream.writeData({ type: 'tool-call', toolCall: delta as any }) + break + case 'tool-result': + dataStream.writeData({ type: 'tool-result', toolResult: delta as any }) + break } - } else if (Array.isArray(firstMessageContent)) { - const textPart = ( - firstMessageContent as { type: string; text?: string }[] - ).find(p => p.type === 'text') - title = - textPart && textPart.text - ? textPart.text.substring(0, 100) - : 'Image Message' } - } - const updatedMessages: AIMessage[] = [ - ...messages, - { - id: nanoid(), - role: 'assistant', - content: `end`, - type: 'end' + // Query Suggestor + const relatedQueries = await querySuggestor(messages) + for await (const obj of relatedQueries.partialObjectStream) { + dataStream.writeData({ type: 'related', object: obj as any }) } - ] - - const { getCurrentUserIdOnServer } = await import( - '@/lib/auth/get-current-user' - ) - const actualUserId = await getCurrentUserIdOnServer() - - if (!actualUserId) { - console.error('onSetAIState: User not authenticated. Chat not saved.') - return - } - const chat: Chat = { - id: chatId, - createdAt, - userId: actualUserId, - path, - title, - messages: updatedMessages + await saveMessages(fullResponse); + }, + onError: (error) => { + console.error('Data stream error:', error) + return 'An error occurred.' } - await saveChat(chat, actualUserId) - } -}) - -export const getUIStateFromAIState = (aiState: AIState): UIState => { - const chatId = aiState.chatId - const isSharePage = aiState.isSharePage - return aiState.messages - .map((message, index) => { - const { role, content, id, type, name } = message - - if ( - !type || - type === 'end' || - (isSharePage && type === 'related') || - (isSharePage && type === 'followup') - ) - return null - - switch (role) { - case 'user': - switch (type) { - case 'input': - case 'input_related': - let messageContent: string | any[] - try { - // For backward compatibility with old messages that stored a JSON string - const json = JSON.parse(content as string) - messageContent = - type === 'input' ? json.input : json.related_query - } catch (e) { - // New messages will store the content array or string directly - messageContent = content - } - return { - id, - component: ( - - ) - } - case 'inquiry': - return { - id, - component: - } - } - break - case 'assistant': - const answer = createStreamableValue() - answer.done(content) - switch (type) { - case 'response': - return { - id, - component: ( -
- -
- ) - } - case 'related': - const relatedQueries = createStreamableValue() - relatedQueries.done(JSON.parse(content as string)) - return { - id, - component: ( -
- -
- ) - } - case 'followup': - return { - id, - component: ( -
- -
- ) - } - case 'resolution_search_result': { - const analysisResult = JSON.parse(content as string); - const geoJson = analysisResult.geoJson as FeatureCollection; - - return { - id, - component: ( - <> - {geoJson && ( - - )} - - ) - } - } - } - break - case 'tool': - try { - const toolOutput = JSON.parse(content as string) - const isCollapsed = createStreamableValue() - isCollapsed.done(true) + }) - if ( - toolOutput.type === 'MAP_QUERY_TRIGGER' && - name === 'geospatialQueryTool' - ) { - return { - id, - component: , - isCollapsed: false - } - } + return dataStreamResponse +} - const searchResults = createStreamableValue() - searchResults.done(JSON.stringify(toolOutput)) - switch (name) { - case 'search': - return { - id, - component: , - isCollapsed: isCollapsed.value - } - case 'retrieve': - return { - id, - component: , - isCollapsed: isCollapsed.value - } - case 'videoSearch': - return { - id, - component: ( - - ), - isCollapsed: isCollapsed.value - } - default: - console.warn( - `Unhandled tool result in getUIStateFromAIState: ${name}` - ) - return { id, component: null } - } - } catch (error) { - console.error( - 'Error parsing tool content in getUIStateFromAIState:', - error - ) - return { - id, - component: null - } - } - break - default: - return { - id, - component: null - } - } - }) - .filter(message => message !== null) as UIState +export async function clearChat() { + // Implementation handled via clearChats in lib/actions/chat.ts } diff --git a/app/page.tsx b/app/page.tsx index 051e54bb..742bf518 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,6 +1,5 @@ import { Chat } from '@/components/chat' import {nanoid } from 'nanoid' -import { AI } from './actions' export const maxDuration = 60 @@ -9,10 +8,8 @@ import { MapDataProvider } from '@/components/map/map-data-context' export default function Page() { const id = nanoid() return ( - - - - - + + + ) } diff --git a/app/search/[id]/page.tsx b/app/search/[id]/page.tsx index 8db74186..8878bcc2 100644 --- a/app/search/[id]/page.tsx +++ b/app/search/[id]/page.tsx @@ -1,7 +1,6 @@ import { notFound, redirect } from 'next/navigation'; import { Chat } from '@/components/chat'; import { getChat, getChatMessages } from '@/lib/actions/chat'; // Added getChatMessages -import { AI } from '@/app/actions'; import { MapDataProvider } from '@/components/map/map-data-context'; import { getCurrentUserIdOnServer } from '@/lib/auth/get-current-user'; // For server-side auth import type { AIMessage } from '@/lib/types'; // For AIMessage type @@ -15,8 +14,6 @@ export interface SearchPageProps { export async function generateMetadata({ params }: SearchPageProps) { const { id } = await params; // Keep as is for now - // TODO: Metadata generation might need authenticated user if chats are private - // For now, assuming getChat can be called or it handles anon access for metadata appropriately const userId = await getCurrentUserIdOnServer(); // Attempt to get user for metadata const chat = await getChat(id, userId || 'anonymous'); // Pass userId or 'anonymous' if none return { @@ -29,15 +26,12 @@ export default async function SearchPage({ params }: SearchPageProps) { const userId = await getCurrentUserIdOnServer(); if (!userId) { - // If no user, redirect to login or show appropriate page - // For now, redirecting to home, but a login page would be better. redirect('/'); } const chat = await getChat(id, userId); if (!chat) { - // If chat doesn't exist or user doesn't have access (handled by getChat) notFound(); } @@ -48,29 +42,15 @@ export default async function SearchPage({ params }: SearchPageProps) { const initialMessages: AIMessage[] = dbMessages.map((dbMsg): AIMessage => { return { id: dbMsg.id, - role: dbMsg.role as AIMessage['role'], // Cast role, ensure AIMessage['role'] includes all dbMsg.role possibilities + role: dbMsg.role as AIMessage['role'], content: dbMsg.content, createdAt: dbMsg.createdAt ? new Date(dbMsg.createdAt) : undefined, - // 'type' and 'name' are not in the basic Drizzle 'messages' schema. - // These would be undefined unless specific logic is added to derive them. - // For instance, if a message with role 'tool' should have a 'name', - // or if some messages have a specific 'type' based on content or other flags. - // This mapping assumes standard user/assistant messages primarily. }; }); return ( - - - - - + + + ); -} \ No newline at end of file +} diff --git a/bun.lock b/bun.lock index 936916d2..7941fb41 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "QCX", @@ -61,14 +62,14 @@ "lottie-react": "^2.4.1", "lucide-react": "^0.507.0", "mapbox-gl": "^3.11.0", - "next": "15.3.6", + "next": "^16.1.4", "next-themes": "^0.3.0", "open-codex": "^0.1.30", "pg": "^8.16.2", "proj4": "^2.20.2", "radix-ui": "^1.3.4", - "react": "19.1.2", - "react-dom": "19.1.2", + "react": "^19.2.3", + "react-dom": "^19.2.3", "react-hook-form": "^7.56.2", "react-icons": "^5.5.0", "react-markdown": "^9.1.0", @@ -97,7 +98,7 @@ "@types/uuid": "^9.0.0", "cross-env": "^7.0.3", "eslint": "^8.57.1", - "eslint-config-next": "^14.2.28", + "eslint-config-next": "^16.1.4", "postcss": "^8.5.3", "tailwindcss": "^3.4.17", "typescript": "^5.8.3", @@ -197,8 +198,40 @@ "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.3", "", {}, "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw=="], + "@babel/code-frame": ["@babel/code-frame@7.28.6", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q=="], + + "@babel/compat-data": ["@babel/compat-data@7.28.6", "", {}, "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg=="], + + "@babel/core": ["@babel/core@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw=="], + + "@babel/generator": ["@babel/generator@7.28.6", "", { "dependencies": { "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], + + "@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], + + "@babel/parser": ["@babel/parser@7.28.6", "", { "dependencies": { "@babel/types": "^7.28.6" }, "bin": "./bin/babel-parser.js" }, "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ=="], + "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="], + "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], + + "@babel/traverse": ["@babel/traverse@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/types": "^7.28.6", "debug": "^4.3.1" } }, "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg=="], + + "@babel/types": ["@babel/types@7.28.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg=="], + "@borewit/text-codec": ["@borewit/text-codec@0.2.1", "", {}, "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw=="], "@colors/colors": ["@colors/colors@1.6.0", "", {}, "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA=="], @@ -365,10 +398,10 @@ "@inkjs/ui": ["@inkjs/ui@2.0.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-spinners": "^3.0.0", "deepmerge": "^4.3.1", "figures": "^6.1.0" }, "peerDependencies": { "ink": ">=5" } }, "sha512-5+8fJmwtF9UvikzLfph9sA+LS+l37Ij/szQltkuXLOAXwNkBX9innfzh4pLGXIB59vKEQUtc6D4qGvhD7h3pAg=="], - "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], - "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], @@ -399,25 +432,25 @@ "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], - "@next/env": ["@next/env@15.3.6", "", {}, "sha512-/cK+QPcfRbDZxmI/uckT4lu9pHCfRIPBLqy88MhE+7Vg5hKrEYc333Ae76dn/cw2FBP2bR/GoK/4DU+U7by/Nw=="], + "@next/env": ["@next/env@16.1.4", "", {}, "sha512-gkrXnZyxPUy0Gg6SrPQPccbNVLSP3vmW8LU5dwEttEEC1RwDivk8w4O+sZIjFvPrSICXyhQDCG+y3VmjlJf+9A=="], - "@next/eslint-plugin-next": ["@next/eslint-plugin-next@14.2.35", "", { "dependencies": { "glob": "10.3.10" } }, "sha512-Jw9A3ICz2183qSsqwi7fgq4SBPiNfmOLmTPXKvlnzstUwyvBrtySiY+8RXJweNAs9KThb1+bYhZh9XWcNOr2zQ=="], + "@next/eslint-plugin-next": ["@next/eslint-plugin-next@16.1.4", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-38WMjGP8y+1MN4bcZFs+GTcBe0iem5GGTzFE5GWW/dWdRKde7LOXH3lQT2QuoquVWyfl2S0fQRchGmeacGZ4Wg=="], - "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.3.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lM/8tilIsqBq+2nq9kbTW19vfwFve0NR7MxfkuSUbRSgXlMQoJYg+31+++XwKVSXk4uT23G2eF/7BRIKdn8t8w=="], + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.1.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-T8atLKuvk13XQUdVLCv1ZzMPgLPW0+DWWbHSQXs0/3TjPrKNxTmUIhOEaoEyl3Z82k8h/gEtqyuoZGv6+Ugawg=="], - "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.3.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-WhwegPQJ5IfoUNZUVsI9TRAlKpjGVK0tpJTL6KeiC4cux9774NYE9Wu/iCfIkL/5J8rPAkqZpG7n+EfiAfidXA=="], + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.1.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-AKC/qVjUGUQDSPI6gESTx0xOnOPQ5gttogNS3o6bA83yiaSZJek0Am5yXy82F1KcZCx3DdOwdGPZpQCluonuxg=="], - "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.3.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-LVD6uMOZ7XePg3KWYdGuzuvVboxujGjbcuP2jsPAN3MnLdLoZUXKRc6ixxfs03RH7qBdEHCZjyLP/jBdCJVRJQ=="], + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.1.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-POQ65+pnYOkZNdngWfMEt7r53bzWiKkVNbjpmCt1Zb3V6lxJNXSsjwRuTQ8P/kguxDC8LRkqaL3vvsFrce4dMQ=="], - "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.3.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-k8aVScYZ++BnS2P69ClK7v4nOu702jcF9AIHKu6llhHEtBSmM2zkPGl9yoqbSU/657IIIb0QHpdxEr0iW9z53A=="], + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.1.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-3Wm0zGYVCs6qDFAiSSDL+Z+r46EdtCv/2l+UlIdMbAq9hPJBvGu/rZOeuvCaIUjbArkmXac8HnTyQPJFzFWA0Q=="], - "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.3.5", "", { "os": "linux", "cpu": "x64" }, "sha512-2xYU0DI9DGN/bAHzVwADid22ba5d/xrbrQlr2U+/Q5WkFUzeL0TDR963BdrtLS/4bMmKZGptLeg6282H/S2i8A=="], + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lWAYAezFinaJiD5Gv8HDidtsZdT3CDaCeqoPoJjeB57OqzvMajpIhlZFce5sCAH6VuX4mdkxCRqecCJFwfm2nQ=="], - "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.3.5", "", { "os": "linux", "cpu": "x64" }, "sha512-TRYIqAGf1KCbuAB0gjhdn5Ytd8fV+wJSM2Nh2is/xEqR8PZHxfQuaiNhoF50XfY90sNpaRMaGhF6E+qjV1b9Tg=="], + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-fHaIpT7x4gA6VQbdEpYUXRGyge/YbRrkG6DXM60XiBqDM2g2NcrsQaIuj375egnGFkJow4RHacgBOEsHfGbiUw=="], - "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.3.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-h04/7iMEUSMY6fDGCvdanKqlO1qYvzNxntZlCzfE8i5P0uqzVQWQquU1TIhlz0VqGQGXLrFDuTJVONpqGqjGKQ=="], + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.1.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-MCrXxrTSE7jPN1NyXJr39E+aNFBrQZtO154LoCz7n99FuKqJDekgxipoodLNWdQP7/DZ5tKMc/efybx1l159hw=="], - "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.3.5", "", { "os": "win32", "cpu": "x64" }, "sha512-5fhH6fccXxnX2KhllnGhkYMndhOiLOLEiVGYjP2nizqeGWkN10sA9taATlXwake2E2XMvYZjjz0Uj7T0y+z1yw=="], + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.1.4", "", { "os": "win32", "cpu": "x64" }, "sha512-JSVlm9MDhmTXw/sO2PE/MRj+G6XOSMZB+BcZ0a7d6KwVFZVpkHcb2okyoYFBaco6LeiL53BBklRlOrDDbOeE5w=="], "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], @@ -431,8 +464,6 @@ "@petamoriken/float16": ["@petamoriken/float16@3.9.3", "", {}, "sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g=="], - "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], - "@playwright/test": ["@playwright/test@1.57.0", "", { "dependencies": { "playwright": "1.57.0" }, "bin": { "playwright": "cli.js" } }, "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA=="], "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], @@ -557,8 +588,6 @@ "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], - "@rushstack/eslint-patch": ["@rushstack/eslint-patch@1.15.0", "", {}, "sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw=="], - "@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="], "@smithy/abort-controller": ["@smithy/abort-controller@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw=="], @@ -667,8 +696,6 @@ "@supabase/supabase-js": ["@supabase/supabase-js@2.90.1", "", { "dependencies": { "@supabase/auth-js": "2.90.1", "@supabase/functions-js": "2.90.1", "@supabase/postgrest-js": "2.90.1", "@supabase/realtime-js": "2.90.1", "@supabase/storage-js": "2.90.1" } }, "sha512-U8KaKGLUgTIFHtwEW1dgw1gK7XrdpvvYo7nzzqPx721GqPe8WZbAiLh/hmyKLGBYQ/mmQNr20vU9tWSDZpii3w=="], - "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], - "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], "@tailwindcss/typography": ["@tailwindcss/typography@0.5.19", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="], @@ -981,25 +1008,25 @@ "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.52.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.52.0", "@typescript-eslint/type-utils": "8.52.0", "@typescript-eslint/utils": "8.52.0", "@typescript-eslint/visitor-keys": "8.52.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.52.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q=="], + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.53.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/type-utils": "8.53.1", "@typescript-eslint/utils": "8.53.1", "@typescript-eslint/visitor-keys": "8.53.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.53.1", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag=="], - "@typescript-eslint/parser": ["@typescript-eslint/parser@8.52.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.52.0", "@typescript-eslint/types": "8.52.0", "@typescript-eslint/typescript-estree": "8.52.0", "@typescript-eslint/visitor-keys": "8.52.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg=="], + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.53.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", "@typescript-eslint/typescript-estree": "8.53.1", "@typescript-eslint/visitor-keys": "8.53.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg=="], - "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.52.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.52.0", "@typescript-eslint/types": "^8.52.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw=="], + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.53.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.53.1", "@typescript-eslint/types": "^8.53.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog=="], - "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.52.0", "", { "dependencies": { "@typescript-eslint/types": "8.52.0", "@typescript-eslint/visitor-keys": "8.52.0" } }, "sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA=="], + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.53.1", "", { "dependencies": { "@typescript-eslint/types": "8.53.1", "@typescript-eslint/visitor-keys": "8.53.1" } }, "sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ=="], - "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.52.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg=="], + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.53.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA=="], - "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.52.0", "", { "dependencies": { "@typescript-eslint/types": "8.52.0", "@typescript-eslint/typescript-estree": "8.52.0", "@typescript-eslint/utils": "8.52.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ=="], + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.53.1", "", { "dependencies": { "@typescript-eslint/types": "8.53.1", "@typescript-eslint/typescript-estree": "8.53.1", "@typescript-eslint/utils": "8.53.1", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w=="], - "@typescript-eslint/types": ["@typescript-eslint/types@8.52.0", "", {}, "sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg=="], + "@typescript-eslint/types": ["@typescript-eslint/types@8.53.1", "", {}, "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A=="], - "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.52.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.52.0", "@typescript-eslint/tsconfig-utils": "8.52.0", "@typescript-eslint/types": "8.52.0", "@typescript-eslint/visitor-keys": "8.52.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ=="], + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.53.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.53.1", "@typescript-eslint/tsconfig-utils": "8.53.1", "@typescript-eslint/types": "8.53.1", "@typescript-eslint/visitor-keys": "8.53.1", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg=="], - "@typescript-eslint/utils": ["@typescript-eslint/utils@8.52.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.52.0", "@typescript-eslint/types": "8.52.0", "@typescript-eslint/typescript-estree": "8.52.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ=="], + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.53.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", "@typescript-eslint/typescript-estree": "8.53.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg=="], - "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.52.0", "", { "dependencies": { "@typescript-eslint/types": "8.52.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ=="], + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.53.1", "", { "dependencies": { "@typescript-eslint/types": "8.53.1", "eslint-visitor-keys": "^4.2.1" } }, "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg=="], "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], @@ -1129,6 +1156,8 @@ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.17", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ=="], + "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], @@ -1141,6 +1170,8 @@ "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], + "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], @@ -1149,8 +1180,6 @@ "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], - "busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="], - "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], @@ -1227,6 +1256,8 @@ "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + "convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="], "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], @@ -1317,12 +1348,12 @@ "earcut": ["earcut@3.0.2", "", {}, "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ=="], - "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], - "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + "electron-to-chromium": ["electron-to-chromium@1.5.277", "", {}, "sha512-wKXFZw4erWmmOz5N/grBoJ2XrNJGDFMu2+W5ACHza5rHtvsqrK4gb6rnLC7XxKB9WlJ+RmyQatuEXmtm86xbnw=="], + "embla-carousel": ["embla-carousel@8.6.0", "", {}, "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA=="], "embla-carousel-react": ["embla-carousel-react@8.6.0", "", { "dependencies": { "embla-carousel": "8.6.0", "embla-carousel-reactive-utils": "8.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA=="], @@ -1373,7 +1404,7 @@ "eslint": ["eslint@8.57.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", "@eslint/js": "8.57.1", "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA=="], - "eslint-config-next": ["eslint-config-next@14.2.35", "", { "dependencies": { "@next/eslint-plugin-next": "14.2.35", "@rushstack/eslint-patch": "^1.3.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.28.1", "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" }, "peerDependencies": { "eslint": "^7.23.0 || ^8.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-BpLsv01UisH193WyT/1lpHqq5iJ/Orfz9h/NOOlAmTUq4GY349PextQ62K4XpnaM9supeiEn3TaOTeQO07gURg=="], + "eslint-config-next": ["eslint-config-next@16.1.4", "", { "dependencies": { "@next/eslint-plugin-next": "16.1.4", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^7.0.0", "globals": "16.4.0", "typescript-eslint": "^8.46.0" }, "peerDependencies": { "eslint": ">=9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-iCrrNolUPpn/ythx0HcyNRfUBgTkaNBXByisKUbusPGCl8DMkDXXAu7exlSTSLGTIsH9lFE/c4s/3Qiyv2qwdA=="], "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="], @@ -1387,7 +1418,7 @@ "eslint-plugin-react": ["eslint-plugin-react@7.37.5", "", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA=="], - "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@4.6.2", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" } }, "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ=="], + "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.0.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA=="], "eslint-scope": ["eslint-scope@7.2.2", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="], @@ -1463,8 +1494,6 @@ "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], - "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], - "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], "form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="], @@ -1493,6 +1522,8 @@ "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + "geojson-equality-ts": ["geojson-equality-ts@1.0.2", "", { "dependencies": { "@types/geojson": "^7946.0.14" } }, "sha512-h3Ryq+0mCSN/7yLs0eDgrZhvc9af23o/QuC4aTiuuzP/MRCtd6mf5rLsLRY44jX0RPUfM8c4GqERQmlUxPGPoQ=="], "geojson-polygon-self-intersections": ["geojson-polygon-self-intersections@1.2.2", "", { "dependencies": { "rbush": "^2.0.1" } }, "sha512-6XRNF4CsRHYmR9z5YuIk5f/aOototnDf0dgMqYGcS7y1l57ttt6MAIAxl3rXyas6lq1HEbTuLMh4PgvO+OV42w=="], @@ -1519,7 +1550,7 @@ "glassmorphic": ["glassmorphic@0.0.3", "", {}, "sha512-sbobZNaKuyup+X450P1brVofyXvl7flXdPGj8UyjvXdT3YXNYTWm3idk/dTcK0YxzhY0igk9t8e1UITP/vNAAw=="], - "glob": ["glob@10.3.10", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.3.5", "minimatch": "^9.0.1", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", "path-scurry": "^1.10.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g=="], + "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], @@ -1573,6 +1604,10 @@ "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], + "hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], + + "hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], + "highlight.js": ["highlight.js@10.7.3", "", {}, "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="], "hono": ["hono@4.11.3", "", {}, "sha512-PmQi306+M/ct/m5s66Hrg+adPnkD5jiO6IjA7WhWw0gSBSo1EcRegwuI1deZ+wd5pzCGynCcn2DprnE4/yEV4w=="], @@ -1705,8 +1740,6 @@ "iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="], - "jackspeak": ["jackspeak@2.3.6", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ=="], - "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], "jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="], @@ -1717,6 +1750,8 @@ "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + "jsmin": ["jsmin@1.0.1", "", { "bin": { "jsmin": "./bin/jsmin" } }, "sha512-OPuL5X/bFKgVdMvEIX3hnpx3jbVpFCrEM8pKPXjFkZUqg521r41ijdyTz7vACOhW6o1neVlcLyd+wkbK5fNHRg=="], "json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="], @@ -1781,7 +1816,7 @@ "lottie-web": ["lottie-web@5.13.0", "", {}, "sha512-+gfBXl6sxXMPe8tKQm7qzLnUy5DUPJPKIyRHwtpCpyUEYjHYRJC/5gjUvdkuO2c3JllrPtHXH5UJJK8LRYl5yQ=="], - "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], "lucide-react": ["lucide-react@0.507.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-XfgE6gvAHwAtnbUvWiTTHx4S3VGR+cUJHEc0vrh9Ogu672I1Tue2+Cp/8JJqpytgcBHAB1FVI297W4XGNwc2dQ=="], @@ -1913,8 +1948,6 @@ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], - "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], - "moo-server": ["moo-server@1.3.0", "", {}, "sha512-9A8/eor2DXwpv1+a4pZAAydqLFVrWoKoO1fzdzqLUhYVXAO1Kgd1FR2gFZi7YdHzF0s4W8cDNwCfKJQrvLqxDw=="], "motion-dom": ["motion-dom@12.24.11", "", { "dependencies": { "motion-utils": "^12.24.10" } }, "sha512-DlWOmsXMJrV8lzZyd+LKjG2CXULUs++bkq8GZ2Sr0R0RRhs30K2wtY+LKiTjhmJU3W61HK+rB0GLz6XmPvTA1A=="], @@ -1935,7 +1968,7 @@ "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], - "next": ["next@15.3.6", "", { "dependencies": { "@next/env": "15.3.6", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.5", "@next/swc-darwin-x64": "15.3.5", "@next/swc-linux-arm64-gnu": "15.3.5", "@next/swc-linux-arm64-musl": "15.3.5", "@next/swc-linux-x64-gnu": "15.3.5", "@next/swc-linux-x64-musl": "15.3.5", "@next/swc-win32-arm64-msvc": "15.3.5", "@next/swc-win32-x64-msvc": "15.3.5", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-oI6D1zbbsh6JzzZFDCSHnnx6Qpvd1fSkVJu/5d8uluqnxzuoqtodVZjYvNovooznUq8udSAiKp7MbwlfZ8Gm6w=="], + "next": ["next@16.1.4", "", { "dependencies": { "@next/env": "16.1.4", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.4", "@next/swc-darwin-x64": "16.1.4", "@next/swc-linux-arm64-gnu": "16.1.4", "@next/swc-linux-arm64-musl": "16.1.4", "@next/swc-linux-x64-gnu": "16.1.4", "@next/swc-linux-x64-musl": "16.1.4", "@next/swc-win32-arm64-msvc": "16.1.4", "@next/swc-win32-x64-msvc": "16.1.4", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-gKSecROqisnV7Buen5BfjmXAm7Xlpx9o2ueVQRo5DxQcjC8d330dOM1xiGWc2k3Dcnz0In3VybyRPOsudwgiqQ=="], "next-themes": ["next-themes@0.3.0", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18", "react-dom": "^16.8 || ^17 || ^18" } }, "sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w=="], @@ -1945,6 +1978,8 @@ "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], @@ -2011,8 +2046,6 @@ "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], - "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], - "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], "pbf": ["pbf@4.0.1", "", { "dependencies": { "resolve-protobuf-schema": "^2.1.0" }, "bin": { "pbf": "bin/pbf" } }, "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA=="], @@ -2117,9 +2150,9 @@ "rbush": ["rbush@3.0.1", "", { "dependencies": { "quickselect": "^2.0.0" } }, "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w=="], - "react": ["react@19.1.2", "", {}, "sha512-MdWVitvLbQULD+4DP8GYjZUrepGW7d+GQkNVqJEzNxE+e9WIa4egVFE/RDfVb1u9u/Jw7dNMmPB4IqxzbFYJ0w=="], + "react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="], - "react-dom": ["react-dom@19.1.2", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.2" } }, "sha512-dEoydsCp50i7kS1xHOmPXq4zQYoGWedUsvqv9H6zdif2r7yLHygyfP9qou71TulRN0d6ng9EbRVsQhSqfUc19g=="], + "react-dom": ["react-dom@19.2.3", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.3" } }, "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg=="], "react-hook-form": ["react-hook-form@7.71.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-oFDt/iIFMV9ZfV52waONXzg4xuSlbwKUPvXVH2jumL1me5qFhBMc4knZxuXiZ2+j6h546sYe3ZKJcg/900/iHw=="], @@ -2207,7 +2240,7 @@ "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], "secure-json-parse": ["secure-json-parse@2.7.0", "", {}, "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="], @@ -2279,12 +2312,8 @@ "stream-shift": ["stream-shift@1.0.3", "", {}, "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ=="], - "streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="], - "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "string.prototype.includes": ["string.prototype.includes@2.0.1", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3" } }, "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg=="], "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="], @@ -2303,8 +2332,6 @@ "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], @@ -2407,6 +2434,8 @@ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "typescript-eslint": ["typescript-eslint@8.53.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.53.1", "@typescript-eslint/parser": "8.53.1", "@typescript-eslint/typescript-estree": "8.53.1", "@typescript-eslint/utils": "8.53.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-gB+EVQfP5RDElh9ittfXlhZJdjSU4jUSTyE2+ia8CYyNvet4ElfaLlAIqDvQV9JPknKx0jQH1racTYe/4LaLSg=="], + "uglify-js": ["uglify-js@1.3.5", "", { "bin": { "uglifyjs": "./bin/uglifyjs" } }, "sha512-YPX1DjKtom8l9XslmPFQnqWzTBkvI4N0pbkzLuPZZ4QTyig0uQqvZz9NgUdfEV+qccJzi7fVcGWdESvRIjWptQ=="], "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="], @@ -2439,6 +2468,8 @@ "unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="], + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], @@ -2505,8 +2536,6 @@ "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], - "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], - "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], "wrench": ["wrench@1.3.9", "", {}, "sha512-srTJQmLTP5YtW+F5zDuqjMEZqLLr/eJOZfDI5ibfPfRMeDh3oBUefAscuH0q5wBKE339ptH/S/0D18ZkfOfmKQ=="], @@ -2521,6 +2550,8 @@ "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="], "yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], @@ -2533,6 +2564,8 @@ "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], + "zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="], + "zstddec": ["zstddec@0.2.0", "", {}, "sha512-oyPnDa1X5c13+Y7mA/FDMNJrn4S8UNBe0KCqtDmor40Re7ALrPN6npFwyYVRRh+PqozZQdeg23QtbcamZnG5rA=="], "zustand": ["zustand@5.0.10", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-U1AiltS1O9hSy3rul+Ub82ut2fqIAefiSuwECWt6jlMVUGejvf+5omLcRBSzqbRagSM3hQZbtzdeRc6QVScXTg=="], @@ -2573,6 +2606,12 @@ "@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + "@babel/core/json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@composio/core/uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="], "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], @@ -2581,16 +2620,12 @@ "@inkjs/ui/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], - "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], - - "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - - "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], - "@mapbox/mapbox-gl-draw/nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="], "@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "@next/eslint-plugin-next/fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="], + "@radix-ui/react-alert-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], "@radix-ui/react-avatar/@radix-ui/react-context": ["@radix-ui/react-context@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw=="], @@ -2659,6 +2694,8 @@ "concaveman/tinyqueue": ["tinyqueue@2.0.3", "", {}, "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA=="], + "eslint-config-next/globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="], + "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], @@ -2681,14 +2718,10 @@ "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], - "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], "geojson-polygon-self-intersections/rbush": ["rbush@2.0.2", "", { "dependencies": { "quickselect": "^1.0.1" } }, "sha512-XBOuALcTm+O/H8G90b6pzu6nX6v2zCKiFG4BJho8a+bY6AER6t8uQUZdi5bomQc0AprCWhEGa7ncAbbRap0bRA=="], - "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], - "hast-util-from-html/parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], "http-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], @@ -2739,8 +2772,6 @@ "react-reconciler/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], - "rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], @@ -2751,10 +2782,6 @@ "string-width/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - - "string-width-cjs/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], - "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], "sweepline-intersections/tinyqueue": ["tinyqueue@2.0.3", "", {}, "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA=="], @@ -2771,8 +2798,6 @@ "wrap-ansi/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "@ai-sdk/react/@ai-sdk/provider-utils/@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], @@ -2827,12 +2852,10 @@ "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], - "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - - "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - "@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "@next/eslint-plugin-next/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "@types/request/form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], @@ -2855,18 +2878,12 @@ "geojson-polygon-self-intersections/rbush/quickselect": ["quickselect@1.1.1", "", {}, "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ=="], - "glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], - "open-codex/openai/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], "string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "teeny-request/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], - "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - - "wrap-ansi-cjs/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], - "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], diff --git a/components/chat-messages.tsx b/components/chat-messages.tsx index 6bfa3642..aba94a49 100644 --- a/components/chat-messages.tsx +++ b/components/chat-messages.tsx @@ -1,11 +1,21 @@ 'use client' -import { StreamableValue, useUIState } from 'ai/rsc' -import type { AI, UIState } from '@/app/actions' +import { Message } from 'ai' import { CollapsibleMessage } from './collapsible-message' +import { UserMessage } from './user-message' +import { BotMessage } from './message' +import { Section } from './section' +import SearchRelated from './search-related' +import { FollowupPanel } from './followup-panel' +import { SearchSection } from './search-section' +import RetrieveSection from './retrieve-section' +import { VideoSearchSection } from './video-search-section' +import { MapQueryHandler } from './map/map-query-handler' +import { CopilotDisplay } from './copilot-display' +import { ResolutionSearchSection } from './resolution-search-section' interface ChatMessagesProps { - messages: UIState + messages: Message[] } export function ChatMessages({ messages }: ChatMessagesProps) { @@ -13,58 +23,101 @@ export function ChatMessages({ messages }: ChatMessagesProps) { return null } - // Group messages based on ID, and if there are multiple messages with the same ID, combine them into one message - const groupedMessages = messages.reduce( - (acc: { [key: string]: any }, message) => { - if (!acc[message.id]) { - acc[message.id] = { - id: message.id, - components: [], - isCollapsed: message.isCollapsed + return ( + <> + {messages.map((message, index) => { + const { role, content, id, toolInvocations, data } = message + + if (role === 'user') { + return ( + + ) + }} + isLastMessage={index === messages.length - 1} + /> + ) } - } - acc[message.id].components.push(message.component) - return acc - }, - {} - ) - // Convert grouped messages into an array with explicit type - const groupedMessagesArray = Object.values(groupedMessages).map(group => ({ - ...group, - components: group.components as React.ReactNode[] - })) as { - id: string - components: React.ReactNode[] - isCollapsed?: StreamableValue - }[] + if (role === 'assistant') { + const extraData = Array.isArray(data) ? data : [] - return ( - <> - {groupedMessagesArray.map( - ( - groupedMessage: { - id: string - components: React.ReactNode[] - isCollapsed?: StreamableValue - }, - index - ) => ( - ( -
{component}
- )), - isCollapsed: groupedMessage.isCollapsed - }} - isLastMessage={ - groupedMessage.id === messages[messages.length - 1].id - } - /> - ) - )} + return ( + + {content && ( +
+ +
+ )} + + {toolInvocations?.map((toolInvocation) => { + const { toolName, toolCallId, state } = toolInvocation + + if (state === 'result') { + const { result } = toolInvocation + + switch (toolName) { + case 'search': + return + case 'retrieve': + return + case 'videoSearch': + return + case 'geospatialQueryTool': + if (result.type === 'MAP_QUERY_TRIGGER') { + return + } + return null + default: + return null + } + } + return null + })} + + {extraData.map((d: any, i) => { + if (d.type === 'related') { + return ( +
+ +
+ ) + } + if (d.type === 'inquiry') { + return + } + if (d.type === 'resolution_search_result') { + return + } + return null + })} + + {index === messages.length - 1 && role === 'assistant' && ( +
+ +
+ )} + + ) + }} + isLastMessage={index === messages.length - 1} + /> + ) + } + return null + })} ) } diff --git a/components/chat-panel.tsx b/components/chat-panel.tsx index c45844d3..26245915 100644 --- a/components/chat-panel.tsx +++ b/components/chat-panel.tsx @@ -1,45 +1,36 @@ 'use client' import { useEffect, useState, useRef, ChangeEvent, forwardRef, useImperativeHandle, useCallback } from 'react' -import type { AI, UIState } from '@/app/actions' -import { useUIState, useActions, readStreamableValue } from 'ai/rsc' -// Removed import of useGeospatialToolMcp as it's no longer used/available import { cn } from '@/lib/utils' -import { UserMessage } from './user-message' import { Button } from './ui/button' -import { ArrowRight, Plus, Paperclip, X } from 'lucide-react' +import { ArrowRight, Paperclip, X } from 'lucide-react' import Textarea from 'react-textarea-autosize' -import { nanoid } from 'nanoid' import { useSettingsStore } from '@/lib/store/settings' import { PartialRelated } from '@/lib/schema/related' import { getSuggestions } from '@/lib/actions/suggest' import { useMapData } from './map/map-data-context' -import SuggestionsDropdown from './suggestions-dropdown' interface ChatPanelProps { - messages: UIState + messages: any[] input: string setInput: (value: string) => void onSuggestionsChange?: (suggestions: PartialRelated | null) => void + handleSubmit: (e: React.FormEvent) => void } export interface ChatPanelRef { handleAttachmentClick: () => void submitForm: () => void + setSelectedFile: (file: File | null) => void } -export const ChatPanel = forwardRef(({ messages, input, setInput, onSuggestionsChange }, ref) => { - const [, setMessages] = useUIState() - const { submit, clearChat } = useActions() - // Removed mcp instance as it's no longer passed to submit +export const ChatPanel = forwardRef(({ messages, input, setInput, onSuggestionsChange, handleSubmit }, ref) => { const { mapProvider } = useSettingsStore() const [isMobile, setIsMobile] = useState(false) const [selectedFile, setSelectedFile] = useState(null) - const [suggestions, setSuggestionsState] = useState(null) const setSuggestions = useCallback((s: PartialRelated | null) => { - setSuggestionsState(s) onSuggestionsChange?.(s) - }, [onSuggestionsChange, setSuggestionsState]) + }, [onSuggestionsChange]) const { mapData } = useMapData() const debounceTimeoutRef = useRef(null) const inputRef = useRef(null) @@ -52,10 +43,12 @@ export const ChatPanel = forwardRef(({ messages, i }, submitForm() { formRef.current?.requestSubmit() + }, + setSelectedFile(file: File | null) { + setSelectedFile(file) } })); - // Detect mobile layout useEffect(() => { const checkMobile = () => { setIsMobile(window.innerWidth <= 1024) @@ -82,60 +75,12 @@ export const ChatPanel = forwardRef(({ messages, i const clearAttachment = () => { setSelectedFile(null) - if (fileInputRef.current) { - fileInputRef.current.value = '' - } - } - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - if (!input.trim() && !selectedFile) { - return - } - - const content: ({ type: 'text'; text: string } | { type: 'image'; image: string })[] = [] - if (input) { - content.push({ type: 'text', text: input }) - } - if (selectedFile && selectedFile.type.startsWith('image/')) { - content.push({ - type: 'image', - image: URL.createObjectURL(selectedFile) - }) - } - - setMessages(currentMessages => [ - ...currentMessages, - { - id: nanoid(), - component: - } - ]) - - const formData = new FormData(e.currentTarget) - if (selectedFile) { - formData.append('file', selectedFile) - } - - setInput('') - clearAttachment() - - const responseMessage = await submit(formData) - setMessages(currentMessages => [...currentMessages, responseMessage as any]) - } - - const handleClear = async () => { - setMessages([]) - clearAttachment() - await clearChat() + if (fileInputRef.current) fileInputRef.current.value = '' } const debouncedGetSuggestions = useCallback( (value: string) => { - if (debounceTimeoutRef.current) { - clearTimeout(debounceTimeoutRef.current) - } - + if (debounceTimeoutRef.current) clearTimeout(debounceTimeoutRef.current) const wordCount = value.trim().split(/\s+/).filter(Boolean).length if (wordCount < 2) { setSuggestions(null) @@ -143,46 +88,37 @@ export const ChatPanel = forwardRef(({ messages, i } debounceTimeoutRef.current = setTimeout(async () => { - const suggestionsStream = await getSuggestions(value, mapData) - for await (const partialSuggestions of readStreamableValue( - suggestionsStream - )) { - if (partialSuggestions) { - setSuggestions(partialSuggestions as PartialRelated) + try { + const response = await getSuggestions(value, mapData) + const reader = response.body?.getReader() + if (!reader) return + let result = '' + while (true) { + const { done, value: chunk } = await reader.read() + if (done) break + result += new TextDecoder().decode(chunk) + try { + const lastFullObject = result.lastIndexOf('}') + if (lastFullObject !== -1) { + const json = JSON.parse(result.substring(0, lastFullObject + 1)) + setSuggestions(json) + } + } catch (e) { } } - } - }, 500) // 500ms debounce delay + } catch (error) { } + }, 500) }, - [mapData] + [mapData, setSuggestions] ) useEffect(() => { inputRef.current?.focus() }, []) - // New chat button (appears when there are messages) - if (messages.length > 0 && !isMobile) { - return ( -
- -
- ) + const onLocalSubmit = (e: React.FormEvent) => { + e.preventDefault(); + handleSubmit(e); + clearAttachment(); } return ( @@ -196,7 +132,7 @@ export const ChatPanel = forwardRef(({ messages, i >
(({ messages, i
@@ -264,15 +200,6 @@ export const ChatPanel = forwardRef(({ messages, i formRef.current?.requestSubmit() } }} - onHeightChange={height => { - if (!inputRef.current) return - const initialHeight = 70 - const initialBorder = 32 - const multiple = (height - initialHeight) / 20 - const newBorder = initialBorder - 4 * multiple - inputRef.current.style.borderRadius = - Math.max(8, newBorder) + 'px' - }} /> - {/* Suggestions are now handled by the parent component (chat.tsx) as an overlay */}
{selectedFile && ( diff --git a/components/chat.tsx b/components/chat.tsx index 04e27ac6..eb22e24d 100644 --- a/components/chat.tsx +++ b/components/chat.tsx @@ -11,28 +11,71 @@ import { cn } from '@/lib/utils' import { useCalendarToggle } from './calendar-toggle-context' import { CalendarNotepad } from './calendar-notepad' import { MapProvider } from './map/map-provider' -import { useUIState, useAIState } from 'ai/rsc' +import { useChat } from 'ai/react' +import { submit } from '@/app/actions' import MobileIconsBar from './mobile-icons-bar' -import { useProfileToggle, ProfileToggleEnum } from "@/components/profile-toggle-context"; -import SettingsView from "@/components/settings/settings-view"; -import { MapDataProvider, useMapData } from './map/map-data-context'; // Add this and useMapData -import { updateDrawingContext } from '@/lib/actions/chat'; // Import the server action -import dynamic from 'next/dynamic' +import { useProfileToggle } from "@/components/profile-toggle-context" +import SettingsView from "@/components/settings/settings-view" +import { MapDataProvider, useMapData } from './map/map-data-context' +import { updateDrawingContext } from '@/lib/actions/chat' import { HeaderSearchButton } from './header-search-button' +import { nanoid } from 'nanoid' type ChatProps = { - id?: string // This is the chatId + id?: string + initialMessages?: any[] } -export function Chat({ id }: ChatProps) { +export function Chat({ id, initialMessages = [] }: ChatProps) { const router = useRouter() const path = usePathname() - const [messages] = useUIState() - const [aiState] = useAIState() + const [chatId, setChatId] = useState(id || nanoid()) + + const { messages, append, reload, stop, isLoading, input, setInput, handleSubmit } = useChat({ + id: chatId, + initialMessages, + fetch: async (input, init) => { + const body = JSON.parse(init?.body as string); + const response = await submit(body.messages, body); + return response; + }, + body: { + chatId, + mapProvider: 'mapbox' // Default map provider + }, + onFinish: (message) => { + if (!path.includes('search')) { + window.history.replaceState({}, '', `/search/${chatId}`) + } + router.refresh() + } + }) + + useEffect(() => { + const handleResolutionSearch = (event: any) => { + const { file } = event.detail; + chatPanelRef.current?.setSelectedFile(file); + + // Use a FormData-like object for the body + append({ + role: 'user', + content: 'Analyze this map view.' + }, { + body: { + action: 'resolution_search', + file: file, + chatId + } + }); + }; + + window.addEventListener('resolution-search', handleResolutionSearch); + return () => window.removeEventListener('resolution-search', handleResolutionSearch); + }, [append, chatId]); + const [isMobile, setIsMobile] = useState(false) const { activeView } = useProfileToggle(); const { isCalendarOpen } = useCalendarToggle() - const [input, setInput] = useState('') const [showEmptyScreen, setShowEmptyScreen] = useState(false) const [isSubmitting, setIsSubmitting] = useState(false) const [suggestions, setSuggestions] = useState(null) @@ -51,59 +94,41 @@ export function Chat({ id }: ChatProps) { }, [messages]) useEffect(() => { - // Check if device is mobile const checkMobile = () => { setIsMobile(window.innerWidth < 768) } - - // Initial check checkMobile() - - // Add event listener for window resize window.addEventListener('resize', checkMobile) - - // Cleanup return () => window.removeEventListener('resize', checkMobile) }, []) useEffect(() => { - if (!path.includes('search') && messages.length === 1) { - window.history.replaceState({}, '', `/search/${id}`) - } - }, [id, path, messages]) - - useEffect(() => { - if (aiState.messages[aiState.messages.length - 1]?.type === 'response') { - // Refresh the page to chat history updates - router.refresh() + if (!path.includes('search') && messages.length > 0) { + window.history.replaceState({}, '', `/search/${chatId}`) } - }, [aiState, router]) + }, [chatId, path, messages]) - // Get mapData to access drawnFeatures const { mapData } = useMapData(); useEffect(() => { if (isSubmitting) { - chatPanelRef.current?.submitForm() + handleSubmit(); setIsSubmitting(false) } - }, [isSubmitting]) + }, [isSubmitting, handleSubmit]) - // useEffect to call the server action when drawnFeatures changes useEffect(() => { - if (id && mapData.drawnFeatures && mapData.cameraState) { - console.log('Chat.tsx: drawnFeatures changed, calling updateDrawingContext', mapData.drawnFeatures); - updateDrawingContext(id, { + if (chatId && mapData.drawnFeatures && mapData.cameraState) { + updateDrawingContext(chatId, { drawnFeatures: mapData.drawnFeatures, cameraState: mapData.cameraState, }); } - }, [id, mapData.drawnFeatures, mapData.cameraState]); + }, [chatId, mapData.drawnFeatures, mapData.cameraState]); - // Mobile layout if (isMobile) { return ( - {/* Add Provider */} +
@@ -119,11 +144,12 @@ export function Chat({ id }: ChatProps) { input={input} setInput={setInput} onSuggestionsChange={setSuggestions} + handleSubmit={handleSubmit} />
{isCalendarOpen ? ( - + ) : showEmptyScreen ? (
@@ -141,7 +167,6 @@ export function Chat({ id }: ChatProps) { onSelect={query => { setInput(query) setSuggestions(null) - // Use a small timeout to ensure state update before submission setTimeout(() => { setIsSubmitting(true) }, 0) @@ -161,22 +186,22 @@ export function Chat({ id }: ChatProps) { ); } - // Desktop layout return ( - {/* Add Provider */} +
- {/* This is the new div for scrolling */}
{isCalendarOpen ? ( - + ) : ( <>
{showEmptyScreen ? ( @@ -196,7 +221,6 @@ export function Chat({ id }: ChatProps) { onSelect={query => { setInput(query) setSuggestions(null) - // Use a small timeout to ensure state update before submission setTimeout(() => { setIsSubmitting(true) }, 0) @@ -216,7 +240,7 @@ export function Chat({ id }: ChatProps) {
{activeView ? : }
diff --git a/components/collapsible-message.tsx b/components/collapsible-message.tsx index b3349bc7..957f78fa 100644 --- a/components/collapsible-message.tsx +++ b/components/collapsible-message.tsx @@ -8,7 +8,6 @@ import { } from '@radix-ui/react-collapsible' import { Button } from './ui/button' import { ChevronDown } from 'lucide-react' -import { StreamableValue, useStreamableValue } from 'ai/rsc' import { motion, AnimatePresence } from 'framer-motion' import { cn } from '@/lib/utils' import { Separator } from './ui/separator' @@ -16,7 +15,7 @@ import { Separator } from './ui/separator' interface CollapsibleMessageProps { message: { id: string - isCollapsed?: StreamableValue + isCollapsed?: boolean component: React.ReactNode } isLastMessage?: boolean @@ -26,8 +25,7 @@ export const CollapsibleMessage: React.FC = ({ message, isLastMessage = false }) => { - const [data] = useStreamableValue(message.isCollapsed) - const isCollapsed = data ?? false + const isCollapsed = message.isCollapsed ?? false const [open, setOpen] = useState(isLastMessage) useEffect(() => { diff --git a/components/copilot.tsx b/components/copilot.tsx index b62b37ed..2a479262 100644 --- a/components/copilot.tsx +++ b/components/copilot.tsx @@ -6,33 +6,20 @@ import { Input } from './ui/input' import { Checkbox } from './ui/checkbox' import { Button } from './ui/button' import { Card } from './ui/card' -import { ArrowRight, Check, FastForward, Sparkles } from 'lucide-react' -import { useActions, useStreamableValue, useUIState } from 'ai/rsc' -// Removed import of useGeospatialToolMcp as it's no longer used/available -import type { AI } from '@/app/actions' -import { - - - } from './ui/icons' -import { cn } from '@/lib/utils' +import { ArrowRight, Check, FastForward } from 'lucide-react' export type CopilotProps = { - inquiry: { value: PartialInquiry }; + inquiry: PartialInquiry; } export const Copilot: React.FC = ({ inquiry }: CopilotProps) => { - const { value } = inquiry; const [completed, setCompleted] = useState(false) const [query, setQuery] = useState('') const [skipped, setSkipped] = useState(false) - const [data, error, pending] = useStreamableValue() const [checkedOptions, setCheckedOptions] = useState<{ [key: string]: boolean }>({}) const [isButtonDisabled, setIsButtonDisabled] = useState(true) - const [, setMessages] = useUIState() - const { submit } = useActions() - // Removed mcp instance as it's no longer passed to submit const handleInputChange = (event: React.ChangeEvent) => { setQuery(event.target.value) @@ -64,7 +51,6 @@ export const Copilot: React.FC = ({ inquiry }: CopilotProps) => { useEffect(() => { checkIfButtonShouldBeEnabled() - // eslint-disable-next-line react-hooks/exhaustive-deps }, [query]) const onFormSubmit = async ( @@ -74,38 +60,12 @@ export const Copilot: React.FC = ({ inquiry }: CopilotProps) => { e.preventDefault() setCompleted(true) setSkipped(skip || false) - - const formData = skip - ? undefined - : new FormData(e.target as HTMLFormElement) - - if (formData) { - formData.set('input', updatedQuery()) - formData.delete('additional_query') - } - - // Removed mcp argument from submit call - const response = await submit(formData, skip) - setMessages(currentMessages => [...currentMessages, response]) } const handleSkip = (e: React.MouseEvent) => { onFormSubmit(e as unknown as React.FormEvent, true) } - if (error) { - return ( - -
- -
- {`error: ${error}`} -
-
-
- ) - } - if (skipped) { return null } @@ -126,14 +86,12 @@ export const Copilot: React.FC = ({ inquiry }: CopilotProps) => {

- {data?.question || value.question} - - + {inquiry.question}

- {value.options?.map((option, index) => ( + {inquiry.options?.map((option, index) => (
= ({ inquiry }: CopilotProps) => {
))}
- {data?.allowsInput && ( + {inquiry.allowsInput && (
@@ -175,12 +133,11 @@ export const Copilot: React.FC = ({ inquiry }: CopilotProps) => { type="button" variant="outline" onClick={handleSkip} - disabled={pending} > Skip - diff --git a/components/followup-panel.tsx b/components/followup-panel.tsx index 08642530..1e0e5836 100644 --- a/components/followup-panel.tsx +++ b/components/followup-panel.tsx @@ -3,36 +3,13 @@ import { useState } from 'react' import { Button } from './ui/button' import { Input } from './ui/input' -import { useActions, useUIState } from 'ai/rsc' -// Removed import of useGeospatialToolMcp as it's no longer used/available -import type { AI } from '@/app/actions' -import { UserMessage } from './user-message' import { ArrowRight } from 'lucide-react' export function FollowupPanel() { const [input, setInput] = useState('') - const { submit } = useActions() - // Removed mcp instance as it's no longer passed to submit - const [, setMessages] = useUIState() const handleSubmit = async (event: React.FormEvent) => { event.preventDefault() - const formData = new FormData(event.currentTarget as HTMLFormElement) - - const userMessage = { - id: Date.now(), - isGenerating: false, - component: - } - - // Removed mcp argument from submit call - const responseMessage = await submit(formData) - setMessages(currentMessages => [ - ...currentMessages, - userMessage, - responseMessage - ]) - setInput('') } diff --git a/components/header-search-button.tsx b/components/header-search-button.tsx index cdbc5c11..57cec50c 100644 --- a/components/header-search-button.tsx +++ b/components/header-search-button.tsx @@ -5,32 +5,19 @@ import { createPortal } from 'react-dom' import { Button } from '@/components/ui/button' import { Search } from 'lucide-react' import { useMap } from './map/map-context' -import { useActions, useUIState } from 'ai/rsc' -import { AI } from '@/app/actions' -import { nanoid } from 'nanoid' -import { UserMessage } from './user-message' import { toast } from 'react-toastify' import { useSettingsStore } from '@/lib/store/settings' import { useMapData } from './map/map-data-context' -// Define an interface for the actions to help TypeScript during build -interface HeaderActions { - submit: (formData: FormData) => Promise; -} - export function HeaderSearchButton() { const { map } = useMap() const { mapProvider } = useSettingsStore() const { mapData } = useMapData() - // Cast the actions to our defined interface to avoid build errors - const actions = useActions() as unknown as HeaderActions - const [, setMessages] = useUIState() const [isAnalyzing, setIsAnalyzing] = useState(false) const [desktopPortal, setDesktopPortal] = useState(null) const [mobilePortal, setMobilePortal] = useState(null) useEffect(() => { - // Portals can only be used on the client-side after the DOM has mounted setDesktopPortal(document.getElementById('header-search-portal')) setMobilePortal(document.getElementById('mobile-header-search-portal')) }, []) @@ -40,24 +27,11 @@ export function HeaderSearchButton() { toast.error('Map is not available yet. Please wait for it to load.') return } - if (!actions) { - toast.error('Search actions are not available.') - return - } setIsAnalyzing(true) try { - setMessages(currentMessages => [ - ...currentMessages, - { - id: nanoid(), - component: - } - ]) - let blob: Blob | null = null; - if (mapProvider === 'mapbox') { const canvas = map!.getCanvas() blob = await new Promise(resolve => { @@ -72,26 +46,17 @@ export function HeaderSearchButton() { } const { center, range } = mapData.cameraState const zoom = Math.round(Math.log2(40000000 / (range || 1))); - let staticMapUrl = `https://maps.googleapis.com/maps/api/staticmap?center=${center.lat},${center.lng}&zoom=${zoom}&size=640x480&maptype=satellite&key=${apiKey}`; - const response = await fetch(staticMapUrl); - if (!response.ok) { - throw new Error('Failed to fetch static map image.'); - } + if (!response.ok) throw new Error('Failed to fetch static map image.'); blob = await response.blob(); } - if (!blob) { - throw new Error('Failed to capture map image.') - } - - const formData = new FormData() - formData.append('file', blob, 'map_capture.png') - formData.append('action', 'resolution_search') + if (!blob) throw new Error('Failed to capture map image.') + const file = new File([blob], 'map_capture.png', { type: 'image/png' }); + const event = new CustomEvent('resolution-search', { detail: { file } }); + window.dispatchEvent(event); - const responseMessage = await actions.submit(formData) - setMessages(currentMessages => [...currentMessages, responseMessage as any]) } catch (error) { console.error('Failed to perform resolution search:', error) toast.error('An error occurred while analyzing the map.') @@ -105,7 +70,7 @@ export function HeaderSearchButton() { variant="ghost" size="icon" onClick={handleResolutionSearch} - disabled={isAnalyzing || !map || !actions} + disabled={isAnalyzing} title="Analyze current map view" > {isAnalyzing ? ( @@ -117,7 +82,7 @@ export function HeaderSearchButton() { ) const mobileButton = ( - @@ -129,4 +94,4 @@ export function HeaderSearchButton() { {mobilePortal && createPortal(mobileButton, mobilePortal)} ) -} \ No newline at end of file +} diff --git a/components/message.tsx b/components/message.tsx index 264aa1f6..06b7d949 100644 --- a/components/message.tsx +++ b/components/message.tsx @@ -1,6 +1,5 @@ 'use client' -import { StreamableValue, useStreamableValue } from 'ai/rsc' import { MemoizedReactMarkdown } from './ui/markdown' import rehypeExternalLinks from 'rehype-external-links' import remarkGfm from 'remark-gfm' @@ -8,14 +7,9 @@ import remarkMath from 'remark-math' import rehypeKatex from 'rehype-katex' import 'katex/dist/katex.min.css' -export function BotMessage({ content }: { content: StreamableValue }) { - const [data, error, pending] = useStreamableValue(content) - - // Currently, sometimes error occurs after finishing the stream. - if (error) return
Error
- +export function BotMessage({ content }: { content: string }) { //modify the content to render LaTeX equations - const processedData = preprocessLaTeX(data || '') + const processedData = preprocessLaTeX(content || '') return (
@@ -31,7 +25,6 @@ export function BotMessage({ content }: { content: StreamableValue }) { } // Preprocess LaTeX equations to be rendered by KaTeX -// ref: https://github.com/remarkjs/react-markdown/issues/785 const preprocessLaTeX = (content: string) => { const blockProcessedContent = content.replace( /\\\[([\s\S]*?)\\\]/g, diff --git a/components/mobile-icons-bar.tsx b/components/mobile-icons-bar.tsx index bde08487..43ab62f6 100644 --- a/components/mobile-icons-bar.tsx +++ b/components/mobile-icons-bar.tsx @@ -1,13 +1,9 @@ 'use client' import React from 'react' -import { useUIState, useActions } from 'ai/rsc' -import { AI } from '@/app/actions' import { Button } from '@/components/ui/button' import { Search, - CircleUserRound, - Map, CalendarDays, TentTree, Paperclip, @@ -26,13 +22,10 @@ interface MobileIconsBarProps { } export const MobileIconsBar: React.FC = ({ onAttachmentClick, onSubmitClick }) => { - const [, setMessages] = useUIState() - const { clearChat } = useActions() const { toggleCalendar } = useCalendarToggle() const handleNewChat = async () => { - setMessages([]) - await clearChat() + window.location.href = '/' } return ( diff --git a/components/resolution-search-section.tsx b/components/resolution-search-section.tsx new file mode 100644 index 00000000..50250c1e --- /dev/null +++ b/components/resolution-search-section.tsx @@ -0,0 +1,58 @@ +'use client' + +import { useEffect } from 'react' +import { useMapData } from './map/map-data-context' +import { Section } from './section' +import { BotMessage } from './message' + +interface ResolutionSearchResult { + summary: string + geoJson: { + type: 'FeatureCollection' + features: Array<{ + type: 'Feature' + geometry: { + type: string + coordinates: any + } + properties: { + name: string + description?: string + } + }> + } +} + +interface ResolutionSearchSectionProps { + result: ResolutionSearchResult +} + +export function ResolutionSearchSection({ result }: ResolutionSearchSectionProps) { + const { setMapData } = useMapData() + + useEffect(() => { + if (result.geoJson && result.geoJson.features) { + const markers = result.geoJson.features + .filter(f => f.geometry.type === 'Point') + .map(f => ({ + latitude: f.geometry.coordinates[1], + longitude: f.geometry.coordinates[0], + title: f.properties.name + })) + + if (markers.length > 0) { + setMapData(prev => ({ + ...prev, + markers: [...(prev.markers || []), ...markers], + targetPosition: { lat: markers[0].latitude, lng: markers[0].longitude } + })) + } + } + }, [result, setMapData]) + + return ( +
+ +
+ ) +} diff --git a/components/retrieve-section.tsx b/components/retrieve-section.tsx index d9c4c404..59d6d02c 100644 --- a/components/retrieve-section.tsx +++ b/components/retrieve-section.tsx @@ -8,6 +8,14 @@ interface RetrieveSectionProps { } const RetrieveSection: React.FC = ({ data }) => { + if ((data as any).error) { + return ( +
+
{(data as any).error}
+
+ ) + } + return (
diff --git a/components/search-related.tsx b/components/search-related.tsx index d4a58b08..efeef2aa 100644 --- a/components/search-related.tsx +++ b/components/search-related.tsx @@ -1,63 +1,21 @@ -'use client' - -import React, { useEffect, useState } from 'react' -import { Button } from './ui/button' -import { ArrowRight } from 'lucide-react' -import { - useActions, - useStreamableValue, - useUIState, - readStreamableValue, - StreamableValue -} from 'ai/rsc' -import { AI } from '@/app/actions' -// Removed import of useGeospatialToolMcp as it's no longer used/available -import { UserMessage } from './user-message' import { PartialRelated } from '@/lib/schema/related' +import { ArrowRight } from 'lucide-react' +import { Button } from './ui/button' export interface SearchRelatedProps { - relatedQueries: StreamableValue + relatedQueries: PartialRelated } export const SearchRelated: React.FC = ({ relatedQueries }) => { - const { submit } = useActions() - // Removed mcp instance as it's no longer passed to submit - const [, setMessages] = useUIState() - const [data, error, pending] = - useStreamableValue(relatedQueries) - const handleSubmit = async (event: React.FormEvent) => { event.preventDefault() - const formData = new FormData(event.currentTarget as HTMLFormElement) - - // // Get the submitter of the form - const submitter = (event.nativeEvent as SubmitEvent) - .submitter as HTMLInputElement - let query = '' - if (submitter) { - formData.append(submitter.name, submitter.value) - query = submitter.value - } - - const userMessage = { - id: Date.now(), - component: - } - - // Removed mcp argument from submit call - const responseMessage = await submit(formData) - setMessages(currentMessages => [ - ...currentMessages, - userMessage, - responseMessage - ]) } return ( - {data?.items + {relatedQueries?.items ?.filter(item => item?.query !== '') .map((item, index) => (
diff --git a/components/search-section.tsx b/components/search-section.tsx index 74695f69..36c46f60 100644 --- a/components/search-section.tsx +++ b/components/search-section.tsx @@ -6,18 +6,35 @@ import { SearchResultsImageSection } from './search-results-image' import { Section } from './section' import { ToolBadge } from './tool-badge' import type { SearchResults as TypeSearchResults } from '@/lib/types' -import { StreamableValue, useStreamableValue } from 'ai/rsc' export type SearchSectionProps = { - result?: StreamableValue + result?: string } export function SearchSection({ result }: SearchSectionProps) { - const [data, error, pending] = useStreamableValue(result) - const searchResults: TypeSearchResults = data ? JSON.parse(data) : undefined + let searchResults: TypeSearchResults | undefined + let error: string | undefined + + if (result) { + try { + const parsed = JSON.parse(result) + if (parsed.error) { + error = parsed.error + } else { + searchResults = parsed + } + } catch (e) { + error = 'Failed to parse search results' + } + } + return (
- {!pending && data ? ( + {error ? ( +
+
{error}
+
+ ) : searchResults ? ( <>
{`${searchResults.query}`} diff --git a/components/video-search-section.tsx b/components/video-search-section.tsx index 49c399e1..5f4a1e6b 100644 --- a/components/video-search-section.tsx +++ b/components/video-search-section.tsx @@ -3,20 +3,37 @@ import { SearchSkeleton } from './search-skeleton' import { Section } from './section' import type { SerperSearchResults } from '@/lib/types' -import { StreamableValue, useStreamableValue } from 'ai/rsc' import { VideoSearchResults } from './video-search-results' import { ToolBadge } from './tool-badge' export type VideoSearchSectionProps = { - result?: StreamableValue + result?: string } export function VideoSearchSection({ result }: VideoSearchSectionProps) { - const [data, error, pending] = useStreamableValue(result) - const searchResults: SerperSearchResults = data ? JSON.parse(data) : undefined + let searchResults: SerperSearchResults | undefined + let error: string | undefined + + if (result) { + try { + const parsed = JSON.parse(result) + if (parsed.error) { + error = parsed.error + } else { + searchResults = parsed + } + } catch (e) { + error = 'Failed to parse video search results' + } + } + return (
- {!pending && data ? ( + {error ? ( +
+
{error}
+
+ ) : searchResults ? ( <>
{`${searchResults.searchParameters.q}`} diff --git a/lib/actions/suggest.ts b/lib/actions/suggest.ts index 8555461c..27e98497 100644 --- a/lib/actions/suggest.ts +++ b/lib/actions/suggest.ts @@ -1,8 +1,5 @@ -'use server' - -import { createStreamableUI, createStreamableValue } from 'ai/rsc' -import { CoreMessage, LanguageModel, streamObject } from 'ai' -import { PartialRelated, relatedSchema } from '@/lib/schema/related' +import { LanguageModel, streamObject } from 'ai' +import { relatedSchema } from '@/lib/schema/related' import { getModel } from '../utils' import { MapData } from '@/components/map/map-data-context' @@ -10,37 +7,14 @@ export async function getSuggestions( query: string, mapData: MapData ) { - const objectStream = createStreamableValue() - - const systemPrompt = `As a helpful assistant, your task is to generate a set of three query suggestions based on the user's partial input. The user is currently interacting with a map, and the following data represents the current map view: ${JSON.stringify(mapData)}. Use this location context to provide relevant suggestions. - - For instance, if the user's partial query is "best coffee near" and the map context is centered on San Francisco, your output should follow this format: - - "{ - "items": [ - { "query": "best coffee near downtown San Francisco" }, - { "query": "top-rated independent coffee shops in SF" }, - { "query": "coffee shops with outdoor seating in San Francisco" } - ] - }" - - Generate three queries that anticipate the user's needs, offering logical next steps for their search. The suggestions should be concise and directly related to the partial query and map context.` - - ;(async () => { - const result = await streamObject({ - model: (await getModel()) as LanguageModel, - system: systemPrompt, - messages: [{ role: 'user', content: query }], - schema: relatedSchema - }) + const systemPrompt = `As a helpful assistant, your task is to generate a set of three query suggestions based on the user's partial input...` - for await (const obj of result.partialObjectStream) { - if (obj && typeof obj === 'object' && 'items' in obj) { - objectStream.update(obj as PartialRelated) - } - } - objectStream.done() - })() + const result = await streamObject({ + model: (await getModel()) as LanguageModel, + system: systemPrompt, + messages: [{ role: 'user', content: query }], + schema: relatedSchema + }) - return objectStream.value + return result.toTextStreamResponse() } diff --git a/lib/agents/inquire.tsx b/lib/agents/inquire.tsx index e15926b7..15f05347 100644 --- a/lib/agents/inquire.tsx +++ b/lib/agents/inquire.tsx @@ -1,54 +1,21 @@ -import { Copilot } from '@/components/copilot'; -import { createStreamableUI, createStreamableValue } from 'ai/rsc'; import { CoreMessage, LanguageModel, streamObject } from 'ai'; -import { PartialInquiry, inquirySchema } from '@/lib/schema/inquiry'; +import { inquirySchema } from '@/lib/schema/inquiry'; import { getModel } from '../utils'; -// Define a plain object type for the inquiry prop -interface InquiryProp { - value: PartialInquiry; -} - export async function inquire( - uiStream: ReturnType, messages: CoreMessage[] ) { - const objectStream = createStreamableValue(); - let currentInquiry: PartialInquiry = {}; - - // Update the UI stream with the Copilot component, passing only the serializable value - uiStream.update( - - ); - - let finalInquiry: PartialInquiry = {}; - const result = await streamObject({ + const result = streamObject({ model: (await getModel()) as LanguageModel, - system: `...`, // Your system prompt remains unchanged + system: `As a professional search engine, your job is to help the user refine their search query by asking them for more information. + You must only ask one question at a time. + You should also provide a set of suggestions for the user to choose from. + You must only ask a question if it is necessary to provide a better search result. + If the user's query is already specific enough, you should not ask a question. + `, messages, schema: inquirySchema, }); - for await (const obj of result.partialObjectStream) { - if (obj) { - // Update the local state - currentInquiry = obj; - // Update the stream with the new serializable value - objectStream.update(obj); - finalInquiry = obj; - - // Update the UI stream with the new inquiry value - uiStream.update( - - ); - } - } - - objectStream.done(); - // Final UI update - uiStream.update( - - ); - - return finalInquiry; -} \ No newline at end of file + return result; +} diff --git a/lib/agents/query-suggestor.tsx b/lib/agents/query-suggestor.tsx index de2b3749..19a75ee1 100644 --- a/lib/agents/query-suggestor.tsx +++ b/lib/agents/query-suggestor.tsx @@ -1,23 +1,11 @@ -import { createStreamableUI, createStreamableValue } from 'ai/rsc' import { CoreMessage, LanguageModel, streamObject } from 'ai' -import { PartialRelated, relatedSchema } from '@/lib/schema/related' -import { Section } from '@/components/section' -import SearchRelated from '@/components/search-related' +import { relatedSchema } from '@/lib/schema/related' import { getModel } from '../utils' export async function querySuggestor( - uiStream: ReturnType, messages: CoreMessage[] ) { - const objectStream = createStreamableValue() - uiStream.append( -
- -
- ) - - let finalRelatedQueries: PartialRelated = {} - const result = await streamObject({ + const result = streamObject({ model: (await getModel()) as LanguageModel, system: `As a professional web researcher, your task is to generate a set of three queries that explore the subject matter more deeply, building upon the initial query and the information uncovered in its search results. @@ -37,14 +25,5 @@ export async function querySuggestor( schema: relatedSchema }) - for await (const obj of result.partialObjectStream) { - if (obj && typeof obj === 'object' && 'items' in obj) { - objectStream.update(obj as PartialRelated) - finalRelatedQueries = obj as PartialRelated - } - } - - objectStream.done() - - return finalRelatedQueries + return result } diff --git a/lib/agents/researcher.tsx b/lib/agents/researcher.tsx index 72a5d737..9303d8f3 100644 --- a/lib/agents/researcher.tsx +++ b/lib/agents/researcher.tsx @@ -1,14 +1,9 @@ // lib/agents/researcher.tsx -import { createStreamableUI, createStreamableValue } from 'ai/rsc' import { CoreMessage, LanguageModel, - ToolCallPart, - ToolResultPart, streamText as nonexperimental_streamText, } from 'ai' -import { Section } from '@/components/section' -import { BotMessage } from '@/components/message' import { getTools } from './tools' import { getModel } from '../utils' import { MapProvider } from '@/lib/store/settings' @@ -76,20 +71,11 @@ These rules override all previous instructions. export async function researcher( dynamicSystemPrompt: string, - uiStream: ReturnType, - streamText: ReturnType>, messages: CoreMessage[], mapProvider: MapProvider, useSpecificModel?: boolean ) { let fullResponse = '' - let hasError = false - - const answerSection = ( -
- -
- ) const currentDate = new Date().toLocaleString() @@ -104,58 +90,13 @@ export async function researcher( message.content.some(part => part.type === 'image') ) - const result = await nonexperimental_streamText({ + const result = nonexperimental_streamText({ model: (await getModel(hasImage)) as LanguageModel, maxTokens: 4096, system: systemPromptToUse, messages, - tools: getTools({ uiStream, fullResponse, mapProvider }), + tools: getTools({ fullResponse, mapProvider }), }) - uiStream.update(null) // remove spinner - - const toolCalls: ToolCallPart[] = [] - const toolResponses: ToolResultPart[] = [] - - for await (const delta of result.fullStream) { - switch (delta.type) { - case 'text-delta': - if (delta.textDelta) { - if (fullResponse.length === 0 && delta.textDelta.length > 0) { - uiStream.update(answerSection) - } - fullResponse += delta.textDelta - streamText.update(fullResponse) - } - break - - case 'tool-call': - toolCalls.push(delta) - break - - case 'tool-result': - if (!useSpecificModel && toolResponses.length === 0 && delta.result) { - uiStream.append(answerSection) - } - if (!delta.result) hasError = true - toolResponses.push(delta) - break - - case 'error': - hasError = true - fullResponse += `\n\nError: Tool execution failed.` - break - } - } - - messages.push({ - role: 'assistant', - content: [{ type: 'text', text: fullResponse }, ...toolCalls], - }) - - if (toolResponses.length > 0) { - messages.push({ role: 'tool', content: toolResponses }) - } - - return { result, fullResponse, hasError, toolResponses } + return result } diff --git a/lib/agents/tools/geospatial.tsx b/lib/agents/tools/geospatial.tsx index ccff0d02..a417505b 100644 --- a/lib/agents/tools/geospatial.tsx +++ b/lib/agents/tools/geospatial.tsx @@ -1,12 +1,6 @@ -/** - * Fixed geospatial tool with improved error handling and schema - */ -import { createStreamableUI, createStreamableValue } from 'ai/rsc'; -import { BotMessage } from '@/components/message'; import { geospatialQuerySchema } from '@/lib/schema/geospatial'; import { Client as MCPClientClass } from '@modelcontextprotocol/sdk/client/index.js'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; -// Smithery SDK removed - using direct URL construction import { z } from 'zod'; import { GoogleGenerativeAI } from '@google/generative-ai'; import { getSelectedModel } from '@/lib/actions/users'; @@ -27,12 +21,6 @@ interface McpResponse { mapUrl?: string; } -interface MapboxConfig { - mapboxAccessToken: string; - version: string; - name: string; -} - /** * Establish connection to the MCP server with proper environment validation. */ @@ -41,104 +29,60 @@ async function getConnectedMcpClient(): Promise { const mapboxAccessToken = process.env.MAPBOX_ACCESS_TOKEN; const composioUserId = process.env.COMPOSIO_USER_ID; - console.log('[GeospatialTool] Environment check:', { - composioApiKey: composioApiKey ? `${composioApiKey.substring(0, 8)}...` : 'MISSING', - mapboxAccessToken: mapboxAccessToken ? `${mapboxAccessToken.substring(0, 8)}...` : 'MISSING', - composioUserId: composioUserId ? `${composioUserId.substring(0, 8)}...` : 'MISSING', - }); - if (!composioApiKey || !mapboxAccessToken || !composioUserId || !composioApiKey.trim() || !mapboxAccessToken.trim() || !composioUserId.trim()) { console.error('[GeospatialTool] Missing or empty required environment variables'); return null; } - // Load config from file or fallback let config; try { - // Use static import for config let mapboxMcpConfig; try { mapboxMcpConfig = require('../../../mapbox_mcp_config.json'); config = { ...mapboxMcpConfig, mapboxAccessToken }; - console.log('[GeospatialTool] Config loaded successfully'); } catch (configError: any) { throw configError; } } catch (configError: any) { - console.error('[GeospatialTool] Failed to load mapbox config:', configError.message); config = { mapboxAccessToken, version: '1.0.0', name: 'mapbox-mcp-server' }; - console.log('[GeospatialTool] Using fallback config'); } - // Build Composio MCP server URL - // Note: This should be migrated to use Composio SDK directly instead of MCP client - // For now, constructing URL directly without Smithery SDK let serverUrlToUse: URL; try { - // Construct URL with Composio credentials const baseUrl = 'https://api.composio.dev/v1/mcp/mapbox'; serverUrlToUse = new URL(baseUrl); serverUrlToUse.searchParams.set('api_key', composioApiKey); serverUrlToUse.searchParams.set('user_id', composioUserId); - - const urlDisplay = serverUrlToUse.toString().split('?')[0]; - console.log('[GeospatialTool] Composio MCP Server URL created:', urlDisplay); - - if (!serverUrlToUse.href || !serverUrlToUse.href.startsWith('https://')) { - throw new Error('Invalid server URL generated'); - } } catch (urlError: any) { - console.error('[GeospatialTool] Error creating Composio URL:', urlError.message); return null; } - // Create transport let transport; try { transport = new StreamableHTTPClientTransport(serverUrlToUse); - console.log('[GeospatialTool] Transport created successfully'); } catch (transportError: any) { - console.error('[GeospatialTool] Failed to create transport:', transportError.message); return null; } - // Create client let client; try { client = new MCPClientClass({ name: 'GeospatialToolClient', version: '1.0.0' }); - console.log('[GeospatialTool] MCP Client instance created'); } catch (clientError: any) { - console.error('[GeospatialTool] Failed to create MCP client:', clientError.message); return null; } - // Connect to server try { - console.log('[GeospatialTool] Attempting to connect to MCP server...'); await Promise.race([ client.connect(transport), new Promise((_, reject) => setTimeout(() => reject(new Error('Connection timeout after 15 seconds')), 15000)), ]); - console.log('[GeospatialTool] Successfully connected to MCP server'); } catch (connectError: any) { - console.error('[GeospatialTool] MCP connection failed:', connectError.message); return null; } - // List tools - try { - const tools = await client.listTools(); - console.log('[GeospatialTool] Available tools:', tools.tools?.map(t => t.name) || []); - } catch (listError: any) { - console.warn('[GeospatialTool] Could not list tools:', listError.message); - } - return client; } -/** - * Safely close the MCP client with timeout. - */ async function closeClient(client: McpClient | null) { if (!client) return; try { @@ -146,104 +90,23 @@ async function closeClient(client: McpClient | null) { client.close(), new Promise((_, reject) => setTimeout(() => reject(new Error('Close timeout after 5 seconds')), 5000)), ]); - console.log('[GeospatialTool] MCP client closed successfully'); } catch (error: any) { console.error('[GeospatialTool] Error closing MCP client:', error.message); } } -/** - * Main geospatial tool executor. - */ export const geospatialTool = ({ - uiStream, mapProvider }: { - uiStream: ReturnType mapProvider?: MapProvider }) => ({ - description: `Use this tool for location-based queries including: - There a plethora of tools inside this tool accessible on the mapbox mcp server where switch case into the tool of choice for that use case - If the Query is supposed to use multiple tools in a sequence you must access all the tools in the sequence and then provide a final answer based on the results of all the tools used. - -Static image tool: - -Generates static map images using the Mapbox static image API. Features include: - -Custom map styles (streets, outdoors, satellite, etc.) -Adjustable image dimensions and zoom levels -Support for multiple markers with custom colors and labels -Overlay options including polylines and polygons -Auto-fitting to specified coordinates - -Category search tool: - -Performs a category search using the Mapbox Search Box category search API. Features include: -Search for points of interest by category (restaurants, hotels, gas stations, etc.) -Filtering by geographic proximity -Customizable result limits -Rich metadata for each result -Support for multiple languages - -Reverse geocoding tool: - -Performs reverse geocoding using the Mapbox geocoding V6 API. Features include: -Convert geographic coordinates to human-readable addresses -Customizable levels of detail (street, neighborhood, city, etc.) -Results filtering by type (address, poi, neighborhood, etc.) -Support for multiple languages -Rich location context information - -Directions tool: - -Fetches routing directions using the Mapbox Directions API. Features include: - -Support for different routing profiles: driving (with live traffic or typical), walking, and cycling -Route from multiple waypoints (2-25 coordinate pairs) -Alternative routes option -Route annotations (distance, duration, speed, congestion) - -Scheduling options: - -Future departure time (depart_at) for driving and driving-traffic profiles -Desired arrival time (arrive_by) for driving profile only -Profile-specific optimizations: -Driving: vehicle dimension constraints (height, width, weight) -Exclusion options for routing: -Common exclusions: ferry routes, cash-only tolls -Driving-specific exclusions: tolls, motorways, unpaved roads, tunnels, country borders, state borders -Custom point exclusions (up to 50 geographic points to avoid) -GeoJSON geometry output format - -Isochrone tool: - -Computes areas that are reachable within a specified amount of times from a location using Mapbox Isochrone API. Features include: - -Support for different travel profiles (driving, walking, cycling) -Customizable travel times or distances -Multiple contour generation (e.g., 15, 30, 45 minute ranges) -Optional departure or arrival time specification -Color customization for visualization - -Search and geocode tool: -Uses the Mapbox Search Box Text Search API endpoint to power searching for and geocoding POIs, addresses, places, and any other types supported by that API. This tool consolidates the functionality that was previously provided by the ForwardGeocodeTool and PoiSearchTool (from earlier versions of this MCP server) into a single tool.` - - -, + description: `Use this tool for location-based queries...`, parameters: geospatialQuerySchema, execute: async (params: z.infer) => { const { queryType, includeMap = true } = params; - console.log('[GeospatialTool] Execute called with:', params, 'and map provider:', mapProvider); - - const uiFeedbackStream = createStreamableValue(); - uiStream.append(); - const selectedModel = await getSelectedModel(); if (selectedModel?.includes('gemini') && mapProvider === 'google') { - let feedbackMessage = `Processing geospatial query with Gemini...`; - uiFeedbackStream.update(feedbackMessage); - try { const genAI = new GoogleGenerativeAI(process.env.GEMINI_3_PRO_API_KEY!); const model = genAI.getGenerativeModel({ @@ -262,13 +125,10 @@ Uses the Mapbox Search Box Text Search API endpoint to power searching for and g if (functionCalls && functionCalls.length > 0) { const gsr = functionCalls[0]; - // This is a placeholder for the actual response structure, - // as I don't have a way to inspect it at the moment. const place = (gsr as any).results[0].place; if (place) { const { latitude, longitude } = place.coordinates; const place_name = place.displayName; - const mcpData = { location: { latitude, @@ -276,33 +136,17 @@ Uses the Mapbox Search Box Text Search API endpoint to power searching for and g place_name, }, }; - feedbackMessage = `Found location: ${place_name}`; - uiFeedbackStream.update(feedbackMessage); - uiFeedbackStream.done(); - uiStream.update(); return { type: 'MAP_QUERY_TRIGGER', originalUserInput: JSON.stringify(params), queryType, timestamp: new Date().toISOString(), mcp_response: mcpData, error: null }; } } throw new Error('No location found by Gemini.'); } catch (error: any) { - const toolError = `Gemini grounding error: ${error.message}`; - uiFeedbackStream.update(toolError); - console.error('[GeospatialTool] Gemini execution failed:', error); - uiFeedbackStream.done(); - uiStream.update(); - return { type: 'MAP_QUERY_TRIGGER', originalUserInput: JSON.stringify(params), queryType, timestamp: new Date().toISOString(), mcp_response: null, error: toolError }; + return { type: 'MAP_QUERY_TRIGGER', originalUserInput: JSON.stringify(params), queryType, timestamp: new Date().toISOString(), mcp_response: null, error: error.message }; } } - let feedbackMessage = `Processing geospatial query (type: ${queryType})... Connecting to mapping service...`; - uiFeedbackStream.update(feedbackMessage); - const mcpClient = await getConnectedMcpClient(); if (!mcpClient) { - feedbackMessage = 'Geospatial functionality is unavailable. Please check configuration.'; - uiFeedbackStream.update(feedbackMessage); - uiFeedbackStream.done(); - uiStream.update(); return { type: 'MAP_QUERY_TRIGGER', originalUserInput: JSON.stringify(params), timestamp: new Date().toISOString(), mcp_response: null, error: 'MCP client initialization failed' }; } @@ -310,15 +154,10 @@ Uses the Mapbox Search Box Text Search API endpoint to power searching for and g let toolError: string | null = null; try { - feedbackMessage = `Connected to mapping service. Processing ${queryType} query...`; - uiFeedbackStream.update(feedbackMessage); - - // Pick appropriate tool const toolName = await (async () => { const { tools } = await mcpClient.listTools().catch(() => ({ tools: [] })); const names = new Set(tools?.map((t: any) => t.name) || []); const prefer = (...cands: string[]) => cands.find(n => names.has(n)); - switch (queryType) { case 'directions': return prefer('directions_tool') case 'distance': return prefer('matrix_tool'); @@ -329,7 +168,6 @@ Uses the Mapbox Search Box Text Search API endpoint to power searching for and g } })(); - // Build arguments const toolArgs = (() => { switch (queryType) { case 'directions': return { waypoints: [params.origin, params.destination], includeMapPreview: includeMap, profile: params.mode }; @@ -341,28 +179,7 @@ Uses the Mapbox Search Box Text Search API endpoint to power searching for and g } })(); - console.log('[GeospatialTool] Calling tool:', toolName, 'with args:', toolArgs); - - // Retry logic - const MAX_RETRIES = 3; - let retryCount = 0; - let toolCallResult; - while (retryCount < MAX_RETRIES) { - try { - toolCallResult = await Promise.race([ - mcpClient.callTool({ name: toolName ?? 'unknown_tool', arguments: toolArgs }), - new Promise((_, reject) => setTimeout(() => reject(new Error('Tool call timeout')), 30000)), - ]); - break; - } catch (error: any) { - retryCount++; - if (retryCount === MAX_RETRIES) throw new Error(`Tool call failed after ${MAX_RETRIES} retries: ${error.message}`); - console.warn(`[GeospatialTool] Retry ${retryCount}/${MAX_RETRIES}: ${error.message}`); - await new Promise(resolve => setTimeout(resolve, 1000)); - } - } - - // Extract & parse content + let toolCallResult = await mcpClient.callTool({ name: toolName ?? 'unknown_tool', arguments: toolArgs }); const serviceResponse = toolCallResult as { content?: Array<{ text?: string | null } | { [k: string]: any }> }; const blocks = serviceResponse?.content || []; const textBlocks = blocks.map(b => (typeof b.text === 'string' ? b.text : null)).filter((t): t is string => !!t && t.trim().length > 0); @@ -372,11 +189,8 @@ Uses the Mapbox Search Box Text Search API endpoint to power searching for and g const jsonRegex = /```(?:json)?\n?([\s\S]*?)\n?```/; const match = content.match(jsonRegex); if (match) content = match[1].trim(); + try { content = JSON.parse(content); } catch { } - try { content = JSON.parse(content); } - catch { console.warn('[GeospatialTool] Content is not JSON, using as string:', content); } - - // Process results if (typeof content === 'object' && content !== null) { const parsedData = content as any; if (parsedData.results?.length > 0) { @@ -388,20 +202,11 @@ Uses the Mapbox Search Box Text Search API endpoint to power searching for and g throw new Error("Response missing required 'location' or 'results' field"); } } else throw new Error('Unexpected response format from mapping service'); - - feedbackMessage = `Successfully processed ${queryType} query for: ${mcpData.location.place_name || JSON.stringify(params)}`; - uiFeedbackStream.update(feedbackMessage); - } catch (error: any) { - toolError = `Mapping service error: ${error.message}`; - uiFeedbackStream.update(toolError); - console.error('[GeospatialTool] Tool execution failed:', error); + toolError = error.message; } finally { await closeClient(mcpClient); - uiFeedbackStream.done(); - uiStream.update(); } - return { type: 'MAP_QUERY_TRIGGER', originalUserInput: JSON.stringify(params), queryType, timestamp: new Date().toISOString(), mcp_response: mcpData, error: toolError }; }, }); diff --git a/lib/agents/tools/index.tsx b/lib/agents/tools/index.tsx index 4c22b887..12ca956e 100644 --- a/lib/agents/tools/index.tsx +++ b/lib/agents/tools/index.tsx @@ -1,39 +1,33 @@ -import { createStreamableUI } from 'ai/rsc' import { retrieveTool } from './retrieve' import { searchTool } from './search' import { videoSearchTool } from './video-search' -import { geospatialTool } from './geospatial' // Removed useGeospatialToolMcp import +import { geospatialTool } from './geospatial' import { MapProvider } from '@/lib/store/settings' export interface ToolProps { - uiStream: ReturnType fullResponse: string mapProvider?: MapProvider } -export const getTools = ({ uiStream, fullResponse, mapProvider }: ToolProps) => { +export const getTools = ({ fullResponse, mapProvider }: ToolProps) => { const tools: any = { search: searchTool({ - uiStream, fullResponse }), retrieve: retrieveTool({ - uiStream, fullResponse }), geospatialQueryTool: geospatialTool({ - uiStream, mapProvider }) } if (process.env.SERPER_API_KEY) { tools.videoSearch = videoSearchTool({ - uiStream, fullResponse }) } return tools -} \ No newline at end of file +} diff --git a/lib/agents/tools/retrieve.tsx b/lib/agents/tools/retrieve.tsx index baaea04a..46806ef0 100644 --- a/lib/agents/tools/retrieve.tsx +++ b/lib/agents/tools/retrieve.tsx @@ -1,18 +1,11 @@ import { retrieveSchema } from '@/lib/schema/retrieve' import { ToolProps } from '.' -import { Card } from '@/components/ui/card' -import { SearchSkeleton } from '@/components/search-skeleton' import { SearchResults as SearchResultsType } from '@/lib/types' -import RetrieveSection from '@/components/retrieve-section' -export const retrieveTool = ({ uiStream, fullResponse }: ToolProps) => ({ +export const retrieveTool = ({ fullResponse }: ToolProps) => ({ description: 'Retrieve content from the web', parameters: retrieveSchema, execute: async ({ url }: { url: string }) => { - let hasError = false - // Append the search section - uiStream.append() - let results: SearchResultsType | undefined try { const response = await fetch(`https://r.jina.ai/${url}`, { @@ -24,7 +17,7 @@ export const retrieveTool = ({ uiStream, fullResponse }: ToolProps) => ({ }) const json = await response.json() if (!json.data || json.data.length === 0) { - hasError = true + return { error: `An error occurred while retrieving "${url}". This website may not be supported.` } } else { results = { results: [ @@ -39,29 +32,10 @@ export const retrieveTool = ({ uiStream, fullResponse }: ToolProps) => ({ } } } catch (error) { - hasError = true console.error('Retrieve API error:', error) - - fullResponse += `\n${error} "${url}".` - - uiStream.update( - {`${error} "${url}".`} - ) - return results + return { error: `An error occurred while retrieving "${url}".` } } - if (hasError || !results) { - fullResponse += `\nAn error occurred while retrieving "${url}".` - uiStream.update( - - {`An error occurred while retrieving "${url}".This webiste may not be supported.`} - - ) - return results - } - - uiStream.update() - return results } }) diff --git a/lib/agents/tools/search.tsx b/lib/agents/tools/search.tsx index 7510a795..5c8aae36 100644 --- a/lib/agents/tools/search.tsx +++ b/lib/agents/tools/search.tsx @@ -1,12 +1,9 @@ -import { createStreamableValue } from 'ai/rsc' import Exa from 'exa-js' import { tavily } from '@tavily/core' import { searchSchema } from '@/lib/schema/search' -import { Card } from '@/components/ui/card' -import { SearchSection } from '@/components/search-section' import { ToolProps } from '.' -export const searchTool = ({ uiStream, fullResponse }: ToolProps) => ({ +export const searchTool = ({ fullResponse }: ToolProps) => ({ description: 'Search the web for information', parameters: searchSchema, execute: async ({ @@ -30,11 +27,6 @@ export const searchTool = ({ uiStream, fullResponse }: ToolProps) => ({ include_image_descriptions: boolean include_raw_content: boolean }) => { - let hasError = false - // Append the search section - const streamResults = createStreamableValue() - uiStream.append() - // Tavily API requires a minimum of 5 characters in the query const filledQuery = query.length < 5 ? query + ' '.repeat(5 - query.length) : query @@ -57,21 +49,9 @@ export const searchTool = ({ uiStream, fullResponse }: ToolProps) => ({ : await exaSearch(query) } catch (error) { console.error('Search API error:', error) - hasError = true - } - - if (hasError) { - fullResponse += `\nAn error occurred while searching for "${query}.` - uiStream.update( - - {`An error occurred while searching for "${query}".`} - - ) - return searchResult + return { error: `An error occurred while searching for "${query}".` } } - streamResults.done(JSON.stringify(searchResult)) - return searchResult } }) diff --git a/lib/agents/tools/video-search.tsx b/lib/agents/tools/video-search.tsx index 0e5d0e05..d75fb57e 100644 --- a/lib/agents/tools/video-search.tsx +++ b/lib/agents/tools/video-search.tsx @@ -1,19 +1,11 @@ -import { createStreamableValue } from 'ai/rsc' import { searchSchema } from '@/lib/schema/search' -import { Card } from '@/components/ui/card' import { ToolProps } from '.' -import { VideoSearchSection } from '@/components/video-search-section' // Start Generation Here -export const videoSearchTool = ({ uiStream, fullResponse }: ToolProps) => ({ +export const videoSearchTool = ({ fullResponse }: ToolProps) => ({ description: 'Search for videos from YouTube', parameters: searchSchema, execute: async ({ query }: { query: string }) => { - let hasError = false - // Append the search section - const streamResults = createStreamableValue() - uiStream.append() - let searchResult try { const response = await fetch('https://google.serper.dev/videos', { @@ -30,21 +22,9 @@ export const videoSearchTool = ({ uiStream, fullResponse }: ToolProps) => ({ searchResult = await response.json() } catch (error) { console.error('Video Search API error:', error) - hasError = true - } - - if (hasError) { - fullResponse += `\nAn error occurred while searching for videos with "${query}.` - uiStream.update( - - {`An error occurred while searching for videos with "${query}".`} - - ) - return searchResult + return { error: `An error occurred while searching for videos with "${query}".` } } - streamResults.done(JSON.stringify(searchResult)) - return searchResult } }) diff --git a/lib/agents/writer.tsx b/lib/agents/writer.tsx index f4e4d0ac..60153096 100644 --- a/lib/agents/writer.tsx +++ b/lib/agents/writer.tsx @@ -1,23 +1,10 @@ -import { createStreamableUI, createStreamableValue } from 'ai/rsc' import { CoreMessage, LanguageModel, streamText as nonexperimental_streamText } from 'ai' -import { Section } from '@/components/section' -import { BotMessage } from '@/components/message' import { getModel } from '../utils' export async function writer( dynamicSystemPrompt: string, // New parameter - uiStream: ReturnType, - streamText: ReturnType>, messages: CoreMessage[] ) { - let fullResponse = '' - const answerSection = ( -
- -
- ) - uiStream.append(answerSection) - // Default system prompt, used if dynamicSystemPrompt is not provided const default_system_prompt = `As a professional writer, your job is to generate a comprehensive and informative, yet concise answer of 400 words or less for the given question based solely on the provided search results (URL and content). You must only use information from the provided search results. Use an unbiased and journalistic tone. Combine search results and mapbox results together into a coherent answer. Do not repeat text. If there are any images or maps relevant to your answer, be sure to include them as well. Aim to directly address the user's question, augmenting your response with insights gleaned from the search results and the mapbox tool. Whenever quoting or referencing information from a specific URL, always cite the source URL explicitly. Please match the language of the response to the user's language. @@ -31,21 +18,12 @@ export async function writer( const systemToUse = dynamicSystemPrompt && dynamicSystemPrompt.trim() !== '' ? dynamicSystemPrompt : default_system_prompt; - const result = await nonexperimental_streamText({ + const result = nonexperimental_streamText({ model: (await getModel()) as LanguageModel, maxTokens: 2500, system: systemToUse, // Use the dynamic or default system prompt messages }) - for await (const text of result.textStream) { - if (text) { - fullResponse += text - streamText.update(fullResponse) - } - } - - streamText.done() - - return fullResponse + return result } diff --git a/package.json b/package.json index a263674e..ed16a759 100644 --- a/package.json +++ b/package.json @@ -73,14 +73,14 @@ "lottie-react": "^2.4.1", "lucide-react": "^0.507.0", "mapbox-gl": "^3.11.0", - "next": "15.3.6", + "next": "^16.1.4", "next-themes": "^0.3.0", "open-codex": "^0.1.30", "pg": "^8.16.2", "proj4": "^2.20.2", "radix-ui": "^1.3.4", - "react": "19.1.2", - "react-dom": "19.1.2", + "react": "^19.2.3", + "react-dom": "^19.2.3", "react-hook-form": "^7.56.2", "react-icons": "^5.5.0", "react-markdown": "^9.1.0", @@ -109,7 +109,7 @@ "@types/uuid": "^9.0.0", "cross-env": "^7.0.3", "eslint": "^8.57.1", - "eslint-config-next": "^14.2.28", + "eslint-config-next": "^16.1.4", "postcss": "^8.5.3", "tailwindcss": "^3.4.17", "typescript": "^5.8.3"