diff --git a/biome.json b/biome.json index 38e8145e4..63ee12a38 100644 --- a/biome.json +++ b/biome.json @@ -24,7 +24,8 @@ "recommended": true, "style": { "useNodejsImportProtocol": "off" - } + }, + "a11y": "off" } }, "javascript": { @@ -48,6 +49,24 @@ "css": { "formatter": { "enabled": true + }, + "parser": { + "cssModules": true + } + }, + "overrides": [ + { + "includes": ["browser/src/**/*"], + "linter": { + "rules": { + "correctness": { + "useExhaustiveDependencies": "off" + }, + "suspicious": { + "noUnknownAtRules": "off" + } + } + } } - } + ] } diff --git a/browser/i18n-README.md b/browser/i18n-README.md deleted file mode 100644 index 60019c28a..000000000 --- a/browser/i18n-README.md +++ /dev/null @@ -1,117 +0,0 @@ -# Browser 项目国际化使用指南 - -## 概述 - -本项目已集成 `react-i18next` 国际化解决方案,支持中文和英文两种语言。 - -## 功能特性 - -- ✅ 支持中文(简体)和英文 -- ✅ 自动语言检测(基于浏览器设置) -- ✅ 语言切换组件 -- ✅ 本地存储语言偏好 -- ✅ TypeScript 支持 - -## 文件结构 - -``` -src/ -├── i18n/ -│ ├── index.ts # 国际化配置 -│ └── locales/ -│ ├── en.json # 英文语言包 -│ └── zh.json # 中文语言包 -├── components/ -│ ├── I18nProvider/ # 国际化提供者组件 -│ └── LanguageSwitcher/ # 语言切换组件 -└── types/ - └── i18n.d.ts # TypeScript 类型声明 -``` - -## 使用方法 - -### 1. 在组件中使用翻译 - -```tsx -import { useTranslation } from 'react-i18next'; - -const MyComponent = () => { - const { t } = useTranslation(); - - return ( -
-

{t('chat.welcomeTitle')}

-

{t('chat.welcomeDescription')}

-
- ); -}; -``` - -### 2. 切换语言 - -用户可以通过侧边栏底部的语言切换按钮来切换语言。语言偏好会自动保存到本地存储。 - -### 3. 添加新的翻译内容 - -在 `src/i18n/locales/` 目录下的 JSON 文件中添加新的翻译键值对: - -**en.json** -```json -{ - "newSection": { - "title": "New Title", - "description": "New Description" - } -} -``` - -**zh.json** -```json -{ - "newSection": { - "title": "新标题", - "description": "新描述" - } -} -``` - -### 4. 语言检测顺序 - -系统会按以下顺序检测语言: -1. localStorage 中保存的语言设置 -2. sessionStorage 中的语言设置 -3. 浏览器语言设置 -4. HTML 标签的 lang 属性 -5. 默认语言(英文) - -## 已国际化的组件 - -- ✅ 侧边栏组件(Sider) -- ✅ 欢迎页面(Welcome) -- ✅ 聊天发送组件(ChatSender) -- ✅ 语言切换组件(LanguageSwitcher) - -## 注意事项 - -1. 所有硬编码的文本都应该使用 `t()` 函数包装 -2. 翻译键应该使用有意义的命名空间结构 -3. 添加新语言时,需要在 `src/i18n/index.ts` 中注册相应的资源 - -## 开发建议 - -- 保持翻译键的一致性和可读性 -- 为新功能添加相应的翻译内容 -- 定期检查翻译内容的准确性和完整性 -- 考虑使用翻译管理工具来维护大型项目的翻译内容 - -## 启动项目 - -```bash -# 安装依赖 -pnpm install - -# 启动开发服务器 -pnpm dev -``` - -启动后,您可以在侧边栏底部看到语言切换按钮,点击即可切换语言。 diff --git a/browser/index.html b/browser/index.html index d98c82ef0..c00fb2cb8 100644 --- a/browser/index.html +++ b/browser/index.html @@ -1,13 +1,16 @@ - - - - - Takumi Browser - AI 编程助手 - - -
- - + + + + + + Neovate Code Agent + + + +
+ + + diff --git a/browser/package.json b/browser/package.json index 4c4b54e57..f3b1c3364 100644 --- a/browser/package.json +++ b/browser/package.json @@ -10,13 +10,10 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@ai-sdk/react": "^1.2.12", - "@ai-sdk/ui-utils": "^1.2.11", "@ant-design/icons": "^6.0.0", "@ant-design/x": "^1.4.0", - "@lexical/react": "0.32.1", - "@lexical/utils": "0.32.1", "@monaco-editor/react": "^4.7.0", + "@shikijs/transformers": "^3.12.0", "@tanstack/react-router": "^1.121.16", "@tanstack/react-router-devtools": "^1.121.21", "ahooks": "^3.7.0", @@ -25,32 +22,38 @@ "axios": "1.10.0", "classnames": "^2.5.1", "dayjs": "^1.11.13", + "diff": "^8.0.2", "i18next": "^25.2.1", "i18next-browser-languagedetector": "^8.2.0", - "lexical": "0.32.1", "lodash-es": "^4.17.21", - "monaco-editor": "^0.52.2", + "quill": "^2.0.3", "rc-util": "^5.44.4", - "react": "^19.1.0", - "react-dom": "^19.1.0", + "react": "^19.1.1", + "react-dom": "^19.1.1", "react-i18next": "^15.5.3", "react-icons": "^5.5.0", "react-markdown": "^9.0.0", "react-syntax-highlighter": "^15.5.0", "remark-gfm": "^4.0.1", + "shiki": "^3.11.0", + "streamdown": "^1.1.6", "swr": "^2.3.3", "valtio": "^2.1.5" }, "devDependencies": { + "@ant-design/v5-patch-for-react-19": "^1.0.3", "@tailwindcss/typography": "^0.5.16", "@tailwindcss/vite": "^4.1.10", "@tanstack/router-plugin": "^1.121.16", + "@types/diff": "^5.2.3", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", "@types/react-i18next": "^8.1.0", "@types/react-syntax-highlighter": "^15.5.13", "@vitejs/plugin-react": "^4.5.2", "globals": "^16.0.0", + "lucide-react": "^0.545.0", + "tailwind-scrollbar": "^4.0.2", "tailwindcss": "^4.1.10", "typescript": "~5.8.3", "vite": "^6.3.5", diff --git a/browser/src/api/appData.ts b/browser/src/api/appData.ts deleted file mode 100644 index 92aae0d0d..000000000 --- a/browser/src/api/appData.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { request } from '@/utils/request'; -import type { ApiResponse, AppData } from './model'; - -export const getAppData = (): Promise> => { - return request.get('/app-data'); -}; diff --git a/browser/src/api/files.ts b/browser/src/api/files.ts index 646983c40..64fe0f5bd 100644 --- a/browser/src/api/files.ts +++ b/browser/src/api/files.ts @@ -1,28 +1,5 @@ import { request } from '@/utils/request'; -import type { ApiResponse, FileItem } from './model'; - -interface FileListResponse { - cwd: string; - directory: string; - items: FileItem[]; - files: string[]; - directories: string[]; - error?: string; -} - -export interface FileListQueries { - searchString?: string; - - maxSize?: number; -} - -export const getFileList = ( - queries?: FileListQueries, -): Promise> => { - return request.get('/files/list', { - params: queries, - }); -}; +import type { ApiResponse } from './model'; interface FileEditResponse { message: string; diff --git a/browser/src/api/folders.ts b/browser/src/api/folders.ts new file mode 100644 index 000000000..7d48284c5 --- /dev/null +++ b/browser/src/api/folders.ts @@ -0,0 +1,18 @@ +import type { FolderResponse } from '@/components/FolderPicker/types'; +import { request } from '@/utils/request'; + +export interface FolderListRequest { + path: string; +} + +export interface FolderListResponse { + success: boolean; + data: FolderResponse; + message?: string; +} + +export async function getFolderList(path: string): Promise { + return await request(`/folders/list?path=${encodeURIComponent(path)}`, { + method: 'GET', + }); +} diff --git a/browser/src/api/mcpService.ts b/browser/src/api/mcpService.ts deleted file mode 100644 index bda6d5728..000000000 --- a/browser/src/api/mcpService.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { - AddMcpServerRequest, - McpOperationResponse, - McpServerResponse, - McpServersResponse, - UpdateMcpServerRequest, -} from '@/types/mcp'; -import { request } from '@/utils/request'; - -// Get server list -export const getMCPServers = (global = false): Promise => { - return request.get(`/mcp/servers?global=${global}`); -}; - -// Get single server -export const getMCPServer = ( - name: string, - global = false, -): Promise => { - return request.get(`/mcp/servers/${name}?global=${global}`); -}; - -// Add server -export const addMCPServer = ( - config: AddMcpServerRequest, -): Promise => { - return request.post(`/mcp/servers`, config); -}; - -// Update server -export const updateMCPServer = ( - name: string, - config: UpdateMcpServerRequest, -): Promise => { - return request.patch(`/mcp/servers/${name}`, config); -}; - -// Remove server -export const removeMCPServer = ( - name: string, - global = false, -): Promise => { - return request.delete(`/mcp/servers/${name}?global=${global}`); -}; diff --git a/browser/src/api/model.ts b/browser/src/api/model.ts index 217414e67..6ad5aee17 100644 --- a/browser/src/api/model.ts +++ b/browser/src/api/model.ts @@ -1,9 +1,3 @@ -export interface FileItem { - path: string; - type: 'file' | 'directory'; - name: string; -} - export interface ImageItem { /** URL or base64 string */ src: string; @@ -16,10 +10,3 @@ export interface ApiResponse { error?: string; message?: string; } - -export interface AppData { - productName: string; - version: string; - cwd: string; - config: Record; -} diff --git a/browser/src/api/project.ts b/browser/src/api/project.ts new file mode 100644 index 000000000..501e2184d --- /dev/null +++ b/browser/src/api/project.ts @@ -0,0 +1,30 @@ +import { request } from '@/utils/request'; +import type { ApiResponse } from './model'; + +interface SessionInfo { + sessionId: string; + modified: Date; + created: Date; + messageCount: number; + summary: string; +} + +export interface ProjectInfo { + name: string; + path: string; + gitBranch?: string; + gitStatus?: 'clean' | 'modified' | 'staged' | 'conflicted'; + sessions: SessionInfo[]; +} + +export const getProjectInfo = ( + folder?: string, +): Promise> => { + return request.get('/project/info', { params: { folder } }); +}; + +export const openProjectInEditor = ( + projectPath: string, +): Promise> => { + return request.post('/project/open-in-editor', { projectPath }); +}; diff --git a/browser/src/api/session.ts b/browser/src/api/session.ts new file mode 100644 index 000000000..e4cf89174 --- /dev/null +++ b/browser/src/api/session.ts @@ -0,0 +1,19 @@ +import type { Message } from '@/types/chat'; +import { request } from '@/utils/request'; +import type { ApiResponse } from './model'; + +interface SessionInitializeResponse { + cwd: string; + sessionId: string; + messages: Message[]; + history: string[]; + logFile: string; +} + +export const initializeSession = (opts: { + cwd?: string; + resume?: string; + continue?: boolean; +}): Promise> => { + return request.post('/session/initialize', { ...opts }); +}; diff --git a/browser/src/api/settings.ts b/browser/src/api/settings.ts deleted file mode 100644 index 84e2f6c1c..000000000 --- a/browser/src/api/settings.ts +++ /dev/null @@ -1,76 +0,0 @@ -import type { - AppSettings, - ModelOption, - SettingsResponse, -} from '@/types/settings'; -import { request } from '@/utils/request'; -import type { ApiResponse } from './model'; - -// Get settings -export const getSettings = ( - scope: 'global' | 'project', -): Promise>> => { - return request.get(`/settings?scope=${scope}`); -}; - -// Set configuration -export const setSetting = ( - scope: 'global' | 'project', - key: string, - value: string | boolean | string[], -): Promise> => { - return request.post('/settings', { scope, key, value }); -}; - -// Batch update settings -export const updateSettings = ( - scope: 'global' | 'project', - settings: Partial, -): Promise> => { - return request.post('/settings/batch', { scope, settings }); -}; - -// Delete configuration item -export const removeSetting = ( - scope: 'global' | 'project', - key: string, -): Promise> => { - return request.delete(`/settings?scope=${scope}&key=${key}`); -}; - -// Get effective settings (merged) -export const getEffectiveSettings = (): Promise> => { - return request.get('/settings/effective'); -}; - -// Get available models list -export const getAvailableModels = (): Promise> => { - return request.get('/settings/models'); -}; - -// Get available plugins list -export const getAvailablePlugins = (): Promise> => { - return request.get('/settings/plugins'); -}; - -// Reset settings -export const resetSettings = ( - scope: 'global' | 'project', -): Promise> => { - return request.post('/settings/reset', { scope }); -}; - -// Export settings -export const exportSettings = ( - scope: 'global' | 'project', -): Promise> => { - return request.get(`/settings/export?scope=${scope}`); -}; - -// Import settings -export const importSettings = ( - scope: 'global' | 'project', - settingsJson: string, -): Promise> => { - return request.post('/settings/import', { scope, settings: settingsJson }); -}; diff --git a/browser/src/api/toolApproval.ts b/browser/src/api/toolApproval.ts deleted file mode 100644 index a3544b7a6..000000000 --- a/browser/src/api/toolApproval.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { request } from '@/utils/request'; - -// 提交工具审批结果 -export function submitToolApproval( - callId: string, - approved: boolean, - option: 'once' | 'always' | 'always_tool' = 'once', -): Promise<{ success: boolean }> { - return request.post('/tool-approval/submit', { - callId, - approved, - option, - }); -} diff --git a/browser/src/assets/codebase-navigation-icon.svg b/browser/src/assets/codebase-navigation-icon.svg new file mode 100644 index 000000000..dd96c7247 --- /dev/null +++ b/browser/src/assets/codebase-navigation-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/browser/src/assets/file-operations-icon.svg b/browser/src/assets/file-operations-icon.svg new file mode 100644 index 000000000..d3fb4e4c7 --- /dev/null +++ b/browser/src/assets/file-operations-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/browser/src/assets/llm-support-icon.svg b/browser/src/assets/llm-support-icon.svg new file mode 100644 index 000000000..ab587ae8c --- /dev/null +++ b/browser/src/assets/llm-support-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/browser/src/assets/plan-mode-icon.svg b/browser/src/assets/plan-mode-icon.svg new file mode 100644 index 000000000..6d4905640 --- /dev/null +++ b/browser/src/assets/plan-mode-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/browser/src/assets/takumi-logo.svg b/browser/src/assets/takumi-logo.svg new file mode 100644 index 000000000..4343c7307 --- /dev/null +++ b/browser/src/assets/takumi-logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/browser/src/assets/welcome-background.png b/browser/src/assets/welcome-background.png new file mode 100644 index 000000000..6bad80162 Binary files /dev/null and b/browser/src/assets/welcome-background.png differ diff --git a/browser/src/client/index.ts b/browser/src/client/index.ts new file mode 100644 index 000000000..26864aa0c --- /dev/null +++ b/browser/src/client/index.ts @@ -0,0 +1,39 @@ +export { MessageBus } from './messaging/MessageBus'; +export type * from './messaging/types'; +export type { + BusMessage, + EventHandler, + EventMessage, + MessageHandler, + PendingRequest, + RequestMessage, + ResponseMessage, +} from './messaging/types'; +export type * from './transport/types'; +export type { + CloseHandler, + ErrorHandler, + TransportConfig, + TransportHandler, + TransportMessage, + TransportState, +} from './transport/types'; +export { WebSocketTransport } from './transport/WebSocketTransport'; +export type * from './types'; + +export type { + CancelParams, + CancelResult, + ClientConfig, + ClientEventData, + ClientEventType, + ClientSession, + GetStatusParams, + GetStatusResult, + InitializeParams, + InitializeResult, + SendMessageParams, + SendMessageResult, + ToolApprovalParams, + ToolApprovalResult, +} from './types'; diff --git a/browser/src/client/messaging/MessageBus.ts b/browser/src/client/messaging/MessageBus.ts new file mode 100644 index 000000000..167e25e14 --- /dev/null +++ b/browser/src/client/messaging/MessageBus.ts @@ -0,0 +1,165 @@ +import type { TransportMessage } from '../transport/types'; +import type { WebSocketTransport } from '../transport/WebSocketTransport'; +import type { + EventHandler, + EventMessage, + MessageHandler, + PendingRequest, + RequestMessage, + ResponseMessage, +} from './types'; + +export class MessageBus { + private transport: WebSocketTransport | null = null; + private pendingRequests = new Map(); + private messageHandlers = new Map(); + private eventHandlers = new Map(); + + setTransport(transport: WebSocketTransport): void { + this.transport = transport; + transport.onMessage((message) => { + this.handleIncomingMessage(message); + }); + } + + generateId(): string { + return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; + } + + async request( + method: string, + params: T, + ): Promise { + if (!this.transport || !this.transport.isConnected()) { + throw new Error('Transport is not connected'); + } + + const id = this.generateId(); + const message: RequestMessage = { + type: 'request', + id, + method, + params, + timestamp: Date.now(), + }; + + return new Promise((resolve, reject) => { + this.pendingRequests.set(id, { + resolve: resolve as (value: unknown) => void, + reject, + }); + + this.transport?.send(message as TransportMessage).catch((error) => { + this.pendingRequests.delete(id); + reject(error); + }); + }); + } + + private handleIncomingMessage(message: TransportMessage): void { + switch (message.type) { + case 'response': + this.handleResponse(message as ResponseMessage); + break; + case 'event': + this.handleEvent(message as EventMessage); + break; + case 'request': + this.handleRequest(message as RequestMessage); + break; + } + } + + private handleResponse(message: ResponseMessage): void { + const pending = this.pendingRequests.get(message.id); + if (pending) { + this.pendingRequests.delete(message.id); + + if (message.error) { + pending.reject( + new Error(message.error.message || String(message.error)), + ); + } else { + pending.resolve(message.result); + } + } + } + + private handleEvent(message: EventMessage): void { + const handlers = this.eventHandlers.get(message.event); + if (handlers) { + handlers.map((handler) => handler(message.data)); + } + } + + private handleRequest(message: RequestMessage): void { + const handler = this.messageHandlers.get(message.method); + if (handler) { + handler(message.params) + .then((result) => { + const response: ResponseMessage = { + type: 'response', + id: message.id, + result, + timestamp: Date.now(), + }; + this.transport?.send(response as TransportMessage); + }) + .catch((error) => { + const response: ResponseMessage = { + type: 'response', + id: message.id, + error: { message: error.message }, + timestamp: Date.now(), + }; + this.transport?.send(response as TransportMessage); + }); + } + } + + onEvent(event: string, handler: EventHandler): void { + if (!this.eventHandlers.has(event)) { + this.eventHandlers.set(event, []); + } + this.eventHandlers.get(event)?.push(handler as EventHandler); + } + + registerHandler( + method: string, + handler: MessageHandler, + ): void { + this.messageHandlers.set(method, handler as MessageHandler); + } + + removeEventHandler( + event: string, + handler: EventHandler, + ): void { + const handlers = this.eventHandlers.get(event); + if (handlers) { + const index = handlers.indexOf(handler as EventHandler); + if (index > -1) { + handlers.splice(index, 1); + } + } + } + + removeMessageHandler(method: string): void { + this.messageHandlers.delete(method); + } + + getPendingRequestsCount(): number { + return this.pendingRequests.size; + } + + cancelPendingRequests(): void { + this.pendingRequests.forEach((pending) => { + pending.reject(new Error('Request cancelled')); + }); + this.pendingRequests.clear(); + } + + isTransportConnected(): boolean { + return this.transport ? this.transport.isConnected() : false; + } +} diff --git a/browser/src/client/messaging/types.ts b/browser/src/client/messaging/types.ts new file mode 100644 index 000000000..4f2bccb10 --- /dev/null +++ b/browser/src/client/messaging/types.ts @@ -0,0 +1,35 @@ +export interface PendingRequest { + resolve: (value: unknown) => void; + reject: (error: Error) => void; +} + +export type EventHandler = (data: T) => void; + +export type MessageHandler = ( + params: T, +) => Promise; + +export interface RequestMessage { + type: 'request'; + id: string; + method: string; + params: unknown; + timestamp: number; +} + +export interface ResponseMessage { + type: 'response'; + id: string; + result?: unknown; + error?: { message: string }; + timestamp: number; +} + +export interface EventMessage { + type: 'event'; + event: string; + data: unknown; + timestamp: number; +} + +export type BusMessage = RequestMessage | ResponseMessage | EventMessage; diff --git a/browser/src/client/transport/WebSocketTransport.ts b/browser/src/client/transport/WebSocketTransport.ts new file mode 100644 index 000000000..7e679b2e5 --- /dev/null +++ b/browser/src/client/transport/WebSocketTransport.ts @@ -0,0 +1,156 @@ +import type { + CloseHandler, + ErrorHandler, + TransportConfig, + TransportHandler, + TransportMessage, + TransportState, +} from './types'; + +export class WebSocketTransport { + private url: string; + private ws: WebSocket | null = null; + private state: TransportState = 'disconnected'; + private messageHandlers: TransportHandler[] = []; + private errorHandlers: ErrorHandler[] = []; + private closeHandlers: CloseHandler[] = []; + private messageBuffer: TransportMessage[] = []; + private reconnectInterval: number; + private maxReconnectInterval: number; + private shouldReconnect: boolean; + + constructor(config: TransportConfig) { + this.url = config.url; + this.reconnectInterval = config.reconnectInterval || 1000; + this.maxReconnectInterval = config.maxReconnectInterval || 30000; + this.shouldReconnect = config.shouldReconnect ?? true; + } + + connect(): Promise { + return new Promise((resolve, reject) => { + try { + this.state = 'connecting'; + this.ws = new WebSocket(this.url); + + this.ws.onopen = () => { + this.state = 'connected'; + this.reconnectInterval = 1000; + this.flushBuffer(); + resolve(); + }; + + this.ws.onmessage = (event) => { + try { + const message = JSON.parse(event.data) as TransportMessage; + this.messageHandlers.map((handler) => handler(message)); + } catch (error) { + console.error('Failed to parse message:', error); + this.errorHandlers.map((handler) => + handler( + error instanceof Error ? error : new Error(String(error)), + ), + ); + } + }; + + this.ws.onerror = (_event) => { + this.state = 'error'; + const error = new Error('WebSocket error'); + this.errorHandlers.map((handler) => handler(error)); + }; + + this.ws.onclose = () => { + this.state = 'disconnected'; + this.closeHandlers.map((handler) => handler()); + + if (this.shouldReconnect) { + setTimeout(() => { + this.connect().catch(console.error); + this.reconnectInterval = Math.min( + this.reconnectInterval * 2, + this.maxReconnectInterval, + ); + }, this.reconnectInterval); + } + }; + } catch (error) { + this.state = 'error'; + reject(error instanceof Error ? error : new Error(String(error))); + } + }); + } + + private flushBuffer(): void { + const messages = [...this.messageBuffer]; + this.messageBuffer = []; + messages.forEach((message) => { + this.send(message).catch(console.error); + }); + } + + isConnected(): boolean { + return ( + this.state === 'connected' && + this.ws !== null && + this.ws !== undefined && + this.ws.readyState === WebSocket.OPEN + ); + } + + onMessage(handler: TransportHandler): void { + this.messageHandlers.push(handler); + } + + onError(handler: ErrorHandler): void { + this.errorHandlers.push(handler); + } + + onClose(handler: CloseHandler): void { + this.closeHandlers.push(handler); + } + + async send(message: TransportMessage): Promise { + if (this.isConnected()) { + this.ws?.send(JSON.stringify(message)); + } else { + this.messageBuffer.push(message); + throw new Error('WebSocket is not connected'); + } + } + + async close(): Promise { + this.shouldReconnect = false; + if (this.ws) { + this.ws.close(); + this.ws = null; + } + this.state = 'closed'; + } + + getState(): TransportState { + return this.state; + } + + getUrl(): string { + return this.url; + } + + removeHandler( + type: 'message' | 'error' | 'close', + handler: TransportHandler | ErrorHandler | CloseHandler, + ): void { + switch (type) { + case 'message': + this.messageHandlers = this.messageHandlers.filter( + (h) => h !== handler, + ); + break; + case 'error': + this.errorHandlers = this.errorHandlers.filter((h) => h !== handler); + break; + case 'close': + this.closeHandlers = this.closeHandlers.filter((h) => h !== handler); + break; + } + } +} diff --git a/browser/src/client/transport/types.ts b/browser/src/client/transport/types.ts new file mode 100644 index 000000000..56a19ec59 --- /dev/null +++ b/browser/src/client/transport/types.ts @@ -0,0 +1,31 @@ +export type TransportState = + | 'disconnected' + | 'connecting' + | 'connected' + | 'error' + | 'closed'; + +export interface TransportMessage { + type: 'request' | 'response' | 'event'; + id?: string; + method?: string; + params?: Record; + result?: unknown; + error?: { message: string }; + event?: string; + data?: unknown; + timestamp: number; +} + +export type TransportHandler = (message: TransportMessage) => void; + +export type ErrorHandler = (error: Error) => void; + +export type CloseHandler = () => void; + +export interface TransportConfig { + url: string; + reconnectInterval?: number; + maxReconnectInterval?: number; + shouldReconnect?: boolean; +} diff --git a/browser/src/client/types/chat.ts b/browser/src/client/types/chat.ts new file mode 100644 index 000000000..b95c77ec4 --- /dev/null +++ b/browser/src/client/types/chat.ts @@ -0,0 +1 @@ +export type ApprovalMode = 'default' | 'autoEdit' | 'yolo'; diff --git a/browser/src/client/types/index.ts b/browser/src/client/types/index.ts new file mode 100644 index 000000000..25ec4e67a --- /dev/null +++ b/browser/src/client/types/index.ts @@ -0,0 +1,97 @@ +import type { ApprovalMode } from './chat'; + +export interface ClientConfig { + reconnectInterval?: number; + maxReconnectInterval?: number; + defaultTimeout?: number; + shouldReconnect?: boolean; +} + +export interface ClientSession { + sessionId?: string; + cwd: string; + planMode?: boolean; +} + +export interface InitializeParams { + cwd: string; + sessionId?: string; +} + +export interface InitializeResult { + success: boolean; + data: { + productName: string; + version: string; + sessionId: string; + model: string; + approvalMode: ApprovalMode; + sessionSummary: string; + pastedTextMap: Record; + pastedImageMap: Record; + }; + error?: { + message: string; + }; +} + +export interface SendMessageParams { + message: string; + cwd: string; + sessionId?: string; + planMode?: boolean; +} + +export interface SendMessageResult { + success: boolean; + sessionId: string; +} + +export interface CancelParams { + cwd: string; + sessionId: string; +} + +export interface CancelResult { + success: boolean; + message: string; +} + +export interface GetStatusParams { + cwd: string; + sessionId: string; +} + +export interface GetStatusResult { + status: string; + sessionId: string; + [key: string]: unknown; +} + +export interface ToolApprovalParams { + toolUse: { + name: string; + args: Record; + [key: string]: unknown; + }; + [key: string]: unknown; +} + +export interface ToolApprovalResult { + approved: boolean; + option?: 'once' | 'always' | 'always_tool'; +} + +export type ClientEventType = + | 'connected' + | 'disconnected' + | 'message' + | 'textDelta' + | 'chunk' + | 'error'; + +export interface ClientEventData { + [key: string]: unknown; +} + +export * from './chat'; diff --git a/browser/src/components/ActivityIndicator/GradientText/index.module.css b/browser/src/components/ActivityIndicator/GradientText/index.module.css new file mode 100644 index 000000000..f54d23d29 --- /dev/null +++ b/browser/src/components/ActivityIndicator/GradientText/index.module.css @@ -0,0 +1,92 @@ +.gradientText { + display: inline-flex; + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +.character { + transition: + color 0.4s cubic-bezier(0.4, 0, 0.2, 1), + text-shadow 0.4s cubic-bezier(0.4, 0, 0.2, 1); + display: inline-block; + white-space: pre; + font-weight: 500; +} + +.character.base { + color: #9ca3af; /* gray-400 */ +} + +.character.highlight { + color: #7c3aed; /* purple-600 */ + text-shadow: + 0 0 8px rgba(124, 58, 237, 0.5), + 0 0 16px rgba(124, 58, 237, 0.3); + filter: brightness(1.1); + font-weight: 600; +} + +.character.fade1 { + color: #8b5cf6; /* purple-500 */ + font-weight: 500; +} + +.character.fade2 { + color: #a78bfa; /* purple-400 */ +} + +.character.fade3 { + color: #c4b5fd; /* purple-300 */ +} + +.character.fade4 { + color: #ddd6fe; /* purple-200 */ +} + +/* 暗色主题适配 */ +@media (prefers-color-scheme: dark) { + .character.base { + color: #6b7280; /* gray-500 */ + } + + .character.highlight { + color: #a855f7; /* purple-500 */ + text-shadow: + 0 0 10px rgba(168, 85, 247, 0.6), + 0 0 20px rgba(168, 85, 247, 0.4); + filter: brightness(1.2); + font-weight: 600; + } + + .character.fade1 { + color: #9333ea; /* purple-600 */ + font-weight: 500; + } + + .character.fade2 { + color: #8b5cf6; /* purple-500 */ + } + + .character.fade3 { + color: #a78bfa; /* purple-400 */ + } + + .character.fade4 { + color: #c4b5fd; /* purple-300 */ + } +} + +/* 自定义颜色变量支持 */ +.gradientText[data-base-color] .character.base { + color: var(--base-color); +} + +.gradientText[data-highlight-color] .character.highlight { + color: var(--highlight-color); +} + +/* 动画暂停状态 */ +.gradientText.paused .character { + transition: none; +} diff --git a/browser/src/components/ActivityIndicator/GradientText/index.tsx b/browser/src/components/ActivityIndicator/GradientText/index.tsx new file mode 100644 index 000000000..b6180ba03 --- /dev/null +++ b/browser/src/components/ActivityIndicator/GradientText/index.tsx @@ -0,0 +1,106 @@ +import { useCallback, useEffect, useMemo, useState } from 'react'; +import styles from './index.module.css'; + +interface GradientTextProps { + text: string; + speed?: number; + className?: string; + baseColor?: string; + highlightColor?: string; + isActive?: boolean; +} + +const FADE_LEVELS = ['fade1', 'fade2', 'fade3', 'fade4'] as const; + +function getColorClassByDistance(distance: number): string { + if (distance === 0) return 'highlight'; + + const fadeLevel = FADE_LEVELS[distance - 1]; + return fadeLevel || 'base'; +} + +const GradientText: React.FC = ({ + text, + speed = 150, + className = '', + baseColor, + highlightColor, + isActive = true, +}) => { + const [highlightIndex, setHighlightIndex] = useState(0); + + useEffect(() => { + if (!isActive || !text) { + return; + } + + const interval = setInterval(() => { + setHighlightIndex((prevIndex) => { + const nextIndex = prevIndex + 1; + return nextIndex >= text.length ? 0 : nextIndex; + }); + }, speed); + + return () => { + clearInterval(interval); + }; + }, [text, speed, isActive]); + + const resetAnimation = useCallback(() => { + setHighlightIndex(0); + }, []); + + const renderedText = useMemo(() => { + if (!text) return null; + + return text.split('').map((char, index) => { + const distance = Math.abs(index - highlightIndex); + const colorClass = getColorClassByDistance(distance); + + return ( + + {char} + + ); + }); + }, [text, highlightIndex]); + + const containerStyle = useMemo(() => { + const style: Record = {}; + + if (baseColor) { + style['--base-color'] = baseColor; + } + + if (highlightColor) { + style['--highlight-color'] = highlightColor; + } + + return style; + }, [baseColor, highlightColor]); + + const containerClass = [ + styles.gradientText, + !isActive && styles.paused, + className, + ] + .filter(Boolean) + .join(' '); + + return ( + + {renderedText} + + ); +}; + +export default GradientText; diff --git a/browser/src/components/ActivityIndicator/index.tsx b/browser/src/components/ActivityIndicator/index.tsx new file mode 100644 index 000000000..468cbabb9 --- /dev/null +++ b/browser/src/components/ActivityIndicator/index.tsx @@ -0,0 +1,54 @@ +import { useMemo } from 'react'; +import { useSnapshot } from 'valtio'; +import { state } from '@/state/chat'; +import GradientText from './GradientText'; + +const ActivityIndicator = () => { + const { status, processingTokens, error, approvalModal } = useSnapshot(state); + + const text = useMemo(() => { + if (status === 'processing') return 'Processing'; + if (status === 'failed') return `Failed: ${error}`; + return 'Processing'; + }, [status, error]); + + const additionalInfo = useMemo(() => { + if (status === 'processing') { + const tokenText = + processingTokens > 0 ? `↓ ${processingTokens} tokens` : ''; + return `(Esc to cancel${tokenText ? `, ${tokenText}` : ''})`; + } + return null; + }, [status, processingTokens]); + + if (status === 'idle') return null; + if (status === 'exit') return null; + if (approvalModal) return null; + + return ( +
+ {status === 'processing' ? ( + <> + + + ... + + {additionalInfo && ( + + {additionalInfo} + + )} + + ) : ( + {text} + )} +
+ ); +}; + +export default ActivityIndicator; diff --git a/browser/src/components/AssistantFooter/index.module.css b/browser/src/components/AssistantFooter/index.module.css new file mode 100644 index 000000000..627195f9b --- /dev/null +++ b/browser/src/components/AssistantFooter/index.module.css @@ -0,0 +1,9 @@ +.assistantFooter { + display: flex; + gap: 8px; +} + +.assistantFooterIcon { + width: 18px !important; + height: 18px !important; +} diff --git a/browser/src/components/AssistantFooter/index.tsx b/browser/src/components/AssistantFooter/index.tsx index d95e32407..48fa43348 100644 --- a/browser/src/components/AssistantFooter/index.tsx +++ b/browser/src/components/AssistantFooter/index.tsx @@ -1,24 +1,21 @@ -import { - CheckOutlined, - CopyOutlined, - DislikeOutlined, - LikeOutlined, - ReloadOutlined, -} from '@ant-design/icons'; -import { Button, Flex, Typography } from 'antd'; -import { last } from 'lodash-es'; -import { useTranslation } from 'react-i18next'; +import { CheckOutlined } from '@ant-design/icons'; +import { Button, Flex, Spin } from 'antd'; +import { useEffect, useState } from 'react'; import { useSnapshot } from 'valtio'; -import { useChatState } from '@/hooks/provider'; -import { actions, state } from '@/state/sender'; -import { type UIMessage, UIMessageType } from '@/types/message'; -import { mergeMessages } from '@/utils/mergeMessages'; - -const { Text } = Typography; +import { useClipboard } from '@/hooks/useClipboard'; +import CopyIcon from '@/icons/copy.svg?react'; +import DislikeIcon from '@/icons/dislike.svg?react'; +import LikeIcon from '@/icons/like.svg?react'; +import RefreshIcon from '@/icons/refresh.svg?react'; +import type { AppStatus } from '@/state/chat'; +import { state } from '@/state/sender'; +import type { Message } from '@/types/chat'; +import ActivityIndicator from '../ActivityIndicator'; +import styles from './index.module.css'; interface AssistantFooterProps { - message: UIMessage; - status: 'submitted' | 'streaming' | 'ready' | 'error'; + message: Message; + status: AppStatus; } const AssistantFooter: React.FC = ({ @@ -26,49 +23,108 @@ const AssistantFooter: React.FC = ({ status, }) => { const { mode } = useSnapshot(state); - const { t } = useTranslation(); - const { approvePlan } = useChatState(); + const { writeText } = useClipboard(); + const [isCopySuccess, setIsCopySuccess] = useState(false); + + /** + * read all Text Message and copy to clipboard + */ + const handleCopy = () => { + let text = ''; + if (typeof message.content === 'string') { + text = message.content; + } else if (Array.isArray(message.content)) { + text = message.content + .map((part) => { + if (typeof part === 'string') return part; + if (part && part.type === 'text' && typeof part.text === 'string') { + return part.text; + } + return ''; + }) + .join(''); + } + writeText(text); + setIsCopySuccess(true); + }; - if (mode === 'plan' && status === 'ready') { - const mergedMessage = mergeMessages(message.annotations || []); - const lastMessage = last(mergedMessage); - if ( - lastMessage?.type === UIMessageType.Text && - lastMessage.mode === 'plan' - ) { - return ( -
- - - {t('plan.approveDescription')} - - - -
- ); + useEffect(() => { + if (isCopySuccess) { + const timer = setTimeout(() => { + setIsCopySuccess(false); + }, 2000); + return () => clearTimeout(timer); } + }, [isCopySuccess]); + + if (mode === 'plan' && status === 'idle') { + // const lastMessage = message; + // if ( + // lastMessage?.type === UIMessageType.Text && + // lastMessage.mode === 'plan' + // ) { + // return ( + //
+ // + // + // {t('plan.approveDescription')} + // + // + // + //
+ // ); + // } + } + + if (status !== 'idle') { + return ( +
+ + +
+ ); } return ( - - + {snap.approvalModal?.category === 'write' && ( + + )} + + + + + ); +} + +export default ApprovalModal; diff --git a/browser/src/components/AssistantMessage/AssistantTextMessage.tsx b/browser/src/components/AssistantMessage/AssistantTextMessage.tsx index f814f8bb1..a9efd589f 100644 --- a/browser/src/components/AssistantMessage/AssistantTextMessage.tsx +++ b/browser/src/components/AssistantMessage/AssistantTextMessage.tsx @@ -1,27 +1,10 @@ -import { Spin } from 'antd'; -import { useTranslation } from 'react-i18next'; -import type { TextMessage } from '@/types/message'; +import type { TextPart } from '@/types/chat'; import MarkdownRenderer from '../MarkdownRenderer'; const AssistantTextMessage: React.FC<{ - message: TextMessage; -}> = ({ message }) => { - const { t } = useTranslation(); - - if (message.text === ' - - {t('message.toolCalling')} - - ); - } - return ; + part: TextPart; +}> = ({ part }) => { + return ; }; export default AssistantTextMessage; diff --git a/browser/src/components/AssistantMessage/AssistantThinkingMessage.tsx b/browser/src/components/AssistantMessage/AssistantThinkingMessage.tsx index 9fc7c4275..5b540025f 100644 --- a/browser/src/components/AssistantMessage/AssistantThinkingMessage.tsx +++ b/browser/src/components/AssistantMessage/AssistantThinkingMessage.tsx @@ -1,14 +1,14 @@ import { RadarChartOutlined, RightOutlined } from '@ant-design/icons'; import { useCallback, useState } from 'react'; -import type { ReasoningMessage } from '@/types/message'; +import type { ReasoningPart } from '@/types/chat'; interface ThinkingMessageProps { - message: ReasoningMessage; + part: ReasoningPart; defaultExpanded?: boolean; } const ThinkingMessage: React.FC = ({ - message, + part, defaultExpanded = true, }) => { const [isExpanded, setIsExpanded] = useState(defaultExpanded); @@ -17,7 +17,7 @@ const ThinkingMessage: React.FC = ({ setIsExpanded((prev) => !prev); }, []); - if (!message.reasoning?.trim()) { + if (!part.text?.trim()) { return null; } @@ -51,7 +51,7 @@ const ThinkingMessage: React.FC = ({ Thinking...
- {message.reasoning.split('\n').length} lines + {part.text.split('\n').length} lines
@@ -62,7 +62,7 @@ const ThinkingMessage: React.FC = ({ >
-            {message.reasoning}
+            {part.text}
           
diff --git a/browser/src/components/AssistantMessage/AssistantToolMessage.tsx b/browser/src/components/AssistantMessage/AssistantToolMessage.tsx index 509e2135d..87369685f 100644 --- a/browser/src/components/AssistantMessage/AssistantToolMessage.tsx +++ b/browser/src/components/AssistantMessage/AssistantToolMessage.tsx @@ -1,6 +1,7 @@ -import React, { useState } from 'react'; +import type React from 'react'; +import { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import type { ToolMessage } from '@/types/message'; +import type { UIToolPart } from '@/types/chat'; import { BashRender, EditRender, @@ -10,72 +11,54 @@ import { GrepRender, LsRender, ReadRender, + TodoRender, WriteRender, } from '../ToolRender'; -const AssistantToolMessage: React.FC<{ message: ToolMessage }> = ({ - message, -}) => { - const { t } = useTranslation(); - const { state, toolName, args, step, result } = message; +const ToolResultItem: React.FC<{ part: UIToolPart }> = ({ part }) => { + if (part.state !== 'tool_result') { + return null; + } - if ( - result?.success === false || - (typeof result === 'string' && - result === 'Tool execution was denied by user.') // 后端类型暂未统一,需要hack - ) { - return ; + const { name, result } = part; + if (result?.isError) { + return ; } - switch (toolName) { + switch (name) { case 'grep': - return ; + return ; case 'read': - return ; + return ; case 'glob': - return ; + return ; case 'ls': - return ; + return ; case 'bash': - return ; + return ; case 'fetch': - return ; + return ; case 'edit': - return ; + return ; case 'write': - return ; + return ; + case 'todoRead': + case 'todoWrite': + return ; + default: + return ; } +}; - // 控制结果展开/收起的状态,默认收起 +const AssistantToolMessage: React.FC<{ + part: UIToolPart; +}> = ({ part }) => { + const { name } = part; + const { t } = useTranslation(); const [isResultExpanded, setIsResultExpanded] = useState(false); - // 根据状态返回不同的图标和颜色 - const getStatusInfo = () => { - switch (state) { - case 'call': - return { - icon: '🔄', - iconColor: 'text-blue-500 animate-spin', - statusText: t('tool.status.executing'), - }; - case 'result': - return { - icon: '✓', - iconColor: 'text-green-500', - statusText: t('tool.status.completed'), - }; - default: - return { - icon: '?', - iconColor: 'text-gray-500', - statusText: t('tool.status.unknown'), - }; - } - }; - - // 根据工具类型获取图标 - const getToolIcon = () => { - switch (toolName) { + const toolIcon = useMemo(() => { + switch (name) { case 'grep': return '🔍'; case 'read': @@ -92,235 +75,70 @@ const AssistantToolMessage: React.FC<{ message: ToolMessage }> = ({ return '📁'; case 'glob': return '🎯'; + case 'todoRead': + case 'todoWrite': + return '📄'; default: return '🔧'; } - }; - - const statusInfo = getStatusInfo(); - - // 渲染简化的参数 - const renderArgs = () => { - if (!args || Object.keys(args).length === 0) return null; - - // 只显示最重要的参数 - const mainArg = getMainArg(); - if (!mainArg) return null; - - return ( - {mainArg} - ); - }; - - // 获取主要参数显示 - const getMainArg = () => { - if (!args) return null; - - if (args.file_path) return String(args.file_path); - if (args.command) return String(args.command); - if (args.url) return String(args.url); - if (args.pattern) return String(args.pattern); - if (args.path) return String(args.path); - - // 如果有其他参数,显示第一个 - const keys = Object.keys(args); - if (keys.length > 0) { - return String(args[keys[0]]); + }, [name]); + + const statusInfo = useMemo(() => { + if (part.result?.isError) { + return { + icon: '❌', + iconColor: 'text-red-500', + statusText: t('tool.status.failed'), + }; } - - return null; - }; - - // 渲染详细结果 - const renderDetailedResult = () => { - if (state !== 'result' || !('result' in message)) return null; - - const result = message.result; - - // 根据工具类型优化结果展示 - const renderToolResult = () => { - // 尝试解析结构化结果 - if ( - typeof result === 'object' && - result !== null && - 'success' in result - ) { - // 处理成功结果 - if (result.success && 'data' in result) { - const data = result.data; - - // grep 和 glob 工具的文件列表展示 - if ( - (toolName === 'grep' || toolName === 'glob') && - typeof data === 'object' && - data !== null && - 'filenames' in data && - Array.isArray(data.filenames) - ) { - return ( -
-
- {t('tool.filesFound', { count: data.filenames.length })} - {'durationMs' in data && - typeof data.durationMs === 'number' && ( - ({data.durationMs}ms) - )} -
-
- {data.filenames - .slice(0, 10) - .map((filename: unknown, index: number) => ( -
- {String(filename)} -
- ))} - {data.filenames.length > 10 && ( -
- {t('tool.moreFiles', { - count: data.filenames.length - 10, - })} -
- )} -
-
- ); - } - - // read 工具的文件内容展示 - if ( - toolName === 'read' && - typeof data === 'object' && - data !== null && - 'content' in data - ) { - return ( -
-
- {'totalLines' in data && - typeof data.totalLines === 'number' && ( - {t('tool.lines', { count: data.totalLines })} - )} -
-
-
-                    {String(data.content)}
-                  
-
-
- ); - } - - // fetch 工具的响应展示 - if ( - toolName === 'fetch' && - typeof data === 'object' && - data !== null && - 'result' in data - ) { - return ( -
-
- {'code' in data && typeof data.code === 'number' && ( - - {data.code} - - )} - {'durationMs' in data && - typeof data.durationMs === 'number' && ( - {data.durationMs}ms - )} -
-
-
- {String(data.result)} -
-
-
- ); - } - - // bash 工具的输出展示 - if (toolName === 'bash' && 'output' in result) { - return ( -
-
-
-                    {String(result.output)}
-                  
-
-
- ); - } - } - - // 处理错误结果 - if (!result.success && 'error' in result) { - return ( -
-
-
- {String(result.error)} -
-
-
- ); - } - } - - // 默认展示 - const resultStr = - typeof result === 'string' ? result : JSON.stringify(result, null, 2); - return ( -
-
-
-              {resultStr}
-            
-
-
- ); - }; - - return renderToolResult(); - }; + switch (part.state) { + case 'tool_use': + return { + icon: '🔄', + iconColor: 'text-blue-500 animate-spin', + statusText: t('tool.status.executing'), + }; + case 'tool_result': + return { + icon: '✓', + iconColor: 'text-green-500', + statusText: t('tool.status.completed'), + }; + default: + return { + icon: '?', + iconColor: 'text-gray-500', + statusText: t('tool.status.unknown'), + }; + } + }, [part.state]); return (
- {/* 工具调用主行 */}
- {getToolIcon()} + {toolIcon} {statusInfo.icon} - {toolName} - {step && #{step}} - {renderArgs()} + {name} + {part.description && ( + {part.description} + )} {statusInfo.statusText} - - {/* 结果展开/收起按钮 */} - {state === 'result' && 'result' in message && ( - - )} +
- - {/* 详细结果 */} - {isResultExpanded && renderDetailedResult()} + {isResultExpanded && ( +
+ +
+ )}
); }; diff --git a/browser/src/components/AssistantMessage/index.module.css b/browser/src/components/AssistantMessage/index.module.css new file mode 100644 index 000000000..4b41f2889 --- /dev/null +++ b/browser/src/components/AssistantMessage/index.module.css @@ -0,0 +1,10 @@ +.assistantMessage { + font-size: 14px; + line-height: 20px; + letter-spacing: 0%; + color: #110c22; +} + +.assistantFooterLoading { + width: 600px; +} diff --git a/browser/src/components/AssistantMessage/index.tsx b/browser/src/components/AssistantMessage/index.tsx index 9fbc1d17a..08bad65e2 100644 --- a/browser/src/components/AssistantMessage/index.tsx +++ b/browser/src/components/AssistantMessage/index.tsx @@ -1,56 +1,58 @@ import { memo, useMemo } from 'react'; import { useStableValue } from '@/hooks/useStableValue'; -import type { UIMessage, UIMessageAnnotation } from '@/types/message'; -import { UIMessageType } from '@/types/message'; -import { mergeMessages } from '@/utils/mergeMessages'; +import type { + ReasoningPart, + TextPart, + UIAssistantMessage, + UIToolPart, +} from '@/types/chat'; import MarkdownRenderer from '../MarkdownRenderer'; -import ToolApprovalConfirmation from '../ToolApprovalConfirmation'; -import ToolApprovalError from '../ToolApprovalError'; -import ToolApprovalResult from '../ToolApprovalResult'; +import ApprovalModal from './ApprovalModal'; import AssistantTextMessage from './AssistantTextMessage'; import AssistantThinkingMessage from './AssistantThinkingMessage'; import AssistantToolMessage from './AssistantToolMessage'; - -interface MessageProps { - message: UIMessage; -} +import styles from './index.module.css'; interface MessagePartProps { - part: UIMessageAnnotation; - index: number; + part: TextPart | ReasoningPart | UIToolPart; + uuid: string; } -const MessagePart: React.FC = memo(({ part, index }) => { +const MessagePart: React.FC = memo(({ part, uuid }) => { switch (part.type) { - case UIMessageType.Text: - return ; - case UIMessageType.Reasoning: - return ; - case UIMessageType.Tool: - return ; - case UIMessageType.ToolApprovalRequest: - return ; - case UIMessageType.ToolApprovalResult: - return ; - case UIMessageType.ToolApprovalError: - return ; + case 'text': + return ; + case 'reasoning': + return ; + case 'tool': + return ( + <> + + + + ); default: return ( -
+
); } }); -MessagePart.displayName = 'MessagePart'; +interface MessageProps { + message: UIAssistantMessage; +} const AssistantMessage: React.FC = ({ message }) => { const mergedMessages = useMemo(() => { - return mergeMessages(message.annotations); - }, [message.annotations]); + if (typeof message.content === 'string') { + return [{ type: 'text', text: message.content }] as TextPart[]; + } + return message.content; + }, [message.content]); const messageParts = useStableValue(mergedMessages); @@ -59,11 +61,11 @@ const AssistantMessage: React.FC = ({ message }) => { } return ( - <> - {messageParts.map((part, index) => ( - +
+ {messageParts.map((part) => ( + ))} - +
); }; diff --git a/browser/src/components/ChatContent/index.module.css b/browser/src/components/ChatContent/index.module.css new file mode 100644 index 000000000..c9dc7339f --- /dev/null +++ b/browser/src/components/ChatContent/index.module.css @@ -0,0 +1,43 @@ +.chat { + height: 100%; + box-sizing: border-box; + display: flex; + flex-direction: column; + padding-block: 16px; + gap: 16px; + flex: 1; +} + +.chat::-webkit-scrollbar { + display: none; +} + +.chatList { + flex: 1; + overflow: auto; +} + +.chatList :global(.ant-bubble-footer) { + width: 100%; + margin-top: 8px; +} + +.chatList :global(.ant-bubble-content) { + min-height: 0px; +} + +.chatList :global(.ant-btn-icon) { + display: flex; + align-items: center; + justify-content: center; +} + +.bubbleList { + height: 100%; + max-width: 800px; + margin-inline: auto; +} + +.skeletonContainer { + width: 600px; +} diff --git a/browser/src/components/ChatContent/index.tsx b/browser/src/components/ChatContent/index.tsx new file mode 100644 index 000000000..0e8986624 --- /dev/null +++ b/browser/src/components/ChatContent/index.tsx @@ -0,0 +1,85 @@ +import { Bubble } from '@ant-design/x'; +import { type GetProp, Skeleton } from 'antd'; +import { useSnapshot } from 'valtio'; +import AssistantFooter from '@/components/AssistantFooter'; +import AssistantMessage from '@/components/AssistantMessage'; +import ChatSender from '@/components/ChatSender'; +import DisplayMessage from '@/components/DisplayMessage'; +import { UserMessage, UserMessageFooter } from '@/components/UserMessage'; +import Welcome from '@/components/Welcome'; +import { state } from '@/state/chat'; +import type { Message } from '@/types/chat'; +import styles from './index.module.css'; + +const ChatContent: React.FC = () => { + const { messages, status } = useSnapshot(state); + + const items = messages?.map((message, index) => { + const isLastMessage = index === messages.length - 1; + return { + ...message, + content: message, + // typing: status === 'processing' ? { step: 20, interval: 150 } : false, + // loading: status === 'processing_stream' && isLastMessage, + footer: + isLastMessage && message.role === 'assistant' + ? () => ( + + ) + : () => , + }; + }); + + const roles: GetProp = { + user: { + placement: 'end', + variant: 'borderless', + messageRender(message) { + return ; + }, + footer(message) { + return ; + }, + }, + assistant: { + placement: 'start', + variant: 'borderless', + messageRender(message) { + return ; + }, + loadingRender() { + return ( +
+ +
+ ); + }, + }, + ui_display: { + placement: 'start', + variant: 'borderless', + messageRender(message) { + return ; + }, + }, + }; + + return ( +
+
+ {items?.length ? ( + + ) : ( + + )} +
+ +
+ ); +}; + +export default ChatContent; diff --git a/browser/src/components/ChatLayout/ResizeHandle.tsx b/browser/src/components/ChatLayout/ResizeHandle.tsx new file mode 100644 index 000000000..dfeac850b --- /dev/null +++ b/browser/src/components/ChatLayout/ResizeHandle.tsx @@ -0,0 +1,121 @@ +import { createStyles } from 'antd-style'; +import React, { useCallback, useEffect, useRef } from 'react'; +import * as layout from '@/state/layout'; + +const useResizeHandleStyles = createStyles(({ css }) => ({ + handle: css` + width: 1px; + height: 100%; + background: #e5e5e5; + cursor: col-resize; + position: relative; + transform-origin: center; + transition: + transform 0.2s ease, + background 0.2s ease; + + &:hover { + background: #7357ff; + transform: scaleX(3); + } + + &:active { + background: #7357ff; + transform: scaleX(3); + } + `, +})); + +interface ResizeHandleProps { + containerRef: React.RefObject; + rightPanelRef: React.RefObject; +} + +const ResizeHandle: React.FC = ({ + containerRef, + rightPanelRef, +}) => { + const { styles } = useResizeHandleStyles(); + const isDragging = useRef(false); + const originalTransition = useRef(''); + + const handleMouseDown = useCallback( + (e: React.MouseEvent) => { + e.preventDefault(); + isDragging.current = true; + + // Use the passed rightPanelRef + if (rightPanelRef.current) { + // Save original transition and disable it for real-time following + originalTransition.current = rightPanelRef.current.style.transition; + rightPanelRef.current.style.transition = 'none'; + } + + const handleMouseMove = (e: MouseEvent) => { + if ( + !isDragging.current || + !containerRef.current || + !rightPanelRef.current + ) + return; + + // Get container information + const containerRect = containerRef.current.getBoundingClientRect(); + const containerWidth = containerRect.width; + + // Calculate mouse relative position + const mouseX = e.clientX - containerRect.left; + + // Calculate right panel width percentage (calculated from right side) + const rightWidthPercent = + ((containerWidth - mouseX) / containerWidth) * 100; + + // Boundary constraints: right panel 20% - dynamic max based on screen size + const minRight = 20; + const maxRight = layout.actions.calculateRightPanelWidth(); + const clampedRightPercent = Math.max( + minRight, + Math.min(maxRight, rightWidthPercent), + ); + + // Directly manipulate DOM styles for real-time following + rightPanelRef.current.style.width = `${clampedRightPercent}%`; + }; + + const handleMouseUp = () => { + isDragging.current = false; + + if (rightPanelRef.current) { + // Read current width directly from DOM styles to avoid precision issues from recalculation + const currentWidth = rightPanelRef.current.style.width; + const finalWidthPercent = parseFloat(currentWidth.replace('%', '')); + + // Restore transition effect + rightPanelRef.current.style.transition = originalTransition.current; + + // Update state to keep it consistent with DOM + layout.actions.setRightPanelWidthPercent(finalWidthPercent); + } + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); + }; + + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); + }, + [containerRef, rightPanelRef], + ); + + // Cleanup on component unmount + useEffect(() => { + return () => { + if (rightPanelRef.current && originalTransition.current) { + rightPanelRef.current.style.transition = originalTransition.current; + } + }; + }, [rightPanelRef]); + + return
; +}; + +export default ResizeHandle; diff --git a/browser/src/components/ChatLayout/RightPanel.tsx b/browser/src/components/ChatLayout/RightPanel.tsx new file mode 100644 index 000000000..2fa140d72 --- /dev/null +++ b/browser/src/components/ChatLayout/RightPanel.tsx @@ -0,0 +1,45 @@ +import { createStyles } from 'antd-style'; +import React from 'react'; +import { useSnapshot } from 'valtio'; +import CodeViewer from '@/components/CodeViewer'; +import * as codeViewer from '@/state/codeViewer'; +import RightPanelHeader from './RightPanelHeader'; + +const useRightPanelStyles = createStyles(({ css }) => ({ + rightPanel: css` + display: flex; + flex-direction: column; + height: 100%; + `, + + content: css` + flex: 1; + padding: 8px; + overflow: hidden; + `, + + codeViewerContainer: css` + height: 100%; + width: 100%; + `, +})); + +const RightPanel: React.FC = () => { + const { styles } = useRightPanelStyles(); + const { visible: codeViewerVisible } = useSnapshot(codeViewer.state); + + return ( +
+ +
+ {codeViewerVisible && ( +
+ +
+ )} +
+
+ ); +}; + +export default RightPanel; diff --git a/browser/src/components/ChatLayout/RightPanelHeader.tsx b/browser/src/components/ChatLayout/RightPanelHeader.tsx new file mode 100644 index 000000000..36b2e1050 --- /dev/null +++ b/browser/src/components/ChatLayout/RightPanelHeader.tsx @@ -0,0 +1,48 @@ +import { createStyles } from 'antd-style'; +import React from 'react'; +import ToggleExpandIcon from '@/icons/toggle-expand.svg?react'; +import * as layout from '@/state/layout'; + +const useHeaderStyles = createStyles(({ css }) => ({ + header: css` + height: 60px; + display: flex; + align-items: center; + padding: 18px 24px 6px 24px; + background: #ffffff; + border-bottom: 1px solid #e5e5e5; + box-sizing: border-box; + `, + + headerContent: css` + display: flex; + align-items: center; + height: 36px; + `, + + collapseButton: css` + cursor: pointer; + `, +})); + +const RightPanelHeader: React.FC = () => { + const { styles } = useHeaderStyles(); + + const handleCollapseClick = () => { + layout.actions.setRightPanelExpanded(false); + }; + + return ( +
+
+ +
+
+ ); +}; + +export default RightPanelHeader; diff --git a/browser/src/components/ChatLayout/SidebarExpandButton.tsx b/browser/src/components/ChatLayout/SidebarExpandButton.tsx new file mode 100644 index 000000000..869d8485d --- /dev/null +++ b/browser/src/components/ChatLayout/SidebarExpandButton.tsx @@ -0,0 +1,39 @@ +import { createStyles } from 'antd-style'; +import React from 'react'; +import { useSnapshot } from 'valtio'; +import ToggleExpandIcon from '@/icons/toggle-expand.svg?react'; +import * as layout from '@/state/layout'; + +const useStyles = createStyles(({ css }) => { + return { + expandButton: css` + position: fixed; + top: 24px; + left: 24px; + z-index: 1000; + transition: all 0.2s ease; + cursor: pointer; + `, + }; +}); + +const SidebarExpandButton: React.FC = () => { + const { styles } = useStyles(); + const { sidebarCollapsed } = useSnapshot(layout.state); + + const handleExpandSidebar = () => { + layout.actions.setSidebarCollapsed(false); + }; + + if (!sidebarCollapsed) { + return null; + } + + return ( +
+ +
+ ); +}; + +export default SidebarExpandButton; diff --git a/browser/src/components/ChatLayout/TopRightExpandButton.tsx b/browser/src/components/ChatLayout/TopRightExpandButton.tsx new file mode 100644 index 000000000..fa793df10 --- /dev/null +++ b/browser/src/components/ChatLayout/TopRightExpandButton.tsx @@ -0,0 +1,34 @@ +import { createStyles } from 'antd-style'; +import React from 'react'; +import { useSnapshot } from 'valtio'; +import ToggleExpandIcon from '@/icons/toggle-expand.svg?react'; +import * as layout from '@/state/layout'; + +const useToggleButtonStyles = createStyles(({ css }) => ({ + topRightToggle: css` + position: fixed; + top: 24px; + right: 24px; + z-index: 10; + width: 25px; + height: 25px; + cursor: pointer; + `, +})); + +const TopRightToggleButton: React.FC = () => { + const { styles } = useToggleButtonStyles(); + const { rightPanelExpanded } = useSnapshot(layout.state); + + const handleClick = () => { + layout.actions.toggleRightPanel(); + }; + + if (rightPanelExpanded) return null; + + return ( + + ); +}; + +export default TopRightToggleButton; diff --git a/browser/src/components/ChatSender/AddContext/index.tsx b/browser/src/components/ChatSender/AddContext/index.tsx index 408877c0d..93c86b020 100644 --- a/browser/src/components/ChatSender/AddContext/index.tsx +++ b/browser/src/components/ChatSender/AddContext/index.tsx @@ -1,49 +1,23 @@ -import Icon from '@ant-design/icons'; -import { Tag } from 'antd'; -import { createStyles } from 'antd-style'; -import { useRef, useState } from 'react'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { useSnapshot } from 'valtio'; import SuggestionList from '@/components/SuggestionList'; import { ContextType } from '@/constants/context'; import { useSuggestion } from '@/hooks/useSuggestion'; import * as context from '@/state/context'; - -const useStyle = createStyles(({ css, token }) => { - return { - tag: css` - user-select: none; - cursor: pointer; - border-style: dashed; - background-color: inherit; - line-height: inherit; - margin-right: 0; - - display: flex; - align-items: center; - `, - icon: css` - font-size: 14px; - height: 22px; - color: ${token.colorText} !important; - `, - }; -}); +import SenderComponent from '../SenderComponent'; const AddContext = () => { - const tagRef = useRef(null); - const { attachedContexts, contextsSelectedValues } = useSnapshot( - context.state, - ); + const { attachedContexts } = useSnapshot(context.state); const [openPopup, setOpenPopup] = useState(false); + const { t } = useTranslation(); + const { defaultSuggestions, handleSearch, - getOriginalContextByValue, loading: suggestionLoading, - } = useSuggestion(contextsSelectedValues); - - const { styles } = useStyle(); + } = useSuggestion(); return ( { onSearch={(type, text) => { handleSearch(type as ContextType, text); }} - onSelect={(type, itemValue) => { + onSelect={(_type, _itemValue, contextItem) => { setOpenPopup(false); - const contextItem = getOriginalContextByValue( - type as ContextType, - itemValue, - ); - if (contextItem) { context.actions.addContext(contextItem); } }} > -
@
} />} - onClick={() => setOpenPopup(true)} - > - {attachedContexts.length === 0 && Add Context} -
+ setOpenPopup(true)}> +
@
+ {attachedContexts.length === 0 &&
{t('context.addContext')}
} +
); }; diff --git a/browser/src/components/ChatSender/LexicalTextArea/AiContextNode.ts b/browser/src/components/ChatSender/LexicalTextArea/AiContextNode.ts deleted file mode 100644 index 3b919a4f4..000000000 --- a/browser/src/components/ChatSender/LexicalTextArea/AiContextNode.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { - DecoratorNode, - type EditorConfig, - type NodeKey, - type SerializedLexicalNode, - type Spread, -} from 'lexical'; -import type { JSX } from 'react'; -import { AI_CONTEXT_NODE_CONFIGS } from '@/constants/context'; -import type { AiContextNodeConfig, AiContextNodeInfo } from '@/types/context'; - -export type SerializedAiContextNode = Spread< - { - config: { type: AiContextNodeConfig['type'] }; - info: AiContextNodeInfo; - }, - SerializedLexicalNode ->; - -export class AiContextNode extends DecoratorNode { - __config: AiContextNodeConfig; - __info: AiContextNodeInfo; - - static getType(): string { - return 'ai-context'; - } - - static clone(node: AiContextNode): AiContextNode { - return new AiContextNode(node.__config, node.__info, node.__key); - } - - constructor( - config: AiContextNodeConfig, - info: AiContextNodeInfo, - key?: NodeKey, - ) { - super(key); - - this.__config = config; - this.__info = info; - } - - static importJSON(serializedNode: SerializedAiContextNode): AiContextNode { - const { config, info } = serializedNode; - const foundConfig = AI_CONTEXT_NODE_CONFIGS.find( - (c) => c.type === config.type, - ); - if (!foundConfig) { - throw new Error( - `[AiContextNode] Can't find the config which type is ${config.type},please check AI_CONTEXT_NODE_CONFIGS`, - ); - } - return new AiContextNode(foundConfig, info); - } - - exportJSON(): SerializedAiContextNode { - return { - type: 'ai-context', - version: 1, - config: { type: this.__config.type }, - info: this.__info, - }; - } - - createDOM(_config: EditorConfig): HTMLElement { - const span = document.createElement('span'); - - return span; - } - - updateDOM(): boolean { - return false; - } - - isIsolated(): boolean { - return false; // 改为 false,允许节点被部分删除 - } - - isInline(): boolean { - return true; - } - - decorate(): JSX.Element { - const { render } = this.__config; - - const node = render({ info: this.__info }); - - return node; - } - - // 控制 getTextContent() 的返回值 - getTextContent(): string { - return this.__info.value; - } -} - -// 工厂方法,供插入指令使用 -export function $createAiContextNode( - config: AiContextNodeConfig, - info: AiContextNodeInfo, - key?: NodeKey, -): AiContextNode { - return new AiContextNode(config, info, key); -} - -// 判断节点类型 -export function $isAiContextNode(node: unknown): node is AiContextNode { - return node instanceof AiContextNode; -} diff --git a/browser/src/components/ChatSender/LexicalTextArea/DisabledPlugin.tsx b/browser/src/components/ChatSender/LexicalTextArea/DisabledPlugin.tsx deleted file mode 100644 index a62239f64..000000000 --- a/browser/src/components/ChatSender/LexicalTextArea/DisabledPlugin.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; -import { memo, useEffect } from 'react'; - -const DisabledPlugin = ({ disabled }: { disabled: boolean }) => { - const [editor] = useLexicalComposerContext(); - - useEffect(() => { - editor.setEditable(!disabled); - }, [disabled, editor]); - - return null; -}; - -export default memo(DisabledPlugin); diff --git a/browser/src/components/ChatSender/LexicalTextArea/EnterEventPlugin.tsx b/browser/src/components/ChatSender/LexicalTextArea/EnterEventPlugin.tsx deleted file mode 100644 index 4d5012c35..000000000 --- a/browser/src/components/ChatSender/LexicalTextArea/EnterEventPlugin.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; -import { - COMMAND_PRIORITY_CRITICAL, - INSERT_PARAGRAPH_COMMAND, - KEY_ENTER_COMMAND, -} from 'lexical'; -import { memo, useEffect } from 'react'; - -interface Props { - onEnterPress?: (e: KeyboardEvent) => void; -} - -const EnterEventPlugin = (props: Props) => { - const { onEnterPress } = props; - const [editor] = useLexicalComposerContext(); - - useEffect(() => { - const removeEnterListener = editor.registerCommand( - KEY_ENTER_COMMAND, - (event) => { - const isShift = event?.shiftKey; - const isMeta = event?.metaKey; // Mac Command - const isCtrl = event?.ctrlKey; // Windows/Linux Ctrl - - if (isShift || isMeta || isCtrl) { - return false; - } - - event?.preventDefault(); - onEnterPress?.(event as KeyboardEvent); - return false; - }, - COMMAND_PRIORITY_CRITICAL, - ); - - const removeInsertParagraphListener = editor.registerCommand( - INSERT_PARAGRAPH_COMMAND, - () => { - return true; - }, - COMMAND_PRIORITY_CRITICAL, - ); - - return () => { - removeEnterListener(); - removeInsertParagraphListener(); - }; - }, [editor, onEnterPress]); - - return null; -}; - -export default memo(EnterEventPlugin); diff --git a/browser/src/components/ChatSender/LexicalTextArea/PastePlugin.tsx b/browser/src/components/ChatSender/LexicalTextArea/PastePlugin.tsx deleted file mode 100644 index e41ff3751..000000000 --- a/browser/src/components/ChatSender/LexicalTextArea/PastePlugin.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; -import { message } from 'antd'; -import { PASTE_COMMAND } from 'lexical'; -import { memo, useCallback, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { ContextType } from '@/constants/context'; -import * as context from '@/state/context'; -import { guessImageMime, imageUrlToBase64 } from '@/utils/context'; - -const PastePlugin = ({ - onPastingImage, -}: { - onPastingImage?: (loading: boolean) => void; -}) => { - const [editor] = useLexicalComposerContext(); - - const [messageInstance, messageContextHolder] = message.useMessage(); - - const { t } = useTranslation(); - - const handleImage = useCallback( - (item: DataTransferItem) => { - const blob = item.getAsFile(); - if (blob) { - const reader = new FileReader(); - reader.onload = (e) => { - const base64String = e.target?.result?.toString(); - if (!base64String) { - return; - } - context.actions.addContext({ - type: ContextType.IMAGE, - value: `@Image:[${Date.now()}]`, - displayText: blob.name, - context: { - src: base64String, - mime: blob.type, - }, - }); - }; - reader.readAsDataURL(blob); - } else { - messageInstance.error('Load image failed'); - } - return true; - }, - [messageInstance], - ); - - const handleHtml = useCallback( - (item: DataTransferItem) => { - item.getAsString((html) => { - if (!html) { - messageInstance.error('Load html failed'); - } else { - const parser = new DOMParser(); - const doc = parser.parseFromString(html, 'text/html'); - const imgs = doc.querySelectorAll('img'); - - if (imgs.length > 0) { - onPastingImage?.(true); - - Promise.all( - Array.from(imgs).map((img) => { - return new Promise((resolve) => { - const src = img.getAttribute('src'); - - if (src) { - const mime = guessImageMime(src); - - imageUrlToBase64(src).then((base64) => { - context.actions.addContext({ - type: ContextType.IMAGE, - value: `@Image:[${Date.now()}]`, - displayText: src.split('/').pop() || src, - context: { - src: base64, - mime, - }, - }); - - resolve(); - }); - } else { - resolve(); - } - }); - }), - ).finally(() => { - onPastingImage?.(false); - }); - } - } - }); - - return true; - }, - [messageInstance], - ); - - useEffect(() => { - return editor.registerCommand( - PASTE_COMMAND, - (event: ClipboardEvent | undefined) => { - if (!event) return false; - - // 阻止默认粘贴行为 - event.preventDefault(); - - const clipboardItems = event.clipboardData?.items; - if (!clipboardItems || clipboardItems.length === 0) return false; - - // 只获取第一个项目 - const item = clipboardItems[0]; - // 处理图片 - if (item.type.startsWith('image/')) { - return handleImage(item); - } - // 从HTML中解析可能的img标签 - else if (item.type === 'text/html') { - return handleHtml(item); - } - // 处理文本 - else if (item.type === 'text/plain') { - return false; - } - // 其他类型 - else { - const errorMsg = t('context.unsupportedType', { type: item.type }); - messageInstance.error(errorMsg); - console.error(errorMsg); - return true; - } - }, - 1, - ); - }, [editor, messageInstance]); - - return <>{messageContextHolder}; -}; - -export default memo(PastePlugin); diff --git a/browser/src/components/ChatSender/LexicalTextArea/PlaceholderPlugin.tsx b/browser/src/components/ChatSender/LexicalTextArea/PlaceholderPlugin.tsx deleted file mode 100644 index bb6f2ff9f..000000000 --- a/browser/src/components/ChatSender/LexicalTextArea/PlaceholderPlugin.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; -import { useLexicalIsTextContentEmpty } from '@lexical/react/useLexicalIsTextContentEmpty'; -import { memo, useEffect } from 'react'; - -const PlaceholderPlugin = (props: { placeholder: string | undefined }) => { - const [editor] = useLexicalComposerContext(); - const isEmpty = useLexicalIsTextContentEmpty(editor); - - /* Set the placeholder on root. */ - useEffect(() => { - const rootElement = editor.getRootElement() as HTMLElement; - if (rootElement) { - if (isEmpty && props.placeholder) { - rootElement.setAttribute('placeholder', props.placeholder); - } else { - rootElement.removeAttribute('placeholder'); - } - } - }, [editor, isEmpty, props.placeholder]); - - return null; -}; - -export default memo(PlaceholderPlugin); diff --git a/browser/src/components/ChatSender/LexicalTextArea/RenderValuePlugin.tsx b/browser/src/components/ChatSender/LexicalTextArea/RenderValuePlugin.tsx deleted file mode 100644 index 852cce1b9..000000000 --- a/browser/src/components/ChatSender/LexicalTextArea/RenderValuePlugin.tsx +++ /dev/null @@ -1,335 +0,0 @@ -import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; -import { - $createParagraphNode, - $createTextNode, - $getRoot, - $getSelection, - $isRangeSelection, - $isTextNode, - type BaseSelection, - type LexicalNode, - TextNode, -} from 'lexical'; -import { memo, useEffect, useRef, useState } from 'react'; -import type { - AiContextCacheNode, - AiContextNodeConfig, - AppendedLexicalNode, -} from '@/types/context'; -import { getTextDiff } from '@/utils/chat'; -import { $createAiContextNode } from './AiContextNode'; - -interface Props { - value?: string; - aiContextNodeConfigs: AiContextNodeConfig[]; - onChange?: (markedText: string, plainText: string) => void; - onChangeNodes?: ( - prevNodes: AiContextCacheNode[], - nextNodes: AiContextCacheNode[], - ) => void; -} - -const RenderValuePlugin = (props: Props) => { - const { value = '', onChange, onChangeNodes, aiContextNodeConfigs } = props; - const [editor] = useLexicalComposerContext(); - - const oldMarkedTextRef = useRef(''); - const oldNodesRef = useRef([]); - const oldLexicalNodesRef = useRef([]); - const oldSelectionRef = useRef(null); - const [innerValue, setInnerValue] = useState(value); - const [isComposing, setIsComposing] = useState(false); - - const SearchRegex = new RegExp( - aiContextNodeConfigs - .map((config, index) => `(?${config.matchRegex.source})`) - .join('|'), - 'g', - ); - - const isNodeEqual = ( - node1: AiContextCacheNode, - node2: AiContextCacheNode, - ) => { - return ( - node1.type === node2.type && - node1.originalText === node2.originalText && - node1.displayText === node2.displayText - ); - }; - - const areNodesEqual = ( - nodes1: AiContextCacheNode[], - nodes2: AiContextCacheNode[], - ) => { - if (nodes1.length !== nodes2.length) return false; - return nodes1.every((node1, index) => { - const node2 = nodes2[index]; - return isNodeEqual(node1, node2); - }); - }; - - const parseContent = (content: string) => { - const nodes: AiContextCacheNode[] = []; - const paragraph = $createParagraphNode(); - - const Regex = new RegExp(SearchRegex); - - let lastIndex = 0; - let plainText = ''; - let match: RegExpExecArray | null; - let targetFunction: (() => void) | null = null; - - // 用于临时存储所有节点的数组(包括文本节点和AiContextNode节点) - const allNodes: LexicalNode[] = []; - - while ((match = Regex.exec(content)) !== null) { - if (match.index > lastIndex) { - const text = content.slice(lastIndex, match.index); - const textNode = $createTextNode(text); - plainText += text; - paragraph.append(textNode); - allNodes.push(textNode); - } - - const matchValue = match.groups?.value; - const originalConfigIndex = - match.findIndex((item) => item === matchValue) / 2 - 1; - - const originalConfig = aiContextNodeConfigs[originalConfigIndex]; - - if (originalConfig) { - const info = originalConfig.pickInfo?.(match); - - if (!info) { - console.warn( - `[LexicalTextArea] 无法从match中提取信息,请检查matchRegex和pickInfo是否正确。`, - ); - continue; - } - - const mentionNode = $createAiContextNode(originalConfig, info); - - paragraph.append(mentionNode); - allNodes.push(mentionNode); - nodes.push({ - type: originalConfig.type, - originalText: info.value, - displayText: info.displayText, - lexicalNode: mentionNode, - }); - plainText += info.displayText; - } - lastIndex = Regex.lastIndex; - } - if (lastIndex < content.length) { - const text = content.slice(lastIndex); - const textNode = $createTextNode(text); - plainText += text; - paragraph.append(textNode); - allNodes.push(textNode); - } - - const prevNodes = oldNodesRef.current; - const isAddingMentionNode = nodes.length > prevNodes.length; - const isRemovingMentionNode = nodes.length < prevNodes.length; - - // 处理光标位置 - targetFunction = () => { - if (isAddingMentionNode) { - // 找到新插入的mention节点 - let insertedNodeIndex = -1; - - for (let i = 0; i < nodes.length; i++) { - if (i >= prevNodes.length || !isNodeEqual(nodes[i], prevNodes[i])) { - insertedNodeIndex = i; - break; - } - } - - if (insertedNodeIndex !== -1) { - const insertedNode = nodes[insertedNodeIndex].lexicalNode; - insertedNode.selectEnd(); - } - } else if (isRemovingMentionNode) { - // 删除情况 - // 找到被删除节点的位置 - let deletedIndex = 0; - for (let i = 0; i < prevNodes.length; i++) { - if (i >= nodes.length || !isNodeEqual(nodes[i], prevNodes[i])) { - deletedIndex = i; - break; - } - } - - // 在变更前的状态中找到被删除节点前后的节点 - const oldNodes = oldLexicalNodesRef.current; - let mentionCount = 0; - let targetIndex = -1; - - // 遍历找到被删除的mention节点的位置 - for (let i = 0; i < oldNodes.length; i++) { - const node = oldNodes[i]; - if (node.type !== 'text') { - if (mentionCount === deletedIndex) { - targetIndex = i; - break; - } - mentionCount++; - } - } - - if (targetIndex !== -1) { - const prevNode = oldNodes[targetIndex - 1]; - const nextNode = oldNodes[targetIndex + 1]; - - if (prevNode?.type === 'text' && nextNode?.type === 'text') { - // 如果前后都是文本节点,记录前一个文本节点的长度 - const prevTextLength = prevNode.length; - - // 获取合并后的文本节点(它将位于删除位置) - const mergedTextNode = paragraph.getChildren()[targetIndex - 1]; - if (mergedTextNode && mergedTextNode.getType() === 'text') { - (mergedTextNode as TextNode).select( - prevTextLength, - prevTextLength, - ); - } - } else if (nextNode) { - // 否则选中当前位置的开始 - const correspondingNode = paragraph.getChildren()[targetIndex]; - if (correspondingNode) { - correspondingNode.selectStart(); - } - } else if (prevNode) { - // 如果是最后一个节点被删除,选中前一个节点的末尾 - const correspondingNode = paragraph.getChildren()[targetIndex - 1]; - if (correspondingNode) { - correspondingNode.selectEnd(); - } - } - } - } else { - if ($isRangeSelection(oldSelectionRef.current)) { - const uniqueTextNode = paragraph.getLastChild(); - - if ($isTextNode(uniqueTextNode)) { - const content = uniqueTextNode.getTextContent(); - - // input break line - const inputDiff = getTextDiff(oldMarkedTextRef.current, content); - const inputBreakLine = - inputDiff.length > 0 && - inputDiff.every( - (diff) => diff.type === '+' && diff.content.includes('\n'), - ); - - if (inputBreakLine) { - const lastDiff = inputDiff[inputDiff.length - 1]; - const lastPos = lastDiff.index + lastDiff.content.length; - uniqueTextNode.select(lastPos, lastPos); - } else { - uniqueTextNode.select( - oldSelectionRef.current.focus.offset, - oldSelectionRef.current.focus.offset, - ); - } - } - } - } - }; - - return { nodes, paragraph, plainText, resetSelection: targetFunction }; - }; - - useEffect(() => { - if (innerValue !== oldMarkedTextRef.current) { - editor.update(() => { - const root = $getRoot(); - const { nodes, paragraph, plainText, resetSelection } = - parseContent(innerValue); - - const withoutSpecialNode = - nodes.length === 0 && oldNodesRef.current.length === 0; - - const shouldRebuild = - withoutSpecialNode || !areNodesEqual(nodes, oldNodesRef.current); - - if (shouldRebuild) { - root.clear(); - root.append(paragraph); - - resetSelection?.(); - - if (!withoutSpecialNode) { - onChangeNodes?.(oldNodesRef.current, nodes); - oldNodesRef.current = nodes; - oldLexicalNodesRef.current = paragraph - .getChildren() - .map((lexicalNode) => { - return { - lexicalNode, - type: lexicalNode.getType(), - length: lexicalNode.getTextContentSize(), - }; - }); - } - } - - onChange?.(innerValue, plainText); - oldMarkedTextRef.current = innerValue; - }); - } - }, [innerValue]); - - useEffect(() => { - if (value !== innerValue) { - setInnerValue(value); - } - }, [value]); - - useEffect(() => { - const dom = editor.getRootElement?.(); - if (!dom) return; - - const handleCompositionStart = () => { - setIsComposing(true); - }; - const handleCompositionEnd = () => { - setIsComposing(false); - // composition 结束时手动同步内容 - editor.update(() => { - const root = $getRoot(); - const markedText = root.getTextContent().trimEnd(); - oldSelectionRef.current = $getSelection(); - setInnerValue(markedText); - }); - }; - - dom.addEventListener('compositionstart', handleCompositionStart); - dom.addEventListener('compositionend', handleCompositionEnd); - - return () => { - dom.removeEventListener('compositionstart', handleCompositionStart); - dom.removeEventListener('compositionend', handleCompositionEnd); - }; - }, [editor]); - - useEffect(() => { - return editor.registerUpdateListener(({ editorState }) => { - editorState.read(() => { - if (isComposing) return; - const root = $getRoot(); - const markedText = root.getTextContent().trimEnd(); - - oldSelectionRef.current = $getSelection(); - - setInnerValue(markedText); - }); - }); - }, [editor, isComposing]); - - return null; -}; - -export default memo(RenderValuePlugin); diff --git a/browser/src/components/ChatSender/LexicalTextArea/index.tsx b/browser/src/components/ChatSender/LexicalTextArea/index.tsx deleted file mode 100644 index c2e9e470f..000000000 --- a/browser/src/components/ChatSender/LexicalTextArea/index.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin'; -import { LexicalComposer } from '@lexical/react/LexicalComposer'; -import { ContentEditable } from '@lexical/react/LexicalContentEditable'; -import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary'; -import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'; -import type { GetProps, GetRef, Input } from 'antd'; -import { createStyles } from 'antd-style'; -import classNames from 'classnames'; -import { $createParagraphNode, $getRoot, type LexicalEditor } from 'lexical'; -import React, { - type KeyboardEvent, - forwardRef, - memo, - useContext, - useImperativeHandle, - useRef, -} from 'react'; -import { LexicalTextAreaContext } from '../LexicalTextAreaContext'; -import { AiContextNode } from './AiContextNode'; -import DisabledPlugin from './DisabledPlugin'; -import EnterEventPlugin from './EnterEventPlugin'; -import PastePlugin from './PastePlugin'; -import PlaceholderPlugin from './PlaceholderPlugin'; -import RenderValuePlugin from './RenderValuePlugin'; - -type Props = GetProps; - -type Ref = GetRef; - -const useStyle = createStyles(({ css }) => { - return { - textAreaEditable: css` - flex: 1; - - &:focus-visible { - outline: none; - } - - p { - margin: 6px 0; - line-height: 22px; - } - - &:not(:focus):before { - position: absolute; - opacity: 0.5; - content: attr(placeholder); - margin: 6px 0; - line-height: 22px; - } - `, - }; -}); - -const LexicalTextArea = forwardRef((props, ref) => { - const { - placeholder = '', - onKeyDown, - onSelect, - disabled, - className, - style, - } = props; - - const { - onEnterPress, - namespace, - value, - onChange, - onChangeNodes, - onPastingImage, - aiContextNodeConfigs, - } = useContext(LexicalTextAreaContext); - - const editorRef = useRef(null); - const contentEditableRef = useRef(null); - - const { styles } = useStyle(); - - useImperativeHandle(ref, () => ({ - focus: () => { - contentEditableRef.current?.focus(); - editorRef.current?.focus(); - }, - blur: () => { - contentEditableRef.current?.blur(); - editorRef.current?.blur(); - }, - get input() { - return contentEditableRef.current; - }, - })); - - const handleKeyDown = (e: KeyboardEvent) => { - onKeyDown?.(e as unknown as React.KeyboardEvent); - }; - - const handleSelect = (e: React.SyntheticEvent) => { - if (onSelect) { - const selection = window.getSelection(); - if (selection && selection.rangeCount > 0) { - onSelect(e as unknown as React.SyntheticEvent); - } - } - }; - - return ( - console.error(error), - nodes: [AiContextNode], - editorState(editor) { - editorRef.current = editor; - editor.update(() => { - $getRoot().append($createParagraphNode()); - }); - }, - }} - > - { - e.stopPropagation(); - }} - onMouseDown={(e) => { - e.stopPropagation(); - }} - onMouseUp={(e) => { - e.stopPropagation(); - }} - className={classNames(styles.textAreaEditable, className)} - /> - } - ErrorBoundary={LexicalErrorBoundary} - /> - - {!disabled && } - - - {!disabled && } - {!disabled && } - { - onChange?.(markedText, plainText); - }} - onChangeNodes={onChangeNodes} - aiContextNodeConfigs={aiContextNodeConfigs} - /> - - ); -}); - -export default memo(LexicalTextArea); diff --git a/browser/src/components/ChatSender/LexicalTextAreaContext/index.tsx b/browser/src/components/ChatSender/LexicalTextAreaContext/index.tsx deleted file mode 100644 index e83cf614c..000000000 --- a/browser/src/components/ChatSender/LexicalTextAreaContext/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { createContext } from 'react'; -import type { AiContextCacheNode, AiContextNodeConfig } from '@/types/context'; - -/** Inject Editor Contexts into LexicalTextArea */ -export const LexicalTextAreaContext = createContext<{ - onEnterPress?: (e: KeyboardEvent) => void; - onChangeNodes?: ( - prevNodes: AiContextCacheNode[], - nextNodes: AiContextCacheNode[], - ) => void; - value?: string; - onChange?: (markedText: string, plainText: string) => void; - onPastingImage?: (loading: boolean) => void; - aiContextNodeConfigs: AiContextNodeConfig[]; - namespace: string; -}>({ aiContextNodeConfigs: [], namespace: '' }); diff --git a/browser/src/components/ChatSender/SenderAttachments/index.tsx b/browser/src/components/ChatSender/SenderAttachments/index.tsx index 199adbddc..1a356c047 100644 --- a/browser/src/components/ChatSender/SenderAttachments/index.tsx +++ b/browser/src/components/ChatSender/SenderAttachments/index.tsx @@ -72,28 +72,17 @@ const SenderAttachments = () => { }), ); + // TODO: do not support file besides image return false; }; return ( <> type.extName).join( ',', )} beforeUpload={handleBeforeUpload} - onChange={({ file }) => { - // TODO server is not ready, so the file status won't be [done] - if (file.status === 'done') { - context.actions.addContext({ - value: file.uid, - displayText: file.name, - type: ContextType.ATTACHMENT, - context: file, - }); - } - }} getDropContainer={() => document.body} placeholder={{ icon: , diff --git a/browser/src/components/ChatSender/SenderComponent/ContextTag.tsx b/browser/src/components/ChatSender/SenderComponent/ContextTag.tsx new file mode 100644 index 000000000..986d85ce7 --- /dev/null +++ b/browser/src/components/ChatSender/SenderComponent/ContextTag.tsx @@ -0,0 +1,103 @@ +import Icon, { AppstoreOutlined, CloseCircleFilled } from '@ant-design/icons'; +import { Popover } from 'antd'; +import { cx } from 'antd-style'; +import { useMemo, useState } from 'react'; +import type { FileItem, ImageItem } from '@/api/model'; +import DevFileIcon from '@/components/DevFileIcon'; +import { ContextType } from '@/constants/context'; +import type { ContextStoreValue } from '@/types/context'; + +interface Props { + /** Whether it can be closed */ + closeable?: boolean; + /** Close callback */ + onClose?: (val: string) => void; + /** Click callback */ + onClick?: (val: string) => void; + /** Tag content */ + label: string; + /** Tag value, must be unique */ + value: string; + + context?: ContextStoreValue; + + contextType?: ContextType; +} + +export const SenderContextTag = (props: Props) => { + const { closeable, onClose, onClick, label, value, context, contextType } = + props; + + const [hover, setHover] = useState(false); + + const icon = useMemo(() => { + if (!context || !contextType) { + return null; + } + switch (contextType) { + case ContextType.FILE: + const fileExt = (context as FileItem).name.split('.').pop() ?? ''; + const isFolder = (context as FileItem).type === 'directory'; + return ; + case ContextType.SLASH_COMMAND: + return ; + case ContextType.IMAGE: + const imageSrc = (context as ImageItem).src; + return ( + + ); + default: + return null; + } + }, [context, contextType]); + + const popoverContent = useMemo(() => { + switch (contextType) { + case ContextType.IMAGE: + const imageSrc = (context as ImageItem).src; + return ( + + ); + case ContextType.FILE: + return (context as FileItem).path; + default: + return null; + } + }, [contextType, context]); + + return ( +
setHover(true)} + onMouseLeave={() => setHover(false)} + onClick={() => onClick?.(value)} + > + {closeable && hover && ( +
{ + onClose?.(value); + }} + > + } /> +
+ )} + +
+
{icon}
+
{label}
+
+
+
+ ); +}; diff --git a/browser/src/components/ChatSender/SenderComponent/SenderButton.tsx b/browser/src/components/ChatSender/SenderComponent/SenderButton.tsx new file mode 100644 index 000000000..13121dcfa --- /dev/null +++ b/browser/src/components/ChatSender/SenderComponent/SenderButton.tsx @@ -0,0 +1,24 @@ +import { cx } from 'antd-style'; +import type { ButtonHTMLAttributes, DetailedHTMLProps } from 'react'; + +export const SenderButton = ( + props: DetailedHTMLProps< + ButtonHTMLAttributes, + HTMLButtonElement + >, +) => { + const { className, ...rest } = props; + + return ( +
- + /> */} ); }; diff --git a/browser/src/components/McpManager/McpAddForm/McpAddMode.tsx b/browser/src/components/McpManager/McpAddForm/McpAddMode.tsx new file mode 100644 index 000000000..b8d95fe09 --- /dev/null +++ b/browser/src/components/McpManager/McpAddForm/McpAddMode.tsx @@ -0,0 +1,60 @@ +import { PlusOutlined } from '@ant-design/icons'; +import { Button } from 'antd'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import type { McpConfigItem as McpConfigItemType } from '@/types/mcp'; +import { McpConfigItem } from './McpConfigItem'; +import styles from './index.module.css'; + +interface McpAddModeProps { + configs: McpConfigItemType[]; + onUpdateConfig: ( + id: string, + field: keyof McpConfigItemType, + value: string | 'global' | 'project' | 'json' | 'form', + ) => void; + onRemoveConfig: (id: string) => void; + onAddNewConfig: () => void; +} + +/** + * Add mode component for adding multiple MCP server configurations + */ +export const McpAddMode: React.FC = ({ + configs, + onUpdateConfig, + onRemoveConfig, + onAddNewConfig, +}) => { + const { t } = useTranslation(); + + return ( + <> + {/* Multiple configuration items */} +
+ {configs.map((config, index) => ( + 1} + onUpdateConfig={onUpdateConfig} + onRemoveConfig={onRemoveConfig} + /> + ))} +
+ + {/* Continue add button */} +
+ +
+ + ); +}; diff --git a/browser/src/components/McpManager/McpAddForm/McpConfigItem.tsx b/browser/src/components/McpManager/McpAddForm/McpConfigItem.tsx new file mode 100644 index 000000000..e006be8e3 --- /dev/null +++ b/browser/src/components/McpManager/McpAddForm/McpConfigItem.tsx @@ -0,0 +1,248 @@ +import { DeleteOutlined } from '@ant-design/icons'; +import { Input, Radio, Select, Typography } from 'antd'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import MessageWrapper from '@/components/MessageWrapper'; +import type { McpConfigItem as McpConfigItemType } from '@/types/mcp'; +import { modalEventHandlers } from '@/utils/eventUtils'; +import { McpJsonEditor } from './McpJsonEditor'; +import { McpScopeSelector } from './McpScopeSelector'; +import styles from './index.module.css'; + +const { Text } = Typography; + +interface McpConfigItemProps { + config: McpConfigItemType; + index: number; + canDelete: boolean; + onUpdateConfig: ( + id: string, + field: keyof McpConfigItemType, + value: string | 'global' | 'project' | 'json' | 'form', + ) => void; + onRemoveConfig: (id: string) => void; +} + +/** + * Single MCP configuration item component for add mode + */ +export const McpConfigItem: React.FC = ({ + config, + index, + canDelete, + onUpdateConfig, + onRemoveConfig, +}) => { + const { t } = useTranslation(); + + return ( + , + onClick: () => onRemoveConfig(config.id), + }, + ] + : [] + } + > +
+ {/* Scope and input mode settings */} +
+ onUpdateConfig(config.id, 'scope', scope)} + /> +
+
+ {t('mcp.inputMode')} +
+ + onUpdateConfig( + config.id, + 'inputMode', + e.target.value as 'json' | 'form', + ) + } + className={styles.radioGroup} + > + + JSON + + + {t('mcp.form')} + + +
+
+ + {/* Configuration content based on input mode */} +
+ {config.inputMode === 'json' ? ( + + ) : ( + + )} +
+
+
+ ); +}; + +// JSON configuration content +const McpJsonConfigContent: React.FC<{ + config: McpConfigItemType; + onUpdateConfig: ( + id: string, + field: keyof McpConfigItemType, + value: string, + ) => void; +}> = ({ config, onUpdateConfig }) => { + const { t } = useTranslation(); + + return ( +
+
+ {t('mcp.configuration')} +
+ onUpdateConfig(config.id, 'jsonConfig', value)} + height="200px" + /> +
+ ); +}; + +// Form configuration content +const McpFormConfigContent: React.FC<{ + config: McpConfigItemType; + onUpdateConfig: ( + id: string, + field: keyof McpConfigItemType, + value: string, + ) => void; +}> = ({ config, onUpdateConfig }) => { + const { t } = useTranslation(); + + return ( +
+ {/* Server name and transport type */} +
+
+
+ {t('mcp.serverName')} + * +
+ onUpdateConfig(config.id, 'name', e.target.value)} + {...modalEventHandlers} + /> +
+
+
+ + {t('mcp.transportType')} + +
+ +
+
+ + {/* Command and arguments / URL */} +
+ {config.transport === 'sse' ? ( +
+
+ {t('mcp.url')} + * +
+ onUpdateConfig(config.id, 'url', e.target.value)} + {...modalEventHandlers} + /> +
+ ) : ( + <> +
+
+ {t('mcp.command')} + * +
+ + onUpdateConfig(config.id, 'command', e.target.value) + } + {...modalEventHandlers} + /> +
+
+
+ + {t('mcp.arguments')} + +
+ + onUpdateConfig(config.id, 'args', e.target.value) + } + {...modalEventHandlers} + /> +
+ + )} +
+ + {/* Environment variables */} +
+
+
+ + {t('mcp.environmentVariables')} + +
+ onUpdateConfig(config.id, 'env', value)} + height="120px" + /> +
+
+
+ ); +}; diff --git a/browser/src/components/McpManager/McpAddForm/McpEditMode.tsx b/browser/src/components/McpManager/McpAddForm/McpEditMode.tsx new file mode 100644 index 000000000..5f8ade6b7 --- /dev/null +++ b/browser/src/components/McpManager/McpAddForm/McpEditMode.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import type { McpAddFormProps } from '@/types/mcp'; +import { McpFormFields } from './McpFormFields'; +import { McpScopeSelector } from './McpScopeSelector'; +import styles from './index.module.css'; + +interface McpEditModeProps { + addScope: McpAddFormProps['addScope']; + onScopeChange: McpAddFormProps['onScopeChange']; +} + +/** + * Edit mode component for editing existing MCP server configuration + */ +export const McpEditMode: React.FC = ({ + addScope, + onScopeChange, +}) => { + return ( + <> + {/* Scope selector for edit mode */} +
+ +
+ + {/* Form fields for editing */} + + + ); +}; diff --git a/browser/src/components/McpManager/McpAddForm/McpFormFields.tsx b/browser/src/components/McpManager/McpAddForm/McpFormFields.tsx new file mode 100644 index 000000000..1922e43e1 --- /dev/null +++ b/browser/src/components/McpManager/McpAddForm/McpFormFields.tsx @@ -0,0 +1,150 @@ +import { Form, Input, Select } from 'antd'; +import { Typography } from 'antd'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { MCP_DEFAULTS } from '@/constants/mcp'; +import { modalEventHandlers } from '@/utils/eventUtils'; +import { McpJsonEditor } from './McpJsonEditor'; +import styles from './index.module.css'; + +const { Text } = Typography; + +/** + * Form fields component for edit mode (single server configuration) + */ +export const McpFormFields: React.FC = () => { + const { t } = useTranslation(); + + return ( +
+ {/* First row: Server name and transport type */} +
+
+
+ {t('mcp.serverName')} + * +
+ + + +
+
+
+ + {t('mcp.transportType')} + +
+ + + +
+
+ + {/* Second row: Command and arguments */} +
+ prev.transport !== curr.transport} + > + {({ getFieldValue }) => { + return getFieldValue('transport') === 'sse' ? ( +
+
+ {t('mcp.url')} + * +
+ + + +
+ ) : ( + <> +
+
+ + {t('mcp.command')} + + * +
+ + + +
+
+
+ + {t('mcp.arguments')} + +
+ + + +
+ + ); + }} +
+
+ + {/* Third row: Environment variables */} +
+
+
+ + {t('mcp.environmentVariables')} + +
+ + + +
+
+
+ ); +}; diff --git a/browser/src/components/McpManager/McpAddForm/McpJsonEditor.tsx b/browser/src/components/McpManager/McpAddForm/McpJsonEditor.tsx new file mode 100644 index 000000000..7048823b1 --- /dev/null +++ b/browser/src/components/McpManager/McpAddForm/McpJsonEditor.tsx @@ -0,0 +1,97 @@ +import { Editor } from '@monaco-editor/react'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import type { McpJsonEditorProps } from '@/types/mcp'; +import styles from './index.module.css'; + +export const McpJsonEditor: React.FC = ({ + value = '', + onChange, + height = '200px', + disabled = false, +}) => { + const { t } = useTranslation(); + const [isValid, setIsValid] = useState(true); + const [errorMessage, setErrorMessage] = useState(''); + + const handleChange = (val: string | undefined) => { + const newValue = val || ''; + + // Validate JSON format + try { + if (newValue.trim()) { + JSON.parse(newValue); + } + setIsValid(true); + setErrorMessage(''); + } catch (error) { + setIsValid(false); + setErrorMessage( + error instanceof Error + ? error.message + : t('mcp.jsonFormatErrorMessage'), + ); + } + + onChange?.(newValue); + }; + + const handleEditorMount = (editor: any) => { + // Set default empty object + if (!value.trim() && !disabled) { + const emptyJson = {}; + const jsonString = JSON.stringify(emptyJson, null, 2); + editor.setValue(jsonString); + handleChange(jsonString); + } + }; + + return ( +
+ + {!isValid && ( +
+ ⚠️ + {errorMessage} +
+ )} +
+ ); +}; diff --git a/browser/src/components/McpManager/McpAddForm/McpScopeSelector.tsx b/browser/src/components/McpManager/McpAddForm/McpScopeSelector.tsx new file mode 100644 index 000000000..de2632700 --- /dev/null +++ b/browser/src/components/McpManager/McpAddForm/McpScopeSelector.tsx @@ -0,0 +1,45 @@ +import { QuestionCircleOutlined } from '@ant-design/icons'; +import { Radio, Tooltip, Typography } from 'antd'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import styles from './index.module.css'; + +const { Text } = Typography; + +interface McpScopeSelectorProps { + value: 'global' | 'project'; + onChange: (scope: 'global' | 'project') => void; +} + +/** + * Scope selector component for choosing between global and project scope + */ +export const McpScopeSelector: React.FC = ({ + value, + onChange, +}) => { + const { t } = useTranslation(); + + return ( +
+
+ {t('mcp.scope')} + + + +
+ onChange(e.target.value as 'global' | 'project')} + className={styles.radioGroup} + > + + {t('mcp.project')} + + + {t('mcp.global')} + + +
+ ); +}; diff --git a/browser/src/components/McpManager/McpAddForm/index.module.css b/browser/src/components/McpManager/McpAddForm/index.module.css new file mode 100644 index 000000000..366a17337 --- /dev/null +++ b/browser/src/components/McpManager/McpAddForm/index.module.css @@ -0,0 +1,538 @@ +/* McpAddForm Component Styles */ + +/* Add Form Modal Styles - Based on Figma Design */ +.addFormModal :global(.ant-modal-content) { + background: #ffffff; + border-radius: 20px; + box-shadow: 0 0 43.3px 13px rgba(100, 99, 119, 0.12); + border: none; + overflow: hidden; + width: 640px !important; + max-width: 640px !important; + height: 635px !important; + padding: 0; +} + +.addFormModal :global(.ant-modal-header) { + background: #ffffff; + border-bottom: none; + padding: 24px 24px 0 24px; + margin: 0; +} + +.addFormModal :global(.ant-modal-body) { + padding: 0; + background: #ffffff; + height: 535px; + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: #e0e0e0 transparent; +} + +.addFormModal :global(.ant-modal-body)::-webkit-scrollbar { + width: 6px; +} + +.addFormModal :global(.ant-modal-body)::-webkit-scrollbar-track { + background: transparent; +} + +.addFormModal :global(.ant-modal-body)::-webkit-scrollbar-thumb { + background-color: #e0e0e0; + border-radius: 3px; + transition: background-color 0.2s; +} + +.addFormModal :global(.ant-modal-body)::-webkit-scrollbar-thumb:hover { + background-color: #c0c0c0; +} + +.addFormModal :global(.ant-modal-close) { + top: 24px; + right: 24px; + width: 16px; + height: 16px; + color: #666f8d; + font-size: 16px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; +} + +.addFormModal :global(.ant-modal-close:hover) { + color: #374151; +} + +.addFormModal :global(.ant-modal-footer) { + border-top: none; + padding: 0 24px 24px 24px; + text-align: right; + margin: 0; +} + +/* Modal Header */ +.modalHeader { + display: flex; + align-items: center; + gap: 2px; +} + +.headerTitle { + color: #110c22; + font-size: 16px; + font-weight: 500; + line-height: 22px; + font-family: "PingFang SC", sans-serif; +} + +/* Modal Body */ +.modalBody { + padding: 24px; + height: calc(100% - 48px); + overflow-y: auto; + display: flex; + flex-direction: column; + scrollbar-width: thin; + scrollbar-color: #e0e0e0 transparent; +} + +.modalBody::-webkit-scrollbar { + width: 6px; +} + +.modalBody::-webkit-scrollbar-track { + background: transparent; +} + +.modalBody::-webkit-scrollbar-thumb { + background-color: #e0e0e0; + border-radius: 3px; + transition: background-color 0.2s; +} + +.modalBody::-webkit-scrollbar-thumb:hover { + background-color: #c0c0c0; +} + +.form { + width: 100%; + flex: 1; + display: flex; + flex-direction: column; +} + +/* Settings Row - 设定描述和输入模式 */ +.settingsRow { + display: flex; + gap: 48px; + margin-bottom: 24px; +} + +.settingGroup { + flex: 1; +} + +.settingHeader { + display: flex; + align-items: center; + gap: 2px; + margin-bottom: 8px; +} + +.settingLabel { + color: #110c22; + font-size: 14px; + font-weight: 500; + font-family: "PingFang SC", sans-serif; +} + +.questionIcon { + width: 16px; + height: 16px; + color: #85878a; +} + +.radioGroup { + display: flex; + gap: 16px; +} + +.radioOption { + color: #110c22; + font-size: 14px; + font-weight: 400; + font-family: "PingFang SC", sans-serif; +} + +/* Radio button styling */ +.addFormModal :global(.ant-radio-wrapper) { + color: #110c22; + font-size: 14px; + font-weight: 400; + font-family: "PingFang SC", sans-serif; +} + +.addFormModal :global(.ant-radio-inner) { + border-color: #dcdde0; + background-color: #ffffff; +} + +.addFormModal :global(.ant-radio-checked .ant-radio-inner) { + border-color: #7357ff; + background-color: #ffffff; +} + +.addFormModal :global(.ant-radio-checked .ant-radio-inner::after) { + background-color: #7357ff; +} + +/* Config Section */ +.configSection { + margin-bottom: 24px; +} + +.jsonFormContainer { + width: 100%; +} + +.jsonFormHeader { + margin-bottom: 8px; +} + +.jsonTextArea { + background: #ffffff; + border: 1px solid #dcdde0; + border-radius: 4px; + font-size: 14px; + font-family: "PingFang SC", sans-serif; +} + +.jsonTextArea:focus, +.jsonTextArea:hover { + border-color: #7357ff; + box-shadow: none; +} + +.jsonDescription { + color: #898b8f; + font-size: 12px; + font-weight: 400; + margin-top: 8px; + opacity: 0.8; + font-family: "PingFang SC", sans-serif; +} + +/* MCP Preview Section */ +.mcpPreviewSection { + background: #f9fbfe; + border: 1px solid #edeef0; + border-radius: 8px; + margin-bottom: 16px; +} + +.mcpPreviewHeader { + background: #f9fbfe; + padding: 12px 16px; + display: flex; + align-items: center; + justify-content: space-between; + border-radius: 8px 8px 0 0; +} + +.mcpLabel { + color: #252931; + font-size: 12px; + font-weight: 500; + font-family: "PingFang SC", sans-serif; +} + +.collapseIcon { + width: 16px; + height: 16px; + color: #12141a; +} + +/* Continue Section */ +.continueSection { + display: flex; + justify-content: flex-start; + margin-top: 16px; +} + +.continueButton { + height: 32px; + padding: 6px 16px; + background: #ffffff; + border: 1px solid #7357ff; + border-radius: 4px; + color: #7357ff; + font-size: 14px; + font-weight: 400; + display: flex; + align-items: center; + gap: 2px; + font-family: "PingFang SC", sans-serif; +} + +.continueButton:hover { + background: #f0f7ff; + border-color: #7357ff; + color: #7357ff; +} + +.continueButton:focus { + background: #ffffff; + border-color: #7357ff; + color: #7357ff; + box-shadow: none; +} + +/* Footer Buttons */ +.cancelButton { + height: 32px; + padding: 6px 16px; + background: #ffffff; + border: 1px solid #dcdde0; + border-radius: 26px; + color: #110c22; + font-size: 14px; + font-weight: 400; + font-family: "PingFang SC", sans-serif; +} + +.cancelButton:hover { + background: #f5f5f5; + border-color: #dcdde0; + color: #110c22; +} + +.cancelButton:focus { + background: #ffffff; + border-color: #dcdde0; + color: #110c22; + box-shadow: none; +} + +.confirmButton { + height: 32px; + padding: 6px 16px; + background: #110c22; + border: 1px solid #110c22; + border-radius: 26px; + color: #ffffff; + font-size: 14px; + font-weight: 400; + font-family: "PingFang SC", sans-serif; +} + +.confirmButton:hover { + background: #110c22 !important; + border-color: #110c22 !important; + color: #ffffff !important; +} + +.confirmButton:focus { + background: #110c22 !important; + border-color: #110c22 !important; + color: #ffffff !important; + box-shadow: none !important; +} + +.confirmButton:active { + background: #110c22 !important; + border-color: #110c22 !important; + color: #ffffff !important; +} + +/* Form Fields Styles - Based on Figma Design */ +.formFieldsContainer { + width: 100%; + flex: 1; + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: #e0e0e0 transparent; +} + +.formFieldsContainer::-webkit-scrollbar { + width: 6px; +} + +.formFieldsContainer::-webkit-scrollbar-track { + background: transparent; +} + +.formFieldsContainer::-webkit-scrollbar-thumb { + background-color: #e0e0e0; + border-radius: 3px; + transition: background-color 0.2s; +} + +.formFieldsContainer::-webkit-scrollbar-thumb:hover { + background-color: #c0c0c0; +} + +.formFieldsRow { + display: flex; + gap: 18px; + margin-bottom: 18px; +} + +.formField { + flex: 1; +} + +.formFieldFull { + width: 100%; +} + +.fieldLabel { + display: flex; + align-items: center; + gap: 2px; + margin-bottom: 8px; +} + +.requiredMark { + color: #ff4050; + font-size: 14px; + font-weight: 400; + font-family: "PingFang SC", sans-serif; +} + +.formInput { + height: 32px; + background: #ffffff; + border: 1px solid #dcdde0; + border-radius: 4px; + color: #110c22; + font-size: 14px; + font-weight: 400; + font-family: "PingFang SC", sans-serif; + padding: 6px 12px; +} + +.formInput:focus, +.formInput:hover { + border-color: #7357ff; + box-shadow: none; +} + +.formInput::placeholder { + color: #aaabaf; + font-size: 14px; + font-weight: 400; + font-family: "PingFang SC", sans-serif; +} + +.formTextArea { + background: #ffffff; + border: 1px solid #dcdde0; + border-radius: 4px; + color: #110c22; + font-size: 14px; + font-weight: 400; + font-family: "PingFang SC", sans-serif; + padding: 6px 12px; + resize: vertical; +} + +/* Override Ant Design form item margins */ +.addFormModal :global(.ant-form-item) { + margin-bottom: 0; +} + +.addFormModal :global(.ant-select-selector) { + height: 32px !important; + border: 1px solid #dcdde0 !important; + border-radius: 4px !important; + background: #ffffff !important; + padding: 6px 12px !important; + color: #110c22 !important; + font-size: 14px !important; + font-weight: 400 !important; + font-family: "PingFang SC", sans-serif !important; +} + +.addFormModal :global(.ant-select-selector:hover) { + border-color: #7357ff !important; +} + +.addFormModal :global(.ant-select-focused .ant-select-selector) { + border-color: #7357ff !important; + box-shadow: none !important; +} + +.addFormModal :global(.ant-select-arrow) { + color: #898b8f; +} + +.addFormModal :global(.ant-select-selection-placeholder) { + color: #aaabaf !important; + font-size: 14px !important; + font-weight: 400 !important; + font-family: "PingFang SC", sans-serif !important; +} + +/* Multiple Form Container */ +.multipleFormContainer { + width: 100%; + display: flex; + flex-direction: column; + gap: 16px; +} + +/* Custom MessageWrapper for MCP Config Items */ +.mcpMessageWrapper { + min-width: 0; + width: 100%; + max-width: 100%; + box-sizing: border-box; +} + +/* Single Form Container */ +.singleFormContainer { + width: 100%; +} + +.formSelect { + width: 100%; +} + +/* Config Content Section */ +.configContentSection { + margin-top: 16px; +} + +/* JSON Editor Styles */ +.jsonEditor { + width: 100%; + border: 1px solid #dcdde0; + border-radius: 4px; + overflow: hidden; + transition: border-color 0.2s; +} + +.jsonEditor:hover { + border-color: #7357ff; +} + +.jsonEditorError { + border-color: #ff4d4f !important; +} + +.jsonEditorErrorMessage { + display: flex; + align-items: center; + gap: 6px; + padding: 8px 12px; + background: #fff2f0; + border-top: 1px solid #ffccc7; + color: #ff4d4f; + font-size: 12px; + font-family: "PingFang SC", sans-serif; +} + +.errorIcon { + font-size: 14px; +} diff --git a/browser/src/components/McpManager/McpAddForm/index.tsx b/browser/src/components/McpManager/McpAddForm/index.tsx new file mode 100644 index 000000000..f440d49bf --- /dev/null +++ b/browser/src/components/McpManager/McpAddForm/index.tsx @@ -0,0 +1,143 @@ +import { Button, Form, Modal } from 'antd'; +import React, { useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useMcpConfigManager } from '@/hooks/useMcpConfigManager'; +import { useMcpFormSubmit } from '@/hooks/useMcpFormSubmit'; +import type { FormValues, McpAddFormProps } from '@/types/mcp'; +import { containerEventHandlers } from '@/utils/eventUtils'; +import { McpAddMode } from './McpAddMode'; +import { McpEditMode } from './McpEditMode'; +import styles from './index.module.css'; + +const McpAddForm: React.FC = ({ + visible, + inputMode: _inputMode, // Keep for interface compatibility but mark as unused + addScope, + onCancel, + onSuccess, + onScopeChange, + editMode = false, + editingServer, + onEditServer, +}) => { + const { t } = useTranslation(); + const [form] = Form.useForm(); + + // Configuration management hook for add mode + const { mcpConfigs, addNewConfig, removeConfig, updateConfig, resetConfigs } = + useMcpConfigManager(); + + // Form submission hook + const { handleSubmit, contextHolder } = useMcpFormSubmit({ + editMode, + editingServer, + addScope, + onEditServer, + onSuccess: () => { + form.resetFields(); + if (!editMode) { + resetConfigs(); + } + onSuccess(); + }, + }); + + // Pre-fill form when in edit mode + useEffect(() => { + if (editMode && editingServer && visible) { + const envString = editingServer.env + ? typeof editingServer.env === 'string' + ? editingServer.env + : JSON.stringify(editingServer.env, null, 2) + : undefined; + + form.setFieldsValue({ + name: editingServer.name, + transport: editingServer.type || 'stdio', + command: editingServer.command, + args: editingServer.args?.join(' '), + url: editingServer.url, + env: envString, + }); + } else if (!editMode) { + form.resetFields(); + } + }, [editMode, editingServer, visible, form]); + + const handleFormSubmit = async (values: FormValues) => { + await handleSubmit(values, editMode ? undefined : mcpConfigs); + }; + + const handleCancel = () => { + form.resetFields(); + if (!editMode) { + resetConfigs(); + } + onCancel(); + }; + + return ( + <> + {contextHolder} + + + {editMode ? t('mcp.editServer') : t('mcp.addServer')} + +
+ } + open={visible} + onCancel={handleCancel} + footer={[ + , + , + ]} + width={640} + className={styles.addFormModal} + destroyOnClose + maskClosable={false} + > +
+
+
+ {editMode ? ( + + ) : ( + + )} +
+
+
+ + + ); +}; + +export default McpAddForm; diff --git a/browser/src/components/McpManager/McpServerTable.tsx b/browser/src/components/McpManager/McpServerTable.tsx new file mode 100644 index 000000000..b0179da57 --- /dev/null +++ b/browser/src/components/McpManager/McpServerTable.tsx @@ -0,0 +1,221 @@ +import { + message, + Pagination, + Popconfirm, + Space, + Switch, + Table, + Tag, + Tooltip, + Typography, +} from 'antd'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { removeMCPServer } from '@/api/mcpService'; +import type { McpManagerServer, McpServerTableProps } from '@/types/mcp'; +import styles from './index.module.css'; + +const { Text } = Typography; + +const McpServerTable: React.FC = ({ + servers, + loading, + onToggleService, + onDeleteSuccess, + onDeleteLocal, + onEditServer, +}) => { + const { t } = useTranslation(); + const [messageApi, contextHolder] = message.useMessage(); + const [deleteLoading, setDeleteLoading] = useState(false); + const [currentPage, setCurrentPage] = useState(1); + const pageSize = 7; + + const handleConfirmDelete = async (server: McpManagerServer) => { + try { + setDeleteLoading(true); + + // If the service is enabled, call the API to close the service first + if (server.installed) { + await removeMCPServer(server.name, server.scope === 'global'); + } + + // Regardless of whether it is enabled, delete it completely from local storage + onDeleteLocal?.(server.name, server.scope); + + messageApi.success(t('mcp.deleteSuccess', { name: server.name })); + onDeleteSuccess?.(); + } catch (error) { + console.error('Delete server failed:', error); + messageApi.error(t('mcp.deleteError')); + } finally { + setDeleteLoading(false); + } + }; + + const columns = [ + { + title: t('mcp.name'), + dataIndex: 'name', + key: 'name', + width: 120, + render: (name: string) => ( + + {name} + + ), + }, + { + title: t('mcp.status'), + key: 'status', + width: 100, + render: (record: McpManagerServer) => ( + { + onToggleService(record.name, checked, record.scope); + }} + size="small" + className={styles.mcpSwitch} + /> + ), + }, + { + title: t('mcp.scope'), + dataIndex: 'scope', + key: 'scope', + width: 100, + render: (scope: string, record: McpManagerServer) => { + const isGlobal = scope === 'global'; + return ( + + {isGlobal ? t('mcp.globalScope') : t('mcp.projectScope')} + + ); + }, + }, + { + title: t('mcp.type'), + dataIndex: 'type', + key: 'type', + width: 80, + render: (type: string, record: McpManagerServer) => ( + + {type?.toUpperCase() || 'STDIO'} + + ), + }, + { + title: t('mcp.config'), + key: 'command', + width: 150, + render: (record: McpManagerServer) => { + let configText = ''; + + if (record.type === 'sse') { + configText = record.url || ''; + } else { + configText = + `${record.command || ''} ${(record.args || []).join(' ')}`.trim(); + } + + return ( + + {configText || '-'} + + ); + }, + }, + { + title: t('mcp.actions'), + key: 'actions', + width: 120, + render: (record: McpManagerServer) => ( + + { + e.stopPropagation(); + onEditServer?.(record); + }} + > + {t('mcp.edit')} + + + handleConfirmDelete(record)} + okText={t('mcp.delete')} + cancelText={t('common.cancel')} + okType="danger" + disabled={deleteLoading || record.isPreset} + > + { + e.preventDefault(); + e.stopPropagation(); + }} + > + {t('mcp.delete')} + + + + + ), + }, + ]; + + const startIndex = (currentPage - 1) * pageSize; + const endIndex = startIndex + pageSize; + const paginatedServers = servers.slice(startIndex, endIndex); + + return ( + <> + {contextHolder} +
+ + {t('mcp.noConfiguration')} +
+ + {t('mcp.clickToStart')} + + + ), + }} + /> + + {servers.length > pageSize && ( +
+ { + setCurrentPage(page); + }} + /> +
+ )} + + + ); +}; + +export default McpServerTable; diff --git a/browser/src/components/McpManager/index.module.css b/browser/src/components/McpManager/index.module.css new file mode 100644 index 000000000..cc3dc0401 --- /dev/null +++ b/browser/src/components/McpManager/index.module.css @@ -0,0 +1,280 @@ +/* McpManager - Consolidated CSS Module */ +.modal :global(.ant-modal-content) { + background: #ffffff; + border-radius: 20px; + box-shadow: 0 0 43.3px 13px rgba(100, 99, 119, 0.12); + border: none; + overflow: hidden; + width: 640px !important; + max-width: 640px !important; + height: 635px !important; + padding: 0; +} + +.modal :global(.ant-modal-header) { + background: #ffffff; + border-bottom: none; + padding: 24px 24px 0 24px; + margin: 0; +} + +.modal :global(.ant-modal-title) { + color: #110c22; + font-size: 16px; + font-weight: 500; + line-height: 22px; + font-family: "PingFang SC", sans-serif; +} + +.modal :global(.ant-modal-body) { + padding: 24px; + background: #ffffff; + display: flex; + flex-direction: column; + align-items: flex-start; + height: 535px; + overflow: hidden; +} + +.modal :global(.ant-modal-close) { + top: 24px; + right: 24px; + width: 16px; + height: 16px; + color: #6b7280; + font-size: 16px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; +} + +.modal :global(.ant-modal-close:hover) { + color: #374151; +} + +.modal :global(.ant-modal-close .ant-modal-close-x) { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + font-size: 16px; +} + +.modal :global(.ant-modal-footer) { + border-top: none; + padding: 0 24px 24px 24px; + text-align: right; + margin: 0; +} + +/* Pagination container */ +.paginationContainer { + display: flex; + justify-content: flex-end; + align-items: center; + padding: 16px 24px; + background: #ffffff; + border-top: 1px solid #f0f0f0; + margin-top: auto; +} + +.paginationContainer :global(.ant-pagination) { + margin: 0; +} + +/* Empty state */ +.emptyState { + padding: 20px; + color: #999999; + text-align: center; +} + +.emptyStateSubtitle { + font-size: 12px; + margin-top: 4px; +} + +.serviceName { + display: inline-block; + color: #252931; + font-family: "PingFang SC"; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 120px; +} + +/* Tag styles */ +.tagEnabled { + opacity: 1; +} + +.tagDisabled { + opacity: 0.5; +} + +/* Config text styles */ +.configCode { + font-size: 12px; + padding: 2px 6px; + border-radius: 4px; + display: inline-block; + max-width: 100px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* Footer button styles - Based on Figma design */ +.modal :global(.ant-btn) { + height: 32px; + border-radius: 26px; + font-size: 14px; + font-weight: 400; + padding: 5px 15px; + border: 1px solid #dcdde0; + background: #ffffff; + color: #110c22; + transition: all 0.2s; + font-family: "PingFang SC", sans-serif; +} + +.modal :global(.ant-btn:hover) { + border-color: #bcbdc0; +} + +.modal :global(.ant-btn-primary) { + background: #110c22; + border-color: #110c22; + color: #ffffff; + padding: 6px 16px; +} + +.modal :global(.ant-btn-primary:hover) { + background: #110c22 !important; + border-color: #110c22 !important; + color: #ffffff !important; +} + +.modal :global(.ant-btn-primary:focus) { + background: #110c22 !important; + border-color: #110c22 !important; + box-shadow: none !important; + color: #ffffff !important; +} + +.modal :global(.ant-btn-primary:active) { + background: #110c22 !important; + border-color: #110c22 !important; + color: #ffffff !important; +} + +.modal :global(.ant-btn:focus) { + box-shadow: none !important; +} + +.modal :global(.ant-btn:active) { + box-shadow: none !important; +} + +/* Additional overrides for potential blue styling */ +.modal :global(.ant-btn-primary:hover:not(:disabled)) { + background: #110c22 !important; + border-color: #110c22 !important; +} + +.modal :global(.ant-btn-primary):not(:disabled):hover { + background: #110c22 !important; + border-color: #110c22 !important; +} + +/* Specific add button styling */ +.modal :global(.mcp-add-button) { + background: #110c22 !important; + border-color: #110c22 !important; + color: #ffffff !important; +} + +.modal :global(.mcp-add-button:hover) { + background: #110c22 !important; + border-color: #110c22 !important; + color: #ffffff !important; + box-shadow: none !important; +} + +.modal :global(.mcp-add-button:focus) { + background: #110c22 !important; + border-color: #110c22 !important; + color: #ffffff !important; + box-shadow: none !important; +} + +.modal :global(.mcp-add-button:active) { + background: #110c22 !important; + border-color: #110c22 !important; + color: #ffffff !important; + box-shadow: none !important; +} + +/* Action link styling */ +.actionLink { + color: #7357ff; + font-size: 14px; + font-weight: 400; + cursor: pointer; + transition: all 0.2s; + text-decoration: none; + font-family: "PingFang SC", sans-serif; +} + +.actionLink:hover { + color: #5d47e6; + text-decoration: none; +} + +.actionLink:active { + color: #4c3dd4; +} + +.actionDisabled { + color: #9ca3af !important; + cursor: not-allowed !important; + opacity: 0.5; +} + +.actionDisabled:hover { + color: #9ca3af !important; +} + +/* Popconfirm styles for better text wrapping */ +.modal :global(.ant-popover-inner) { + max-width: 280px; +} + +.modal :global(.ant-popover-message) { + margin-bottom: 8px; +} + +.modal :global(.ant-popover-message-title) { + font-size: 14px; + font-weight: 500; + color: #262626; + line-height: 1.4; + word-wrap: break-word; + white-space: normal; +} + +.modal :global(.ant-popconfirm-description) { + font-size: 12px; + color: #595959; + line-height: 1.4; + word-wrap: break-word; + white-space: normal; + margin-top: 4px; +} diff --git a/browser/src/components/McpManager/index.tsx b/browser/src/components/McpManager/index.tsx index 1513ed0d4..8f6f9b0a8 100644 --- a/browser/src/components/McpManager/index.tsx +++ b/browser/src/components/McpManager/index.tsx @@ -1,52 +1,25 @@ -import { ApiOutlined, PlusOutlined } from '@ant-design/icons'; -import { useBoolean, useSetState, useToggle } from 'ahooks'; -import { - Button, - Checkbox, - Divider, - Form, - Input, - Modal, - Radio, - Select, - Space, - Table, - Tag, - Typography, - message, -} from 'antd'; -import React, { useEffect, useState } from 'react'; +import { PlusOutlined } from '@ant-design/icons'; +import { useSetState, useToggle } from 'ahooks'; +import { Button, Modal } from 'antd'; +import React, { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; -import { - MCP_DEFAULTS, - MCP_KEY_PREFIXES, - getJsonExample, - getSimpleJsonExample, - getSingleServerExample, - getSseJsonExample, -} from '@/constants/mcp'; -import { useMcpServices } from '@/hooks/useMcpServices'; -import type { - FormValues, - JsonConfigFormat, - McpManagerProps, - McpManagerServer, - McpServerConfig, -} from '@/types/mcp'; -import { containerEventHandlers, modalEventHandlers } from '@/utils/eventUtils'; -import { addMCPServer, removeMCPServer } from '../../api/mcpService'; - -const { Text } = Typography; +import { MCP_DEFAULTS } from '@/constants/mcp'; +import { useMcpServerLoader } from '@/hooks/useMcpServerLoader'; +import { actions as configActions } from '@/state/config'; +import type { McpManagerProps, McpManagerServer } from '@/types/mcp'; +import { containerEventHandlers } from '@/utils/eventUtils'; +import styles from './index.module.css'; +import McpAddForm from './McpAddForm'; +import McpServerTable from './McpServerTable'; const McpManager: React.FC = ({ visible, onClose }) => { const { t } = useTranslation(); - // Simplified boolean states with ahooks - const [loading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = - useBoolean(false); + // Form states const [showAddForm, { toggle: toggleAddForm }] = useToggle(false); - - // Combined form states + const [showEditForm, { toggle: toggleEditForm }] = useToggle(false); + const [editingServer, setEditingServer] = + React.useState(null); const [formState, setFormState] = useSetState<{ inputMode: 'json' | 'form'; addScope: 'global' | 'project'; @@ -55,787 +28,113 @@ const McpManager: React.FC = ({ visible, onClose }) => { addScope: MCP_DEFAULTS.SCOPE, }); - // Keep complex state as is since it's not a simple boolean - const [servers, setServers] = useState([]); - const [form] = Form.useForm(); - + // Use unified hook for server management const { - allKnownServices, - serviceConfigs, - updateKnownServices, - updateServiceConfigs, - loadMcpData, - } = useMcpServices(); + managerServers: servers, + loading, + loadServers, + handleToggleService, + handleEditServer, + handleDeleteLocal, + } = useMcpServerLoader(); useEffect(() => { if (visible) { - loadServers(); - } - }, [visible]); - - const loadServers = async () => { - setLoadingTrue(); - try { - // Load global and project configurations simultaneously - const { globalServers, projectServers } = await loadMcpData(); - - // Merge service lists and mark scopes - const allInstalledServers: McpManagerServer[] = []; - - // Add global services - Object.entries(globalServers).forEach(([name, config]) => { - const serverConfig = config as McpServerConfig; - allInstalledServers.push({ - key: `${MCP_KEY_PREFIXES.GLOBAL}-${name}`, - name, - scope: 'global', - command: serverConfig.command, - args: serverConfig.args || [], - url: serverConfig.url, - type: serverConfig.type || (serverConfig.url ? 'sse' : 'stdio'), - env: serverConfig.env, - installed: true, - }); - }); - - // Add project services - Object.entries(projectServers).forEach(([name, config]) => { - const serverConfig = config as McpServerConfig; - allInstalledServers.push({ - key: `${MCP_KEY_PREFIXES.PROJECT}-${name}`, - name, - scope: 'project', - command: serverConfig.command, - args: serverConfig.args || [], - url: serverConfig.url, - type: serverConfig.type || (serverConfig.url ? 'sse' : 'stdio'), - env: serverConfig.env, - installed: true, - }); - }); - - // Update known services set - const currentInstalledNames = new Set( - allInstalledServers.map((s) => s.name), - ); - updateKnownServices( - new Set([...allKnownServices, ...currentInstalledNames]), - ); - - // Cache configurations of currently installed services - const newConfigs = new Map(serviceConfigs); - allInstalledServers.forEach((server) => { - newConfigs.set(`${server.scope}-${server.name}`, { - command: server.command, - args: server.args, - url: server.url, - type: server.type, - env: server.env, - scope: server.scope, - }); + configActions.getConfig().then(() => { + loadServers(); }); - updateServiceConfigs(newConfigs); - - // Create complete service list (including disabled services) - const allServices = [...allInstalledServers]; - - // Add known but uninstalled services using cached configurations - allKnownServices.forEach((serviceName) => { - // Check if already installed in global or project - const hasGlobal = allInstalledServers.some( - (s) => s.name === serviceName && s.scope === 'global', - ); - const hasProject = allInstalledServers.some( - (s) => s.name === serviceName && s.scope === 'project', - ); - - // Try to restore configuration from cache for each scope separately - const globalCachedConfig = serviceConfigs.get( - `${MCP_KEY_PREFIXES.GLOBAL}-${serviceName}`, - ); - const projectCachedConfig = serviceConfigs.get( - `${MCP_KEY_PREFIXES.PROJECT}-${serviceName}`, - ); - - // Add global cached config if not currently installed but has cache - if (!hasGlobal && globalCachedConfig) { - allServices.push({ - key: `${MCP_KEY_PREFIXES.DISABLED_GLOBAL}-${serviceName}`, - name: serviceName, - scope: 'global', - command: globalCachedConfig.command || '', - args: globalCachedConfig.args || [], - url: globalCachedConfig.url || '', - type: globalCachedConfig.type || 'stdio', - env: globalCachedConfig.env || {}, - installed: false, - }); - } - - // Add project cached config if not currently installed but has cache - if (!hasProject && projectCachedConfig) { - allServices.push({ - key: `${MCP_KEY_PREFIXES.DISABLED_PROJECT}-${serviceName}`, - name: serviceName, - scope: 'project', - command: projectCachedConfig.command || '', - args: projectCachedConfig.args || [], - url: projectCachedConfig.url || '', - type: projectCachedConfig.type || 'stdio', - env: projectCachedConfig.env || {}, - installed: false, - }); - } - }); - - setServers(allServices); - } catch (error) { - console.error('Failed to load servers:', error); - message.error(t('mcp.loadFailed')); - } finally { - setLoadingFalse(); } - }; - - const handleAdd = async (values: FormValues) => { - try { - if (formState.inputMode === 'json') { - const jsonConfig = JSON.parse(values.jsonConfig!) as JsonConfigFormat; - - // Support two formats: - // 1. { "mcpServers": { "name": { config } } } - // 2. { "name": { config } } or direct configuration object - - if (jsonConfig.mcpServers) { - // Format 1: Complete mcpServers wrapper - const servers = jsonConfig.mcpServers; - const serverNames = Object.keys(servers); - - if (serverNames.length === 0) { - throw new Error(t('mcp.noServersFound')); - } + }, [visible, loadServers]); - // Add all servers - for (const [name, config] of Object.entries(servers)) { - const serverConfig = config as McpServerConfig; - await addMCPServer({ - name, - command: serverConfig.command, - args: serverConfig.args, - url: serverConfig.url, - transport: serverConfig.type, - env: serverConfig.env - ? JSON.stringify(serverConfig.env) - : undefined, - global: formState.addScope === 'global', - }); - } - - message.success( - t('mcp.addedMultiple', { count: serverNames.length }), - ); - } else { - // Format 2: Direct service configuration or service name mapping - const keys = Object.keys(jsonConfig); - - if ( - keys.includes('name') || - keys.includes('command') || - keys.includes('url') - ) { - // Direct configuration object (contains name field) - if (jsonConfig.name) { - await addMCPServer({ - name: jsonConfig.name, - command: jsonConfig.command, - args: jsonConfig.args, - url: jsonConfig.url, - transport: jsonConfig.transport, - env: jsonConfig.env - ? JSON.stringify(jsonConfig.env) - : undefined, - global: formState.addScope === 'global', - }); - message.success(t('mcp.added', { name: jsonConfig.name })); - } else { - throw new Error('Name is required'); - } - } else { - // Format 2: Service name mapping { "name": { config } } - const serverNames = Object.keys(jsonConfig); - - if (serverNames.length === 0) { - throw new Error(t('mcp.noServersFound')); - } - - // Add all servers - for (const [name, config] of Object.entries(jsonConfig)) { - const serverConfig = config as McpServerConfig; - await addMCPServer({ - name, - command: serverConfig.command, - args: serverConfig.args, - url: serverConfig.url, - transport: serverConfig.type, - env: serverConfig.env - ? JSON.stringify(serverConfig.env) - : undefined, - global: formState.addScope === 'global', - }); - } - - message.success( - t('mcp.addedMultiple', { count: serverNames.length }), - ); - } - } - } else { - if (values.name) { - await addMCPServer({ - name: values.name, - command: values.command, - url: values.url, - transport: values.transport, - env: values.env, - global: formState.addScope === 'global', - args: values.args ? values.args.split(' ').filter(Boolean) : [], - }); - message.success(t('mcp.addedSingle')); - } else { - throw new Error('Name is required'); - } - } - - toggleAddForm(); - form.resetFields(); - setFormState({ inputMode: MCP_DEFAULTS.INPUT_MODE }); - loadServers(); - } catch (error) { - message.error( - formState.inputMode === 'json' - ? t('mcp.jsonFormatError') - : t('mcp.addFailed'), - ); - console.error('Add server error:', error); - } + const handleAddSuccess = () => { + toggleAddForm(); + setFormState({ inputMode: MCP_DEFAULTS.INPUT_MODE }); + loadServers(); }; - const columns = [ - { - title: t('mcp.status'), - key: 'status', - width: 80, - render: (record: McpManagerServer) => ( - { - e.stopPropagation(); - handleToggleService(record.name, e.target.checked, record.scope); - }} - className={record.installed ? 'opacity-100' : 'opacity-60'} - /> - ), - }, - { - title: t('mcp.name'), - dataIndex: 'name', - key: 'name', - width: 180, - render: (name: string, record: McpManagerServer) => ( - - {name} - - ), - }, - { - title: t('mcp.scope'), - dataIndex: 'scope', - key: 'scope', - width: 100, - render: (scope: string, record: McpManagerServer) => { - const isGlobal = scope === 'global'; - return ( - - {isGlobal ? t('mcp.globalScope') : t('mcp.projectScope')} - - ); - }, - }, - { - title: t('mcp.type'), - dataIndex: 'type', - key: 'type', - width: 80, - render: (type: string, record: McpManagerServer) => ( - - {type?.toUpperCase() || 'STDIO'} - - ), - }, - { - title: t('mcp.config'), - key: 'command', - render: (record: McpManagerServer) => { - if (!record.installed) { - return ( - - {t('mcp.disabledStatus')} - - ); - } - - if (record.type === 'sse') { - return ( - - {record.url} - - ); - } - const commandText = - `${record.command || ''} ${(record.args || []).join(' ')}`.trim(); - return ( - - {commandText || '-'} - - ); - }, - }, - ]; - - const handleToggleService = async ( - serverName: string, - enabled: boolean, - scope: string, - ) => { - try { - if (enabled) { - // Enable service - need to re-add to configuration - const server = servers.find( - (s) => s.name === serverName && s.scope === scope, - ); - if (server) { - // If service is already installed, no need to add again - if (server.installed) { - message.info(t('mcp.alreadyEnabled', { name: serverName })); - return; - } - - // For uninstalled services, need to re-add - await addMCPServer({ - name: server.name, - command: server.command, - args: server.args, - url: server.url, - transport: server.type, - env: server.env ? JSON.stringify(server.env) : undefined, - global: scope === 'global', - }); - message.success(t('mcp.enabled', { name: serverName })); - await loadServers(); - } else { - message.error(t('mcp.configNotFound', { name: serverName })); - } - } else { - // Disable service - remove from configuration but keep in list - const serverToDisable = servers.find( - (s) => s.name === serverName && s.scope === scope, - ); - - // Save configuration to cache before deletion - if (serverToDisable) { - const newConfigs = new Map(serviceConfigs); - newConfigs.set(`${scope}-${serverName}`, { - command: serverToDisable.command, - args: serverToDisable.args, - url: serverToDisable.url, - type: serverToDisable.type, - env: serverToDisable.env, - scope: scope as 'global' | 'project', - }); - updateServiceConfigs(newConfigs); - } - - await removeMCPServer(serverName, scope === 'global'); - message.success(t('mcp.disabled', { name: serverName })); + const handleAddCancel = () => { + toggleAddForm(); + setFormState({ inputMode: MCP_DEFAULTS.INPUT_MODE }); + }; - // Update service status to uninstalled but keep in list and save configuration info - setServers((prev) => - prev.map((server) => - server.name === serverName && server.scope === scope - ? { - ...server, - installed: false, - // Ensure configuration info is retained for re-enabling - command: serverToDisable?.command || server.command, - args: serverToDisable?.args || server.args, - url: serverToDisable?.url || server.url, - type: serverToDisable?.type || server.type, - env: serverToDisable?.env || server.env, - } - : server, - ), - ); + const handleEditClick = (server: McpManagerServer) => { + setEditingServer(server); + setFormState({ + inputMode: 'form', + addScope: server.scope, + }); + toggleEditForm(); + }; - // Ensure service name is recorded in known services - updateKnownServices(new Set([...allKnownServices, serverName])); - } - } catch (error) { - message.error(t('mcp.updateFailed', { name: serverName })); - console.error('Toggle service error:', error); - } + const handleEditSuccess = () => { + toggleEditForm(); + setEditingServer(null); + setFormState({ inputMode: MCP_DEFAULTS.INPUT_MODE }); + loadServers(); }; - const handleCancel = () => { - toggleAddForm(); - form.resetFields(); + const handleEditCancel = () => { + toggleEditForm(); + setEditingServer(null); setFormState({ inputMode: MCP_DEFAULTS.INPUT_MODE }); }; return ( - - {t('mcp.mcpManagementTitle')} - - - } + title={t('mcp.mcpManagementTitle')} open={visible} onCancel={onClose} - width={900} - footer={null} - className="[&_.ant-modal-body]:px-6 [&_.ant-modal-body]:py-4" - > - -
-
- {t('mcp.noConfiguration')} -
- - {t('mcp.clickToStart')} - - - ), - }} - /> - - - - - {t('mcp.addServer')} - - } - open={showAddForm} - onCancel={handleCancel} - onOk={form.submit} - okText={t('mcp.addServer')} - cancelText={t('common.cancel')} - width={700} - className="[&_.ant-modal-body]:px-6 [&_.ant-modal-body]:py-5" + width={640} + footer={[ + , + , + ]} + className={styles.modal} + > +
+ - {t('mcp.transportType')}} - initialValue={MCP_DEFAULTS.TRANSPORT_TYPE} - > - - + setFormState({ inputMode: mode })} + onScopeChange={(scope) => setFormState({ addScope: scope })} + /> - - prev.transport !== curr.transport - } - > - {({ getFieldValue }) => { - return getFieldValue('transport') === 'sse' ? ( - {t('mcp.url')}} - rules={[ - { - required: true, - message: t('mcp.configurationPlaceholder'), - }, - ]} - > - - - ) : ( - <> - {t('mcp.command')}} - rules={[ - { - required: true, - message: t('mcp.configurationPlaceholder'), - }, - ]} - > - - - {t('mcp.arguments')}} - > - - - {t('mcp.environmentVariables')} - } - > - - - - ); - }} - - - )} - - - + {/* Edit form */} + setFormState({ inputMode: mode })} + onScopeChange={(scope) => setFormState({ addScope: scope })} + editMode={true} + editingServer={editingServer || undefined} + onEditServer={handleEditServer} + /> +
); }; diff --git a/browser/src/components/MessageProcessor/index.tsx b/browser/src/components/MessageProcessor/index.tsx deleted file mode 100644 index 9d1908b4d..000000000 --- a/browser/src/components/MessageProcessor/index.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import type { UIMessage as BaseUIMessage } from '@ai-sdk/ui-utils'; -import { useEffect, useRef } from 'react'; -import { toolApprovalActions } from '@/state/toolApproval'; -import type { UIMessageAnnotation } from '@/types/message'; -import { UIMessageType } from '@/types/message'; - -interface MessageProcessorProps { - messages: BaseUIMessage[]; -} - -export default function MessageProcessor({ messages }: MessageProcessorProps) { - const processedMessagesRef = useRef(new Set()); - - useEffect(() => { - if (!messages || messages.length === 0) { - return; - } - - const lastMessage = messages[messages.length - 1]; - const messageId = `${lastMessage.id || 'unknown'}-${lastMessage.annotations?.length || 0}`; - - if (processedMessagesRef.current.has(messageId)) { - return; - } - - processedMessagesRef.current.add(messageId); - - if (lastMessage.annotations && Array.isArray(lastMessage.annotations)) { - lastMessage.annotations.forEach((annotation: any) => { - if (isToolApprovalAnnotation(annotation)) { - processToolApprovalMessage(annotation as UIMessageAnnotation); - } - }); - } - - if (processedMessagesRef.current.size > 100) { - const oldEntries = Array.from(processedMessagesRef.current).slice(0, 50); - oldEntries.forEach((entry) => { - processedMessagesRef.current.delete(entry); - }); - } - }, [messages]); - - return null; -} - -function isToolApprovalAnnotation(annotation: any): boolean { - return ( - annotation && - typeof annotation === 'object' && - (annotation.type === UIMessageType.ToolApprovalRequest || - annotation.type === UIMessageType.ToolApprovalResult || - annotation.type === UIMessageType.ToolApprovalError) - ); -} - -function processToolApprovalMessage(annotation: UIMessageAnnotation): void { - switch (annotation.type) { - case UIMessageType.ToolApprovalRequest: - toolApprovalActions.handleApprovalRequest(annotation); - break; - - case UIMessageType.ToolApprovalResult: - toolApprovalActions.handleApprovalResult(annotation); - break; - - case UIMessageType.ToolApprovalError: - toolApprovalActions.handleApprovalError(annotation); - break; - - default: - break; - } -} diff --git a/browser/src/components/MessageWrapper/constants.tsx b/browser/src/components/MessageWrapper/constants.tsx new file mode 100644 index 000000000..68bd37693 --- /dev/null +++ b/browser/src/components/MessageWrapper/constants.tsx @@ -0,0 +1,50 @@ +import { + CloseCircleOutlined, + ExclamationCircleOutlined, + Loading3QuartersOutlined, +} from '@ant-design/icons'; +import React from 'react'; +import Completed from '@/icons/completed.svg?react'; +import { MessageWrapperStatus } from './types'; + +export interface StatusConfig { + icon: React.ReactNode; + text: + | 'messageWrapper.status.thinking' + | 'messageWrapper.status.completed' + | 'messageWrapper.status.cancelled' + | 'messageWrapper.status.error'; + className: string; +} + +// Status configuration - Based on Figma design +// Note: Text values will be replaced by i18n keys in component usage +export const STATUS_CONFIG: Record = { + [MessageWrapperStatus.Thinking]: { + icon: React.createElement(Loading3QuartersOutlined, { + className: 'w-3.5 h-3.5 animate-spin', + style: { color: '#7357FF' }, + }), + text: 'messageWrapper.status.thinking', + className: 'text-[#666F8D]', + }, + [MessageWrapperStatus.Completed]: { + icon: , + text: 'messageWrapper.status.completed', + className: 'text-[#666F8D]', + }, + [MessageWrapperStatus.Cancelled]: { + icon: React.createElement(CloseCircleOutlined, { + className: 'w-3.5 h-3.5', + }), + text: 'messageWrapper.status.cancelled', + className: 'text-[#666F8D]', + }, + [MessageWrapperStatus.Error]: { + icon: React.createElement(ExclamationCircleOutlined, { + className: 'w-3.5 h-3.5', + }), + text: 'messageWrapper.status.error', + className: 'text-[#666F8D]', + }, +}; diff --git a/browser/src/components/MessageWrapper/index.module.css b/browser/src/components/MessageWrapper/index.module.css new file mode 100644 index 000000000..991527f16 --- /dev/null +++ b/browser/src/components/MessageWrapper/index.module.css @@ -0,0 +1,224 @@ +/* MessageWrapper CSS Module */ + +/* Container styles */ +.container { + background-color: white; + border-radius: 8px; + border: 1px solid #ebebeb; + margin-bottom: 12px; + min-width: 789px; +} + +/* Header styles */ +.header { + background-color: #f9fbfe; + border-radius: 8px 8px 0 0; + display: flex; + align-items: center; + gap: 6px; + padding: 8px 16px; + height: 36px; + min-height: 36px; +} + +/* Header when content is collapsed */ +.headerCollapsed { + border-radius: 8px; +} + +.headerExpandable { + cursor: pointer; +} + +.headerNotExpandable { + cursor: default; +} + +.headerLeft { + display: flex; + align-items: center; + gap: 6px; +} + +.headerRight { + display: flex; + align-items: center; + gap: 4px; + margin-left: auto; +} + +/* Icon styles */ +.icon { + color: #666f8d; + width: 14px; + height: 14px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +/* Title styles */ +.title { + font-weight: 500; + color: #666f8d; + font-size: 12px; + line-height: 16px; + max-width: 375px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: inline-block; + vertical-align: middle; + margin-left: 4px; +} + +/* Expand icon styles */ +.expandIcon { + transition: transform 0.3s ease-in-out; + cursor: pointer; +} + +.arrowExpanded { + transform: rotate(180deg); +} + +/* Status styles */ +.statusContainer { + display: flex; + align-items: center; + gap: 4px; + height: 14px; +} + +.statusIcon { + width: 14px; + height: 14px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.statusText { + font-size: 12px; + line-height: 16px; + margin-left: 4px; + font-weight: 500; + color: #666f8d; +} + +/* Action button styles */ +.actionButton { + padding: 4px; + border-radius: 4px; + transition: background-color 0.2s ease; + color: #666f8d; + background: none; + border: none; + cursor: pointer; +} + +.actionButton:hover { + background-color: rgba(0, 0, 0, 0.04); +} + +.actionButton svg { + width: 14px; + height: 14px; +} + +.actionButtonGroup { + display: flex; + align-items: center; + gap: 4px; +} + +/* When expand icon is shown, add margin to action button group */ +.actionButtonGroupWithExpandIcon { + margin-right: 8px; +} + +/* Content styles */ +.content { + overflow: hidden; +} + +.contentInner { + padding: 12px 16px 16px; +} + +.scrollable { + overflow-y: auto; + scrollbar-width: none; + -ms-overflow-style: none; +} + +.scrollable::-webkit-scrollbar { + display: none; +} + +/* Gradient mask styles */ +.gradientMaskTop { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 48px; + pointer-events: none; + z-index: 10; + background: linear-gradient( + 0deg, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 1) 100% + ); +} + +.gradientMaskBottom { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 48px; + pointer-events: none; + z-index: 10; + background: linear-gradient( + 180deg, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 1) 100% + ); +} + +/* Content wrapper styles */ +.contentWrapper { + position: relative; +} + +/* Footer styles */ +.footerContainer { + display: flex; + align-items: center; + gap: 4px; + justify-content: flex-end; +} + +.footerButton { + padding: 6px 22.5px; + border: solid 1px #f0f2f5; + border-radius: 40px; + font-family: PingFang SC; + font-weight: 400; + font-style: Regular; + font-size: 12px; + line-height: 130%; + letter-spacing: 0%; + text-align: center; + + margin-bottom: 16px; + margin-right: 12px; + margin-top: 7px; +} + +.footerButton:last-child { + margin-right: 16px; +} diff --git a/browser/src/components/MessageWrapper/index.tsx b/browser/src/components/MessageWrapper/index.tsx new file mode 100644 index 000000000..0784f8f56 --- /dev/null +++ b/browser/src/components/MessageWrapper/index.tsx @@ -0,0 +1,282 @@ +import { Button } from 'antd'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import ExpandArrowIcon from '@/icons/expand-arrow.svg?react'; +import { STATUS_CONFIG } from './constants'; +import styles from './index.module.css'; +import type { MessageWrapperProps } from './types'; + +const StatusIndicator: React.FC<{ + status: MessageWrapperProps['status']; + statusIcon?: React.ReactNode; + statusText?: string; + statusClassName?: string; +}> = ({ status, statusIcon, statusText, statusClassName }) => { + const { t } = useTranslation(); + + if (!status) return null; + + const defaultConfig = STATUS_CONFIG[status]; + + const finalConfig = { + icon: statusIcon || defaultConfig.icon, + text: statusText || t(defaultConfig.text), + className: statusClassName || defaultConfig.className, + }; + + return ( +
+
{finalConfig.icon}
+ {finalConfig.text} +
+ ); +}; + +const MessageWrapper: React.FC = ({ + children, + className = '', + title, + icon, + status, + statusIcon, + statusText, + statusClassName, + defaultExpanded = true, + expanded, + onExpandChange, + showExpandIcon = true, + expandable = true, + maxHeight = 220, + showGradientMask = true, + actions = [], + onActionClick, + footers = [], +}) => { + const [canScrollUp, setCanScrollUp] = useState(false); + const [canScrollDown, setCanScrollDown] = useState(false); + const contentRef = useRef(null); + // State management + const [isExpanded, setIsExpanded] = useState(expanded ?? defaultExpanded); + + // Sync controlled state + useEffect(() => { + if (expanded !== undefined) { + setIsExpanded(expanded); + } + }, [expanded]); + + // Check scroll state + const checkScrollState = useCallback(() => { + if (contentRef.current && isExpanded) { + const { scrollHeight, clientHeight, scrollTop } = contentRef.current; + const hasScrollbar = scrollHeight > clientHeight; + + if (hasScrollbar) { + // Check if can scroll up (not at top) + const canScrollUp = scrollTop > 1; + // Check if can scroll down (not at bottom) + const canScrollDown = + Math.abs(scrollHeight - clientHeight - scrollTop) > 1; + + setCanScrollUp(canScrollUp); + setCanScrollDown(canScrollDown); + } else { + setCanScrollUp(false); + setCanScrollDown(false); + } + } else { + setCanScrollUp(false); + setCanScrollDown(false); + } + }, [isExpanded]); + + // Listen for content changes and expand state changes + useEffect(() => { + checkScrollState(); + // Delayed check to ensure DOM is updated + const timer = setTimeout(checkScrollState, 100); + return () => clearTimeout(timer); + }, [checkScrollState, children, isExpanded]); + + // Handle expand state change + const handleToggleExpand = useCallback(() => { + // If not expandable, return directly + if (!expandable) return; + + const newExpanded = !isExpanded; + + // If controlled component, only trigger callback + if (expanded !== undefined) { + onExpandChange?.(newExpanded); + } else { + // Uncontrolled component, update internal state + setIsExpanded(newExpanded); + onExpandChange?.(newExpanded); + } + }, [isExpanded, expanded, onExpandChange, expandable]); + + // Handle action button click + const handleActionClick = useCallback( + (actionKey: string, actionCallback?: () => void) => { + return (e: React.MouseEvent) => { + e.stopPropagation(); // Prevent bubbling to header click event + actionCallback?.(); + onActionClick?.(actionKey); + }; + }, + [onActionClick], + ); + + // Render icon + const renderIcon = () => { + if (!icon) return null; + + return
{icon}
; + }; + + return ( +
+ {/* Header area */} +
{ + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + handleToggleExpand(); + } + } + : undefined + } + > +
+ {/* Icon */} + {renderIcon()} + + {/* Title */} + {title && {title}} + + {/* Status indicator */} + +
+ +
+ {/* Top right action buttons */} + {actions && actions.length > 0 && ( +
+ {actions.map((action) => ( + + ))} +
+ )} + + {/* Expand arrow */} + {showExpandIcon && expandable && ( + + )} +
+
+ + {/* Content area */} + {expandable + ? isExpanded && ( +
+
+
+ {children} +
+ {/* Top gradient mask - show when gradient is enabled and can scroll up */} + {showGradientMask && canScrollUp && ( +
+ )} + + {/* Bottom gradient mask - show when gradient is enabled and can scroll down */} + {showGradientMask && canScrollDown && ( +
+ )} +
+
+ ) + : // When not expandable, show content based on defaultExpanded + isExpanded && ( +
+
+ {children} +
+ {/* Top gradient mask - show when gradient is enabled and can scroll up */} + {showGradientMask && canScrollUp && ( +
+ )} + + {/* Bottom gradient mask - show when gradient is enabled and can scroll down */} + {showGradientMask && canScrollDown && ( +
+ )} +
+ )} + + {/* Bottom action buttons */} + {footers && footers.length > 0 && ( +
+ {footers.map((footer) => ( + + ))} +
+ )} +
+ ); +}; + +export default MessageWrapper; +export * from './types'; diff --git a/browser/src/components/MessageWrapper/types.ts b/browser/src/components/MessageWrapper/types.ts new file mode 100644 index 000000000..1eb8e12aa --- /dev/null +++ b/browser/src/components/MessageWrapper/types.ts @@ -0,0 +1,69 @@ +export interface MessageWrapperProps { + // === Basic Configuration === + children?: React.ReactNode; + className?: string; + + // === Header Content Configuration === + title?: React.ReactNode; + icon?: React.ReactNode; + + // === Status Configuration === + status?: MessageWrapperStatus; + statusIcon?: React.ReactNode; + statusText?: string; + statusClassName?: string; + + // === Expand/Collapse Configuration === + defaultExpanded?: boolean; + expanded?: boolean; + onExpandChange?: (expanded: boolean) => void; + showExpandIcon?: boolean; + expandable?: boolean; + maxHeight?: number | string; + showGradientMask?: boolean; + + // === Top Right Action Buttons === + actions?: ActionButtonProps[]; + onActionClick?: (actionKey: string) => void; + + // === Bottom Action Buttons === + footers?: FooterButtonProps[]; +} + +export interface FooterButtonProps { + key: string; + text: string; + icon?: React.ReactNode; + color?: + | 'default' + | 'primary' + | 'danger' + | 'blue' + | 'purple' + | 'cyan' + | 'green' + | 'magenta' + | 'pink' + | 'red' + | 'orange' + | 'yellow' + | 'volcano' + | 'geekblue' + | 'lime' + | 'gold' + | undefined; + onClick?: () => void; +} + +export interface ActionButtonProps { + key: string; + icon: React.ReactNode; + onClick?: () => void; +} + +export enum MessageWrapperStatus { + Thinking = 'thinking', + Completed = 'completed', + Cancelled = 'cancelled', + Error = 'error', +} diff --git a/browser/src/components/ProjectSelectModal/index.tsx b/browser/src/components/ProjectSelectModal/index.tsx new file mode 100644 index 000000000..be109df51 --- /dev/null +++ b/browser/src/components/ProjectSelectModal/index.tsx @@ -0,0 +1,160 @@ +import { FolderOpenOutlined, ImportOutlined } from '@ant-design/icons'; +import { Button, Modal, message, Space, Typography } from 'antd'; +import { createStyles } from 'antd-style'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSnapshot } from 'valtio'; +import FolderPicker from '@/components/FolderPicker'; +import { state } from '@/state/project'; +import { uiActions, uiState } from '@/state/ui'; + +const useStyle = createStyles(({ css, token }) => { + return { + headerSection: css` + padding: 20px 24px 16px; + background: linear-gradient(135deg, ${token.colorPrimaryBg} 0%, ${token.colorFillAlter} 100%); + border-bottom: 1px solid ${token.colorBorderSecondary}; + `, + headerTitle: css` + margin: 0 0 8px 0; + color: ${token.colorTextHeading}; + font-size: 16px; + font-weight: 600; + display: flex; + align-items: center; + gap: 8px; + `, + headerDescription: css` + margin: 0; + color: ${token.colorTextSecondary}; + font-size: 14px; + line-height: 1.5; + `, + content: css` + background: ${token.colorBgContainer}; + padding: 8px; + `, + footer: css` + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px 24px; + background: ${token.colorFillAlter}; + border-top: 1px solid ${token.colorBorderSecondary}; + `, + footerInfo: css` + color: ${token.colorTextTertiary}; + font-size: 12px; + `, + footerActions: css` + display: flex; + gap: 12px; + `, + cancelButton: css` + height: 36px; + border-radius: 6px; + font-weight: 500; + `, + confirmButton: css` + height: 36px; + border-radius: 6px; + font-weight: 500; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + + &:hover { + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); + transform: translateY(-1px); + } + `, + }; +}); + +const ProjectSelectModal = () => { + const { t } = useTranslation(); + const { projectSelectModalOpen } = useSnapshot(uiState); + const { loading, projectInfo } = useSnapshot(state); + const { styles } = useStyle(); + const [selectedFolder, setSelectedFolder] = useState( + projectInfo?.path || '', + ); + + if (loading) { + return null; + } + + const handleFolderChange = (folderPath: string) => { + setSelectedFolder(folderPath); + }; + + const handleConfirm = async () => { + if (!selectedFolder) { + message.error(t('project.selectModal.error')); + return; + } + // await actions.getProjectInfo(selectedFolder); + uiActions.closeProjectSelectModal(); + // TODO 做状态清理, 更好的体验 + window.location.href = `/session?folder=${selectedFolder}`; + }; + + return ( + + + {t('project.selectModal.title')} + + } + footer={null} + width={920} + centered + styles={{ + body: { maxHeight: '70vh', overflow: 'hidden' }, + }} + > +
+
+ + {t('project.selectModal.headerTitle')} +
+ + {t('project.selectModal.headerDescription')} + +
+ +
+ +
+ +
+
+ {t('project.selectModal.footerInfo')} +
+
+ + +
+
+
+ ); +}; + +export default ProjectSelectModal; diff --git a/browser/src/components/QuillEditor/ContextBlot.ts b/browser/src/components/QuillEditor/ContextBlot.ts new file mode 100644 index 000000000..21fa25cc8 --- /dev/null +++ b/browser/src/components/QuillEditor/ContextBlot.ts @@ -0,0 +1,52 @@ +import Quill, { type Parchment } from 'quill'; + +const Embed = Quill.import('blots/embed') as typeof Parchment.EmbedBlot; + +export interface ContextBlotData { + /** the text to display */ + text: string; + /** the value of the blot */ + value: string; + /** the text when translate blot to prompt, if not provided, use value */ + prompt?: string; + /** + * the prefix to display before the text + * + * @default '@' + */ + prefix?: string; +} + +class ContextBlot extends Embed { + static blotName = 'takumi-context'; + static tagName = 'takumi-context'; + + static create(data: ContextBlotData) { + const node = super.create(); + + if (node instanceof HTMLElement === false) { + return node; + } + + node.innerHTML = `${data.prefix ?? '@'}${data.text}`; + node.className = 'takumi-context'; + + node.setAttribute('contenteditable', 'false'); + + node.dataset.value = data.value; + node.dataset.text = data.text; + if (data.prefix) { + node.dataset.prefix = data.prefix; + } + if (typeof data.prompt === 'string') { + node.dataset.prompt = data.prompt; + } + return node; + } + + static value(node: HTMLElement) { + return node.dataset; + } +} + +export default ContextBlot; diff --git a/browser/src/components/QuillEditor/QuillContext.ts b/browser/src/components/QuillEditor/QuillContext.ts new file mode 100644 index 000000000..152a33170 --- /dev/null +++ b/browser/src/components/QuillEditor/QuillContext.ts @@ -0,0 +1,27 @@ +import type { Bounds, Delta } from 'quill'; +import type Quill from 'quill'; +import React from 'react'; + +interface QuillContextType { + /** call this function when should open/close context menu */ + onInputAt?: (inputing: boolean, index?: number, bounds?: Bounds) => void; + /** call this function when quill instance is ready */ + onQuillLoad?: (quill: Quill) => void; + /** call this function when user delete context */ + onDeleteContexts?: (contextValues: string[]) => void; + /** call this function when keyboard key down in editor */ + onKeyDown?: (code: number) => void; + /** native keyboard handler */ + onNativeKeyDown?: (e: React.KeyboardEvent) => void; + + /** call this function when editor value change */ + onChange?: (value: string, delta: Delta) => void; + + searchingAtIndex?: number; + onSearch?: (text: string) => void; + onExitSearch?: () => void; + + readonly?: boolean; +} + +export const QuillContext = React.createContext({}); diff --git a/browser/src/components/QuillEditor/events.ts b/browser/src/components/QuillEditor/events.ts new file mode 100644 index 000000000..6b8c46551 --- /dev/null +++ b/browser/src/components/QuillEditor/events.ts @@ -0,0 +1,48 @@ +export function makeChangeEvent(text: string, target: HTMLDivElement | null) { + const mockEvent = { + target: { + value: text, + }, + currentTarget: { + value: text, + }, + type: 'change', + nativeEvent: { + target, + type: 'input', + }, + preventDefault: () => {}, + stopPropagation: () => {}, + } as React.ChangeEvent; + + return mockEvent; +} + +export function makeSelectEvent( + start: number, + end: number, + value: string, + target: HTMLDivElement | null, +) { + const mockEvent = { + target: { + value, + selectionStart: start, + selectionEnd: end, + }, + currentTarget: { + value, + selectionStart: start, + selectionEnd: end, + }, + type: 'select', + nativeEvent: { + target, + type: 'select', + }, + preventDefault: () => {}, + stopPropagation: () => {}, + } as unknown as React.SyntheticEvent; + + return mockEvent; +} diff --git a/browser/src/components/QuillEditor/index.tsx b/browser/src/components/QuillEditor/index.tsx new file mode 100644 index 000000000..16374cf18 --- /dev/null +++ b/browser/src/components/QuillEditor/index.tsx @@ -0,0 +1,240 @@ +import type { TextAreaProps, TextAreaRef } from 'antd/es/input/TextArea'; +import { createStyles } from 'antd-style'; +import Quill, { Delta } from 'quill'; +import 'quill/dist/quill.core.css'; +import { + forwardRef, + useContext, + useEffect, + useImperativeHandle, + useLayoutEffect, + useRef, + useState, +} from 'react'; +import ContextBlot from './ContextBlot'; +import { makeChangeEvent, makeSelectEvent } from './events'; +import { QuillContext } from './QuillContext'; +import { + getDeletedLength, + getInsertText, + getRemovedTakumiContexts, + getTextWithTakumiContext, + isInsertingAt, +} from './utils'; + +interface ISearchInfo { + atPosition: number; + length: number; +} + +interface IQuillEditorProps extends TextAreaProps {} + +interface IQuillEditorRef extends TextAreaRef {} + +if (!Quill.imports['formats/takumi-context']) { + Quill.register(ContextBlot); +} + +const useStyles = createStyles( + ({ css }, { isCompositing }: { isCompositing: boolean }) => { + return { + editor: css` + .ql-editor { + position: relative; + p { + font-size: 14px; + line-height: 22px; + } + + .takumi-context { + color: #7357ff; + background-color: #eeebff; + padding: 0 2px; + user-select: none; + } + } + `, + + blank: css` + .ql-blank { + ::before { + color: #8d8a95 !important; + font-style: normal !important; + font-size: 14px; + line-height: 22px; + display: var(--placeholder-display, block); + opacity: ${isCompositing ? 0 : 1} !important; + } + } + `, + }; + }, +); + +const Editor = forwardRef((props, ref) => { + const { onChange, onSelect, onPaste, placeholder } = props; + const editorRef = useRef(null); + const quillRef = useRef(null); + const oldContentsRef = useRef(new Delta()); + const [isCompositing, setIsCompositing] = useState(false); + + const searchInfoRef = useRef(null); + + const { + onInputAt, + onQuillLoad, + onKeyDown, + onDeleteContexts, + onNativeKeyDown, + onExitSearch, + onSearch, + searchingAtIndex, + onChange: onQuillChange, + readonly, + } = useContext(QuillContext); + + const onKeyDownRef = useRef(onKeyDown); + onKeyDownRef.current = onKeyDown; + + const { styles } = useStyles({ isCompositing }); + + useImperativeHandle(ref, () => { + return { + focus: () => { + quillRef.current?.focus(); + }, + blur: () => { + quillRef.current?.blur(); + }, + }; + }); + + useEffect(() => { + if (typeof searchingAtIndex === 'number') { + searchInfoRef.current = { + atPosition: searchingAtIndex, + length: 0, + }; + } else { + searchInfoRef.current = null; + onExitSearch?.(); + } + }, [searchingAtIndex]); + + useLayoutEffect(() => { + if (editorRef.current && !quillRef.current) { + const quillInstance = new Quill(editorRef.current, { + placeholder: placeholder, + formats: ['takumi-context'], // plain text and takumi-context only + modules: { + toolbar: false, + keyboard: { + bindings: { + enter: { + key: 'Enter', + handler: () => { + onKeyDownRef.current?.(KeyCode.Enter); + return false; + }, + }, + }, + }, + }, + readOnly: readonly, + }); + + quillInstance.on('selection-change', (range, oldRange, source) => { + // when selection change, exit search mode + // if focus switched to popup(range === null && oldRange !== null), should keep search mode + if (!(range === null && oldRange !== null) && source === 'user') { + onExitSearch?.(); + } + if (range) { + onSelect?.( + makeSelectEvent( + range.index, + range.index + range.length, + quillInstance.getText(range), + editorRef.current, + ), + ); + } + }); + + quillInstance.on('text-change', (delta, _oldContent, source) => { + if (source === 'user') { + if (isInsertingAt(delta) && searchInfoRef.current === null) { + const selection = quillInstance.getSelection(); + + if (selection) { + const bounds = quillInstance.getBounds(selection.index); + + onInputAt?.(true, selection?.index, bounds ?? undefined); + } + } else { + onInputAt?.(false); + } + + const currentContents = quillInstance.getContents(); + + if (searchInfoRef.current) { + const insertText = getInsertText(delta); + if (typeof insertText === 'string') { + searchInfoRef.current.length += insertText.length; + } + const deletedLength = getDeletedLength(delta); + if (typeof deletedLength === 'number') { + searchInfoRef.current.length -= deletedLength; + if (searchInfoRef.current.length < 0) { + onExitSearch?.(); + } + } + const searchText = quillInstance.getText({ + index: Math.max(searchInfoRef.current.atPosition, 1), // skip the @ + length: searchInfoRef.current.length, + }); + + onSearch?.(searchText); + } + + const removedTakumiContexts = getRemovedTakumiContexts( + oldContentsRef.current, + currentContents, + ); + + onDeleteContexts?.(removedTakumiContexts.map((ctx) => ctx.value)); + + const currentText = getTextWithTakumiContext(currentContents); + + onChange?.(makeChangeEvent(currentText, editorRef.current)); + onQuillChange?.(currentText, currentContents); + } + oldContentsRef.current = quillInstance.getContents(); + }); + + onQuillLoad?.(quillInstance); + + quillRef.current = quillInstance; + } + }, []); + + return ( +
e.stopPropagation()} + onMouseUp={(e) => e.stopPropagation()} + onCompositionStart={() => setIsCompositing(true)} + onCompositionEnd={() => setIsCompositing(false)} + onKeyDown={onNativeKeyDown} + // @ts-expect-error + onPaste={onPaste} + /> + ); +}); + +export default Editor; + +export enum KeyCode { + Enter = 13, +} diff --git a/browser/src/components/QuillEditor/utils.ts b/browser/src/components/QuillEditor/utils.ts new file mode 100644 index 000000000..e439bfd53 --- /dev/null +++ b/browser/src/components/QuillEditor/utils.ts @@ -0,0 +1,145 @@ +import { differenceBy } from 'lodash-es'; +import { Delta } from 'quill'; +import { CONTEXT_BLOT_NAME } from '@/constants'; +import type { ContextBlotData } from './ContextBlot'; + +export function getBlotName(blotData: ContextBlotData) { + const { prefix } = blotData; + if (prefix === '@') { + return `[File ${prefix} ${blotData.text}]`; + } + + if (prefix === '/') { + return `[Slash Command ${prefix} ${blotData.text}]`; + } + + return `[Context ${prefix} ${blotData.text}]`; +} + +/** DO NOT USE QUILL's `getText`, it won't work with takumi-context.*/ +export function getTextWithTakumiContext(contents: Delta) { + return contents.ops + .map((op) => { + if (typeof op.insert === 'string') { + return op.insert; + } + + if (op.insert?.[CONTEXT_BLOT_NAME]) { + const blotData = op.insert?.[CONTEXT_BLOT_NAME] as ContextBlotData; + return getBlotName(blotData); + } + return op.insert; + }) + .join(''); +} + +export function getTakumiContexts(contents: Delta) { + return contents.ops + .filter( + (op) => + op.insert && + typeof op.insert === 'object' && + op.insert[CONTEXT_BLOT_NAME], + ) + .map( + (op) => + (op.insert as Record<'takumi-context', ContextBlotData>)?.[ + CONTEXT_BLOT_NAME + ], + ); +} + +export function getRemovedTakumiContexts( + oldContents: Delta, + newContents: Delta, +) { + const oldContexts = getTakumiContexts(oldContents); + const newContexts = getTakumiContexts(newContents); + return differenceBy(oldContexts, newContexts, 'value'); +} + +export function getInsertText(delta: Delta) { + if (!delta.ops.length) { + return undefined; + } + const last = delta.ops[delta.ops.length - 1]; + return last.insert; +} + +export function getDeletedLength(delta: Delta) { + if (!delta.ops.length) { + return undefined; + } + const last = delta.ops[delta.ops.length - 1]; + return last.delete; +} + +export function isInsertingAt(delta: Delta) { + return getInsertText(delta) === '@'; +} + +const BLOT_NAME_REGEX = /\[(File|Slash Command|Context)\s+([^\]]+)\]/g; +// Separate plain text and delta in the text, e.g. [File @ file.ts] demo [File @ file2.ts] +export function convertTextToDelta(text: string): Delta { + const delta = new Delta(); + let lastIndex = 0; + let match: RegExpExecArray | null; + + // Reset regex to start from beginning + BLOT_NAME_REGEX.lastIndex = 0; + + // biome-ignore lint/suspicious/noAssignInExpressions: + while ((match = BLOT_NAME_REGEX.exec(text)) !== null) { + const [fullMatch, type, content] = match; + const matchStart = match.index; + + // Add text before the match as regular text + if (matchStart > lastIndex) { + const textBefore = text.slice(lastIndex, matchStart); + delta.insert(textBefore); + } + + // Parse the blot content and create ContextBlot + const blotData = parseBlotContent(type, content); + if (blotData) { + delta.insert({ [CONTEXT_BLOT_NAME]: blotData }); + } else { + // If parsing fails, insert as regular text + delta.insert(fullMatch); + } + + lastIndex = matchStart + fullMatch.length; + } + + // Add remaining text after the last match + if (lastIndex < text.length) { + const remainingText = text.slice(lastIndex); + delta.insert(remainingText); + } + + return delta; +} + +function parseBlotContent( + type: string, + content: string, +): ContextBlotData | null { + // Parse "@ filename", "/ commandname", or other formats + const parts = content.trim().split(' '); + if (parts.length < 2) return null; + + const prefix = parts[0]; + const text = parts.slice(1).join(' '); + + // Validate prefix based on type + if (type === 'File' && prefix !== '@') return null; + if (type === 'Slash Command' && prefix !== '/') return null; + if (type === 'Context' && !prefix) return null; + + return { + prefix, + text, + value: `${prefix}${text}`, + prompt: `${prefix}${text}`, // Use the same value for prompt by default + }; +} diff --git a/browser/src/components/SettingsModal/components/GeneralSettings.tsx b/browser/src/components/SettingsModal/components/GeneralSettings.tsx new file mode 100644 index 000000000..f3bdf96da --- /dev/null +++ b/browser/src/components/SettingsModal/components/GeneralSettings.tsx @@ -0,0 +1,171 @@ +import { useRequest } from 'ahooks'; +import { Divider, Input, Select, Switch, Typography } from 'antd'; +import { createStyles } from 'antd-style'; +import classNames from 'classnames'; +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSnapshot } from 'valtio'; +import { state as chatState } from '@/state/chat'; +import { actions, state } from '@/state/config'; +import type { Config } from '@/types/config'; +import SettingItem from './SettingItem'; + +const { Title, Paragraph } = Typography; + +const useStyles = createStyles(({ css }) => ({ + general: css` + + `, + w2: css` + width: 216px; + `, +})); + +const GeneralSettings: React.FC = () => { + const { t } = useTranslation(); + const { productName } = useSnapshot(chatState); + const { styles } = useStyles(); + const { config } = useSnapshot(state); + const { loading, data } = useRequest(() => actions.getModelsList()); + + const modelsList = useMemo(() => { + if (data?.success) { + return data.data.groupedModels; + } + return []; + }, [data]); + + const generalCls = classNames('flex-1 p-6 overflow-y-auto', styles.general); + + const handleFieldChange = async (field: keyof Config, value: any) => { + await actions.set(field, value); + }; + + return ( +
+ + {t('settings.general.title')} + + + {t('settings.general.description', { productName })} + + + + {/* 默认模型 */} + handleFieldChange('model', value)} + value={config?.model} + className="w-60" + optionLabelProp="label" + > + {modelsList.map((providerGroup) => ( + + {providerGroup.models.map((model) => ( + + {model.name} + + ))} + + ))} + + } + /> + + handleFieldChange('language', e.target.value)} + /> + } + /> + + handleFieldChange('approvalMode', value)} + value={config.approvalMode} + className="w-60" + > + + {t('settings.general.approvalModeOptions.default')} + + + {t('settings.general.approvalModeOptions.autoEdit')} + + + {t('settings.general.approvalModeOptions.yolo')} + + + } + /> + + handleFieldChange('todo', checked)} + disabled={state.loading} + /> + } + /> + + handleFieldChange('autoCompact', checked)} + disabled={state.loading} + /> + } + /> + + handleFieldChange('autoUpdate', checked)} + disabled={state.loading} + /> + } + /> + + handleFieldChange('browser', checked)} + disabled={state.loading} + /> + } + /> +
+ ); +}; + +export default GeneralSettings; diff --git a/browser/src/components/SettingsModal/components/SettingItem.tsx b/browser/src/components/SettingsModal/components/SettingItem.tsx new file mode 100644 index 000000000..a92989dc3 --- /dev/null +++ b/browser/src/components/SettingsModal/components/SettingItem.tsx @@ -0,0 +1,51 @@ +import { Typography } from 'antd'; +import { createStyles } from 'antd-style'; +import clsx from 'classnames'; + +const { Title, Paragraph } = Typography; + +const useStyles = createStyles(({ css }) => ({ + item: css` + display: flex; + flex-direction: row; + gap: 16px; + align-items: center; + `, + label: css` + `, + content: css` + flex: 1; + display: flex; + justify-content: flex-end; + `, +})); + +interface SettingItemProps { + label: string; + description: string; + content: React.ReactNode; +} + +const SettingItem: React.FC = ({ + label, + description, + content, +}) => { + const { styles } = useStyles(); + const itemCls = clsx(styles.item, 'w-full'); + return ( +
+
+ + {label} + + + {description} + +
+
{content}
+
+ ); +}; + +export default SettingItem; diff --git a/browser/src/components/SettingsModal/components/SettingsSidebar.tsx b/browser/src/components/SettingsModal/components/SettingsSidebar.tsx new file mode 100644 index 000000000..b03373e68 --- /dev/null +++ b/browser/src/components/SettingsModal/components/SettingsSidebar.tsx @@ -0,0 +1,116 @@ +import { + BookOutlined, + CloudOutlined, + FileTextOutlined, + SettingOutlined, +} from '@ant-design/icons'; +import { Menu } from 'antd'; +import { createStyles } from 'antd-style'; +import { Cpu, Slash, SquareArrowOutUpRight, ToyBrick } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; +import type { SettingsTab, SettingsTabItem } from '../types/settings'; + +interface SettingsSidebarProps { + activeTab: SettingsTab; + onTabChange: (tab: SettingsTab) => void; +} + +const useStyles = createStyles(({ css }) => ({ + menu: css` + padding: 16px 4px; + + .ant-menu-item-extra { + width: 14px; + } + + .ant-menu-title-content { + width: 180px; + display: inline-flex; + align-items: center; + gap: 8px; + } + `, + icon: css` + width: 14px; + height: 14px; + `, +})); + +const SettingsSidebar: React.FC = ({ + activeTab, + onTabChange, +}) => { + const { t } = useTranslation(); + const { styles } = useStyles(); + + const menuItems: SettingsTabItem[] = [ + { + key: 'general', + label: t('settings.tabs.general'), + icon: , + }, + { + key: 'commands', + label: 'Commands', + icon: , + }, + { + key: 'memory', + label: 'Memory', + icon: , + }, + { + key: 'plugins', + label: 'Plugins', + icon: , + }, + { + key: 'provider', + label: 'Provider', + icon: , + }, + { + key: 'changelog', + label: t('settings.tabs.changelog'), + icon: , + link: 'https://github.com/neovateai/neovate-code/releases', + }, + { + key: 'docs', + label: t('settings.tabs.docs'), + icon: , + link: 'https://neovateai.dev', + }, + ]; + + const handleMenuClick = ({ key }: { key: string }) => { + onTabChange(key as SettingsTab); + }; + + return ( +
+ ({ + key: item.key, + icon: item.icon, + label: item.link ? ( + + {item.label} + + ) : ( + item.label + ), + extra: item.link ? ( + + ) : null, + }))} + /> +
+ ); +}; + +export default SettingsSidebar; diff --git a/browser/src/components/SettingsModal/index.tsx b/browser/src/components/SettingsModal/index.tsx new file mode 100644 index 000000000..3a30897fc --- /dev/null +++ b/browser/src/components/SettingsModal/index.tsx @@ -0,0 +1,82 @@ +import { useMount } from 'ahooks'; +import { Modal, Spin } from 'antd'; +import { createStyles } from 'antd-style'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSnapshot } from 'valtio'; +import { actions, state } from '@/state/config'; +import { uiActions, uiState } from '@/state/ui'; +import GeneralSettings from './components/GeneralSettings'; +import SettingsSidebar from './components/SettingsSidebar'; +import type { SettingsTab } from './types/settings'; + +const useStyles = createStyles(({ css }) => ({ + setting: css` + display: flex; + flex-direction: row; + gap: 12px; + + .ant-modal-header { + background-color: var(--color-gray-50); + display: none; + padding: 16px 20px; + margin: 0; + } + + .ant-modal-content { + padding: 0; + } + + .ant-menu-light { + background: transparent; + } + `, +})); + +const SettingsModal = () => { + const { t } = useTranslation(); + const { settingsModalOpen } = useSnapshot(uiState); + const { styles } = useStyles(); + const [activeTab, setActiveTab] = useState('general'); + const { loading } = useSnapshot(state); + + const renderContent = () => { + switch (activeTab) { + case 'general': + return ; + default: + return ( +
+

+ We are working on it, please stay tuned. +

+
+ ); + } + }; + + useMount(() => { + actions.getConfig(); + }); + + return ( + + +
+ + {renderContent()} +
+
+
+ ); +}; + +export default SettingsModal; diff --git a/browser/src/components/SettingsModal/types/settings.ts b/browser/src/components/SettingsModal/types/settings.ts new file mode 100644 index 000000000..071799d75 --- /dev/null +++ b/browser/src/components/SettingsModal/types/settings.ts @@ -0,0 +1,18 @@ +export type SettingsTab = + | 'general' + | 'commands' + | 'agents' + | 'memory' + | 'plugins' + | 'provider' + | 'experimental' + | 'changelog' + | 'docs'; + +export interface SettingsTabItem { + key: SettingsTab; + label: string; + icon: React.ReactNode; + disabled?: boolean; + link?: string; +} diff --git a/browser/src/components/SettingsPage/BehaviorSettings.tsx b/browser/src/components/SettingsPage/BehaviorSettings.tsx deleted file mode 100644 index 61840d0f9..000000000 --- a/browser/src/components/SettingsPage/BehaviorSettings.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import { InfoCircleOutlined } from '@ant-design/icons'; -import { Form, Select, Switch, Tooltip, Typography } from 'antd'; -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSnapshot } from 'valtio'; -import { actions, state } from '@/state/settings'; -import type { AppSettings } from '@/types/settings'; - -const { Text } = Typography; - -const BehaviorSettings: React.FC = () => { - const { t, i18n } = useTranslation(); - const { settings } = useSnapshot(state); - - const currentSettings = - settings.currentScope === 'global' - ? settings.globalSettings - : settings.projectSettings; - - const onSettingChange = async ( - key: keyof AppSettings, - value: string | boolean, - ) => { - try { - await actions.updateSettingValue(key, value); - } catch (error) { - console.error('Failed to update behavior setting:', error); - } - }; - - const onLanguageChange = async (value: string) => { - try { - // Switch interface language immediately - const languageCode = value === 'Chinese' ? 'zh' : 'en'; - await i18n.changeLanguage(languageCode); - - // Save to configuration file - await actions.updateSettingValue('language', value); - } catch (error) { - console.error('Failed to update language setting:', error); - } - }; - - const getApprovalModeLabel = (mode: string) => { - switch (mode) { - case 'suggest': - return t('settings.behavior.approvalModes.suggest'); - case 'auto-edit': - return t('settings.behavior.approvalModes.autoEdit'); - case 'full-auto': - return t('settings.behavior.approvalModes.fullAuto'); - default: - return mode; - } - }; - - return ( -
- - {t('settings.behavior.language')} - - - -
- } - > - onSettingChange('approvalMode', value)} - options={[ - { - value: 'suggest', - label: t('settings.behavior.approvalModeLabels.suggest'), - }, - { - value: 'auto-edit', - label: t('settings.behavior.approvalModeLabels.autoEdit'), - }, - { - value: 'full-auto', - label: t('settings.behavior.approvalModeLabels.fullAuto'), - }, - ]} - placeholder={ - settings.effectiveSettings.approvalMode - ? `${t('settings.model.defaultPrefix')}: ${getApprovalModeLabel(settings.effectiveSettings.approvalMode)}` - : t('settings.behavior.approvalModePlaceholder') - } - allowClear - /> - - - - {t('settings.behavior.quiet')} - - - -
- } - > -
- onSettingChange('quiet', checked)} - /> - - {currentSettings.quiet !== undefined - ? currentSettings.quiet - ? t('settings.behavior.enabled') - : t('settings.behavior.disabled') - : `${t('settings.model.defaultPrefix')}: ${settings.effectiveSettings.quiet ? t('settings.behavior.enabled') : t('settings.behavior.disabled')}`} - -
- - - ); -}; - -export default BehaviorSettings; diff --git a/browser/src/components/SettingsPage/ModelSettings.tsx b/browser/src/components/SettingsPage/ModelSettings.tsx deleted file mode 100644 index 4b0933e11..000000000 --- a/browser/src/components/SettingsPage/ModelSettings.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { InfoCircleOutlined } from '@ant-design/icons'; -import { Form, Select, Tooltip } from 'antd'; -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSnapshot } from 'valtio'; -import { actions, state } from '@/state/settings'; -import type { AppSettings } from '@/types/settings'; - -const ModelSettings: React.FC = () => { - const { t } = useTranslation(); - const { settings } = useSnapshot(state); - - const currentSettings = - settings.currentScope === 'global' - ? settings.globalSettings - : settings.projectSettings; - - const onModelChange = async (key: keyof AppSettings, value: string) => { - try { - await actions.updateSettingValue(key, value); - } catch (error) { - console.error('Failed to update model setting:', error); - } - }; - - const getModelOptions = () => { - return settings.availableModels.map((model) => { - const displayText = `${model.key}(${model.value})`; - return { - value: model.key, - label: ( - -
- {displayText} -
-
- ), - }; - }); - }; - - const filterOption = ( - input: string, - option?: { value: string; label: React.ReactElement }, - ) => { - if (!option) return false; - const model = settings.availableModels.find((m) => m.key === option.value); - if (!model) return false; - - const searchText = `${model.key}(${model.value})`.toLowerCase(); - return searchText.includes(input.toLowerCase()); - }; - - return ( -
- - {t('settings.model.main')} - - - -
- } - > - onModelChange('smallModel', value)} - options={getModelOptions()} - placeholder={ - settings.effectiveSettings.smallModel - ? `${t('settings.model.defaultPrefix')}: ${settings.effectiveSettings.smallModel}` - : t('settings.model.smallPlaceholder') - } - allowClear - showSearch - filterOption={filterOption} - /> - - - - {t('settings.model.plan')} - - - -
- } - > - setNewPlugin(e.target.value)} - placeholder={t('settings.plugins.placeholder')} - onPressEnter={onAddPlugin} - /> - - - - {currentPlugins.length > 0 ? ( - ( - } - onClick={() => onRemovePlugin(plugin)} - danger - />, - ]} - > - {plugin} - - )} - /> - ) : ( - {t('settings.plugins.empty')} - )} - - {settings.effectiveSettings.plugins && - JSON.stringify(settings.effectiveSettings.plugins) !== - JSON.stringify(currentPlugins) && ( - - {t('settings.plugins.effective')}:{' '} - {[...(settings.effectiveSettings.plugins || [])].join(', ') || - t('settings.plugins.none')} - - )} - - - - ); -}; - -export default PluginSettings; diff --git a/browser/src/components/SettingsPage/SettingsScopeSwitch.tsx b/browser/src/components/SettingsPage/SettingsScopeSwitch.tsx deleted file mode 100644 index 23fbb2bd0..000000000 --- a/browser/src/components/SettingsPage/SettingsScopeSwitch.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { FolderOutlined, GlobalOutlined } from '@ant-design/icons'; -import { Card, Segmented, Typography } from 'antd'; -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSnapshot } from 'valtio'; -import { actions, state } from '@/state/settings'; - -const { Text } = Typography; - -const SettingsScopeSwitch: React.FC = () => { - const { t } = useTranslation(); - const { settings } = useSnapshot(state); - - return ( - -
-
- {t('settings.scope.title')} -
- - {settings.currentScope === 'global' - ? t('settings.scope.globalDesc') - : t('settings.scope.projectDesc')} - -
-
- - - actions.switchScope(value as 'global' | 'project') - } - options={[ - { - label: ( -
- - {t('settings.scope.global')} -
- ), - value: 'global', - }, - { - label: ( -
- - {t('settings.scope.project')} -
- ), - value: 'project', - }, - ]} - /> -
-
- ); -}; - -export default SettingsScopeSwitch; diff --git a/browser/src/components/SettingsPage/index.tsx b/browser/src/components/SettingsPage/index.tsx deleted file mode 100644 index e917be08e..000000000 --- a/browser/src/components/SettingsPage/index.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { - ApiOutlined, - CloseOutlined, - ControlOutlined, - RobotOutlined, -} from '@ant-design/icons'; -import { useNavigate } from '@tanstack/react-router'; -import { Button, Card, Col, Row, Spin, Typography } from 'antd'; -import React, { useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSnapshot } from 'valtio'; -import { actions, state } from '@/state/settings'; -import BehaviorSettings from './BehaviorSettings'; -import ModelSettings from './ModelSettings'; -import PluginSettings from './PluginSettings'; -import SettingsScopeSwitch from './SettingsScopeSwitch'; - -const { Text } = Typography; - -const SettingsPage: React.FC = () => { - const { t } = useTranslation(); - const { settings } = useSnapshot(state); - const navigate = useNavigate(); - - useEffect(() => { - if (!settings.loaded && !settings.loading) { - actions.loadSettings(); - } - }, [settings.loaded, settings.loading]); - - const handleClose = () => { - navigate({ to: '/chat' }); - }; - - if (settings.loading) { - return ( -
- - {t('settings.loading')} -
- ); - } - - return ( -
-
-
- {/* Settings Panel */} -
- {/* Header */} -
-
{t('settings.title')}
-
- - {/* Settings Content */} -
-
- {/* Scope Switch */} -
- -
- - {/* Settings Content */} - -
- - - {t('settings.model.title')} - - } - className="h-fit border border-gray-200 [&_.ant-card-body]:p-4" - > - - - - - - - - {t('settings.behavior.title')} - - } - className="h-fit border border-gray-200 [&_.ant-card-body]:p-4" - > - - - - - - - - {t('settings.plugins.title')} - - } - className="border border-gray-200 [&_.ant-card-body]:p-4" - > - - - - - - - - - - - ); -}; - -export default SettingsPage; diff --git a/browser/src/components/Sider/Conversations.tsx b/browser/src/components/Sider/Conversations.tsx new file mode 100644 index 000000000..b267714a4 --- /dev/null +++ b/browser/src/components/Sider/Conversations.tsx @@ -0,0 +1,207 @@ +import { Conversations, type ConversationsProps } from '@ant-design/x'; +import { useSearch } from '@tanstack/react-router'; +import { Empty, type GetProp, Spin } from 'antd'; +import { createStyles } from 'antd-style'; +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSnapshot } from 'valtio'; +import { state as chatState } from '@/state/chat'; +import { state } from '@/state/project'; + +const useStyle = createStyles(({ token, css }) => { + return { + conversations: css` + width: 100%; + flex: 1; + margin-top: 16px; + display: flex; + flex-direction: column; + min-height: 0; + + .ant-conversations { + padding: 0; + + .ant-conversations-group { + margin-bottom: 16px; + + .ant-conversations-group-title { + padding: 0 0 8px 0; + font-size: 13px; + font-weight: 500; + color: ${token.colorPrimary}; + line-height: 1.4; + border-bottom: 1px solid ${token.colorBorderSecondary}; + margin-bottom: 8px; + } + } + + .ant-conversations-item { + margin-bottom: 6px; + transition: all 0.2s ease; + cursor: pointer; + border-radius: 8px; + padding: 0 8px; + + &:hover { + background: ${token.colorBgTextHover}; + transform: translateX(4px); + + .ant-conversations-item-content { + color: ${token.colorPrimary}; + } + } + + &.ant-conversations-item-active { + background: ${token.colorPrimaryBg}; + border: 1px solid ${token.colorPrimaryBorder}; + + .ant-conversations-item-content { + color: ${token.colorPrimary}; + font-weight: 500; + } + } + + .ant-conversations-item-content { + padding: 12px 8px; + font-size: 14px; + line-height: 1.4; + color: ${token.colorText}; + transition: all 0.2s ease; + + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + word-break: break-word; + } + } + } + `, + conversationsTitle: css` + font-size: 12px; + font-weight: 500; + color: ${token.colorTextTertiary}; + margin-bottom: 12px; + text-transform: uppercase; + letter-spacing: 0.5px; + flex-shrink: 0; + `, + conversationsContent: css` + width: 100%; + flex: 1; + overflow-y: auto; + overflow-x: hidden; + min-height: 0; + padding-right: 4px; + margin-right: -4px; + + /* 自定义滚动条样式 */ + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-track { + background: transparent; + border-radius: 3px; + } + + &::-webkit-scrollbar-thumb { + background: ${token.colorBorderSecondary}; + border-radius: 3px; + transition: background 0.2s ease; + + &:hover { + background: ${token.colorBorder}; + } + } + + /* Firefox 滚动条样式 */ + scrollbar-width: thin; + scrollbar-color: ${token.colorBorderSecondary} transparent; + `, + loading: css` + display: flex; + justify-content: center; + align-items: center; + height: 60px; + margin-top: 16px; + `, + empty: css` + margin-top: 24px; + text-align: center; + padding: 24px 16px; + border-radius: 8px; + border: 1px dashed ${token.colorBorderSecondary}; + + .ant-empty-description { + font-size: 13px; + color: ${token.colorTextTertiary}; + } + `, + }; +}); +const ConversationList: React.FC = () => { + const { projectInfo, loading } = useSnapshot(state); + const { sessionId } = useSnapshot(chatState); + const { styles } = useStyle(); + const { t } = useTranslation(); + const { folder } = useSearch({ from: '/session/' }); + + const handleActiveChange = (key: string) => { + // reload the page + const params = new URLSearchParams(); + params.set('sessionId', key); + if (folder) params.set('folder', folder); + // TODO 做状态清理, 更好的体验 + window.location.href = `/session?${params.toString()}`; + }; + + const items = useMemo>(() => { + return ( + projectInfo?.sessions?.map((item) => { + return { + label: item.summary || t('conversations.unnamed'), + value: item.sessionId, + key: item.sessionId, + }; + }) || [] + ); + }, [projectInfo?.sessions, t]); + + if (loading) { + return ( +
+ +
+ ); + } + + if (!items.length) { + return ( +
+ +
+ ); + } + + return ( +
+
+ {t('conversations.title')} +
+
+ +
+
+ ); +}; + +export default ConversationList; diff --git a/browser/src/components/Sider/LogoArea.tsx b/browser/src/components/Sider/LogoArea.tsx new file mode 100644 index 000000000..a5db244ca --- /dev/null +++ b/browser/src/components/Sider/LogoArea.tsx @@ -0,0 +1,51 @@ +import { createStyles } from 'antd-style'; +import React from 'react'; +// import LogoIcon from '@/icons/logo.svg?react'; +import CollapseIcon from '@/icons/toggle-expand.svg?react'; +import * as layout from '@/state/layout'; + +const useStyle = createStyles(({ css }) => { + return { + logoArea: css` + display: flex; + align-items: center; + justify-content: space-between; + padding: 24px 10px; + box-sizing: border-box; + + > div:first-child svg { + width: 116px; + height: 24px; + } + + > svg:last-child { + width: 25px; + height: 25px; + cursor: pointer; + } + `, + logo: css` + font-size: 20px; + font-weight: bold; + line-height: 24px; + `, + }; +}); + +const LogoArea: React.FC = () => { + const { styles } = useStyle(); + + const handleCollapse = () => { + layout.actions.setSidebarCollapsed(true); + }; + + return ( +
+ {/* */} +

Neovate

+ +
+ ); +}; + +export default LogoArea; diff --git a/browser/src/components/Sider/ProjectInfoArea.tsx b/browser/src/components/Sider/ProjectInfoArea.tsx new file mode 100644 index 000000000..f2b27c843 --- /dev/null +++ b/browser/src/components/Sider/ProjectInfoArea.tsx @@ -0,0 +1,238 @@ +import { useSearch } from '@tanstack/react-router'; +import { useMount } from 'ahooks'; +import { Spin, Tooltip } from 'antd'; +import { createStyles } from 'antd-style'; +import { ExternalLinkIcon, FolderPlusIcon, GitBranchIcon } from 'lucide-react'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSnapshot } from 'valtio'; +import { openProjectInEditor } from '@/api/project'; +import { actions, state } from '@/state/project'; + +const useStyles = createStyles(({ css, token }) => { + return { + projectInfoArea: css` + margin-top: 10px; + `, + + projectCard: css` + padding: 12px; + background: ${token.colorBgContainer}; + border: 1px solid ${token.colorBorderSecondary}; + border-radius: 8px; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + cursor: pointer; + + &:hover { + background: ${token.colorBgTextHover}; + border-color: ${token.colorBorder}; + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + } + `, + + projectName: css` + font-size: 14px; + font-weight: 600; + color: ${token.colorText}; + margin-bottom: 6px; + line-height: 1.2; + word-break: break-all; + `, + + projectPath: css` + font-size: 11px; + color: ${token.colorTextTertiary}; + margin-bottom: 8px; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + line-height: 1.2; + word-break: break-all; + `, + + gitInfo: css` + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + `, + + branchInfo: css` + display: flex; + align-items: center; + gap: 4px; + flex: 1; + min-width: 0; + + svg { + width: 12px; + height: 12px; + color: ${token.colorTextSecondary}; + flex-shrink: 0; + } + `, + + branchName: css` + font-size: 11px; + color: ${token.colorTextSecondary}; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + `, + + openInEditorButton: css` + display: flex; + align-items: center; + justify-content: center; + padding: 3px; + background: ${token.colorPrimary}; + color: ${token.colorWhite}; + border: none; + border-radius: 3px; + cursor: pointer; + transition: all 0.15s ease; + flex-shrink: 0; + width: 18px; + height: 18px; + + svg { + width: 10px; + height: 10px; + flex-shrink: 0; + } + + &:hover { + background: ${token.colorPrimaryHover}; + transform: scale(1.1); + } + + &:active { + transform: scale(0.95); + } + + &:disabled { + background: ${token.colorTextDisabled}; + color: ${token.colorTextDisabled}; + cursor: not-allowed; + transform: none; + opacity: 0.6; + } + `, + + noProject: css` + padding: 8px; + background: ${token.colorBgContainer}; + border: 1px dashed ${token.colorBorderSecondary}; + border-radius: 8px; + text-align: center; + color: ${token.colorTextSecondary}; + font-size: 12px; + font-weight: 500; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + cursor: pointer; + line-height: 1.4; + display: flex; + align-items: center; + justify-content: center; + + &:hover { + background: ${token.colorBgTextHover}; + border-color: ${token.colorBorder}; + color: ${token.colorText}; + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + } + + .no-project-icon { + width: 16px; + height: 16px; + opacity: 0.6; + margin-right: 8px; + } + + &:hover .no-project-icon { + opacity: 0.8; + } + `, + }; +}); + +const ProjectInfoArea: React.FC = () => { + const { styles } = useStyles(); + const { t } = useTranslation(); + const { projectInfo, loading } = useSnapshot(state); + const [isOpening, setIsOpening] = useState(false); + const { folder } = useSearch({ from: '/session/' }); + + useMount(() => { + actions.getProjectInfo(folder); + }); + + const handleOpenInEditor = async () => { + if (!projectInfo?.path || isOpening) { + return; + } + + setIsOpening(true); + try { + await openProjectInEditor(projectInfo.path); + } catch (error) { + console.error('Failed to open project in editor:', error); + } finally { + setIsOpening(false); + } + }; + + if (loading) { + return ( +
+
+ +
+
+ ); + } + + if (!projectInfo) { + return ( +
+
+ + {t('project.noProject')} +
+
+ ); + } + + return ( +
+
+ +
{projectInfo.name}
+
+
+
+ + + {projectInfo.gitBranch || 'main'} + +
+ + + + +
+
+
+ ); +}; + +export default ProjectInfoArea; diff --git a/browser/src/components/Sider/SiderMain.tsx b/browser/src/components/Sider/SiderMain.tsx index ac9b836df..6adf1e1da 100644 --- a/browser/src/components/Sider/SiderMain.tsx +++ b/browser/src/components/Sider/SiderMain.tsx @@ -1,32 +1,48 @@ import { - DeleteOutlined, - EditOutlined, + FolderOpenOutlined, PlusOutlined, QuestionCircleOutlined, SettingOutlined, } from '@ant-design/icons'; -import { Conversations } from '@ant-design/x'; -import { useNavigate } from '@tanstack/react-router'; -import { Avatar, Button } from 'antd'; +import { Button } from 'antd'; import { createStyles } from 'antd-style'; -import type { ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; -import logoPng from './imgs/kmi-ai.png'; - -interface SiderMainProps { - popoverButton?: ReactNode; -} +import { useSnapshot } from 'valtio'; +import { useSession } from '@/hooks/useSession'; +import * as layout from '@/state/layout'; +import { uiActions } from '@/state/ui'; +import ConversationList from './Conversations'; +import LogoArea from './LogoArea'; +import ProjectInfoArea from './ProjectInfoArea'; const useStyle = createStyles(({ token, css }) => { return { sider: css` - background: ${token.colorBgLayout}80; - width: 280px; + position: relative; height: 100%; + padding: 0 16px; + box-sizing: border-box; + border-right: 1px solid ${token.colorBorderSecondary}; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + background: linear-gradient(180deg, + ${token.colorBgContainer} 0%, + ${token.colorBgLayout} 100%); + overflow: hidden; display: flex; flex-direction: column; - padding: 0 12px; - box-sizing: border-box; + backdrop-filter: blur(10px); + `, + siderExpanded: css` + width: 280px; + opacity: 1; + visibility: visible; + `, + siderCollapsed: css` + width: 0; + padding: 0; + opacity: 0; + visibility: hidden; + border-right: none; `, logo: css` display: flex; @@ -43,107 +59,118 @@ const useStyle = createStyles(({ token, css }) => { font-size: 16px; } `, - addBtn: css` - background: #1677ff0f; - border: 1px solid #1677ff34; - height: 40px; - `, - conversations: css` - flex: 1; - overflow-y: auto; - margin-top: 12px; - padding: 0; - - .ant-conversations-list { - padding-inline-start: 0; - } - `, siderFooter: css` + margin-top: auto; border-top: 1px solid ${token.colorBorderSecondary}; - height: 40px; display: flex; align-items: center; justify-content: space-between; + border-radius: 8px 8px 0 0; + opacity: 1; + transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1); + padding: 8px 0; + `, + siderFooterHidden: css` + opacity: 0; + pointer-events: none; + `, + siderFooterLeft: css` + display: flex; + align-items: center; + gap: 4px; + + .ant-btn { + padding: 6px 8px; + height: auto; + font-size: 12px; + border-radius: 6px; + color: ${token.colorTextSecondary}; + + &:hover { + color: ${token.colorPrimary}; + background: ${token.colorPrimaryBg}; + } + } `, siderFooterRight: css` display: flex; align-items: center; - gap: 8px; + width: 48px; + + .ant-btn { + width: 24px; + height: 24px; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + border-radius: 6px; + color: ${token.colorTextSecondary}; + transition: all 0.2s ease; + + &:hover { + color: ${token.colorPrimary}; + background: ${token.colorPrimaryBg}; + transform: translateY(-1px); + } + } + `, + siderExpanIcon: css` + transform: rotateY(180deg); + `, + + addBtn: css` + height: 40px; + border-radius: 8px; + color: ${token.colorPrimary}; + background: #F9F8FF; + border: 1px solid $F9F8FF; `, - popoverButtonWrapper: css``, }; }); -const SiderMain = (props: SiderMainProps) => { - const { popoverButton } = props; +const SiderMain = () => { const { styles } = useStyle(); const { t } = useTranslation(); - const navigate = useNavigate(); + const { sidebarCollapsed } = useSnapshot(layout.state); + const { newSession } = useSession(); return ( -
- {popoverButton} - {/* 🌟 Logo */} -
- logo - Takumi -
- - {/* 🌟 添加会话 */} +
+ - - {/* 🌟 会话管理 */} - { - console.log('onActiveChange', val); - }} - groupable - styles={{ item: { padding: '0 8px' } }} - menu={(conversation) => ({ - items: [ - { - label: t('menu.rename'), - key: 'rename', - icon: , - }, - { - label: t('menu.delete'), - key: 'delete', - icon: , - danger: true, - onClick: () => { - console.log('delete conversation', conversation); - }, - }, - ], - })} - /> - -
- + + +
+
+ +
diff --git a/browser/src/components/Sider/imgs/sider-bg.png b/browser/src/components/Sider/imgs/sider-bg.png new file mode 100644 index 000000000..22270e97d Binary files /dev/null and b/browser/src/components/Sider/imgs/sider-bg.png differ diff --git a/browser/src/components/Sider/index.tsx b/browser/src/components/Sider/index.tsx index 33078e5ec..b7839a7b3 100644 --- a/browser/src/components/Sider/index.tsx +++ b/browser/src/components/Sider/index.tsx @@ -4,6 +4,7 @@ import { createStyles } from 'antd-style'; import { useEffect, useState } from 'react'; import { useSnapshot } from 'valtio'; import * as codeViewer from '@/state/codeViewer'; +import * as layout from '@/state/layout'; import SiderMain from './SiderMain'; const useStyles = createStyles(({ css, cx, token }) => { @@ -51,13 +52,51 @@ const useStyles = createStyles(({ css, cx, token }) => { display: block; margin: 0 0 20px 0; `, + // Floating mode styles + floatingWrapper: css` + position: fixed; + top: 0; + left: 0; + z-index: 1001; + height: 100vh; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + `, + floatingContent: css` + width: 280px; + height: 100vh; + background: ${token.colorBgContainer}; + box-shadow: 4px 4px 30px 0px rgba(184, 184, 184, 0.25); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + transform: translateX(0); + `, + // Hidden state - use transform instead of display:none + hidden: css` + position: fixed; + top: 0; + left: 0; + z-index: 1001; + height: 100vh; + transform: translateX(-100%); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + visibility: hidden; + opacity: 0; + `, + hiddenContent: css` + width: 280px; + height: 100vh; + background: ${token.colorBgContainer}; + box-shadow: 4px 4px 30px 0px rgba(184, 184, 184, 0.25); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + `, }; }); const Sider = () => { const { visible: codeViewerVisible } = useSnapshot(codeViewer.state); + const { sidebarCollapsed, rightPanelExpanded } = useSnapshot(layout.state); const [active, setActive] = useState(false); const { styles } = useStyles(); + const isHidden = sidebarCollapsed; const MenuButton = (
+ ); + } + + // Keep original codeViewer logic unchanged + return codeViewerVisible ? ( + active ? ( +
setActive(false)} + > +
+
- ) : ( -
{MenuButton}
- )} - +
+ ) : ( +
{MenuButton}
+ ) ) : ( ); diff --git a/browser/src/components/SuggestionList/AutoTooltip.tsx b/browser/src/components/SuggestionList/AutoTooltip.tsx deleted file mode 100644 index 0bdf5c2fa..000000000 --- a/browser/src/components/SuggestionList/AutoTooltip.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Tooltip } from 'antd'; -import React, { useLayoutEffect, useRef, useState } from 'react'; - -interface Props - extends React.DetailedHTMLProps< - React.HTMLAttributes, - HTMLDivElement - > { - maxWidth?: number; -} - -const AutoTooltip = (props: Props) => { - const ref = useRef(null); - const [showTip, setShowTip] = useState(false); - - useLayoutEffect(() => { - if (ref.current) { - if (ref.current.scrollWidth > ref.current.clientWidth) { - setShowTip(true); - } else { - setShowTip(false); - } - } - }, []); - - return ( - -
- - ); -}; - -export default AutoTooltip; diff --git a/browser/src/components/SuggestionList/ListFooter.tsx b/browser/src/components/SuggestionList/ListFooter.tsx new file mode 100644 index 000000000..b933a0a59 --- /dev/null +++ b/browser/src/components/SuggestionList/ListFooter.tsx @@ -0,0 +1,32 @@ +import { useTranslation } from 'react-i18next'; + +interface IProps { + selectedFirstKey?: string; +} + +const ListFooter = ({ selectedFirstKey }: IProps) => { + const { t } = useTranslation(); + + return ( +
+
+
+
↑↓
+
{t('suggestion.navigate')}
+
+
+
Enter
+
{t('suggestion.select')}
+
+
+
Esc
+
+ {selectedFirstKey ? t('suggestion.back') : t('suggestion.close')} +
+
+
+
+ ); +}; + +export default ListFooter; diff --git a/browser/src/components/SuggestionList/SmartText.tsx b/browser/src/components/SuggestionList/SmartText.tsx new file mode 100644 index 000000000..463dde3f4 --- /dev/null +++ b/browser/src/components/SuggestionList/SmartText.tsx @@ -0,0 +1,137 @@ +import { type GetProps, Tooltip } from 'antd'; +import { cx } from 'antd-style'; +import React, { useLayoutEffect, useMemo, useRef, useState } from 'react'; + +interface Props { + label: React.ReactNode; + extra?: React.ReactNode; + maxWidth?: number; + gap?: number; + minExtraWidth?: number; + showTip?: boolean; + placement?: GetProps['placement']; + renderTooltip?: ( + label: React.ReactNode, + extra?: React.ReactNode, + ) => React.ReactNode; +} + +type DisplayMode = 'full' | 'extra-truncated' | 'label-truncated'; + +const SmartText = (props: Props) => { + const { + label, + extra, + maxWidth = 280, + gap = 4, + minExtraWidth = 40, + showTip, + placement = 'top', + renderTooltip, + } = props; + + const containerRef = useRef(null); + const labelRef = useRef(null); + const extraRef = useRef(null); + + const [displayMode, setDisplayMode] = useState('full'); + + const hasTip = useMemo(() => displayMode !== 'full', [displayMode]); + + const showExtra = useMemo( + () => displayMode !== 'label-truncated', + [displayMode], + ); + + useLayoutEffect(() => { + if (!containerRef.current || !labelRef.current) return; + + const labelEl = labelRef.current; + const extraEl = extraRef.current; + + const labelWidth = labelEl.scrollWidth; + const extraWidth = extraEl ? extraEl.scrollWidth : 0; + const totalNaturalWidth = labelWidth + (extraEl ? extraWidth + gap : 0); + + if (totalNaturalWidth <= maxWidth) { + // Everything fits, no truncation needed + setDisplayMode('full'); + } else if (!extra) { + // Only label, truncate if necessary + if (labelWidth > maxWidth) { + setDisplayMode('label-truncated'); + } + } else { + // Both label and extra exist + // Priority: show label completely first, then extra if space allows + if (labelWidth + gap + extraWidth <= maxWidth) { + // Both fit perfectly + setDisplayMode('full'); + } else if (labelWidth >= maxWidth) { + // Label itself needs more space than available, hide extra and truncate label + setDisplayMode('label-truncated'); + } else { + // Label fits, but not enough space for both + const remainingWidth = maxWidth - labelWidth - gap; + + if (extraWidth <= remainingWidth) { + // Extra fits in remaining space + setDisplayMode('full'); + } else { + // Try to show truncated extra + + if (remainingWidth >= minExtraWidth) { + // Show truncated extra + setDisplayMode('extra-truncated'); + } else { + // Not enough space for meaningful extra, hide it + setDisplayMode('label-truncated'); + } + } + } + } + }, [label, extra, maxWidth, gap, minExtraWidth]); + + return ( + +
+
+ {label} +
+ {showExtra && extra && ( + <> +
+
+ {extra} +
+ + )} +
+ + ); +}; + +export default SmartText; diff --git a/browser/src/components/SuggestionList/TooltipRender/FileTooltipRender.tsx b/browser/src/components/SuggestionList/TooltipRender/FileTooltipRender.tsx new file mode 100644 index 000000000..3b4be732c --- /dev/null +++ b/browser/src/components/SuggestionList/TooltipRender/FileTooltipRender.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +interface Props { + fullPath: React.ReactNode; + icon: React.ReactNode; +} + +const FileTooltipRender = (props: Props) => { + const { fullPath, icon } = props; + return ( +
+
{icon}
+
{fullPath}
+
+ ); +}; + +export default FileTooltipRender; diff --git a/browser/src/components/SuggestionList/TooltipRender/SlashCommandTooltipRender.tsx b/browser/src/components/SuggestionList/TooltipRender/SlashCommandTooltipRender.tsx new file mode 100644 index 000000000..c45aa1389 --- /dev/null +++ b/browser/src/components/SuggestionList/TooltipRender/SlashCommandTooltipRender.tsx @@ -0,0 +1,14 @@ +interface Props { + description: React.ReactNode; +} + +const SlashCommandTooltipRender = (props: Props) => { + const { description } = props; + return ( +
+
{description}
+
+ ); +}; + +export default SlashCommandTooltipRender; diff --git a/browser/src/components/SuggestionList/TooltipRender/index.tsx b/browser/src/components/SuggestionList/TooltipRender/index.tsx new file mode 100644 index 000000000..9793fb130 --- /dev/null +++ b/browser/src/components/SuggestionList/TooltipRender/index.tsx @@ -0,0 +1,7 @@ +import FileTooltipRender from './FileTooltipRender'; +import SlashCommandTooltipRender from './SlashCommandTooltipRender'; + +export default { + File: FileTooltipRender, + SlashCommand: SlashCommandTooltipRender, +}; diff --git a/browser/src/components/SuggestionList/index.tsx b/browser/src/components/SuggestionList/index.tsx index d26c7fa71..4b3f5a2f5 100644 --- a/browser/src/components/SuggestionList/index.tsx +++ b/browser/src/components/SuggestionList/index.tsx @@ -1,270 +1,526 @@ -import { LeftOutlined } from '@ant-design/icons'; -import { Button, Input, type InputRef, List, Popover } from 'antd'; -import { createStyles } from 'antd-style'; -import React, { useEffect, useMemo, useRef, useState } from 'react'; +import Icon, { + ArrowRightOutlined, + CheckOutlined, + LeftOutlined, +} from '@ant-design/icons'; +import { Input, type InputRef, List, Popover } from 'antd'; +import { cx } from 'antd-style'; +import { groupBy, throttle } from 'lodash-es'; +import { + forwardRef, + useCallback, + useEffect, + useImperativeHandle, + useMemo, + useRef, + useState, +} from 'react'; import { useTranslation } from 'react-i18next'; -import AutoTooltip from './AutoTooltip'; +import { useSnapshot } from 'valtio'; +import { ContextType } from '@/constants/context'; +import * as context from '@/state/context'; +import type { ContextItem } from '@/types/context'; +import ListFooter from './ListFooter'; +import SmartText from './SmartText'; +import TooltipRender from './TooltipRender'; export type SuggestionItem = { label: React.ReactNode; value: string; - icon?: React.ReactNode; - children?: SuggestionItem[]; - extra?: React.ReactNode; + contextItem?: ContextItem; + disabled?: boolean; }; -interface Props { +interface SearchControlConfig { + searchText?: string; + onSearchStart?: () => void; +} + +interface ISuggestionListProps { className?: string; children?: React.ReactElement; open?: boolean; onOpenChange?: (open: boolean) => void; items: SuggestionItem[]; - virtual?: boolean; - onSelect?: (firstKey: string, itemValue: string) => void; - /** 返回值会覆盖默认的二级列表 */ - onSearch?: (firstKey: string, text: string) => SuggestionItem[] | void; + onSelect?: ( + firstKey: string, + itemValue: string, + contextItem?: ContextItem, + ) => void; + onSearch?: (firstKey: string, text: string) => void; + onLostFocus?: () => void; loading?: boolean; + offset?: { left: number; top: number }; + /** if not undefined, will hide the input inside the popup */ + searchControl?: SearchControlConfig; +} + +export interface ISuggestionListRef { + triggerKeyDown: (event: React.KeyboardEvent) => void; } -const useStyles = createStyles(({ css, token }) => { - return { - listItem: css` - min-width: 200px; - height: 40px; - user-select: none; - cursor: pointer; +const renderItemText = (text: React.ReactNode, searchText?: string | null) => { + if (!searchText || typeof text !== 'string') { + return text; + } else { + const searchRegex = new RegExp( + `(${searchText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, + 'gi', + ); + const parts = text.split(searchRegex); + + return parts + .map((part) => { + if (part.toLowerCase() === searchText.toLowerCase()) { + return ( + + {part} + + ); + } + return part; + }) + .filter((part) => part !== ''); + } +}; + +const SuggestionList = forwardRef( + (props, ref) => { + const { + children, + onSearch, + onOpenChange, + onSelect, + open, + items, + className, + loading, + offset, + onLostFocus, + searchControl, + } = props; - &:hover { - background-color: ${token.controlItemBgHover}; + const { t } = useTranslation(); + + const [selectedFirstKey, setSelectedFirstKey] = useState(); + const [searchResults, setSearchResults] = useState(); + const [listPointerEvents, setListPointerEvents] = + useState('auto'); + const [selectedIndex, setSelectedIndex] = useState(-1); + const inputRef = useRef(null); + const popupRef = useRef(null); + const listRef = useRef(null); + + const searchText = + inputRef.current?.input?.value ?? searchControl?.searchText; + + const firstLevelList = useMemo(() => items, [items]); + + const secondLevelList = useMemo(() => { + if (searchResults) { + return searchResults; + } else { + return ( + items.find((item) => item.value === selectedFirstKey)?.children || [] + ); } - `, - listItemLabel: css` - font-weight: 600; - `, - listItemLabelSearch: css` - color: #ff0000; - `, - listItemContent: css` - padding: 0 ${token.paddingSM}px; - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - column-gap: 12px; - `, - listItemContentMain: css` - display: flex; - align-items: center; - column-gap: 12px; - `, - listHeader: css` - display: flex; - align-items: center; - justify-content: space-between; - padding: 4px 0; - `, - listInput: css` - margin: 0 4px; - `, - list: css` - max-height: 500px; - overflow-y: auto; - width: 400px; - `, - popup: css` - background-color: ${token.colorBgElevated}; - border-radius: ${token.borderRadius}px; - border: 1px solid ${token.colorBorder}; - padding: 4px; - width: fit-content; - `, - }; -}); - -const SuggestionList = (props: Props) => { - const { - children, - onSearch, - onOpenChange, - onSelect, - open, - items, - className, - loading, - } = props; - - const { t } = useTranslation(); - const { styles } = useStyles(); - - const [selectedFirstKey, setSelectedFirstKey] = useState(); - const [searchResults, setSearchResults] = useState(); - const inputRef = useRef(null); - const popupRef = useRef(null); - - const firstLevelList = useMemo(() => items, [items]); - - const secondLevelList = useMemo(() => { - if (searchResults) { - return searchResults; - } else { - return ( - items.find((item) => item.value === selectedFirstKey)?.children || [] - ); - } - }, [items, searchResults, selectedFirstKey]); - - const clearSearch = (targetFirstKey: string) => { - if (inputRef.current?.input) { - inputRef.current.input.value = ''; - } - setSearchResults(undefined); - onSearch?.(targetFirstKey, ''); - }; - - const renderItemText = ( - text: React.ReactNode, - searchText?: string | null, - ) => { - if (!searchText || typeof text !== 'string') { - return text; - } else { - const normalTexts = text.split(searchText); - const renderedTexts = [ - normalTexts[0], - ...normalTexts.slice(1).map((text, index) => ( - - {searchText} - {text} - - )), - ]; - return renderedTexts; - } - }; - - const renderItem = (item: SuggestionItem) => { - return ( - { - if (selectedFirstKey) { - onSelect?.(selectedFirstKey, item.value); - setSelectedFirstKey(undefined); - } else { - clearSearch(item.value); - setSelectedFirstKey(item.value); - } - }} - > -
-
-
{item.icon}
- - {renderItemText(item.label, inputRef.current?.input?.value)} - -
- - {renderItemText(item.extra, inputRef.current?.input?.value)} - -
-
+ }, [items, searchResults, selectedFirstKey]); + + const currentList = useMemo( + () => (selectedFirstKey ? secondLevelList : firstLevelList), + [firstLevelList, selectedFirstKey, secondLevelList], ); - }; - const ListHeader = useMemo(() => { - if (selectedFirstKey) { - return ( -
-
+ } + }} + > +
+
+
{item.icon}
+ { + switch (selectedFirstKey) { + case ContextType.FILE: + return ( + + ); + case ContextType.SLASH_COMMAND: + return ( + item.contextItem && ( + + ) + ); + default: + return null; + } + }} + maxWidth={260} + showTip={isSelected} + placement="right" + /> +
+ {isFirstLevel && } + {!isFirstLevel && isSecondSeleted && ( + + )} +
+ ); - } else { + }; + + const handleBackClick = useCallback( + () => setSelectedFirstKey(undefined), + [], + ); + + const handleInputChange = useCallback( + (e: React.ChangeEvent) => { + if (selectedFirstKey) { + const searchResults = onSearch?.(selectedFirstKey, e.target.value); + setSearchResults(searchResults || undefined); + } + }, + [onSearch, selectedFirstKey], + ); + + const ListHeader = useMemo(() => { + if (selectedFirstKey) { + return ( +
+ + {searchControl ? ( +
+ { + firstLevelList.find((item) => item.value === selectedFirstKey) + ?.label + } +
+ ) : ( + + )} +
+ ); + } return null; - } - }, [onSearch, selectedFirstKey]); - - // auto close popup when lost focus - useEffect(() => { - if (!open) return; - function handleClickOutside(event: MouseEvent) { - if ( - popupRef.current && - !popupRef.current.contains(event.target as Node) - ) { + }, [ + selectedFirstKey, + firstLevelList, + handleBackClick, + handleInputChange, + t, + searchControl, + ]); + + // Combined effect for popup state management + useEffect(() => { + if (open) { + // Focus popup container when it opens + if (popupRef.current) { + popupRef.current.focus(); + } + if (selectedFirstKey) { - clearSearch(selectedFirstKey); + if (searchControl) { + searchControl.onSearchStart?.(); + } else { + inputRef.current?.focus(); + } } - setSelectedFirstKey(undefined); - onOpenChange?.(false); + // Set default selection (first item) + setSelectedIndex(currentList.length > 0 ? 0 : -1); } - } - document.addEventListener('mousedown', handleClickOutside); - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, [open, onOpenChange]); - - return ( - ( -
- {ListHeader} - -
- )} - trigger={[]} - arrow={false} - styles={{ - body: { - padding: 0, - }, - }} - > - {children} -
- ); -}; + }, [open, selectedFirstKey, currentList, searchControl?.onSearchStart]); + + useEffect(() => { + if (searchControl?.searchText) { + if (selectedFirstKey) { + const searchResults = onSearch?.( + selectedFirstKey, + searchControl?.searchText, + ); + setSearchResults(searchResults || undefined); + } + } + }, [searchControl?.searchText, selectedFirstKey]); + + // Handle click outside to close popup + useEffect(() => { + if (!open) return; + + function handleClickOutside(event: MouseEvent) { + if ( + popupRef.current && + !popupRef.current.contains(event.target as Node) + ) { + if (selectedFirstKey) { + clearSearch(selectedFirstKey); + } + setSelectedFirstKey(undefined); + setSelectedIndex(-1); + onOpenChange?.(false); + onLostFocus?.(); + } + } + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [open, onOpenChange, selectedFirstKey, onLostFocus]); + + // Scroll selected item into view + useEffect(() => { + if (selectedIndex >= 0 && listRef.current) { + const selectedItem = listRef.current.querySelector( + `[data-index="${selectedIndex}"]`, + ) as HTMLElement; + + if (selectedItem) { + const listContainer = + listRef.current.querySelector('.ant-list') || listRef.current; + const containerRect = listContainer.getBoundingClientRect(); + const itemRect = selectedItem.getBoundingClientRect(); + const isAbove = itemRect.top < containerRect.top; + const isBelow = itemRect.bottom > containerRect.bottom; + + if (isAbove || isBelow) { + selectedItem.scrollIntoView({ + behavior: 'auto', + block: 'nearest', + inline: 'nearest', + }); + } + } + } + }, [selectedIndex]); + + const offsetStyles = useMemo(() => { + if (offset) { + return { + ...offset, + position: 'relative', + }; + } else { + return {}; + } + }, [offset]); + + return ( + ( +
+ {ListHeader} + + +
+ )} + trigger={[]} + arrow={false} + styles={{ + body: { + padding: 0, + ...offsetStyles, + }, + }} + > + {children} +
+ ); + }, +); export default SuggestionList; diff --git a/browser/src/components/ToolApprovalConfirmation/index.tsx b/browser/src/components/ToolApprovalConfirmation/index.tsx deleted file mode 100644 index 6e2581155..000000000 --- a/browser/src/components/ToolApprovalConfirmation/index.tsx +++ /dev/null @@ -1,245 +0,0 @@ -import { CheckOutlined, CloseOutlined, EyeOutlined } from '@ant-design/icons'; -import { Button, Flex, Tag, Typography } from 'antd'; -import { createStyles } from 'antd-style'; -import { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSnapshot } from 'valtio'; -import { toolApprovalActions, toolApprovalState } from '@/state/toolApproval'; -import type { ToolApprovalRequestMessage } from '@/types/message'; - -const { Text } = Typography; - -const useStyle = createStyles(({ token, css }) => { - return { - container: css` - padding: ${token.paddingMD}px 0; - border-left: 3px solid ${token.colorWarning}; - padding-left: ${token.paddingMD}px; - background: ${token.colorWarningBg}; - border-radius: 0 ${token.borderRadiusLG}px ${token.borderRadiusLG}px 0; - margin: ${token.marginXS}px 0; - `, - title: css` - color: ${token.colorWarningText}; - font-weight: 500; - margin-bottom: ${token.marginXS}px; - `, - description: css` - color: ${token.colorTextSecondary}; - margin-bottom: ${token.marginMD}px; - `, - toolInfo: css` - background: ${token.colorBgContainer}; - border-radius: ${token.borderRadius}px; - padding: ${token.paddingSM}px; - margin-bottom: ${token.marginMD}px; - `, - params: css` - background: ${token.colorFillQuaternary}; - border-radius: ${token.borderRadius}px; - padding: ${token.paddingSM}px; - margin-top: ${token.marginXS}px; - font-family: ${token.fontFamilyCode}; - font-size: ${token.fontSizeSM}px; - max-height: 120px; - overflow-y: auto; - white-space: pre-wrap; - `, - errorAlert: css` - background: ${token.colorErrorBg}; - border: 1px solid ${token.colorErrorBorder}; - border-radius: ${token.borderRadius}px; - padding: ${token.paddingSM}px; - margin-bottom: ${token.marginMD}px; - `, - loadingIndicator: css` - color: ${token.colorPrimary}; - font-size: ${token.fontSizeSM}px; - `, - buttonGroup: css` - gap: ${token.marginXS}px; - flex-wrap: wrap; - `, - }; -}); - -interface ToolApprovalConfirmationProps { - message: ToolApprovalRequestMessage; -} - -export default function ToolApprovalConfirmation({ - message, -}: ToolApprovalConfirmationProps) { - const { t } = useTranslation(); - const { styles } = useStyle(); - const snap = useSnapshot(toolApprovalState); - const [showParams, setShowParams] = useState(false); - - // 检查当前消息是否为当前待处理的请求 - if ( - !snap.currentRequest || - snap.currentRequest.toolCallId !== message.toolCallId - ) { - return null; - } - - // 格式化工具参数描述 - const getToolDescription = ( - toolName: string, - params: Record, - ) => { - switch (toolName) { - case 'read': - return t('toolApproval.toolDescriptions.read', { - filePath: params.file_path, - }); - case 'bash': - return t('toolApproval.toolDescriptions.bash', { - command: params.command, - }); - case 'edit': - return t('toolApproval.toolDescriptions.edit', { - filePath: params.file_path, - }); - case 'write': - return t('toolApproval.toolDescriptions.write', { - filePath: params.file_path, - }); - case 'fetch': - return t('toolApproval.toolDescriptions.fetch', { url: params.url }); - case 'glob': - return t('toolApproval.toolDescriptions.glob', { - pattern: params.pattern, - }); - case 'grep': - return t('toolApproval.toolDescriptions.grep', { - pattern: params.pattern, - }); - case 'ls': - return t('toolApproval.toolDescriptions.ls', { - dirPath: params.dir_path, - }); - default: - return t('toolApproval.toolDescriptions.default', { toolName }); - } - }; - - const onApprove = (option: 'once' | 'always' | 'always_tool') => { - toolApprovalActions.approveToolUse(true, option); - }; - - const onDeny = () => { - toolApprovalActions.approveToolUse(false); - }; - - const onRetry = () => { - toolApprovalActions.retrySubmit(); - }; - - const description = getToolDescription(message.toolName, message.args); - const isSubmitting = snap.submitting; - const hasError = !!snap.submitError; - - return ( -
- {/* 标题 */} - - 🔐 {t('toolApproval.title', '工具执行权限确认')} - - - {/* 描述 */} - - {t('toolApproval.description', 'AI 请求执行以下工具,是否允许?')} - - - {/* 工具信息 */} -
- - - {message.toolName} - {description} - - - - - {showParams && ( -
-            {JSON.stringify(message.args, null, 2)}
-          
- )} -
- - {/* 错误提示 */} - {hasError && ( -
- - - -
- {t('toolApproval.submitFailed')} -
- - {snap.submitError} - -
-
- -
-
- )} - - {/* 提交中提示 */} - {isSubmitting && ( - - ⏳ {t('toolApproval.submitting')} - - )} - - {/* 操作按钮 */} - - - - - - - - - -
- ); -} diff --git a/browser/src/components/ToolApprovalError/index.tsx b/browser/src/components/ToolApprovalError/index.tsx deleted file mode 100644 index 511f9b41f..000000000 --- a/browser/src/components/ToolApprovalError/index.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { - ClockCircleOutlined, - ExclamationCircleOutlined, -} from '@ant-design/icons'; -import { Alert, Card, Space, Typography } from 'antd'; -import { useTranslation } from 'react-i18next'; -import type { ToolApprovalErrorMessage } from '@/types/message'; - -const { Text } = Typography; - -interface ToolApprovalErrorProps { - message: ToolApprovalErrorMessage; -} - -export default function ToolApprovalError({ message }: ToolApprovalErrorProps) { - const { t } = useTranslation(); - - const formatTime = (timestamp: number) => { - return new Date(timestamp).toLocaleTimeString(); - }; - - return ( - - - {t('toolApproval.error', '工具审批错误')} - - } - > - -
- {t('toolApproval.toolName', '工具名称')}: - {message.toolName} -
- - - -
- - - {formatTime(message.timestamp)} - -
-
-
- ); -} diff --git a/browser/src/components/ToolApprovalResult/index.tsx b/browser/src/components/ToolApprovalResult/index.tsx deleted file mode 100644 index 79c484aa8..000000000 --- a/browser/src/components/ToolApprovalResult/index.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons'; -import { Card, Space, Tag, Typography } from 'antd'; -import { useTranslation } from 'react-i18next'; -import type { ToolApprovalResultMessage } from '@/types/message'; - -const { Text } = Typography; - -interface ToolApprovalResultProps { - message: ToolApprovalResultMessage; -} - -export default function ToolApprovalResult({ - message, -}: ToolApprovalResultProps) { - const { t } = useTranslation(); - - const getApprovalOptionText = (option?: string) => { - switch (option) { - case 'once': - return t('toolApproval.approveOnce', '允许(一次)'); - case 'always': - return t('toolApproval.approveAlways', '允许(此命令总是)'); - case 'always_tool': - return t( - 'toolApproval.approveAlwaysTool', - `允许(${message.toolName} 总是)`, - ); - default: - return ''; - } - }; - - return ( - - {message.approved ? ( - - ) : ( - - )} - - {message.approved - ? t('toolApproval.approved', '工具执行已批准') - : t('toolApproval.denied', '工具执行已拒绝')} - - - } - > - -
- {t('toolApproval.toolName', '工具名称')}: - {message.toolName} -
- - {message.approved && message.option && ( -
- {t('toolApproval.approvalOption', '批准选项')}: - {getApprovalOptionText(message.option)} -
- )} -
-
- ); -} diff --git a/browser/src/components/ToolRender/BashRender.tsx b/browser/src/components/ToolRender/BashRender.tsx deleted file mode 100644 index d64557aea..000000000 --- a/browser/src/components/ToolRender/BashRender.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { RightOutlined } from '@ant-design/icons'; -import { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { BsTerminal } from 'react-icons/bs'; -import type { ToolMessage } from '@/types/message'; -import type { IBashToolResult } from '@/types/tool'; -import { ToolStatus } from './ToolStatus'; - -export default function BashRender({ message }: { message?: ToolMessage }) { - if (!message) return null; - const { args, result, state } = message; - const [isExpanded, setIsExpanded] = useState(false); - const { t } = useTranslation(); - - const toggleExpand = () => { - setIsExpanded(!isExpanded); - }; - - const command = (args?.command as string) || ''; - const { stdout, stderr } = (result?.data || {}) as IBashToolResult; - const output = (result as { stdout?: string })?.stdout; - - const renderContent = () => { - if (output) { - return
{output}
; - } - if (!stdout && !stderr) { - return ( -

- {t('toolRenders.bash.noOutput')} -

- ); - } - return ( -
- {stdout && ( -
{stdout}
- )} - {stderr && ( -
-            {stderr}
-          
- )} -
- ); - }; - - return ( -
-
- - - - - - {command} - -
- -
-
-
-
- {renderContent()} -
-
-
- ); -} diff --git a/browser/src/components/ToolRender/BashRender/index.tsx b/browser/src/components/ToolRender/BashRender/index.tsx new file mode 100644 index 000000000..6947eaba3 --- /dev/null +++ b/browser/src/components/ToolRender/BashRender/index.tsx @@ -0,0 +1,56 @@ +import { CheckOutlined } from '@ant-design/icons'; +import { useEffect, useState } from 'react'; +import CodeRenderer from '@/components/CodeRenderer'; +import MessageWrapper from '@/components/MessageWrapper'; +import { useClipboard } from '@/hooks/useClipboard'; +import BashIcon from '@/icons/bash.svg?react'; +import CopyIcon from '@/icons/copy.svg?react'; +import type { UIToolPart } from '@/types/chat'; + +export default function BashRender({ part }: { part: UIToolPart }) { + const { input, result } = part; + const command = (input?.command as string) || ''; + const llmContent = result?.llmContent as string; + + const { writeText } = useClipboard(); + const [isCopySuccess, setIsCopySuccess] = useState(false); + + const handleCopy = () => { + writeText(llmContent || ''); + setIsCopySuccess(true); + }; + + useEffect(() => { + if (isCopySuccess) { + const timer = setTimeout(() => { + setIsCopySuccess(false); + }, 2000); + return () => clearTimeout(timer); + } + }, [isCopySuccess]); + + return ( + } + showExpandIcon={false} + expandable={false} + expanded={!!llmContent?.length} + actions={[ + { + key: 'copy', + icon: isCopySuccess ? : , + onClick: handleCopy, + }, + ]} + > + {llmContent ? ( + + ) : null} + + ); +} diff --git a/browser/src/components/ToolRender/DebugInfo.tsx b/browser/src/components/ToolRender/DebugInfo.tsx index 846a1887e..b2d29bef4 100644 --- a/browser/src/components/ToolRender/DebugInfo.tsx +++ b/browser/src/components/ToolRender/DebugInfo.tsx @@ -1,11 +1,11 @@ import { RightOutlined } from '@ant-design/icons'; import { useState } from 'react'; -import type { ToolMessage } from '@/types/message'; +import type { UIToolPart } from '@/types/chat'; -export default function DebugInfo({ message }: { message?: ToolMessage }) { +export default function DebugInfo({ part }: { part?: UIToolPart }) { const [isExpanded, setIsExpanded] = useState(false); - if (!import.meta.env.DEV || !message) { + if (!import.meta.env.DEV || !part) { return null; } @@ -34,7 +34,7 @@ export default function DebugInfo({ message }: { message?: ToolMessage }) { }`} >
-          {JSON.stringify(message, null, 2)}
+          {JSON.stringify(part, null, 2)}
         
diff --git a/browser/src/components/ToolRender/EditRender.tsx b/browser/src/components/ToolRender/EditRender.tsx deleted file mode 100644 index 18c3514c9..000000000 --- a/browser/src/components/ToolRender/EditRender.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { useEffect, useMemo } from 'react'; -import { useSnapshot } from 'valtio'; -import { fileChangesActions, fileChangesState } from '@/state/fileChanges'; -import type { ToolMessage } from '@/types/message'; -import CodeDiffOutline from '../CodeDiffOutline'; - -export default function EditRender({ message }: { message?: ToolMessage }) { - if (!message) { - return null; - } - const { toolCallId, args, state } = message; - const { file_path, old_string, new_string } = args as { - file_path: string; - old_string: string; - new_string: string; - }; - - useEffect(() => { - fileChangesActions.initFileState(file_path, [ - { toolCallId, old_string, new_string }, - ]); - }, [file_path, toolCallId, old_string, new_string]); - - const { files } = useSnapshot(fileChangesState); - - const editStatus = useMemo(() => { - return files[file_path]?.edits.find( - (edit) => edit.toolCallId === toolCallId, - )?.editStatus; - }, [files, file_path, toolCallId]); - - return ( - - ); -} diff --git a/browser/src/components/ToolRender/EditRender/index.tsx b/browser/src/components/ToolRender/EditRender/index.tsx new file mode 100644 index 000000000..f1770d2bf --- /dev/null +++ b/browser/src/components/ToolRender/EditRender/index.tsx @@ -0,0 +1,24 @@ +import CodeDiffOutline from '@/components/CodeDiffOutline'; +import type { UIToolPart } from '@/types/chat'; + +export default function EditRender({ part }: { part: UIToolPart }) { + const { id, input, state } = part; + + const { file_path, old_string, new_string } = input as { + file_path: string; + old_string: string; + new_string: string; + }; + + return ( + + ); +} diff --git a/browser/src/components/ToolRender/FailRender.tsx b/browser/src/components/ToolRender/FailRender.tsx index b3f629ab9..96dcf982f 100644 --- a/browser/src/components/ToolRender/FailRender.tsx +++ b/browser/src/components/ToolRender/FailRender.tsx @@ -1,12 +1,21 @@ import { CloseCircleOutlined, RightOutlined } from '@ant-design/icons'; -import { useState } from 'react'; +import { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import type { ToolMessage } from '@/types/message'; +import type { UIToolPart } from '@/types/chat'; -export default function FailRender({ message }: { message: ToolMessage }) { +export default function FailRender({ part }: { part: UIToolPart }) { const { t } = useTranslation(); const [isExpanded, setIsExpanded] = useState(false); + const message = useMemo(() => { + const { result } = part; + let text = result?.returnDisplay || result?.llmContent; + if (typeof text !== 'string') { + text = JSON.stringify(text); + } + return text; + }, [part]); + const handleToggle = () => { setIsExpanded(!isExpanded); }; @@ -27,7 +36,7 @@ export default function FailRender({ message }: { message: ToolMessage }) { -
{t('tool.callFailed', { toolName: message.toolName })}
+
{t('tool.callFailed', { toolName: part.name })}
{ - setIsExpanded(!isExpanded); - }; - - const url = (args?.url as string) || ''; - const { result: fetchResult, durationMs } = (result?.data || - {}) as IFetchToolResult; - - const renderContent = () => { - return ( -
-

{fetchResult}

-
- ); - }; - - return ( -
-
- - - - -
{url}
-
- {state === 'result' && {durationMs}ms} - -
-
-
-
{renderContent()}
-
-
- ); -} diff --git a/browser/src/components/ToolRender/FetchRender/index.tsx b/browser/src/components/ToolRender/FetchRender/index.tsx new file mode 100644 index 000000000..8a3e8f168 --- /dev/null +++ b/browser/src/components/ToolRender/FetchRender/index.tsx @@ -0,0 +1,59 @@ +import { LoadingOutlined } from '@ant-design/icons'; +import { useMemo } from 'react'; +import MessageWrapper from '@/components/MessageWrapper'; +import { useClipboard } from '@/hooks/useClipboard'; +import CopyIcon from '@/icons/copy.svg?react'; +import SearchIcon from '@/icons/search.svg?react'; +import type { UIToolPart } from '@/types/chat'; +import { jsonSafeParse } from '@/utils/message'; + +export default function FetchRender({ part }: { part: UIToolPart }) { + const { input, state, result } = part; + + const { writeText } = useClipboard(); + + const url = (input?.url as string) || ''; + const prompt = (input?.prompt as string) || ''; + + const actions = useMemo(() => { + if (state === 'tool_result') { + return [ + { + key: 'success', + icon: , + onClick: () => { + writeText(url); + }, + }, + ]; + } + + return [ + { + key: 'loading', + icon: ( + + ), + }, + ]; + }, [state]); + + const llmContent = useMemo(() => { + if (typeof result?.llmContent === 'string') { + return jsonSafeParse(result?.llmContent)?.result; + } + return null; + }, [result?.llmContent]); + + return ( + } + title={`${prompt} ${url}`} + actions={actions} + > +
+ {llmContent} +
+
+ ); +} diff --git a/browser/src/components/ToolRender/GlobRender.tsx b/browser/src/components/ToolRender/GlobRender.tsx deleted file mode 100644 index 6a3e920f6..000000000 --- a/browser/src/components/ToolRender/GlobRender.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { CodeOutlined, RightOutlined } from '@ant-design/icons'; -import { useState } from 'react'; -import type { ToolMessage } from '@/types/message'; -import type { IGlobToolResult } from '@/types/tool'; -import InnerList, { type ListItem } from './InnerList'; -import { ToolStatus } from './ToolStatus'; - -export default function GlobRender({ message }: { message?: ToolMessage }) { - if (!message) return null; - - const { toolName, result, state } = message; - const [isExpanded, setIsExpanded] = useState(true); - - const toggleExpand = () => { - setIsExpanded(!isExpanded); - }; - - const renderContent = () => { - if (typeof result === 'string') { - return
{result}
; - } - - if (result?.data) { - const { filenames, message } = result.data as IGlobToolResult; - - const items: ListItem[] = filenames.map((filename) => ({ - name: filename, - isDirectory: filename.endsWith('/'), - })); - - return ( -
- {message &&

{message}

} - -
- ); - } - - if (typeof result === 'object' && result !== null) { - return
{JSON.stringify(result, null, 2)}
; - } - return null; - }; - - return ( -
-
- - - - - {toolName} - -
-
-
- {renderContent()} -
-
-
- ); -} diff --git a/browser/src/components/ToolRender/GlobRender/index.tsx b/browser/src/components/ToolRender/GlobRender/index.tsx new file mode 100644 index 000000000..e92161b9f --- /dev/null +++ b/browser/src/components/ToolRender/GlobRender/index.tsx @@ -0,0 +1,32 @@ +import { useMemo } from 'react'; +import { useSnapshot } from 'valtio'; +import MessageWrapper from '@/components/MessageWrapper'; +import FolderIcon from '@/icons/folder.svg?react'; +import { state } from '@/state/chat'; +import type { UIToolPart } from '@/types/chat'; +import { jsonSafeParse } from '@/utils/message'; +import InnerList, { type ListItem } from '../LsRender/InnerList'; + +export default function GlobRender({ part }: { part: UIToolPart }) { + const snap = useSnapshot(state); + const { name, result } = part; + const filenames = useMemo(() => { + if (typeof result?.llmContent === 'string') { + return jsonSafeParse(result?.llmContent)?.filenames || []; + } + return []; + }, [result?.llmContent]); + + const items = useMemo(() => { + return filenames.map((filename) => ({ + name: snap.cwd ? filename.replace(snap.cwd, '') : filename, + isDirectory: filename.endsWith('/'), + })); + }, [filenames, snap.cwd]); + + return ( + } title={name}> + + + ); +} diff --git a/browser/src/components/ToolRender/GrepRender.tsx b/browser/src/components/ToolRender/GrepRender.tsx deleted file mode 100644 index 2559e7234..000000000 --- a/browser/src/components/ToolRender/GrepRender.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { RightOutlined } from '@ant-design/icons'; -import { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { VscSearch } from 'react-icons/vsc'; -import type { ToolMessage } from '@/types/message'; -import type { IGrepToolResult } from '@/types/tool'; -import InnerList, { type ListItem } from './InnerList'; -import { ToolStatus } from './ToolStatus'; - -export default function GrepRender({ message }: { message?: ToolMessage }) { - if (!message) return null; - const { t } = useTranslation(); - - const { result, args, state } = message; - const [isExpanded, setIsExpanded] = useState(true); - const { filenames, durationMs } = (result?.data || {}) as IGrepToolResult; - - const toggleExpand = () => { - setIsExpanded(!isExpanded); - }; - - const renderContent = () => { - if (!filenames?.length) return null; - const items: ListItem[] = filenames.map((filename) => ({ - name: filename, - isDirectory: filename.endsWith('/'), - })); - return ; - }; - - return ( -
-
- - - - -
- - {t('toolRenders.grep.grep')}  - - {(args?.pattern as string) || ''} - -  {' '} - {t('toolRenders.grep.inFiles', { count: filenames?.length || 0 })} - -
- {durationMs &&

{durationMs}ms

} - -
-
-
-
-
- {renderContent()} -
-
-
- ); -} diff --git a/browser/src/components/ToolRender/GrepRender/index.tsx b/browser/src/components/ToolRender/GrepRender/index.tsx new file mode 100644 index 000000000..9719f5584 --- /dev/null +++ b/browser/src/components/ToolRender/GrepRender/index.tsx @@ -0,0 +1,42 @@ +import { useMemo } from 'react'; +import { useSnapshot } from 'valtio'; +import MessageWrapper from '@/components/MessageWrapper'; +import SearchIcon from '@/icons/grep-search.svg?react'; +import SuccessIcon from '@/icons/success.svg?react'; +import { state } from '@/state/chat'; +import type { UIToolPart } from '@/types/chat'; +import { formatParamsDescription, jsonSafeParse } from '@/utils/message'; +import type { ListItem } from '../LsRender/InnerList'; +import InnerList from '../LsRender/InnerList'; + +export default function GrepRender({ part }: { part: UIToolPart }) { + const snap = useSnapshot(state); + const { result } = part; + const title = useMemo(() => { + return formatParamsDescription(part.input); + }, [part.input]); + + const filenames = useMemo(() => { + if (typeof result?.llmContent === 'string') { + return jsonSafeParse(result?.llmContent)?.filenames || []; + } + return []; + }, [result?.llmContent]); + + const items = useMemo(() => { + return filenames.map((filename) => ({ + name: snap.cwd ? filename.replace(snap.cwd, '') : filename, + isDirectory: filename.endsWith('/'), + })); + }, [filenames, snap.cwd]); + + return ( + } + statusIcon={} + title={title} + > + + + ); +} diff --git a/browser/src/components/ToolRender/LsRender.tsx b/browser/src/components/ToolRender/LsRender.tsx deleted file mode 100644 index 68067a546..000000000 --- a/browser/src/components/ToolRender/LsRender.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { FolderOutlined, RightOutlined } from '@ant-design/icons'; -import { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import type { ToolMessage } from '@/types/message'; -import InnerList, { type ListItem } from './InnerList'; -import { ToolStatus } from './ToolStatus'; - -const parseLsResult = (result: unknown): ListItem[] => { - if (typeof result !== 'string' || !result) return []; - - const lines = result.trim().split('\n'); - const rootItems: ListItem[] = []; - const parentStack: ListItem[] = []; - - // Edge case for single line path from original code. - if (lines.length === 1 && !lines[0].trim().startsWith('- ')) { - const name = lines[0].trim(); - return [ - { - name: name.endsWith('/') ? name.slice(0, -1) : name, - isDirectory: name.endsWith('/'), - }, - ]; - } - - lines.forEach((line) => { - const match = line.match(/^(\s*)- (.*)/); - if (!match) return; - - const indentation = match[1].length; - const level = Math.floor(indentation / 2); - - let name = match[2]; - const isDirectory = name.endsWith('/'); - if (isDirectory) { - name = name.slice(0, -1); - } - - const newItem: ListItem = { - name, - isDirectory, - children: isDirectory ? [] : undefined, - }; - - while (parentStack.length > level) { - parentStack.pop(); - } - - if (parentStack.length === 0) { - rootItems.push(newItem); - } else { - const parent = parentStack[parentStack.length - 1]; - parent.children?.push(newItem); - } - - if (newItem.isDirectory) { - parentStack.push(newItem); - } - }); - - return rootItems; -}; - -export default function LsRender({ message }: { message?: ToolMessage }) { - if (!message) return null; - - const { state } = message; - const items = parseLsResult(message.result?.data); - const dirPath = (message.args?.dir_path as string) || ''; - const { t } = useTranslation(); - - const [isExpanded, setIsExpanded] = useState(true); - - const toggleExpand = () => { - if (items.length > 0) { - setIsExpanded(!isExpanded); - } - }; - - let displayPath = dirPath; - let itemsCount = items.length; - - if (items.length === 1 && items[0].isDirectory) { - displayPath = items[0].name; - itemsCount = items[0].children?.length || 0; - } - - return ( -
-
- - - - - - {t('toolRenders.ls.listedItems', { - count: itemsCount, - path: displayPath.split('/').pop() || displayPath, - })} - - -
-
0 ? 'max-h-40' : 'max-h-0' - }`} - > - -
-
- ); -} diff --git a/browser/src/components/ToolRender/InnerList.tsx b/browser/src/components/ToolRender/LsRender/InnerList.tsx similarity index 70% rename from browser/src/components/ToolRender/InnerList.tsx rename to browser/src/components/ToolRender/LsRender/InnerList.tsx index e2b0ff4ce..fdaafab4d 100644 --- a/browser/src/components/ToolRender/InnerList.tsx +++ b/browser/src/components/ToolRender/LsRender/InnerList.tsx @@ -1,6 +1,3 @@ -/** - * 渲染toolRender中涉及到list展示的组件 - */ import { DatabaseOutlined, FileImageOutlined, @@ -9,9 +6,7 @@ import { FileProtectOutlined, FileTextOutlined, FileZipOutlined, - FolderOutlined, LockOutlined, - RightOutlined, } from '@ant-design/icons'; import { useState } from 'react'; import { FaJava } from 'react-icons/fa'; @@ -31,6 +26,9 @@ import { SiTypescript, SiYaml, } from 'react-icons/si'; +import FolderIcon from '@/icons/folder.svg?react'; +import RightArrowIcon from '@/icons/rightArrow.svg?react'; +import styles from './index.module.css'; export interface ListItem { name: string; @@ -46,7 +44,7 @@ interface InnerListProps { const getIconForFile = (filename: string) => { if (filename.endsWith('/')) { - return ; + return ; } const extension = filename.split('.').pop()?.toLowerCase(); @@ -120,45 +118,52 @@ const RenderItem = ({ }) => { const [isExpanded, setIsExpanded] = useState(false); - const hasChildren = item.children && item.children.length > 0; + // flatten children count + const childrenCount = + item.children?.reduce( + (acc, child) => acc + (child.children?.length || 0), + 0, + ) || 0; const toggleExpand = () => { - if (hasChildren) { + if (childrenCount > 0) { setIsExpanded(!isExpanded); } }; return ( <> -
  • +
  • 0 && isExpanded + ? 'rotate(90deg)' + : 'rotate(0deg)', transition: 'transform 0.2s ease-in-out', }} > - {hasChildren ? ( - - ) : ( - - )} + {childrenCount > 0 && } - - {item.isDirectory ? : getIconForFile(item.name)} + + {item.isDirectory ? : getIconForFile(item.name)} - + {showPath ? item.name : item.name.split('/').pop()} + + {childrenCount > 0 ? childrenCount : null} +
  • - {hasChildren && isExpanded && ( -
      + {childrenCount > 0 && isExpanded && ( +
        {item.children?.map((child, index) => ( - + ))}
      )} @@ -168,9 +173,9 @@ const RenderItem = ({ export default function InnerList({ items, showPath = true }: InnerListProps) { return ( -
        - {items.map((item, index) => ( - +
          + {items.map((item) => ( + ))}
        ); diff --git a/browser/src/components/ToolRender/LsRender/index.module.css b/browser/src/components/ToolRender/LsRender/index.module.css new file mode 100644 index 000000000..37aa69af7 --- /dev/null +++ b/browser/src/components/ToolRender/LsRender/index.module.css @@ -0,0 +1,61 @@ +.itemContainer { + display: flex; + gap: 6px; + align-items: center; +} + +.itemIcon { + width: 14px; + height: 14px; + color: #666f8d; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; +} + +.itemTitle { + font-family: PingFang HK; + font-weight: 400; + font-style: Regular; + font-size: 12px; + letter-spacing: 0%; + color: #110c22; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; +} + +.listContainer { + display: flex; + flex-direction: column; + gap: 16px; +} + +.itemCount { + font-family: Inter; + font-weight: 600; + font-style: Semi Bold; + font-size: 8px; + letter-spacing: 0%; + color: #666f8d; + + background-color: #f7f8fa; + border-radius: 50%; + padding: 2px 4px; + + display: flex; + align-items: center; + justify-content: center; +} + +.expandIcon { + transform: rotate(0deg); + transition: transform 0.2s ease-in-out; +} + +.expandIconExpanded { + transform: rotate(90deg); + transition: transform 0.2s ease-in-out; +} diff --git a/browser/src/components/ToolRender/LsRender/index.tsx b/browser/src/components/ToolRender/LsRender/index.tsx new file mode 100644 index 000000000..c5e20d50e --- /dev/null +++ b/browser/src/components/ToolRender/LsRender/index.tsx @@ -0,0 +1,104 @@ +import MessageWrapper from '@/components/MessageWrapper'; +import FolderIcon from '@/icons/folder.svg?react'; +import type { UIToolPart } from '@/types/chat'; +import InnerList, { type ListItem } from './InnerList'; + +// Find the node by targetPath in the tree structure +const findNodeByPath = ( + items: ListItem[], + targetPath: string, + currentPath: string = '', +): ListItem | null => { + for (const item of items) { + // Concatenate the current path + const fullPath = currentPath ? `${currentPath}/${item.name}` : item.name; + + // Check if the full path matches the target path + if (fullPath === targetPath) { + return item; + } + + // If the current item is a directory and the target path starts with the current path, continue searching recursively + if (item.children && targetPath.startsWith(fullPath + '/')) { + const found = findNodeByPath(item.children, targetPath, fullPath); + if (found) return found; + } + } + return null; +}; + +const parseLsResult = (result: unknown, parentPath: string): ListItem[] => { + if (typeof result !== 'string' || !result) return []; + + const lines = result.trim().split('\n'); + const rootItems: ListItem[] = []; + const parentStack: ListItem[] = []; + + // Edge case for single line path from original code. + if (lines.length === 1 && !lines[0].trim().startsWith('- ')) { + const name = lines[0].trim(); + return [ + { + name: name.endsWith('/') ? name.slice(0, -1) : name, + isDirectory: name.endsWith('/'), + }, + ]; + } + + lines.forEach((line) => { + const match = line.match(/^(\s*)- (.*)/); + if (!match) return; + + const indentation = match[1].length; + const level = Math.floor(indentation / 2); + + let name = match[2]; + const isDirectory = name.endsWith('/'); + if (isDirectory) { + name = name.slice(0, -1); + } + + const newItem: ListItem = { + name, + isDirectory, + children: isDirectory ? [] : undefined, + }; + + while (parentStack.length > level) { + parentStack.pop(); + } + + if (parentStack.length === 0) { + rootItems.push(newItem); + } else { + const parent = parentStack[parentStack.length - 1]; + parent.children?.push(newItem); + } + + if (newItem.isDirectory) { + parentStack.push(newItem); + } + }); + + // flatten rootItems, remove parentPath from the result + if (parentPath && rootItems.length > 0) { + // find the target node by full parentPath + const targetNode = findNodeByPath(rootItems, parentPath); + if (targetNode && targetNode?.children) { + return targetNode.children; + } + } + + return rootItems; +}; + +export default function LsRender({ part }: { part: UIToolPart }) { + const dirPath = (part.input?.dir_path as string) || ''; + const items = parseLsResult(part.result?.llmContent, dirPath); + + return ( + }> + + + ); +} diff --git a/browser/src/components/ToolRender/ReadRender.tsx b/browser/src/components/ToolRender/ReadRender.tsx deleted file mode 100644 index a5e14ec8d..000000000 --- a/browser/src/components/ToolRender/ReadRender.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { EyeOutlined } from '@ant-design/icons'; -import { useTranslation } from 'react-i18next'; -import type { ToolMessage } from '@/types/message'; -import type { IReadToolResult } from '@/types/tool'; -import { ToolStatus } from './ToolStatus'; - -export default function ReadRender({ message }: { message?: ToolMessage }) { - if (!message) return null; - const { args, result, state } = message; - const { t } = useTranslation(); - - return ( -
        - -
        - {t('toolRenders.read.read')} {args?.file_path as string} -
        -
        - {(result?.data as IReadToolResult)?.totalLines}{' '} - {t('toolRenders.read.lines')} -
        - -
        - ); -} diff --git a/browser/src/components/ToolRender/ReadRender/index.tsx b/browser/src/components/ToolRender/ReadRender/index.tsx new file mode 100644 index 000000000..115c9c95c --- /dev/null +++ b/browser/src/components/ToolRender/ReadRender/index.tsx @@ -0,0 +1,117 @@ +import { CheckOutlined, ExpandAltOutlined } from '@ant-design/icons'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import CodeRenderer from '@/components/CodeRenderer'; +import MessageWrapper from '@/components/MessageWrapper'; +import { useClipboard } from '@/hooks/useClipboard'; +import CopyIcon from '@/icons/copy.svg?react'; +import ReadFileIcon from '@/icons/readFile.svg?react'; +import * as codeViewer from '@/state/codeViewer'; +import type { UIToolPart } from '@/types/chat'; +import { jsonSafeParse } from '@/utils/message'; + +export default function ReadRender({ part }: { part: UIToolPart }) { + const { input, result } = part; + + const { writeText } = useClipboard(); + const [isCopySuccess, setIsCopySuccess] = useState(false); + const { t } = useTranslation(); + + const file_path = input?.file_path; + const language = useMemo( + () => input?.file_path?.split('.').pop() || 'text', + [input?.file_path], + ); + + const content = useMemo(() => { + if (typeof result?.llmContent === 'string') { + return jsonSafeParse(result?.llmContent); + } + return null; + }, [result?.llmContent]); + + const code = content?.content || ''; + + const handleCopy = useCallback(() => { + if (!code) return; + writeText(code); + setIsCopySuccess(true); + }, [code, writeText]); + + const handleShowCodeViewer = useCallback(() => { + if (!file_path || !code) return; + codeViewer.actions.openCodeViewer(file_path, code, code, 'new'); + }, [file_path, code]); + + useEffect(() => { + if (isCopySuccess) { + const timer = setTimeout(() => { + setIsCopySuccess(false); + }, 2000); + return () => clearTimeout(timer); + } + }, [isCopySuccess]); + + const actions = useMemo( + () => [ + { + key: 'copy', + icon: isCopySuccess ? : , + onClick: handleCopy, + disabled: !code, + }, + { + key: 'expand', + icon: , + onClick: handleShowCodeViewer, + disabled: !file_path || !code, + }, + ], + [isCopySuccess, handleCopy, handleShowCodeViewer, code, file_path], + ); + + if (!file_path) { + return ( + } + defaultExpanded={true} + > +
        + {t('toolRenders.error.file.pathEmpty')} +
        +
        + ); + } + + if (!code) { + return ( + } + defaultExpanded={true} + actions={actions} + > +
        + {t('toolRenders.error.file.contentEmpty')} +
        +
        + ); + } + + return ( + } + defaultExpanded={true} + actions={actions} + > + + + ); +} diff --git a/browser/src/components/ToolRender/TodoRender/TodoList/index.module.css b/browser/src/components/ToolRender/TodoRender/TodoList/index.module.css new file mode 100644 index 000000000..88e2af731 --- /dev/null +++ b/browser/src/components/ToolRender/TodoRender/TodoList/index.module.css @@ -0,0 +1,79 @@ +.todoList { + display: flex; + flex-direction: column; + gap: 0; +} + +.todoItem { + display: flex; + align-items: center; + padding: 6px 6px; + gap: 8px; + transition: background-color 0.2s ease; +} + +.todoItem:hover { + background-color: #f9fafb; +} + +.todoCheckbox { + position: relative; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + width: 16px; + height: 16px; + border-radius: 2px; +} + +.todoCheckboxPending { + background-color: #ffffff; + border: 1px solid #d1d5db; +} + +.loadingIcon { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.loadingSpinner { + width: 16px; + height: 16px; + color: #7357ff; + animation: spin 1s linear infinite; +} + +.todoText { + font-family: "PingFang SC", -apple-system, BlinkMacSystemFont, sans-serif; + font-size: 14px; + line-height: 1.4285714285714286em; + color: #252931; + flex: 1; +} + +.noTodos { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 2rem 0; + color: #6b7280; +} + +.noTodosText { + margin-top: 0.5rem; + font-size: 14px; +} diff --git a/browser/src/components/ToolRender/TodoRender/TodoList/index.tsx b/browser/src/components/ToolRender/TodoRender/TodoList/index.tsx new file mode 100644 index 000000000..4e51c81a3 --- /dev/null +++ b/browser/src/components/ToolRender/TodoRender/TodoList/index.tsx @@ -0,0 +1,82 @@ +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import TodoCompleted from '@/icons/todo-completed.svg?react'; +import TodoLoading from '@/icons/todo-progress.svg?react'; +import styles from './index.module.css'; + +export interface TodoItem { + id: string; + content: string; + status: 'pending' | 'in_progress' | 'completed'; + priority: 'low' | 'medium' | 'high'; +} + +interface TodoListProps { + oldTodos: TodoItem[]; + newTodos: TodoItem[]; +} + +const statusWeights = { + completed: 0, + in_progress: 1, + pending: 2, +}; + +const priorityWeights = { + high: 0, + medium: 1, + low: 2, +}; + +function compareTodos(todoA: TodoItem, todoB: TodoItem) { + // Sort by status first + const statusDiff = statusWeights[todoA.status] - statusWeights[todoB.status]; + if (statusDiff !== 0) return statusDiff; + + // Then sort by priority + return priorityWeights[todoA.priority] - priorityWeights[todoB.priority]; +} + +const TodoList: React.FC = ({ newTodos }) => { + const { t } = useTranslation(); + + const todos = useMemo(() => { + return [...newTodos].sort(compareTodos); + }, [newTodos]); + + if (newTodos.length === 0) { + return ( +
        +

        + {String(t('toolRenders.todo.noTodos'))} +

        +
        + ); + } + + return ( +
        + {todos.map((todo) => { + return ( +
        +
        + {todo.status === 'completed' && } + {todo.status === 'in_progress' && ( +
        + +
        + )} +
        + {todo.content} +
        + ); + })} +
        + ); +}; + +export default TodoList; diff --git a/browser/src/components/ToolRender/TodoRender/index.module.css b/browser/src/components/ToolRender/TodoRender/index.module.css new file mode 100644 index 000000000..2254d446c --- /dev/null +++ b/browser/src/components/ToolRender/TodoRender/index.module.css @@ -0,0 +1,21 @@ +.errorContainer { + font-size: 14px; + color: #dc2626; + padding: 0.75rem; +} + +.errorDetail { + margin-top: 0.5rem; + font-size: 12px; +} + +.errorContainer { + font-size: 14px; + color: #dc2626; + padding: 0.75rem; +} + +.errorDetail { + margin-top: 0.5rem; + font-size: 12px; +} diff --git a/browser/src/components/ToolRender/TodoRender/index.tsx b/browser/src/components/ToolRender/TodoRender/index.tsx new file mode 100644 index 000000000..7f12a1503 --- /dev/null +++ b/browser/src/components/ToolRender/TodoRender/index.tsx @@ -0,0 +1,92 @@ +import { useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import MessageWrapper from '@/components/MessageWrapper'; +import TodoIcon from '@/icons/todo.svg?react'; +import type { UIToolPart } from '@/types/chat'; +import styles from './index.module.css'; +import TodoList, { type TodoItem } from './TodoList'; + +interface TodoReadResult { + llmContent: string; + isError?: boolean; + returnDisplay: { + type: 'todo_read'; + todos: TodoItem[]; + }; +} + +interface TodoWriteResult { + llmContent: string; + isError?: boolean; + returnDisplay: { + type: 'todo_write'; + oldTodos: TodoItem[]; + newTodos: TodoItem[]; + }; +} + +interface TodoRenderProps { + part?: UIToolPart; +} + +const TodoRender: React.FC = ({ part }) => { + const { t } = useTranslation(); + const [isExpanded, setIsExpanded] = useState(true); + const result = part?.result as TodoReadResult | TodoWriteResult; + + const todos = useMemo<{ + oldTodos: TodoItem[]; + newTodos: TodoItem[]; + }>(() => { + if (result?.returnDisplay?.type === 'todo_read') { + return { + oldTodos: [], + newTodos: result.returnDisplay.todos, + }; + } + if (result?.returnDisplay?.type === 'todo_write') { + return { + oldTodos: result.returnDisplay.oldTodos, + newTodos: result.returnDisplay.newTodos, + }; + } + return { + oldTodos: [], + newTodos: [], + }; + }, [result]); + + if (todos.newTodos.length === 0) { + return ( + } + defaultExpanded={true} + expandable={false} + > +
        + {String(t('toolRenders.todo.operationFailed'))} +
        {result.llmContent}
        +
        +
        + ); + } + + return ( + } + expanded={isExpanded} + onExpandChange={setIsExpanded} + expandable={true} + defaultExpanded={true} + maxHeight={300} + showGradientMask={true} + className="todo-render-wrapper" + > + + + ); +}; + +export default TodoRender; diff --git a/browser/src/components/ToolRender/ToolStatus.tsx b/browser/src/components/ToolRender/ToolStatus.tsx deleted file mode 100644 index 781986dd3..000000000 --- a/browser/src/components/ToolRender/ToolStatus.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { - CheckCircleFilled, - CloseCircleFilled, - LoadingOutlined, -} from '@ant-design/icons'; -import type { ToolMessage } from '@/types/message'; - -interface ToolStatusProps { - state: ToolMessage['state']; -} - -export function ToolStatus({ state }: ToolStatusProps) { - if (state === 'call') { - return ; - } - if (state === 'result') { - return ; - } - return ; -} diff --git a/browser/src/components/ToolRender/WriteRender.tsx b/browser/src/components/ToolRender/WriteRender.tsx deleted file mode 100644 index 84c875a5f..000000000 --- a/browser/src/components/ToolRender/WriteRender.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { useEffect, useMemo } from 'react'; -import { useSnapshot } from 'valtio'; -import { fileChangesActions, fileChangesState } from '@/state/fileChanges'; -import type { ToolMessage } from '@/types/message'; -import CodeDiffOutline from '../CodeDiffOutline'; - -// write tool认为都是新增的 -export default function WriteRender({ message }: { message?: ToolMessage }) { - if (!message) { - return null; - } - const { args, toolCallId, state } = message; - const { file_path, content } = args as { - file_path: string; - content: string; - }; - - useEffect(() => { - fileChangesActions.initNewFileState(file_path, content, [ - { - toolCallId, - old_string: '', - new_string: content, - }, - ]); - }, [file_path, content]); - - const { files } = useSnapshot(fileChangesState); - - const editStatus = useMemo(() => { - return files[file_path]?.edits.find( - (edit) => edit.toolCallId === toolCallId, - )?.editStatus; - }, [files, file_path, toolCallId]); - - return ( - - ); -} diff --git a/browser/src/components/ToolRender/WriteRender/index.tsx b/browser/src/components/ToolRender/WriteRender/index.tsx new file mode 100644 index 000000000..7a9113f9d --- /dev/null +++ b/browser/src/components/ToolRender/WriteRender/index.tsx @@ -0,0 +1,22 @@ +import CodeDiffOutline from '@/components/CodeDiffOutline'; +import type { UIToolPart } from '@/types/chat'; + +export default function WriteRender({ part }: { part: UIToolPart }) { + const { id, input, state } = part; + const { file_path, content } = input as { + file_path: string; + content: string; + }; + + return ( + + ); +} diff --git a/browser/src/components/ToolRender/index.tsx b/browser/src/components/ToolRender/index.tsx index 5e0a498b7..4b5a51872 100644 --- a/browser/src/components/ToolRender/index.tsx +++ b/browser/src/components/ToolRender/index.tsx @@ -6,6 +6,7 @@ import GlobRenderComponent from './GlobRender'; import GrepRenderComponent from './GrepRender'; import LsRenderComponent from './LsRender'; import ReadRenderComponent from './ReadRender'; +import TodoRenderComponent from './TodoRender'; import WriteRenderComponent from './WriteRender'; import { withDebugInfo } from './withDebugInfo'; @@ -18,3 +19,4 @@ export const ReadRender = withDebugInfo(ReadRenderComponent); export const EditRender = withDebugInfo(EditRenderComponent); export const WriteRender = withDebugInfo(WriteRenderComponent); export const FailRender = withDebugInfo(FailRenderComponent); +export const TodoRender = withDebugInfo(TodoRenderComponent); diff --git a/browser/src/components/ToolRender/withDebugInfo.tsx b/browser/src/components/ToolRender/withDebugInfo.tsx index 6f37b0ae5..aca125e38 100644 --- a/browser/src/components/ToolRender/withDebugInfo.tsx +++ b/browser/src/components/ToolRender/withDebugInfo.tsx @@ -1,8 +1,8 @@ -import type { ToolMessage } from '@/types/message'; +import type { UIToolPart } from '@/types/chat'; import DebugInfo from './DebugInfo'; interface ToolRenderProps { - message?: ToolMessage; + part?: UIToolPart; } export function withDebugInfo

        ( @@ -12,7 +12,7 @@ export function withDebugInfo

        ( return (

        - +
        ); }; diff --git a/browser/src/components/Truncate/index.tsx b/browser/src/components/Truncate/index.tsx new file mode 100644 index 000000000..7ac762286 --- /dev/null +++ b/browser/src/components/Truncate/index.tsx @@ -0,0 +1,41 @@ +import { Tooltip } from 'antd'; +import classNames from 'classnames'; +import { useLayoutEffect, useRef, useState } from 'react'; + +interface TruncateProps { + children: React.ReactNode; + className?: string; +} + +const Truncate: React.FC = ({ children, className }) => { + const textRef = useRef(null); + const [isTruncated, setIsTruncated] = useState(false); + + useLayoutEffect(() => { + const element = textRef.current; + if (!element) return; + + const checkTruncation = () => { + setIsTruncated(element.scrollWidth > element.clientWidth); + }; + + checkTruncation(); + + const resizeObserver = new ResizeObserver(checkTruncation); + resizeObserver.observe(element); + + return () => { + resizeObserver.disconnect(); + }; + }, [children]); + + return ( + +
        + {children} +
        +
        + ); +}; + +export default Truncate; diff --git a/browser/src/components/UserMessage/UserMessage.tsx b/browser/src/components/UserMessage/UserMessage.tsx index 22ee2f600..bf6706189 100644 --- a/browser/src/components/UserMessage/UserMessage.tsx +++ b/browser/src/components/UserMessage/UserMessage.tsx @@ -1,29 +1,108 @@ -import { memo } from 'react'; -import { AI_CONTEXT_NODE_CONFIGS } from '@/constants/context'; -import type { UIUserMessage } from '@/types/message'; -import LexicalTextArea from '../ChatSender/LexicalTextArea'; -import { LexicalTextAreaContext } from '../ChatSender/LexicalTextAreaContext'; +import { createStyles, cx } from 'antd-style'; +import { memo, useMemo } from 'react'; +import QuillEditor from '@/components/QuillEditor'; +import { QuillContext } from '@/components/QuillEditor/QuillContext'; +import { convertTextToDelta } from '@/components/QuillEditor/utils'; +import { BLOT_NAME_CONTENT_REGEX } from '@/constants'; +import type { UIUserMessage } from '@/types/chat'; +import { getMessageText, isCanceledMessage } from '@/utils/message'; interface UserMessageProps { message: UIUserMessage; } -const UserMessage = (props: UserMessageProps) => { +const useStyles = createStyles(({ css }) => ({ + container: css` + display: flex; + justify-content: flex-end; + width: 100%; + `, + messageBox: css` + background: #f6f8fb; + border-radius: 10px; + max-width: 600px; + width: fit-content; + + .ql-editor { + font-family: + 'PingFang SC', + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + sans-serif; + font-size: 14px; + line-height: 1.5em; + color: #110c22; + } + + p { + margin: 0; + } + + .ql-editor p { + margin: 0 !important; + line-height: 1.5em !important; + } + `, + textWrapper: css` + padding: 12px 15px; + font-family: + 'PingFang SC', + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + sans-serif; + font-size: 14px; + line-height: 1.5em; + color: #110c22; + `, + canceled: css` + color: #8b8b8b; + `, +})); + +function UserMessage(props: UserMessageProps) { const { message } = props; + const { styles } = useStyles(); + const text = getMessageText(message); + const isCanceled = isCanceledMessage(message); - const { content, contextContent } = message; + const delta = useMemo(() => { + if (BLOT_NAME_CONTENT_REGEX.test(text)) { + const delta = convertTextToDelta(text); + return delta; + } + return null; + }, [text]); + + const textCls = cx(styles.textWrapper, { + [styles.canceled]: isCanceled, + }); + + if (message.hidden) { + return null; + } return ( - - - +
        +
        + {delta ? ( + quill.setContents(delta), + readonly: true, + }} + > + + + ) : ( +
        {text}
        + )} +
        +
        ); -}; +} export default memo(UserMessage); diff --git a/browser/src/components/UserMessage/UserMessageFooter.tsx b/browser/src/components/UserMessage/UserMessageFooter.tsx index 4c8893291..4e4f23291 100644 --- a/browser/src/components/UserMessage/UserMessageFooter.tsx +++ b/browser/src/components/UserMessage/UserMessageFooter.tsx @@ -3,11 +3,11 @@ import { Button } from 'antd'; import { createStyles } from 'antd-style'; import { memo, useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import type { UIUserMessage } from '@/types/message'; -import { renderContextTag } from '../ChatSender/SenderHeader'; +import type { Message } from '@/types/chat'; +import SenderComponent from '../ChatSender/SenderComponent'; interface UserMessageFooterProps { - message: UIUserMessage; + message: Message; } const useStyle = createStyles(({ css }) => { @@ -16,11 +16,20 @@ const useStyle = createStyles(({ css }) => { display: flex; flex-direction: column; align-items: flex-end; - max-width: 800px; + max-width: 70%; + margin-left: auto; row-gap: 6px; `, button: css` font-size: 12px; + color: #8b8b8b; + padding: 2px 8px; + height: auto; + + &:hover { + color: #110c22; + background: rgba(246, 248, 251, 0.5); + } `, itemsContainer: css` display: flex; @@ -48,10 +57,15 @@ const UserMessageFooter = memo((props) => { }, []); const contextTags = useMemo(() => { - return ( - attachedContexts?.map((contextItem) => renderContextTag(contextItem)) || - null - ); + return attachedContexts?.map((contextItem, index) => ( + + )); }, [attachedContexts]); const buttonText = useMemo(() => { @@ -87,6 +101,4 @@ const UserMessageFooter = memo((props) => { ); }); -UserMessageFooter.displayName = 'UserMessageFooter'; - export default UserMessageFooter; diff --git a/browser/src/components/Welcome/index.module.css b/browser/src/components/Welcome/index.module.css new file mode 100644 index 000000000..4dd736467 --- /dev/null +++ b/browser/src/components/Welcome/index.module.css @@ -0,0 +1,118 @@ +.container { + position: relative; + width: 100%; + min-width: 800px; + background: #ffffff; + padding: 168px 0 80px 0; +} + +.backgroundImage { + position: absolute; + top: 100px; + left: 0; + width: 800px; + height: 100%; + background-image: url("/src/assets/welcome-background.png"); + background-size: cover; + background-repeat: no-repeat; + background-position: center; + z-index: 0; +} + +.content { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + min-width: 800px; + margin: 0 auto; + z-index: 1; +} + +.welcomeTitle { + font-size: 36px; + font-weight: 600; + color: #110c22ff; + text-align: center; + display: flex; + align-items: center; + margin-bottom: 73px; + gap: 8px; +} + +.waveEmoji { + font-size: 36px; + color: initial; +} + +.capabilitiesTitle { + font-size: 14px; + color: #4f4b5c; + font-weight: 400; + width: 100%; +} + +.capabilitiesGrid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 24px; + width: 100%; +} + +.capabilityCard { + width: 185px; + background: #ffffff; + border-radius: 20px; + padding: 20px; + box-shadow: 0px 8px 48px 0px #eeeeee; + cursor: pointer; + transition: all 0.2s; +} + +.capabilityCard:hover { + transform: translateY(-2px); + box-shadow: 0px 12px 56px 0px #e0e0e0; +} + +.capabilityHeader { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 12px; +} + +.capabilityIcon { + width: 20px; + height: 20px; + flex-shrink: 0; +} + +.capabilityTitle { + font-size: 14px; + color: #110c22; + font-weight: 500; + line-height: 1.7; +} + +.capabilityDescription { + font-size: 11px; + color: #666f8dff; + font-weight: 400; + line-height: 1.6; +} + +.outerContainer { + padding-inline: calc(calc(100% - 800px) / 2); +} + +.capabilitiesContainer { + width: 100%; +} + +.capabilitiesRow { + width: 100%; +} + +.capabilityItem { + flex: 1; +} diff --git a/browser/src/components/Welcome/index.tsx b/browser/src/components/Welcome/index.tsx index 056cc7f55..e68347e2f 100644 --- a/browser/src/components/Welcome/index.tsx +++ b/browser/src/components/Welcome/index.tsx @@ -1,175 +1,112 @@ -import { - CommentOutlined, - EllipsisOutlined, - HeartOutlined, - PaperClipOutlined, - ShareAltOutlined, - SmileOutlined, -} from '@ant-design/icons'; -import { Prompts, Welcome as WelcomeX } from '@ant-design/x'; -import { Button, Flex, Space } from 'antd'; -import { createStyles } from 'antd-style'; +import { Flex, Space } from 'antd'; +import type React from 'react'; import { useTranslation } from 'react-i18next'; -import { useChatState } from '@/hooks/provider'; +import { useSnapshot } from 'valtio'; +import { actions, state } from '@/state/chat'; +import styles from './index.module.css'; const useWelcomeData = () => { const { t } = useTranslation(); - const HOT_TOPICS = { - key: '1', - label: t('chat.quickStart'), - children: [ - { - key: '1-1', - description: t('chat.askQuestions'), - icon: 🤖, - }, - { - key: '1-2', - description: t('chat.beSpecific'), - icon: 📁, - }, - { - key: '1-3', - description: t('chat.createTakumiMd'), - icon: , - }, - { - key: '1-4', - description: t('chat.generateTests'), - icon: 🧪, - }, - { - key: '1-5', - description: t('chat.fixBugs'), - icon: 🔧, - }, - ], - }; - const DESIGN_GUIDE = { - key: '2', - label: t('chat.capabilities'), - children: [ + capabilities: [ { - key: '2-1', - icon: , - label: t('chat.llmSupport'), - description: t('chat.llmSupportDesc'), + key: 'llmSupport', + icon: '/src/assets/llm-support-icon.svg', + title: t('welcome.llmSupport.title'), + description: t('welcome.llmSupport.description'), }, { - key: '2-2', - icon: , - label: t('chat.fileOperations'), - description: t('chat.fileOperationsDesc'), + key: 'fileOperations', + icon: '/src/assets/file-operations-icon.svg', + title: t('welcome.fileOperations.title'), + description: t('welcome.fileOperations.description'), }, { - key: '2-3', - icon: , - label: t('chat.codebaseNavigation'), - description: t('chat.codebaseNavigationDesc'), + key: 'codebaseNavigation', + icon: '/src/assets/codebase-navigation-icon.svg', + title: t('welcome.codebaseNavigation.title'), + description: t('welcome.codebaseNavigation.description'), }, { - key: '2-4', - icon: , - label: t('chat.planMode'), - description: t('chat.planModeDesc'), + key: 'planMode', + icon: '/src/assets/plan-mode-icon.svg', + title: t('welcome.planMode.title'), + description: t('welcome.planMode.description'), }, ], }; - return { HOT_TOPICS, DESIGN_GUIDE }; -}; + const handleCapabilityClick = (capability: any) => { + actions.send( + t('welcome.introduceCapability', { + capability: capability.title, + }), + ); + }; -const useStyle = createStyles(({ css }) => { return { - placeholder: css` - padding-inline: calc(calc(100% - 700px) / 2); - `, - chatPrompt: css` - .ant-prompts-label { - color: #000000e0 !important; - } - .ant-prompts-desc { - color: #000000a6 !important; - width: 100%; - } - .ant-prompts-icon { - color: #000000a6 !important; - } - `, + DESIGN_GUIDE, + handleCapabilityClick, }; -}); +}; -const Welcome = () => { - const { styles } = useStyle(); - const { append } = useChatState(); +const Welcome: React.FC = () => { const { t } = useTranslation(); - const { HOT_TOPICS, DESIGN_GUIDE } = useWelcomeData(); + const { DESIGN_GUIDE, handleCapabilityClick } = useWelcomeData(); + const { productName } = useSnapshot(state); return ( - - - -
    - )} - - {messages.map((m) => ( -
    - {m.role === 'user' ? 'User: ' : 'AI: '} - {m.parts.map((p) => ( -
    -
    -                思考
    -                {p.type === 'reasoning' && p.reasoning}
    -              
    - {p.type === 'text' && p.text} -
    - ))} -
    -
    -
    - ))} - - {(status === 'submitted' || status === 'streaming') && ( -
    - {status === 'submitted' &&
    Loading...
    } - -
    - )} - -
    - setInput(e.target.value)} - /> - -
    - ); -}; - -export const Route = createFileRoute('/demo')({ - component: Demo, -}); diff --git a/browser/src/pages/session.route.tsx b/browser/src/pages/session.route.tsx new file mode 100644 index 000000000..1476f56be --- /dev/null +++ b/browser/src/pages/session.route.tsx @@ -0,0 +1,43 @@ +import { createFileRoute, Outlet } from '@tanstack/react-router'; +import { createStyles } from 'antd-style'; +import { useSnapshot } from 'valtio'; +import ProjectSelectModal from '@/components/ProjectSelectModal'; +import SettingsModal from '@/components/SettingsModal'; +import Sider from '@/components/Sider'; +import { uiState } from '@/state/ui'; + +const useStyle = createStyles(({ token, css }) => { + return { + layout: css` + width: 100%; + min-width: 1000px; + height: 100vh; + display: flex; + background: ${token.colorBgContainer}; + font-family: AlibabaPuHuiTi, ${token.fontFamily}, sans-serif; + `, + siderWrapper: css` + flex-shrink: 0; + `, + }; +}); + +const Session: React.FC = () => { + const { styles } = useStyle(); + const { settingsModalOpen, projectSelectModalOpen } = useSnapshot(uiState); + + return ( +
    +
    + +
    + + {settingsModalOpen && } + {projectSelectModalOpen && } +
    + ); +}; + +export const Route = createFileRoute('/session')({ + component: Session, +}); diff --git a/browser/src/pages/session/-components/Chat.tsx b/browser/src/pages/session/-components/Chat.tsx new file mode 100644 index 000000000..93d152862 --- /dev/null +++ b/browser/src/pages/session/-components/Chat.tsx @@ -0,0 +1,94 @@ +import { createStyles } from 'antd-style'; +import { useCallback, useEffect, useRef } from 'react'; +import { useSnapshot } from 'valtio'; +import ChatContent from '@/components/ChatContent'; +import ResizeHandle from '@/components/ChatLayout/ResizeHandle'; +import RightPanel from '@/components/ChatLayout/RightPanel'; +import SidebarExpandButton from '@/components/ChatLayout/SidebarExpandButton'; +import TopRightExpandButton from '@/components/ChatLayout/TopRightExpandButton'; +import * as layout from '@/state/layout'; + +const useStyles = createStyles(({ css }) => { + return { + container: css` + display: flex; + height: 100vh; + width: 100%; + overflow: hidden; + `, + + leftSection: css` + flex: 1; + display: flex; + overflow: hidden; + min-width: 300px; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + `, + + rightSection: css` + flex-shrink: 0; + transition: + width 0.3s cubic-bezier(0.4, 0, 0.2, 1), + opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), + visibility 0.3s cubic-bezier(0.4, 0, 0.2, 1); + overflow: hidden; + min-width: 0; + `, + }; +}); + +const Chat: React.FC = () => { + const { styles } = useStyles(); + const containerRef = useRef(null); + const rightPanelRef = useRef(null); + const { rightPanelExpanded, rightPanelWidthPercent } = useSnapshot( + layout.state, + ); + + // Auto collapse Sidebar when right panel is expanded + const handleRightPanelExpansion = useCallback(() => { + if (rightPanelExpanded) { + layout.actions.setSidebarCollapsed(true); + } + }, [rightPanelExpanded]); + + useEffect(() => { + handleRightPanelExpansion(); + }, [handleRightPanelExpansion]); + + // Only calculate right panel width, left side automatically fills remaining space + const rightWidth = rightPanelExpanded ? `${rightPanelWidthPercent}%` : '0%'; + + return ( +
    + +
    +
    + + +
    +
    + + {rightPanelExpanded && ( + + )} + +
    + +
    +
    + ); +}; + +export default Chat; diff --git a/browser/src/pages/session/index.tsx b/browser/src/pages/session/index.tsx new file mode 100644 index 000000000..c50eeab33 --- /dev/null +++ b/browser/src/pages/session/index.tsx @@ -0,0 +1,52 @@ +import { createFileRoute } from '@tanstack/react-router'; +import { useMount, useUnmount } from 'ahooks'; +import { createStyles } from 'antd-style'; +import { z } from 'zod'; +import Loading from '@/components/Loading'; +import { useSession } from '@/hooks/useSession'; +import { actions } from '@/state/chat'; +import Chat from './-components/Chat'; + +const useStyle = createStyles(({ css }) => { + return { + container: css` + display: flex; + height: 100vh; + width: 100%; + align-items: center; + justify-content: center; + `, + }; +}); + +const Session: React.FC = () => { + const { run, loading } = useSession(); + + const { styles } = useStyle(); + + useMount(() => { + run(); + }); + + useUnmount(() => { + actions.destroy(); + }); + + if (loading) { + return ( +
    + +
    + ); + } + + return ; +}; + +export const Route = createFileRoute('/session/')({ + validateSearch: z.object({ + sessionId: z.union([z.string(), z.number()]).optional(), + folder: z.string().optional(), + }), + component: Session, +}); diff --git a/browser/src/pages/settings.tsx b/browser/src/pages/settings.tsx deleted file mode 100644 index 89b260294..000000000 --- a/browser/src/pages/settings.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { createFileRoute } from '@tanstack/react-router'; -import SettingsPage from '@/components/SettingsPage'; - -export const Route = createFileRoute('/settings')({ - component: SettingsPage, -}); diff --git a/browser/src/routeTree.gen.ts b/browser/src/routeTree.gen.ts index 046017895..3ee61fd0b 100644 --- a/browser/src/routeTree.gen.ts +++ b/browser/src/routeTree.gen.ts @@ -11,86 +11,77 @@ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './pages/__root' -import { Route as SettingsRouteImport } from './pages/settings' -import { Route as DemoRouteImport } from './pages/demo' -import { Route as ChatRouteImport } from './pages/chat' +import { Route as SessionRouteRouteImport } from './pages/session.route' +import { Route as SessionIndexRouteImport } from './pages/session/index' -const SettingsRoute = SettingsRouteImport.update({ - id: '/settings', - path: '/settings', +const SessionRouteRoute = SessionRouteRouteImport.update({ + id: '/session', + path: '/session', getParentRoute: () => rootRouteImport, } as any) -const DemoRoute = DemoRouteImport.update({ - id: '/demo', - path: '/demo', - getParentRoute: () => rootRouteImport, -} as any) -const ChatRoute = ChatRouteImport.update({ - id: '/chat', - path: '/chat', - getParentRoute: () => rootRouteImport, +const SessionIndexRoute = SessionIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => SessionRouteRoute, } as any) export interface FileRoutesByFullPath { - '/chat': typeof ChatRoute - '/demo': typeof DemoRoute - '/settings': typeof SettingsRoute + '/session': typeof SessionRouteRouteWithChildren + '/session/': typeof SessionIndexRoute } export interface FileRoutesByTo { - '/chat': typeof ChatRoute - '/demo': typeof DemoRoute - '/settings': typeof SettingsRoute + '/session': typeof SessionIndexRoute } export interface FileRoutesById { __root__: typeof rootRouteImport - '/chat': typeof ChatRoute - '/demo': typeof DemoRoute - '/settings': typeof SettingsRoute + '/session': typeof SessionRouteRouteWithChildren + '/session/': typeof SessionIndexRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/chat' | '/demo' | '/settings' + fullPaths: '/session' | '/session/' fileRoutesByTo: FileRoutesByTo - to: '/chat' | '/demo' | '/settings' - id: '__root__' | '/chat' | '/demo' | '/settings' + to: '/session' + id: '__root__' | '/session' | '/session/' fileRoutesById: FileRoutesById } export interface RootRouteChildren { - ChatRoute: typeof ChatRoute - DemoRoute: typeof DemoRoute - SettingsRoute: typeof SettingsRoute + SessionRouteRoute: typeof SessionRouteRouteWithChildren } declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/settings': { - id: '/settings' - path: '/settings' - fullPath: '/settings' - preLoaderRoute: typeof SettingsRouteImport + '/session': { + id: '/session' + path: '/session' + fullPath: '/session' + preLoaderRoute: typeof SessionRouteRouteImport parentRoute: typeof rootRouteImport } - '/demo': { - id: '/demo' - path: '/demo' - fullPath: '/demo' - preLoaderRoute: typeof DemoRouteImport - parentRoute: typeof rootRouteImport - } - '/chat': { - id: '/chat' - path: '/chat' - fullPath: '/chat' - preLoaderRoute: typeof ChatRouteImport - parentRoute: typeof rootRouteImport + '/session/': { + id: '/session/' + path: '/' + fullPath: '/session/' + preLoaderRoute: typeof SessionIndexRouteImport + parentRoute: typeof SessionRouteRoute } } } +interface SessionRouteRouteChildren { + SessionIndexRoute: typeof SessionIndexRoute +} + +const SessionRouteRouteChildren: SessionRouteRouteChildren = { + SessionIndexRoute: SessionIndexRoute, +} + +const SessionRouteRouteWithChildren = SessionRouteRoute._addFileChildren( + SessionRouteRouteChildren, +) + const rootRouteChildren: RootRouteChildren = { - ChatRoute: ChatRoute, - DemoRoute: DemoRoute, - SettingsRoute: SettingsRoute, + SessionRouteRoute: SessionRouteRouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) diff --git a/browser/src/state/appData.ts b/browser/src/state/appData.ts deleted file mode 100644 index b1ed936ff..000000000 --- a/browser/src/state/appData.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { proxy } from 'valtio'; -import { getAppData } from '@/api/appData'; - -interface AppDataState { - productName: string; - version: string; - cwd: string; - config: Record; - loading: boolean; - loaded: boolean; -} - -export const state = proxy<{ appData: AppDataState }>({ - appData: { - productName: '', - version: '', - cwd: '', - config: {}, - loading: false, - loaded: false, - }, -}); - -export const actions = { - setAppData: (appData: Omit) => { - state.appData = { - ...appData, - loading: false, - loaded: true, - }; - }, - getAppData: async () => { - if (state.appData.loading || state.appData.loaded) { - return; - } - - state.appData.loading = true; - try { - const response = await getAppData(); - actions.setAppData(response.data); - } catch (error) { - state.appData.loading = false; - throw error; - } - }, -}; diff --git a/browser/src/state/chat.ts b/browser/src/state/chat.ts new file mode 100644 index 000000000..a748eac9d --- /dev/null +++ b/browser/src/state/chat.ts @@ -0,0 +1,526 @@ +import type { Delta } from 'quill'; +import { proxy } from 'valtio'; +import type { ApprovalMode, InitializeResult } from '@/client'; +import { BLOT_NAME_CONTENT_REGEX, SLASH_COMMAND_REGEX } from '@/constants'; +import type { + ApprovalCategory, + ApprovalResult, + CommandEntry, + FileItem, + FilePart, + ImagePart, + LoopResult, + Message, + NodeBridgeResponse, + ToolResultPart, + ToolUse, + UIAssistantMessage, + UIDisplayMessage, + UIMessage, + UserMessage, +} from '@/types/chat'; +import { formatMessages, isToolResultMessage } from '@/utils/message'; +import { getPrompt } from '@/utils/quill'; +import { parseSlashCommand } from '@/utils/slashCommand'; +import { countTokens } from '@/utils/tokenCounter'; +import { actions as clientActions } from './client'; + +export type AppStatus = + | 'idle' + | 'processing' + | 'planning' + | 'plan_approving' + | 'tool_approving' + | 'tool_executing' + | 'compacting' + | 'failed' + | 'cancelled' + | 'slash_command_executing' + | 'help' + | 'exit'; + +function isExecuting(status: AppStatus) { + return ( + status === 'processing' || + status === 'planning' || + status === 'tool_executing' || + status === 'compacting' + ); +} + +interface ChatState { + cwd: string | null; + sessionId: string | null; + version: string | null; + productName: string | null; + model: string | null; + approvalMode: ApprovalMode; + planMode: boolean; + status: AppStatus; + messages: UIMessage[]; + loading: boolean; + approvalModal: { + toolUse: ToolUse; + category: ApprovalCategory; + resolve: (result: ApprovalResult) => Promise; + } | null; + error: string | null; + + processingTokens: number; + initialized: boolean; +} + +interface ChatActions { + initialize(opts: { + cwd: string; + sessionId: string; + messages: Message[]; + }): Promise<() => void>; + send(message: string, delta?: Delta): void; + addMessage(message: UIMessage | UIMessage[]): void; + destroy(): void; + sendMessage(opts: { + message: string | null; + planMode?: boolean; + model?: string; + }): Promise; + getSlashCommands(): Promise; + getFiles(opts: { query?: string }): Promise; + cancel(): Promise; + setSummary(opts: { + userPrompt: string; + result: LoopResult | { success: false; error: Error }; + }): Promise; +} + +export const state = proxy({ + cwd: null, + sessionId: null, + version: null, + productName: null, + model: null, + approvalMode: 'default', + planMode: false, + status: 'idle', + messages: [], + loading: false, + approvalModal: null, + error: null, + processingTokens: 0, + initialized: false, +}); + +export const actions: ChatActions = { + async initialize(opts) { + await clientActions.connect(); + const response = (await clientActions.request('session.initialize', { + cwd: opts.cwd, + sessionId: opts.sessionId, + messages: opts.messages, + })) as InitializeResult; + state.initialized = true; + + if (!response.success) { + throw new Error(response.error?.message || 'Initialize failed'); + } + + state.cwd = opts.cwd; + state.sessionId = opts.sessionId || null; + state.messages = formatMessages(opts.messages); + state.productName = response.data.productName; + state.version = response.data.version; + state.model = response.data.model; + state.approvalMode = response.data.approvalMode; + state.planMode = false; + state.status = 'idle'; + state.approvalModal = null; + state.error = null; + state.processingTokens = 0; + state.loading = false; + + const handleMessage = (data: { message: Message }) => { + const { message } = data; + if ( + message.role === 'assistant' && + Array.isArray(message.content) && + message.content.some((content) => content.type === 'tool_use') + ) { + const uiMessage = { + ...message, + content: message.content.map((part) => { + if (part.type === 'tool_use') { + return { + ...part, + type: 'tool', + state: 'tool_use', + }; + } + return part; + }), + } as UIMessage; + state.messages.push(uiMessage); + return; + } + if (isToolResultMessage(message)) { + const lastMessage = state.messages[ + state.messages.length - 1 + ] as UIAssistantMessage; + if (lastMessage) { + const toolResult = message.content[0] as ToolResultPart; + const matchToolUse = lastMessage.content.find( + (part) => + part.type === 'tool' && + part.state === 'tool_use' && + part.id === toolResult.id, + ); + if (!matchToolUse) { + throw new Error( + 'Tool result message must be after tool use message', + ); + } + const uiMessage = { + ...lastMessage, + content: lastMessage.content.map((part) => { + if (part.type === 'tool') { + return { + ...part, + ...toolResult, + type: 'tool', + state: 'tool_result', + }; + } + return part; + }), + } as UIMessage; + state.messages[state.messages.length - 1] = uiMessage; + return; + } else { + throw new Error('Tool result message must be after tool use message'); + } + } + state.messages.push(message as UIMessage); + }; + + const handleChunk = (data: any) => { + if (data.sessionId === state.sessionId && data.cwd === state.cwd) { + const chunk = data.chunk; + + // Collect tokens from text-delta and reasoning events + if ( + chunk.type === 'raw_model_stream_event' && + chunk.data?.type === 'model' && + (chunk.data.event?.type === 'text-delta' || + chunk.data.event?.type === 'reasoning') + ) { + const textDelta = chunk.data.event.textDelta || ''; + const tokenCount = countTokens(textDelta); + state.processingTokens += tokenCount; + } + } + }; + + clientActions.onEvent('message', handleMessage); + clientActions.onEvent('chunk', handleChunk); + + clientActions.toolApproval(async (toolUse, category) => { + return new Promise<{ approved: boolean }>((resolve) => { + state.approvalModal = { + toolUse, + category, + resolve: async (result: ApprovalResult) => { + state.approvalModal = null; + const isApproved = result !== 'deny'; + if (result === 'approve_always_edit') { + await clientActions.request('sessionConfig.setApprovalMode', { + cwd: state.cwd, + sessionId: state.sessionId, + approvalMode: 'autoEdit', + }); + } else if (result === 'approve_always_tool') { + await clientActions.request('sessionConfig.addApprovalTools', { + cwd: state.cwd, + sessionId: state.sessionId, + approvalTool: toolUse.name, + }); + } + resolve({ approved: isApproved }); + }, + }; + }); + }); + + return () => { + clientActions.removeEventHandler('message', handleMessage); + clientActions.removeEventHandler('chunk', handleChunk); + }; + }, + + async send(message, delta: Delta) { + const { cwd, sessionId } = state; + + const isDelta = BLOT_NAME_CONTENT_REGEX.test(message); + + clientActions.request('utils.telemetry', { + cwd, + name: 'send', + payload: { message, sessionId }, + }); + + if (!isDelta) { + const result = await this.sendMessage({ message }); + await this.setSummary({ userPrompt: message, result }); + return; + } + + const isCommand = SLASH_COMMAND_REGEX.test(message); + const prompt = getPrompt(delta); + + if (!isCommand) { + await clientActions.request('session.addMessages', { + cwd, + sessionId, + messages: [ + { + role: 'user', + content: prompt, + uiContent: message, + }, + ], + }); + const result = await this.sendMessage({ message: null }); + await this.setSummary({ userPrompt: message, result }); + return; + } + + if (isCommand) { + const parsed = parseSlashCommand(prompt); + const result = (await clientActions.request('slashCommand.get', { + cwd, + command: parsed.command, + })) as NodeBridgeResponse<{ commandEntry: CommandEntry }>; + const commandeEntry = result.data?.commandEntry; + + if (!commandeEntry) { + this.addMessage({ + role: 'ui_display', + content: { + type: 'error', + text: `Unknown slash command: ${parsed.command}`, + }, + }); + return; + } + + const command = commandeEntry.command; + const type = command.type; + const isPrompt = type === 'prompt'; + const userMessage: UserMessage = { + role: 'user', + content: prompt, + uiContent: message, + }; + + if (isPrompt) { + await clientActions.request('session.addMessages', { + cwd, + sessionId, + messages: [userMessage], + }); + } else { + this.addMessage(userMessage); + } + + const executeResult = (await clientActions.request( + 'slashCommand.execute', + { + cwd, + sessionId, + command: parsed.command, + args: parsed.args, + }, + )) as NodeBridgeResponse<{ messages: UIMessage[] }>; + const isLocal = type === 'local'; + + if (executeResult.success) { + const messages = executeResult.data.messages; + if (isPrompt) { + await clientActions.request('session.addMessages', { + cwd, + sessionId, + messages: messages, + }); + await this.sendMessage({ message: null }); + } else if (isLocal) { + const parsedMessages = messages.map((message) => { + if (message.role === 'user') { + const contentArray = Array.isArray(message.content) + ? message.content + : []; + const text = + typeof message.content === 'string' + ? message.content + : contentArray + .map((part) => + part.type === 'text' ? part.text : String(part), + ) + .join('\n'); + return { + role: 'ui_display', + content: { + type: 'info', + text, + }, + } as UIDisplayMessage; + } + return message; + }); + + this.addMessage(parsedMessages); + } + } + } + }, + + async sendMessage(opts: { + message: string | null; + planMode?: boolean; + model?: string; + }) { + try { + state.status = 'processing'; + state.processingTokens = 0; + state.loading = true; + const { cwd, sessionId } = state; + let attachments: Array = []; + + const response = (await clientActions.request('session.send', { + message: opts.message, + planMode: opts.planMode, + model: opts.model, + cwd, + sessionId, + attachments, + })) as LoopResult; + + if (response.success) { + state.status = 'idle'; + state.processingTokens = 0; + } else { + state.status = 'failed'; + state.processingTokens = 0; + state.error = response.error?.message; + this.addMessage({ + role: 'ui_display', + content: { type: 'error', text: response.error?.message }, + }); + } + + state.loading = false; + return response; + } catch (error) { + console.error('Send message error:', error); + state.status = 'failed'; + state.processingTokens = 0; + const errorMessage = + error instanceof Error ? error.message : String(error); + state.error = errorMessage; + this.addMessage({ + role: 'ui_display', + content: { type: 'error', text: errorMessage }, + }); + return { + success: false, + error: error as Error, + }; + } + }, + + addMessage(messages: UIMessage | UIMessage[]) { + const msgs = Array.isArray(messages) ? messages : [messages]; + state.messages.push(...msgs); + }, + + async getSlashCommands() { + const response = (await clientActions.request('slashCommand.list', { + cwd: state.cwd, + })) as NodeBridgeResponse<{ slashCommands: CommandEntry[] }>; + return response.data.slashCommands; + }, + + async cancel() { + if (!isExecuting(state.status)) { + return; + } + const { cwd, sessionId } = state; + await clientActions.request('session.cancel', { + cwd, + sessionId, + }); + state.status = 'idle'; + state.processingTokens = 0; + }, + + async getFiles(opts: { query?: string }) { + if (!state.cwd) { + throw new Error( + 'Current working directory (cwd) is not set. Please select or initialize a working directory first.', + ); + } + const response = (await clientActions.request('utils.files.list', { + cwd: state.cwd, + query: opts.query, + })) as NodeBridgeResponse<{ files: FileItem[] }>; + return response.data.files; + }, + + async setSummary(opts: { userPrompt: string; result: LoopResult }) { + try { + const { cwd, sessionId } = state; + if (opts.result.success) { + const queryResult = (await clientActions.request('utils.query', { + cwd, + systemPrompt: + "Analyze if this message indicates a new conversation topic. If it does, extract a 2-3 word title that captures the new topic. Format your response as a JSON object with one fields: 'title' (string). Only include these fields, no other text.", + userPrompt: opts.userPrompt, + })) as NodeBridgeResponse<{ text: string }>; + if (queryResult.success && queryResult.data?.text) { + const response = JSON.parse(queryResult.data.text); + if (response?.title) { + document.title = response.title; + } + await clientActions.request('session.config.setSummary', { + cwd, + sessionId, + summary: response.title, + }); + } + } + } catch (error) { + console.error('Set summary error:', error); + } + }, + + destroy() { + if (state.approvalModal) { + // TODO: Optimization needed. We can't wait for approvalModal's resolve to complete here, + // so we need to manually handle tool message denial. + state.approvalModal.resolve('deny'); + state.approvalModal = null; + } + state.messages = []; + state.cwd = null; + state.sessionId = null; + state.productName = null; + state.version = null; + state.model = null; + state.approvalMode = 'default'; + state.planMode = false; + state.status = 'idle'; + state.approvalModal = null; + state.error = null; + state.processingTokens = 0; + state.loading = false; + state.initialized = false; + clientActions.unmount(); + }, +}; diff --git a/browser/src/state/client.ts b/browser/src/state/client.ts new file mode 100644 index 000000000..e8e5ebed4 --- /dev/null +++ b/browser/src/state/client.ts @@ -0,0 +1,147 @@ +import { proxy } from 'valtio'; +import { MessageBus, WebSocketTransport } from '@/client'; + +export type ApprovalResult = + | 'approve_once' + | 'approve_always_edit' + | 'approve_always_tool' + | 'deny'; + +export type ToolUse = { + name: string; + params: Record; + callId: string; +}; + +export type ApprovalCategory = 'read' | 'write' | 'command' | 'network'; + +export interface ClientState { + state: 'disconnected' | 'connecting' | 'connected' | 'error' | 'closed'; + transport: WebSocketTransport | null; + messageBus: MessageBus | null; + approvalModal: { + toolUse: ToolUse; + category: ApprovalCategory; + resolve: (result: ApprovalResult) => Promise; + } | null; +} + +export const state = proxy({ + state: 'disconnected', + transport: null, + messageBus: null, + approvalModal: null, +}); + +export const actions = { + async connect() { + if (state.transport?.isConnected()) { + return; + } + + try { + state.state = 'connecting'; + const transport = new WebSocketTransport({ + url: `ws://${window.location.hostname}:${window.location.port}/ws`, + reconnectInterval: 1000, + maxReconnectInterval: 30000, + shouldReconnect: true, + }); + + transport.onError(() => { + state.state = 'error'; + }); + + transport.onClose(() => { + state.state = 'disconnected'; + }); + + const messageBus = new MessageBus(); + messageBus.setTransport(transport); + + state.transport = transport; + state.messageBus = messageBus; + + await transport.connect(); + + state.state = 'connected'; + } catch (error) { + state.state = 'error'; + throw error; + } + }, + + toolApproval( + handler: ( + toolUse: ToolUse, + category: ApprovalCategory, + ) => Promise<{ + approved: boolean; + }>, + ) { + if (!state.messageBus) { + throw new Error('Message bus not available'); + } + state.messageBus.registerHandler( + 'toolApproval', + async ({ + toolUse, + category, + }: { + toolUse: ToolUse; + category: ApprovalCategory; + }) => { + const result = await handler(toolUse, category); + return { approved: result.approved }; + }, + ); + }, + + async disconnect() { + if (state.transport?.isConnected()) { + await state.transport.close(); + } + if (state.messageBus) { + state.messageBus.cancelPendingRequests(); + } + state.state = 'disconnected'; + state.transport = null; + state.messageBus = null; + }, + + async request( + method: string, + params: T, + ): Promise { + if (!state.messageBus) { + throw new Error('Message bus not available'); + } + return state.messageBus.request(method, params); + }, + + onEvent(event: string, handler: (data: T) => void) { + if (!state.messageBus) { + throw new Error('Message bus not available'); + } + state.messageBus.onEvent(event, handler); + }, + + removeEventHandler(event: string, handler: (data: T) => void) { + if (!state.messageBus) { + throw new Error('Message bus not available'); + } + state.messageBus.removeEventHandler(event, handler); + }, + + unmount() { + if (state.transport) { + state.transport.close(); + } + if (state.messageBus) { + state.messageBus.cancelPendingRequests(); + } + state.state = 'disconnected'; + state.transport = null; + state.messageBus = null; + }, +}; diff --git a/browser/src/state/codeViewer.ts b/browser/src/state/codeViewer.ts index f57a9b390..af96c445d 100644 --- a/browser/src/state/codeViewer.ts +++ b/browser/src/state/codeViewer.ts @@ -6,7 +6,8 @@ import type { CodeViewerTabItem, DiffStat, } from '@/types/codeViewer'; -import { inferFileType } from '@/utils/codeViewer'; +import { diff, inferFileType } from '@/utils/codeViewer'; +import * as layout from './layout'; interface CodeViewerState { visible: boolean; @@ -16,10 +17,10 @@ interface CodeViewerState { } interface DisplayNormalViewerConfigs { - /** 如果为空但传入了path,会尝试使用path的后缀推断 */ + /** If empty but path is provided, will try to infer from path extension */ language?: CodeViewerLanguage; code: string; - /** 文件路径,默认作为key使用 */ + /** File path, used as key by default */ path?: string; mode?: CodeNormalViewerMode; } @@ -43,6 +44,9 @@ export const state = proxy({ export const actions = { setVisible: (visible: boolean) => { state.visible = visible; + // Auto expand right panel when CodeViewer is shown + // Auto collapse right panel when CodeViewer is hidden + layout.actions.setRightPanelExpanded(visible); }, hideDiffActions: (path: string) => { @@ -62,8 +66,10 @@ export const actions = { if (nextItems.length === 0) { state.visible = false; + // Auto collapse right panel when there are no more tabs + layout.actions.setRightPanelExpanded(false); } else { - // TODO 可以优化成打开上一个而不是第一个 + // TODO: Could be optimized to open the previous tab instead of the first one const nextActiveId = nextItems[0].id; state.activeId = nextActiveId; } @@ -118,9 +124,12 @@ export const actions = { } state.activeId = id; + // Auto expand and show right panel when opening CodeViewer + state.visible = true; + layout.actions.setRightPanelExpanded(true); }, - /** 代码有更新后,也需要重新调用一次这个函数刷新展示 */ + /** Need to call this function again to refresh display after code updates */ updateDiffViewerConfig: (config: DisplayDiffViewerConfigs) => { const { path, modifiedCode, originalCode, language, diffStat } = config; @@ -131,7 +140,7 @@ export const actions = { const targetLanguage = language || inferFileType(path); let reuseTab = false; - // 更新所有满足要求的item + // Update all matching items state.codeViewerTabItems = state.codeViewerTabItems.map((item) => { if (item.id === id) { reuseTab = true; @@ -166,11 +175,14 @@ export const actions = { } state.activeId = id; + // Auto expand and show right panel when opening CodeViewer + state.visible = true; + layout.actions.setRightPanelExpanded(true); }, /** - * 展示编辑器、打开特定的文件并跳转特定的行,如果是DiffView,则跳转ModifiedModel对应的行 - * @param path 文件路径 + * Show editor, open specific file and jump to specific line. For DiffView, jump to corresponding line in ModifiedModel + * @param path File path */ jumpToLine: (path: string, lineCount: number) => { const remainingItem = state.codeViewerTabItems.find( @@ -182,13 +194,39 @@ export const actions = { if (remainingItem && jumpFunction) { state.activeId = remainingItem.id; state.visible = true; + // Auto expand right panel when jumping to code line + layout.actions.setRightPanelExpanded(true); jumpFunction(lineCount); } }, - /** 注册跳转函数 */ + /** Register jump function */ registerJumpFunction: (path: string, fn: (_: number) => void) => { state.jumpFunctionMap[path] = fn; }, + + async openCodeViewer( + path: string, + originalCode: string, + modifiedCode: string, + mode?: CodeNormalViewerMode, + ) { + if (mode) { + this.updateNormalViewerConfig({ + code: mode === 'new' ? modifiedCode : originalCode, + path, + mode, + }); + } else { + const diffStat = await diff(originalCode, modifiedCode); + this.updateDiffViewerConfig({ + path, + originalCode, + modifiedCode, + diffStat, + }); + } + this.setVisible(true); + }, }; diff --git a/browser/src/state/config.ts b/browser/src/state/config.ts new file mode 100644 index 000000000..74a4fcc52 --- /dev/null +++ b/browser/src/state/config.ts @@ -0,0 +1,128 @@ +import { message } from 'antd'; +import i18n from 'i18next'; +import { proxy } from 'valtio'; +import type { NodeBridgeResponse } from '@/types/chat'; +import type { Config, ProviderConfig } from '@/types/config'; +import { state as chatState } from './chat'; +import { actions as clientActions } from './client'; + +interface ConfigState { + globalConfigDir: string; + projectConfigDir: string; + config: Partial; + loading: boolean; +} + +export const state = proxy({ + globalConfigDir: '', + projectConfigDir: '', + config: { + language: 'English', + approvalMode: 'default', + todo: true, + autoCompact: true, + autoUpdate: true, + browser: false, + }, + loading: false, +}); + +interface GroupedModel { + provider: string; + providerId: string; + models: { + name: string; + modelId: string; + value: string; + }[]; +} + +export const actions = { + async getConfig() { + const { cwd } = chatState; + state.loading = true; + const response = (await clientActions.request('config.list', { + cwd, + })) as NodeBridgeResponse<{ + globalConfigDir: string; + projectConfigDir: string; + config: Config; + }>; + if (!response.success) { + message.error(response.message || 'Get config failed'); + state.loading = false; + return; + } + const { globalConfigDir, projectConfigDir, config } = response.data; + state.globalConfigDir = globalConfigDir; + state.projectConfigDir = projectConfigDir; + state.config = config; + state.loading = false; + }, + + async getProvidersList() { + const { cwd } = chatState; + const response = (await clientActions.request('providers.list', { + cwd, + })) as NodeBridgeResponse<{ + providers: ProviderConfig[]; + }>; + return response; + }, + + async getModelsList() { + const { cwd } = chatState; + const response = (await clientActions.request('models.list', { + cwd, + })) as NodeBridgeResponse<{ + groupedModels: GroupedModel[]; + currentModel: string | null; + currentModelInfo: { + providerName: string; + modelName: string; + modelId: string; + } | null; + }>; + return response; + }, + + async set(key: keyof Config, value: any, isGlobal = false) { + const { cwd } = chatState; + state.loading = true; + const serializedValue = + typeof value === 'object' ? JSON.stringify(value) : String(value); + const response = (await clientActions.request('config.set', { + cwd, + isGlobal, + key, + value: serializedValue, + })) as NodeBridgeResponse; + + if (!response.success) { + message.error(response.message || i18n.t('settings.updateError')); + state.loading = false; + return false; + } + + message.success(i18n.t('settings.updateSuccess')); + await this.getConfig(); + state.loading = false; + return true; + }, + + async remove(key: keyof Config, isGlobal = false, values?: string[]) { + const { cwd } = chatState; + state.loading = true; + const response = (await clientActions.request('config.remove', { + cwd, + isGlobal, + key, + values, + })) as NodeBridgeResponse; + if (!response.success) { + message.error(response.message || i18n.t('settings.updateError')); + state.loading = false; + return false; + } + }, +}; diff --git a/browser/src/state/context.ts b/browser/src/state/context.ts index 65bebbbe7..d45616fee 100644 --- a/browser/src/state/context.ts +++ b/browser/src/state/context.ts @@ -1,18 +1,17 @@ import { proxy } from 'valtio'; -import type { FileItem } from '@/api/model'; import { ContextType } from '@/constants/context'; import * as sender from '@/state/sender'; +import type { FileItem, SlashCommand } from '@/types/chat'; import type { ContextItem } from '@/types/context'; interface ContextState { contexts: { files: Omit[]; + slashCommands: Pick[]; }; attachedContexts: ContextItem[]; - contextsSelectedValues: string[]; - loading: boolean; } @@ -21,10 +20,6 @@ export const state = proxy({ attachedContexts: [], - get contextsSelectedValues() { - return this.attachedContexts.map((item: ContextItem) => item.displayText); - }, - get contexts() { const files = this.attachedContexts .filter( @@ -39,8 +34,23 @@ export const state = proxy({ }; }); + const slashCommands = this.attachedContexts + .filter( + (contextItem: ContextItem) => + contextItem.type === ContextType.SLASH_COMMAND, + ) + .map((contextItem: ContextItem) => { + const cmd = contextItem.context as SlashCommand; + + return { + name: cmd?.name, + description: cmd?.description, + }; + }); + return { files, + slashCommands, }; }, }); diff --git a/browser/src/state/fileChanges.ts b/browser/src/state/fileChanges.ts deleted file mode 100644 index b29069f86..000000000 --- a/browser/src/state/fileChanges.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { proxy } from 'valtio'; -import { editFile, readFile } from '@/api/files'; -import * as codeViewer from '@/state/codeViewer'; -import type { - CodeNormalViewerMode, - CodeViewerEditStatus, -} from '@/types/codeViewer'; -import { diff } from '@/utils/codeViewer'; - -export interface FileEdit { - toolCallId: string; - old_string: string; - new_string: string; - /** 表示该edit的状态,undefined时表示未修改 */ - editStatus?: CodeViewerEditStatus; -} - -interface FileState { - path: string; - content: string; - edits: FileEdit[]; -} - -interface FileChangesStore { - files: Record; -} - -// 因为edit tool会直接修改本地文件,所以需要根据edits计算出原始内容 -const calculateOriginalContent = (finalContent: string, edits: FileEdit[]) => { - return edits.reduceRight( - (content, edit) => content.replace(edit.new_string, edit.old_string), - finalContent, - ); -}; - -export const fileChangesState = proxy({ - files: {}, -}); - -export const fileChangesActions = { - // 修改本地file内容 - writeFileContent: async (path: string, newCode: string) => { - const fileState = fileChangesState.files[path]; - if (fileState) { - await editFile(path, newCode); - fileState.content = newCode; - } - }, - - // 更新fileState - updateFileState: async ( - path: string, - fileStateFn: (prevFileState: FileState) => FileState, - mode?: CodeNormalViewerMode, - ) => { - const prevFileState = fileChangesState.files[path]; - if (!prevFileState) { - return; - } - const nextFileState = fileStateFn(prevFileState); - fileChangesState.files[path] = nextFileState; - - const originalCode = nextFileState.content; - const modifiedCode = fileChangesActions.getFinalContent(path) || ''; - fileChangesActions.updateCodeViewerState( - path, - originalCode, - modifiedCode, - mode, - ); - }, - - acceptEdit: ( - path: string, - editToAccept: FileEdit, - mode?: CodeNormalViewerMode, - ) => { - fileChangesActions.updateFileState( - path, - (prevState) => { - const newContent = prevState.content.replace( - editToAccept.old_string, - editToAccept.new_string, - ); - const nextEdit: FileEdit = { - ...editToAccept, - editStatus: 'accept', - }; - return { - ...prevState, - content: newContent, - edits: prevState.edits.map((edit) => - edit.toolCallId === nextEdit.toolCallId ? nextEdit : edit, - ), - }; - }, - mode, - ); - }, - - rejectEdit: ( - path: string, - editToReject: FileEdit, - mode?: CodeNormalViewerMode, - ) => { - fileChangesActions.updateFileState( - path, - (prevState) => { - const nextEdit: FileEdit = { - ...editToReject, - editStatus: 'reject', - }; - return { - ...prevState, - edits: prevState.edits.map((edit) => - edit.toolCallId === nextEdit.toolCallId ? nextEdit : edit, - ), - }; - }, - mode, - ); - }, - - updateCodeViewerState: async ( - path: string, - originalCode: string, - modifiedCode: string, - mode?: CodeNormalViewerMode, - ) => { - if (mode) { - codeViewer.actions.updateNormalViewerConfig({ - code: mode === 'new' ? modifiedCode : originalCode, - path, - mode, - }); - } else { - const diffStat = await diff(originalCode, modifiedCode); - codeViewer.actions.updateDiffViewerConfig({ - path, - originalCode, - modifiedCode, - diffStat, - }); - } - }, - - // 初始化新文件的state,content为文件内容;不需要read因为本地没有这个文件 - initNewFileState: async ( - path: string, - content: string, - edits: FileEdit[], - ) => { - fileChangesState.files[path] = proxy({ - path, - content, - edits, - }); - }, - - // 初始化fileState, push edits - initFileState: async (path: string, edits: FileEdit[]) => { - const fileState = fileChangesState.files[path]; - if (!fileState) { - const fileContent = await readFile(path); - if (fileContent.success) { - const originalContent = calculateOriginalContent( - fileContent.data.content, - edits, - ); - fileChangesState.files[path] = proxy({ - path, - content: originalContent, - edits, - }); - } - } else { - // 因为存在多个edit render call,所以content可能初始化过了 - fileState.edits.push(...edits); - } - }, - - // 读取对应path文件中的所有edits,并应用这些edits,得到最终内容 - getFinalContent: (path: string) => { - const fileState = fileChangesState.files[path]; - if (!fileState) { - return ''; - } - return fileChangesState.files[path]?.edits.reduce((content, edit) => { - // 如果没有accept或reject过,则认为edit是有效的,需要应用 - if (!edit.editStatus) { - return content.replace(edit.old_string, edit.new_string); - } - if (edit.editStatus === 'accept') { - // `accept` 意味着 content 已经被更新 - return content; - } - return content; - }, fileState.content); - }, -}; diff --git a/browser/src/state/layout.ts b/browser/src/state/layout.ts new file mode 100644 index 000000000..cf22919f4 --- /dev/null +++ b/browser/src/state/layout.ts @@ -0,0 +1,55 @@ +import { proxy } from 'valtio'; + +interface LayoutState { + sidebarCollapsed: boolean; + rightPanelExpanded: boolean; + rightPanelWidthPercent: number; +} + +export const state = proxy({ + sidebarCollapsed: false, + rightPanelExpanded: false, + rightPanelWidthPercent: 60, +}); + +export const actions = { + toggleSidebar: () => { + state.sidebarCollapsed = !state.sidebarCollapsed; + }, + setSidebarCollapsed: (collapsed: boolean) => { + state.sidebarCollapsed = collapsed; + }, + toggleRightPanel: () => { + const wasExpanded = state.rightPanelExpanded; + state.rightPanelExpanded = !state.rightPanelExpanded; + + // Calculate optimal width when first opening the panel + if (!wasExpanded && state.rightPanelExpanded) { + const optimalWidth = actions.calculateRightPanelWidth(); + state.rightPanelWidthPercent = optimalWidth; + } + }, + setRightPanelExpanded: (expanded: boolean) => { + state.rightPanelExpanded = expanded; + }, + setRightPanelWidthPercent: (widthPercent: number) => { + const maxWidth = actions.calculateRightPanelWidth(); + state.rightPanelWidthPercent = Math.max( + 20, + Math.min(maxWidth, widthPercent), + ); + }, + calculateRightPanelWidth: () => { + const containerWidth = window.innerWidth; + const inputMaxWidth = 800; + const inputPadding = 48; // 24px * 2 + const reservedLeftSpace = inputMaxWidth + inputPadding; + + // Right panel width = 100vw - 800px - 48px + const rightPixels = containerWidth - reservedLeftSpace; + const rightPercent = (rightPixels / containerWidth) * 100; + + // Ensure minimum width of 20% + return Math.max(20, rightPercent); + }, +}; diff --git a/browser/src/state/mcp.ts b/browser/src/state/mcp.ts new file mode 100644 index 000000000..a96dcf86c --- /dev/null +++ b/browser/src/state/mcp.ts @@ -0,0 +1,145 @@ +import { message } from 'antd'; +import { proxy } from 'valtio'; +import type { NodeBridgeResponse } from '@/types/chat'; +import type { McpServerConfig } from '@/types/config'; +import { state as chatState } from './chat'; +import { actions as bridge } from './client'; +import { actions as configActions } from './config'; + +export type McpServerItemConfig = McpServerConfig & { + name: string; +}; + +interface McpState { + mcpServers: McpServerItemConfig[]; + recommendedMcpServices: McpServerItemConfig[]; +} + +export const state = proxy({ + mcpServers: [], + recommendedMcpServices: [ + { + name: 'Context7', + type: 'stdio' as const, + command: 'npx', + args: ['-y', '@upstash/context7-mcp', '--api-key', 'YOUR_API_KEY'], + }, + { + name: 'Figma', + command: 'npx', + args: [ + '-y', + 'figma-developer-mcp', + '--figma-api-key=YOUR-KEY', + '--stdio', + ], + type: 'stdio' as const, + }, + ], +}); + +interface ServerData { + status: string; + error?: string; + toolCount: number; + tools: string[]; +} + +export const actions = { + getMcpStatus: async () => { + const result = (await bridge.request('mcp.getStatus', { + cwd: chatState.cwd, + })) as NodeBridgeResponse<{ + servers: Record; + configs: Record; + globalConfigPath: string; + projectConfigPath: string; + isReady: boolean; + isLoading: boolean; + }>; + if (!result.success) { + message.error(result.message || 'Failed to get MCP status'); + return; + } + + state.mcpServers = Object.entries(result.data.configs).map( + ([name, config]) => ({ + name, + ...config, + }), + ); + }, + + addMcpServer: async (server: McpServerItemConfig, isGlobal = false) => { + try { + const { name, ...config } = server; + const currentServers = state.mcpServers.reduce( + (acc, s) => { + const { name: serverName, ...serverConfig } = s; + acc[serverName] = serverConfig; + return acc; + }, + {} as Record, + ); + + const newServers = { + ...currentServers, + [name]: config, + }; + + const success = await configActions.set( + 'mcpServers', + newServers, + isGlobal, + ); + + if (success) { + await actions.getMcpStatus(); + message.success(`Added MCP server: ${name}`); + } + return success; + } catch (error) { + message.error(`Failed to add MCP server: ${error}`); + return false; + } + }, + + toggleMcpServer: async (server: McpServerItemConfig, isGlobal = false) => { + try { + const { name, disable, ...config } = server; + const currentServers = state.mcpServers.reduce( + (acc, s) => { + const { name: serverName, ...serverConfig } = s; + acc[serverName] = serverConfig; + return acc; + }, + {} as Record, + ); + + const newServers = { + ...currentServers, + [name]: { + ...config, + disable: !disable, + }, + }; + + const success = await configActions.set( + 'mcpServers', + newServers, + isGlobal, + ); + + if (success) { + await actions.getMcpStatus(); + message.success( + `${disable ? 'Enabled' : 'Disabled'} MCP server: ${name}`, + ); + } + return success; + } catch (error) { + message.error(`Failed to toggle MCP server: ${error}`); + return false; + } + }, +}; diff --git a/browser/src/state/message.ts b/browser/src/state/message.ts deleted file mode 100644 index 582faf805..000000000 --- a/browser/src/state/message.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { proxy } from 'valtio'; - -interface Message { - role: 'user' | 'assistant'; - content: string; -} - -interface MessageState { - messages: Message[]; -} - -export const state = proxy({ - messages: [], -}); - -export const actions = { - addMessage: (message: Message) => { - state.messages.push(message); - }, -}; diff --git a/browser/src/state/project.ts b/browser/src/state/project.ts new file mode 100644 index 000000000..b2ed8545d --- /dev/null +++ b/browser/src/state/project.ts @@ -0,0 +1,23 @@ +import { proxy } from 'valtio'; +import { getProjectInfo, type ProjectInfo } from '@/api/project'; + +export interface ProjectState { + loading: boolean; + projectInfo: ProjectInfo | null; +} +export const state = proxy({ + loading: false, + projectInfo: null, +}); + +export const actions = { + async getProjectInfo(folder?: string) { + state.loading = true; + try { + const response = await getProjectInfo(folder); + state.projectInfo = response.data; + } finally { + state.loading = false; + } + }, +}; diff --git a/browser/src/state/sender.ts b/browser/src/state/sender.ts index 7a8541afe..21b2afec5 100644 --- a/browser/src/state/sender.ts +++ b/browser/src/state/sender.ts @@ -1,17 +1,17 @@ +import { Delta } from 'quill'; import { proxy } from 'valtio'; interface SenderState { prompt: string; - plainText: string; mode: string; - openFooter: boolean; + /** For quill editor render rich text */ + delta: Delta; } export const state = proxy({ prompt: '', - plainText: '', mode: 'agent', - openFooter: false, + delta: new Delta(), }); export const actions = { @@ -19,20 +19,11 @@ export const actions = { state.prompt = prompt; }, - updatePlainText: (plainText: string) => { - state.plainText = plainText; - }, - - updateOpenFooter: (openFooter: boolean) => { - state.openFooter = openFooter; - }, - updateMode: (mode: string) => { state.mode = mode; }, - updateModeAndFooterVisible: (mode: string, openFooter: boolean) => { - state.mode = mode; - state.openFooter = openFooter; + updateDelta: (delta: Delta) => { + state.delta = delta; }, }; diff --git a/browser/src/state/settings.ts b/browser/src/state/settings.ts deleted file mode 100644 index 8ca81ef97..000000000 --- a/browser/src/state/settings.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { proxy } from 'valtio'; -import { - getAvailableModels, - getAvailablePlugins, - getEffectiveSettings, - getSettings, - removeSetting, - setSetting, -} from '@/api/settings'; -import type { AppSettings, SettingsState } from '../types/settings'; - -export const state = proxy<{ settings: SettingsState }>({ - settings: { - currentScope: 'project', - globalSettings: {}, - projectSettings: {}, - effectiveSettings: { - language: 'English', - quiet: false, - approvalMode: 'suggest', - plugins: [], - }, - availableModels: [], - availablePlugins: [], - loading: false, - loaded: false, - hasUnsavedChanges: false, - }, -}); - -export const actions = { - // Switch scope - switchScope: (scope: 'global' | 'project') => { - state.settings.currentScope = scope; - }, - - // Load settings - loadSettings: async () => { - if (state.settings.loading) return; - - state.settings.loading = true; - try { - const [ - globalResponse, - projectResponse, - effectiveResponse, - modelsResponse, - pluginsResponse, - ] = await Promise.all([ - getSettings('global'), - getSettings('project'), - getEffectiveSettings(), - getAvailableModels(), - getAvailablePlugins(), - ]); - - state.settings.globalSettings = globalResponse.data || {}; - state.settings.projectSettings = projectResponse.data || {}; - state.settings.effectiveSettings = - effectiveResponse.data || state.settings.effectiveSettings; - state.settings.availableModels = modelsResponse.data || []; - state.settings.availablePlugins = pluginsResponse.data || []; - state.settings.loading = false; - state.settings.loaded = true; - } catch (error) { - console.error('Failed to load settings:', error); - state.settings.loading = false; - throw error; - } - }, - - // Update setting value (save directly) - updateSettingValue: async ( - key: keyof AppSettings, - value: AppSettings[keyof AppSettings], - ) => { - const { currentScope } = state.settings; - - // Update local state - if (currentScope === 'global') { - if (value === null || value === undefined) { - // If value is empty, remove the field - const { [key]: removed, ...rest } = state.settings.globalSettings; - state.settings.globalSettings = rest; - } else { - state.settings.globalSettings = { - ...state.settings.globalSettings, - [key]: value, - }; - } - } else { - if (value === null || value === undefined) { - // If value is empty, remove the field - const { [key]: removed, ...rest } = state.settings.projectSettings; - state.settings.projectSettings = rest; - } else { - state.settings.projectSettings = { - ...state.settings.projectSettings, - [key]: value, - }; - } - } - - // Save directly to server - try { - if (value === null || value === undefined) { - // Remove setting item - await removeSetting(currentScope, key); - } else { - // Use single setting update instead of batch update - await setSetting(currentScope, key, value); - } - - // Reload effective settings - const effectiveResponse = await getEffectiveSettings(); - state.settings.effectiveSettings = - effectiveResponse.data || state.settings.effectiveSettings; - } catch (error) { - console.error('Failed to save settings:', error); - // If save fails, consider rolling back local state - } - }, - - // Add plugin - addPlugin: async (plugin: string) => { - const { currentScope } = state.settings; - const currentPlugins = - currentScope === 'global' - ? state.settings.globalSettings.plugins || [] - : state.settings.projectSettings.plugins || []; - - if (!currentPlugins.includes(plugin)) { - const newPlugins = [...currentPlugins, plugin]; - - // Update local state - if (currentScope === 'global') { - state.settings.globalSettings = { - ...state.settings.globalSettings, - plugins: newPlugins, - }; - } else { - state.settings.projectSettings = { - ...state.settings.projectSettings, - plugins: newPlugins, - }; - } - - // Save plugin configuration directly - try { - await setSetting(currentScope, 'plugins', newPlugins); - - // Reload effective settings - const effectiveResponse = await getEffectiveSettings(); - state.settings.effectiveSettings = - effectiveResponse.data || state.settings.effectiveSettings; - } catch (error) { - console.error('Failed to add plugin:', error); - throw error; - } - } - }, - - // Remove plugin - removePlugin: async (plugin: string) => { - const { currentScope } = state.settings; - const currentPlugins = - currentScope === 'global' - ? state.settings.globalSettings.plugins || [] - : state.settings.projectSettings.plugins || []; - - const newPlugins = currentPlugins.filter((p) => p !== plugin); - - // Update local state - if (currentScope === 'global') { - if (newPlugins.length === 0) { - const { plugins: removed, ...rest } = state.settings.globalSettings; - state.settings.globalSettings = rest; - } else { - state.settings.globalSettings = { - ...state.settings.globalSettings, - plugins: newPlugins, - }; - } - } else { - if (newPlugins.length === 0) { - const { plugins: removed, ...rest } = state.settings.projectSettings; - state.settings.projectSettings = rest; - } else { - state.settings.projectSettings = { - ...state.settings.projectSettings, - plugins: newPlugins, - }; - } - } - - // Save plugin configuration directly - try { - if (newPlugins.length === 0) { - await removeSetting(currentScope, 'plugins'); - } else { - await setSetting(currentScope, 'plugins', newPlugins); - } - - // Reload effective settings - const effectiveResponse = await getEffectiveSettings(); - state.settings.effectiveSettings = - effectiveResponse.data || state.settings.effectiveSettings; - } catch (error) { - console.error('Failed to remove plugin:', error); - throw error; - } - }, -}; diff --git a/browser/src/state/suggestion.ts b/browser/src/state/suggestion.ts index 18c3beb89..affcebafe 100644 --- a/browser/src/state/suggestion.ts +++ b/browser/src/state/suggestion.ts @@ -1,43 +1,77 @@ import { proxy } from 'valtio'; -import { type FileListQueries, getFileList } from '@/api/files'; -import type { FileItem } from '@/api/model'; +import { actions as chatActions } from '@/state/chat'; +import type { CommandEntry, FileItem } from '@/types/chat'; interface SuggestionState { fileList: FileItem[]; - fileMap: Map; + slashCommandList: CommandEntry[]; loading: boolean; } export const state = proxy({ fileList: [], - fileMap: new Map(), + slashCommandList: [], loading: false, }); +const filterSlashCommands = ( + commands: CommandEntry[], + searchString?: string, +): CommandEntry[] => { + if (!searchString || !searchString.trim()) { + return commands; + } + + const lowerSearch = searchString.toLowerCase().trim(); + return commands.filter( + (cmd) => + cmd.command.name.toLowerCase().includes(lowerSearch) || + cmd.command.description.toLowerCase().includes(lowerSearch), + ); +}; + export const actions = { - setFileList: (value: FileItem[]) => { - state.fileList = value; + getFileList: async (query?: string) => { + if (state.loading) { + return; + } + + state.loading = true; + try { + const items = await chatActions.getFiles({ query }); + state.fileList = items || []; + state.loading = false; + } catch (error) { + state.loading = false; + console.error('Failed to get file list:', error); + } + }, + setSlashCommandList: (value: CommandEntry[]) => { + state.slashCommandList = value; state.loading = false; - value.forEach((file) => { - state.fileMap.set(file.path, file); - }); }, - getFileList: async (queries?: FileListQueries) => { + + getSlashCommandList: async ({ + searchString, + }: { + searchString?: string; + } = {}) => { if (state.loading) { return; } state.loading = true; try { - const response = await getFileList(queries); - actions.setFileList(response.data.items || []); + const slashCommands = await chatActions.getSlashCommands(); + const filteredCommands = filterSlashCommands( + slashCommands, + searchString, + ).filter((cmd) => cmd.command.type !== 'local-jsx'); + actions.setSlashCommandList(filteredCommands); } catch (error) { state.loading = false; - console.error('Failed to get file list:', error); + console.error('Failed to get slash command list:', error); } }, - getFileByPath: (path: string) => { - return state.fileMap.get(path); - }, }; diff --git a/browser/src/state/toolApproval.ts b/browser/src/state/toolApproval.ts deleted file mode 100644 index 3ba262b50..000000000 --- a/browser/src/state/toolApproval.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { proxy } from 'valtio'; -import { submitToolApproval } from '@/api/toolApproval'; -import type { CodeNormalViewerMode } from '@/types/codeViewer'; -import type { - ToolApprovalErrorMessage, - ToolApprovalRequestMessage, - ToolApprovalResultMessage, -} from '@/types/message'; -import * as fileChanges from './fileChanges'; - -export interface EditPayload { - path: string; - edit: fileChanges.FileEdit; - mode?: CodeNormalViewerMode; -} - -export interface ToolApprovalState { - // 当前审批状态 - pending: boolean; - callId: string | null; - toolName: string | null; - params: Record | null; - editPayload?: EditPayload; - - // 提交状态 - submitting: boolean; - submitError: string | null; - lastSubmitOption: 'once' | 'always' | 'always_tool'; - - // 当前的审批请求消息(用于UI显示) - currentRequest: ToolApprovalRequestMessage | null; -} - -export const toolApprovalState = proxy({ - pending: false, - callId: null, - toolName: null, - params: null, - submitting: false, - submitError: null, - lastSubmitOption: 'once', - currentRequest: null, -}); - -export const toolApprovalActions = { - // 处理来自后端的工具审批请求 - handleApprovalRequest: (message: ToolApprovalRequestMessage) => { - toolApprovalState.pending = true; - toolApprovalState.callId = message.toolCallId; - toolApprovalState.toolName = message.toolName; - toolApprovalState.params = message.args as Record; - toolApprovalState.currentRequest = message; - toolApprovalState.submitError = null; - - if (message.toolName === 'edit' || message.toolName === 'write') { - const { file_path, old_string, new_string, mode } = message.args as { - file_path: string; - old_string: string; - new_string: string; - mode?: CodeNormalViewerMode; - }; - toolApprovalState.editPayload = { - path: file_path, - edit: { - toolCallId: message.toolCallId, - old_string, - new_string, - }, - mode, - }; - } - }, - - // 处理审批结果 - handleApprovalResult: (_message: ToolApprovalResultMessage) => { - toolApprovalState.pending = false; - toolApprovalState.callId = null; - toolApprovalState.toolName = null; - toolApprovalState.params = null; - toolApprovalState.currentRequest = null; - toolApprovalState.submitting = false; - toolApprovalState.submitError = null; - toolApprovalState.lastSubmitOption = 'once'; - toolApprovalState.editPayload = undefined; - }, - - // 处理审批错误 - handleApprovalError: (message: ToolApprovalErrorMessage) => { - toolApprovalState.submitting = false; - toolApprovalState.submitError = message.error; - }, - - // 批准工具使用 - approveToolUse: async ( - approved: boolean, - option: 'once' | 'always' | 'always_tool' = 'once', - ) => { - if (!toolApprovalState.callId) { - return; - } - - if ( - toolApprovalState.toolName === 'edit' && - toolApprovalState.editPayload - ) { - const { path, edit, mode } = toolApprovalState.editPayload; - if (approved) { - fileChanges.fileChangesActions.acceptEdit(path, edit, mode); - } else { - fileChanges.fileChangesActions.rejectEdit(path, edit, mode); - } - } - - toolApprovalState.submitting = true; - toolApprovalState.submitError = null; - toolApprovalState.lastSubmitOption = option; - - try { - await submitToolApproval(toolApprovalState.callId, approved, option); - } catch (error) { - toolApprovalState.submitting = false; - toolApprovalState.submitError = - error instanceof Error ? error.message : String(error); - } - }, - - // 清除当前审批状态(用于取消或重置) - clearCurrentApproval: () => { - toolApprovalState.pending = false; - toolApprovalState.callId = null; - toolApprovalState.toolName = null; - toolApprovalState.params = null; - toolApprovalState.currentRequest = null; - toolApprovalState.submitting = false; - toolApprovalState.submitError = null; - toolApprovalState.lastSubmitOption = 'once'; - toolApprovalState.editPayload = undefined; - }, - - // 重试提交(在出错时使用) - retrySubmit: () => { - if (toolApprovalState.submitError && toolApprovalState.callId) { - const lastOption = toolApprovalState.lastSubmitOption; - toolApprovalActions.approveToolUse(true, lastOption); - } - }, -}; diff --git a/browser/src/state/ui.ts b/browser/src/state/ui.ts new file mode 100644 index 000000000..70aab34fa --- /dev/null +++ b/browser/src/state/ui.ts @@ -0,0 +1,37 @@ +import { proxy } from 'valtio'; + +interface UIState { + settingsModalOpen: boolean; + projectSelectModalOpen: boolean; +} + +export const uiState = proxy({ + settingsModalOpen: false, + projectSelectModalOpen: false, +}); + +export const uiActions = { + openSettingsModal: () => { + uiState.settingsModalOpen = true; + }, + + closeSettingsModal: () => { + uiState.settingsModalOpen = false; + }, + + toggleSettingsModal: () => { + uiState.settingsModalOpen = !uiState.settingsModalOpen; + }, + + openProjectSelectModal: () => { + uiState.projectSelectModalOpen = true; + }, + + closeProjectSelectModal: () => { + uiState.projectSelectModalOpen = false; + }, + + toggleProjectSelectModal: () => { + uiState.projectSelectModalOpen = !uiState.projectSelectModalOpen; + }, +}; diff --git a/browser/src/types/chat.ts b/browser/src/types/chat.ts new file mode 100644 index 000000000..3b729285b --- /dev/null +++ b/browser/src/types/chat.ts @@ -0,0 +1,249 @@ +import type { Delta } from 'quill'; + +export type SystemMessage = { + role: 'system'; + content: string; +}; + +export type TextPart = { + type: 'text'; + text: string; +}; + +export type ImagePart = { + type: 'image'; + data: string; + mimeType: string; +}; + +export type FilePart = { + type: 'file'; + filename?: string; + data: string; + mimeType: string; +}; + +export type UserContent = string | Array; + +export type UserMessage = { + role: 'user'; + content: UserContent; + hidden?: boolean; + uiContent?: string; +}; + +export type ReasoningPart = { + type: 'reasoning'; + text: string; +}; + +export type ToolUsePart = { + type: 'tool_use'; + id: string; + name: string; + input: Record; + displayName?: string; + description?: string; +}; + +// assistant message +export type AssistantContent = + | string + | Array; + +export type AssistantMessage = { + role: 'assistant'; + uuid: string; + parentUuid: string | null; + content: AssistantContent; + text: string; + model: string; + usage: { + input_tokens: number; + output_tokens: number; + cache_read_input_tokens?: number; + cache_creation_input_tokens?: number; + }; +}; + +// tool message +type TodoItem = { + id: string; + content: string; + status: 'pending' | 'in_progress' | 'completed'; + priority: 'low' | 'medium' | 'high'; +}; + +type TodoReadReturnDisplay = { + type: 'todo_read'; + todos: TodoItem[]; +}; + +type TodoWriteReturnDisplay = { + type: 'todo_write'; + oldTodos: TodoItem[]; + newTodos: TodoItem[]; +}; + +type DiffViewerReturnDisplay = { + type: 'diff_viewer'; + originalContent: string | { inputKey: string }; + newContent: string | { inputKey: string }; + filePath: string; + [key: string]: any; +}; + +export type ToolResult = { + llmContent: string | (TextPart | ImagePart)[]; + returnDisplay?: + | string + | DiffViewerReturnDisplay + | TodoReadReturnDisplay + | TodoWriteReturnDisplay; + isError?: boolean; +}; + +export type ToolResultPart = { + type: 'tool_result'; + id: string; + name: string; + input: Record; + result: ToolResult; +}; + +export type ToolResultContent = Array; + +export type ToolResultMessage = { + role: 'user'; + content: ToolResultContent; +}; + +export type ToolUseMessage = { + role: 'user'; + content: ToolUsePart; +}; + +export type Message = + | SystemMessage + | UserMessage + | AssistantMessage + | ToolResultMessage; + +export type UIToolPart = { + type: 'tool'; + state: 'tool_use' | 'tool_result'; + id: string; + name: string; + input: Record; + // tool_result + result?: ToolResult; + + // tool_use + displayName?: string; + description?: string; +}; + +export type UIAssistantContent = Array; + +export type UIAssistantMessage = { + role: 'assistant'; + uuid: string; + parentUuid: string | null; + content: UIAssistantContent; + text: string; + model: string; + usage: { + input_tokens: number; + output_tokens: number; + cache_read_input_tokens?: number; + cache_creation_input_tokens?: number; + }; +}; + +export type UIDisplayContent = { + type: 'error' | 'info' | 'compression'; + text: string; +}; + +export type UIDisplayMessage = { + role: 'ui_display'; + content: UIDisplayContent; +}; + +export type UIMessage = + | SystemMessage + | UserMessage + | UIAssistantMessage + | UIDisplayMessage; + +export type LoopResult = + | { + success: true; + data: Record; + metadata: { + turnsCount: number; + toolCallsCount: number; + duration: number; + }; + } + | { + success: false; + error: { + type: 'tool_denied' | 'max_turns_exceeded' | 'api_error' | 'canceled'; + message: string; + details?: Record; + }; + }; + +// approval +export type ApprovalResult = + | 'approve_once' + | 'approve_always_edit' + | 'approve_always_tool' + | 'deny'; + +export type ToolUse = { + name: string; + params: Record; + callId: string; +}; + +export type ApprovalCategory = 'read' | 'write' | 'command' | 'network'; + +// slash command +export interface SlashCommand { + type: 'local' | 'local-jsx' | 'prompt'; + name: string; + description: string; + isEnabled?: boolean; +} + +export enum CommandSource { + Builtin = 'builtin', + User = 'user', + Project = 'project', + Plugin = 'plugin', +} + +export type CommandEntry = { + command: SlashCommand; + source: CommandSource; +}; + +export type NodeBridgeResponse = { + success: boolean; + data: T; + error?: string; + message?: string; +}; + +export interface UIUserMessage extends UserMessage { + delta?: Delta; +} + +// files +export interface FileItem { + path: string; + type: 'file' | 'directory'; + name: string; +} diff --git a/browser/src/types/config.ts b/browser/src/types/config.ts new file mode 100644 index 000000000..2e498a097 --- /dev/null +++ b/browser/src/types/config.ts @@ -0,0 +1,69 @@ +export type ApprovalMode = 'default' | 'autoEdit' | 'yolo'; + +export type CommitConfig = { + language: string; +}; + +export type McpStdioServerConfig = { + type: 'stdio'; + command: string; + args: string[]; + env?: Record; + disable?: boolean; +}; +export type McpSSEServerConfig = { + type: 'sse'; + url: string; + disable?: boolean; + headers?: Record; +}; +export type McpHttpServerConfig = { + type: 'http'; + url: string; + disable?: boolean; + headers?: Record; +}; +export type McpServerConfig = + | McpStdioServerConfig + | McpSSEServerConfig + | McpHttpServerConfig; + +export interface ProviderConfig { + id: string; + env: string[]; + name: string; + apiEnv?: string[]; + api?: string; + doc: string; + models: Record; + options?: { + baseURL?: string; + apiKey?: string; + headers?: Record; + }; +} + +export type Config = { + model: string; + planModel: string; + language: string; + quiet: boolean; + approvalMode: ApprovalMode; + plugins: string[]; + mcpServers: Record; + provider?: Record; + systemPrompt?: string; + todo?: boolean; + /** + * Controls whether automatic conversation compression is enabled. + * When set to false, conversation history will accumulate and context limit will be exceeded. + * + * @default true + */ + autoCompact?: boolean; + commit?: CommitConfig; + outputStyle?: string; + outputFormat?: 'text' | 'stream-json' | 'json'; + autoUpdate?: boolean; + browser?: boolean; +}; diff --git a/browser/src/types/context.ts b/browser/src/types/context.ts index 6ebaeb75d..c5e51d0f3 100644 --- a/browser/src/types/context.ts +++ b/browser/src/types/context.ts @@ -1,61 +1,17 @@ -import type { UploadFile } from 'antd'; -import type { LexicalNode } from 'lexical'; -import type { JSX } from 'react'; import type { FileItem, ImageItem } from '@/api/model'; import type { ContextType } from '@/constants/context'; +import type { SlashCommand } from '@/types/chat'; -type AttachmentItem = UploadFile; - -export type ContextStoreValue = FileItem | ImageItem | AttachmentItem; +export type ContextStoreValue = FileItem | ImageItem | SlashCommand; export interface ContextItem { type: ContextType; - /** - * Only when it's used in context popup menu, it woule be like \@Abc:[xyz] - * - * it should be unique - */ value: string; displayText: string; context?: ContextStoreValue; - [key: string]: any; } export interface ContextFileType { extName: string; mime: string; } - -export interface AiContextNodeInfo { - displayText: string; - value: string; -} - -export interface AiContextNodeConfig { - type: ContextType; - /** if don't used in context popup menu, it's not required */ - matchRegex: RegExp; - aiContextId: string; - valueFormatter?: (value: string) => string; - /** if don't used in context popup menu, it's not required */ - pickInfo?: (regExpExecArray: RegExpExecArray) => AiContextNodeInfo; - /** render function */ - render: (args: { - info: AiContextNodeInfo; - onClose?: () => void; - context?: ContextStoreValue; - }) => JSX.Element; -} - -export interface AiContextCacheNode { - type: ContextType; - originalText: string; - displayText: string; - lexicalNode: LexicalNode; -} - -export interface AppendedLexicalNode { - lexicalNode: LexicalNode; - type: string; - length?: number; -} diff --git a/browser/src/types/mcp.ts b/browser/src/types/mcp.ts index 734fbd85a..a297060b9 100644 --- a/browser/src/types/mcp.ts +++ b/browser/src/types/mcp.ts @@ -1,27 +1,4 @@ -/** - * MCP (Message Control Protocol) related type definitions - */ - -// Basic MCP server configuration -export interface McpServerConfig { - command?: string; - args?: string[]; - url?: string; - type?: 'sse' | 'stdio'; - env?: Record; - scope?: 'global' | 'project'; -} - -// MCP server instance for dropdown component -export interface McpServer { - key: string; - name: string; - config: McpServerConfig; - installed: boolean; - scope: 'global' | 'project'; -} - -// MCP server instance for manager component +import { type McpServerItemConfig } from '@/state/mcp'; export interface McpManagerServer { key: string; name: string; @@ -32,26 +9,12 @@ export interface McpManagerServer { type?: 'sse' | 'stdio'; env?: Record; installed: boolean; -} - -// Preset MCP service configuration -export interface PresetMcpService { - key: string; - name: string; - description: string; - requiresApiKey?: boolean; - apiKeyLabel?: string; - apiKeyPlaceholder?: string; - config: { - name: string; - command: string; - args: string[]; - }; + isPreset?: boolean; } // JSON configuration format for adding services export interface JsonConfigFormat { - mcpServers?: Record; + mcpServers?: Record; name?: string; command?: string; args?: string[]; @@ -71,11 +34,6 @@ export interface FormValues { env?: string; } -// Component props -export interface McpDropdownProps { - loading?: boolean; -} - export interface McpManagerProps { visible: boolean; onClose: () => void; @@ -90,20 +48,20 @@ export type McpTransportType = 'sse' | 'stdio'; // API Response types export interface McpServersResponse { success: true; - servers: Record; + servers: Record; scope: 'global' | 'project'; } export interface McpServerResponse { success: true; - server: McpServerConfig; + server: McpServerItemConfig; name: string; } export interface McpOperationResponse { success: true; message: string; - server?: McpServerConfig; + server?: McpServerItemConfig; } // API Request types @@ -125,3 +83,150 @@ export interface UpdateMcpServerRequest { env?: string; global?: boolean; } + +// Hook types +export interface UseMcpServerLoaderOptions { + onLoadError?: (error: Error) => void; + onToggleError?: (error: Error, serverName: string) => void; +} + +export interface UseMcpServerLoaderReturn { + // Common state + loading: boolean; + + // For McpDropdown + mcpServers: McpServerItemConfig[]; + loadMcpServers: () => Promise; + handleToggleEnabled: ( + serverName: string, + enabled: boolean, + scope: string, + ) => Promise; + handleQuickAdd: (service: McpServerItemConfig) => Promise; + + // For McpManager + managerServers: McpManagerServer[]; + loadServers: () => Promise; + handleToggleService: ( + serverName: string, + enabled: boolean, + scope: string, + ) => Promise; + handleEditServer: ( + originalName: string, + originalScope: string, + newConfig: { + name: string; + command?: string; + args?: string[]; + url?: string; + transport?: string; + env?: string; + global?: boolean; + }, + ) => Promise; + handleDeleteLocal: (serverName: string, scope: string) => void; +} + +export interface UseMcpServicesOptions { + onLoadError?: (error: Error) => void; + onToggleError?: (error: Error, serverName: string) => void; +} + +export interface UseMcpServicesReturn { + allKnownServices: Set; + serviceConfigs: Map; + updateKnownServices: (newServices: Set) => void; + updateServiceConfigs: (newConfigs: Map) => void; + loadMcpData: () => Promise<{ + globalServers: Record; + projectServers: Record; + }>; + handleToggleService: ( + serverName: string, + enabled: boolean, + scope: string, + onSuccess?: () => void | Promise, + ) => Promise; + initializeFromLocalStorage: () => { + knownServices: Set; + configs: Map; + }; +} + +export interface McpServiceItemProps { + server: McpServerItemConfig; + onToggle: (serverName: string, enabled: boolean, scope: string) => void; +} + +export interface McpServerTableProps { + servers: McpManagerServer[]; + loading: boolean; + onToggleService: ( + serverName: string, + enabled: boolean, + scope: string, + ) => void; + onDeleteSuccess?: () => void; + onDeleteLocal?: (serverName: string, scope: string) => void; + onEditServer?: (server: McpManagerServer) => void; +} + +export interface McpAddFormProps { + visible: boolean; + inputMode: 'json' | 'form'; + addScope: 'global' | 'project'; + onCancel: () => void; + onSuccess: () => void; + onInputModeChange: (mode: 'json' | 'form') => void; + onScopeChange: (scope: 'global' | 'project') => void; + editMode?: boolean; + editingServer?: McpManagerServer; + onEditServer?: ( + originalName: string, + originalScope: string, + newConfig: { + name: string; + command?: string; + args?: string[]; + url?: string; + transport?: string; + env?: string; + global?: boolean; + }, + ) => Promise; +} + +export interface McpEditFormProps { + visible: boolean; + inputMode: 'json' | 'form'; + editScope: 'global' | 'project'; + editingServer: McpManagerServer; + onCancel: () => void; + onSuccess: () => void; + onInputModeChange: (mode: 'json' | 'form') => void; + onScopeChange: (scope: 'global' | 'project') => void; +} + +// MCP configuration item for add form +export interface McpConfigItem { + id: string; + scope: 'global' | 'project'; + inputMode: 'json' | 'form'; + name: string; + transport: string; + command?: string; + args?: string; + url?: string; + env?: string; + jsonConfig?: string; +} + +// MCP JSON Editor component props +export interface McpJsonEditorProps { + value?: string; + onChange?: (value: string) => void; + placeholder?: string; + height?: string; + disabled?: boolean; +} diff --git a/browser/src/types/message.ts b/browser/src/types/message.ts deleted file mode 100644 index ef78c189d..000000000 --- a/browser/src/types/message.ts +++ /dev/null @@ -1,133 +0,0 @@ -import type { - UIMessage as BaseUIMessage, - FileUIPart, - ReasoningUIPart, - SourceUIPart, - StepStartUIPart, - ToolInvocationUIPart, -} from '@ai-sdk/ui-utils'; -import type { ContextItem } from './context'; - -export enum UIMessageType { - Text = 'text', - Reasoning = 'reasoning', - Source = 'source', - File = 'file', - ToolCall = 'tool_call', - ToolCallResult = 'tool_result', - Tool = 'tool', - TextDelta = 'text_delta', - ToolApprovalRequest = 'tool_approval_request', - ToolApprovalResult = 'tool_approval_result', - ToolApprovalError = 'tool_approval_error', -} - -export type UIMessage = Omit & { - annotations: UIMessageAnnotation[]; -}; - -export type UIUserMessage = Omit & { - role: 'user'; - attachedContexts: ContextItem[]; - /** - * Original content input by the user, including context markers - */ - contextContent: string; -}; - -export type UIMessageAnnotation = - | TextDeltaMessage - | TextMessage - | ReasoningMessage - | ToolCallMessage - | ToolCallResultMessage - | ToolMessage - | ToolApprovalRequestMessage - | ToolApprovalResultMessage - | ToolApprovalErrorMessage; - -export interface ToolMessage { - type: UIMessageType.Tool; - toolCallId: string; - toolName: string; - // call corresponds to tool_call, result corresponds to tool_result - state: 'call' | 'result'; - // 0 is call, 1 is result - step: number; - args: Record; - result?: Record; -} - -export interface TextDeltaMessage { - type: UIMessageType.TextDelta; - text: string; -} - -export interface ToolCallMessage { - type: UIMessageType.ToolCall; - toolCallId: string; - toolName: string; - args: Record; -} - -export interface ToolCallResultMessage { - type: UIMessageType.ToolCallResult; - toolCallId: string; - toolName: string; - args: Record; - result: Record; -} - -export type TextMessage = { - type: UIMessageType.Text; - text: string; - state?: UIMessageType.TextDelta | UIMessageType.Text; - mode?: string; -}; - -export type ReasoningMessage = ReasoningUIPart & { - text?: string; -}; - -export type ToolInvocationMessage = ToolInvocationUIPart; - -export type SourceMessage = SourceUIPart; - -export type FileMessage = FileUIPart; - -export type StepStartMessage = StepStartUIPart; - -export type MessageAnnotation = - | TextMessage - | ReasoningMessage - | ToolInvocationMessage - | SourceMessage - | FileMessage - | StepStartMessage; - -export interface ToolApprovalRequestMessage { - type: UIMessageType.ToolApprovalRequest; - toolCallId: string; - toolName: string; - args: Record; - [key: string]: unknown; -} - -export interface ToolApprovalResultMessage { - type: UIMessageType.ToolApprovalResult; - toolCallId: string; - toolName: string; - approved: boolean; - option?: 'once' | 'always' | 'always_tool'; - timestamp: number; - [key: string]: unknown; -} - -export interface ToolApprovalErrorMessage { - type: UIMessageType.ToolApprovalError; - toolCallId: string; - toolName: string; - error: string; - timestamp: number; - [key: string]: unknown; -} diff --git a/browser/src/types/settings.ts b/browser/src/types/settings.ts deleted file mode 100644 index 85985af4a..000000000 --- a/browser/src/types/settings.ts +++ /dev/null @@ -1,45 +0,0 @@ -export interface AppSettings { - model?: string; - smallModel?: string; - planModel?: string; - language: string; - quiet: boolean; - approvalMode: 'suggest' | 'auto-edit' | 'full-auto'; - plugins: string[]; -} - -export interface SettingsScope { - type: 'global' | 'project'; - label: string; - path: string; -} - -export interface ModelOption { - key: string; - value: string; -} - -export interface SettingsState { - // Current configuration scope - currentScope: 'global' | 'project'; - // Configuration data - globalSettings: Partial; - projectSettings: Partial; - // Merged effective configuration - effectiveSettings: AppSettings; - // Available models list - availableModels: ModelOption[]; - // Available plugins list - availablePlugins: string[]; - // Loading state - loading: boolean; - loaded: boolean; - // Whether there are unsaved changes - hasUnsavedChanges: boolean; -} - -export interface SettingsResponse { - success: boolean; - data?: { success: boolean } | AppSettings | string; - error?: string; -} diff --git a/browser/src/types/tool.ts b/browser/src/types/tool.ts deleted file mode 100644 index 7b239fcb8..000000000 --- a/browser/src/types/tool.ts +++ /dev/null @@ -1,26 +0,0 @@ -export interface IFetchToolResult { - result?: string; - code?: number; - codeText?: string; - contentType?: string; - durationMs?: number; -} - -export interface IBashToolResult { - stdout?: string; - stderr?: string; -} - -export interface IGlobToolResult { - filenames: string[]; - message?: string; -} - -export interface IGrepToolResult { - filenames: string[]; - durationMs?: number; -} - -export interface IReadToolResult { - totalLines?: number; -} diff --git a/browser/src/utils/chat.ts b/browser/src/utils/chat.ts deleted file mode 100644 index 9aacf6761..000000000 --- a/browser/src/utils/chat.ts +++ /dev/null @@ -1,76 +0,0 @@ -interface TextDiff { - content: string; - type: '+' | '-'; - index: number; -} - -/** - * 获取两个字符串之间的差异 - * @param str1 原始字符串 - * @param str2 新字符串 - * @returns 返回一个字符串数组,每个元素代表一个差异 - * 如果是添加的文本,会以'+'开头 - * 如果是删除的文本,会以'-'开头 - */ -export function getTextDiff(str1: string, str2: string): TextDiff[] { - const diffs: TextDiff[] = []; - - // 如果字符串完全相同,直接返回空数组 - if (str1 === str2) { - return diffs; - } - - // 找出较长和较短的字符串 - const [shorter, longer] = - str1.length < str2.length ? [str1, str2] : [str2, str1]; - const isAddition = str1.length < str2.length; - - let i = 0; - let j = 0; - let currentDiff = ''; - - while (i < longer.length) { - if (j >= shorter.length || longer[i] !== shorter[j]) { - // 累积差异字符 - currentDiff += longer[i]; - } else { - // 如果有累积的差异,添加到结果中 - if (currentDiff) { - const diff: TextDiff = { - content: currentDiff, - type: isAddition ? '+' : '-', - index: j, - }; - - diffs.push(diff); - currentDiff = ''; - } - j++; - } - i++; - } - - // 处理最后可能剩余的差异 - if (currentDiff) { - const diff: TextDiff = { - content: currentDiff, - type: isAddition ? '+' : '-', - index: j, - }; - - diffs.push(diff); - } - - return diffs; -} - -export function getInputInfo(prevContent: string, nextContent: string) { - const diffs = getTextDiff(prevContent, nextContent); - - // 本次输入只输入了一个@,代表唤起菜单 - return { - isInputingAiContext: - diffs.length === 1 && diffs[0].content === '@' && diffs[0].type === '+', - position: diffs[0]?.index, - }; -} diff --git a/browser/src/utils/codeViewer.ts b/browser/src/utils/codeViewer.ts index 868f099b6..db50aae61 100644 --- a/browser/src/utils/codeViewer.ts +++ b/browser/src/utils/codeViewer.ts @@ -1,90 +1,94 @@ -import * as monaco from 'monaco-editor'; import type { CodeViewerLanguage, DiffStat } from '@/types/codeViewer'; -async function computeDiff(original: string, modified: string) { - const element = document.createElement('div'); - - const editor = monaco.editor.createDiffEditor(element); - - const originalModel = monaco.editor.createModel(original); - const modifiedModel = monaco.editor.createModel(modified); - - editor.setModel({ - original: originalModel, - modified: modifiedModel, - }); - - const diffReady = new Promise<{ - changes: monaco.editor.ILineChange[]; - lines: { original: number; modified: number }; - }>((resolve) => { - const disposable = editor.onDidUpdateDiff(() => { - disposable.dispose(); // 确保只触发一次 - const changes = editor.getLineChanges() || []; - const lines = { - original: originalModel.getLineCount(), - modified: modifiedModel.getLineCount(), - }; - - editor.dispose(); - originalModel.dispose(); - modifiedModel.dispose(); - resolve({ changes, lines }); +// Simple line-by-line diff algorithm +function computeLineDiff(originalLines: string[], modifiedLines: string[]) { + const diffBlocks = []; + let originalIndex = 0; + let modifiedIndex = 0; + + while ( + originalIndex < originalLines.length || + modifiedIndex < modifiedLines.length + ) { + // Find the start of a difference + while ( + originalIndex < originalLines.length && + modifiedIndex < modifiedLines.length && + originalLines[originalIndex] === modifiedLines[modifiedIndex] + ) { + originalIndex++; + modifiedIndex++; + } + + if ( + originalIndex >= originalLines.length && + modifiedIndex >= modifiedLines.length + ) { + break; + } + + const blockStart = { + original: originalIndex, + modified: modifiedIndex, + }; + + // Find the end of the difference + let originalEnd = originalIndex; + let modifiedEnd = modifiedIndex; + + // Simple heuristic: advance until we find matching lines or reach the end + while ( + (originalEnd < originalLines.length || + modifiedEnd < modifiedLines.length) && + (originalEnd >= originalLines.length || + modifiedEnd >= modifiedLines.length || + originalLines[originalEnd] !== modifiedLines[modifiedEnd]) + ) { + if (originalEnd < originalLines.length) originalEnd++; + if (modifiedEnd < modifiedLines.length) modifiedEnd++; + } + + diffBlocks.push({ + originalStartLineNumber: blockStart.original + 1, + originalEndLineNumber: originalEnd, + modifiedStartLineNumber: blockStart.modified + 1, + modifiedEndLineNumber: modifiedEnd, + addLines: modifiedEnd - blockStart.modified, + removeLines: originalEnd - blockStart.original, }); - }); - return diffReady; + originalIndex = originalEnd; + modifiedIndex = modifiedEnd; + } + + return diffBlocks; } export async function diff( original: string, modified: string, ): Promise { - const rawDiff = await computeDiff(original, modified); - - const diffResult: DiffStat = { - addLines: 0, - removeLines: 0, - diffBlockStats: [], - originalLines: rawDiff.lines.original, - modifiedLines: rawDiff.lines.modified, + const originalLines = original.split('\n'); + const modifiedLines = modified.split('\n'); + + const diffBlockStats = computeLineDiff(originalLines, modifiedLines); + + const addLines = diffBlockStats.reduce( + (sum, block) => sum + block.addLines, + 0, + ); + const removeLines = diffBlockStats.reduce( + (sum, block) => sum + block.removeLines, + 0, + ); + + return { + addLines, + removeLines, + diffBlockStats, + originalLines: originalLines.length, + modifiedLines: modifiedLines.length, }; - - for (const rawDiffItem of rawDiff.changes) { - const { - modifiedEndLineNumber, - modifiedStartLineNumber, - originalEndLineNumber, - originalStartLineNumber, - } = rawDiffItem; - - diffResult.addLines += - modifiedEndLineNumber > 0 - ? modifiedEndLineNumber - modifiedStartLineNumber + 1 - : 0; - - diffResult.removeLines += - originalEndLineNumber > 0 - ? originalEndLineNumber - originalStartLineNumber + 1 - : 0; - - diffResult.diffBlockStats.push({ - modifiedEndLineNumber, - modifiedStartLineNumber, - originalEndLineNumber, - originalStartLineNumber, - addLines: - modifiedEndLineNumber > 0 - ? modifiedEndLineNumber - modifiedStartLineNumber + 1 - : 0, - removeLines: - originalEndLineNumber > 0 - ? originalEndLineNumber - originalStartLineNumber + 1 - : 0, - }); - } - - return diffResult; } const extToLanguage: Record = { diff --git a/browser/src/utils/context.ts b/browser/src/utils/context.ts index 95a7896d1..e93b8e4ee 100644 --- a/browser/src/utils/context.ts +++ b/browser/src/utils/context.ts @@ -1,3 +1,8 @@ +import type { ImageItem } from '@/api/model'; +import { ContextType } from '@/constants/context'; +import type { FileItem, SlashCommand } from '@/types/chat'; +import type { ContextItem, ContextStoreValue } from '@/types/context'; + export async function imageUrlToBase64(url: string) { return new Promise((resolve) => { const canvas = document.createElement('canvas'); @@ -28,7 +33,7 @@ export async function imageUrlToBase64(url: string) { }); } -// 根据图片 src 猜测 mime 类型 +// Guess mime type based on image src export function guessImageMime(src: string): string { if (src.startsWith('data:')) { const match = src.match(/^data:(image\/[a-zA-Z0-9.+-]+)[;,]/); @@ -46,3 +51,37 @@ export function guessImageMime(src: string): string { } return 'image/png'; } + +export function storeValueToContextItem( + storeValue: ContextStoreValue, + type: ContextType, +): ContextItem | null { + switch (type) { + case ContextType.FILE: + return { + type: ContextType.FILE, + value: (storeValue as FileItem).path, + displayText: (storeValue as FileItem).name, + context: storeValue, + }; + + case ContextType.IMAGE: + return { + type: ContextType.IMAGE, + value: (storeValue as ImageItem).src, + displayText: 'Image', + context: storeValue, + }; + + case ContextType.SLASH_COMMAND: + return { + type: ContextType.SLASH_COMMAND, + value: (storeValue as SlashCommand).name, + displayText: (storeValue as SlashCommand).name, + context: storeValue, + }; + + default: + return null; + } +} diff --git a/browser/src/utils/mergeMessages.ts b/browser/src/utils/mergeMessages.ts deleted file mode 100644 index a60ff9b1d..000000000 --- a/browser/src/utils/mergeMessages.ts +++ /dev/null @@ -1,194 +0,0 @@ -import type { - ReasoningMessage, - TextMessage, - ToolApprovalErrorMessage, - ToolApprovalRequestMessage, - ToolApprovalResultMessage, - ToolMessage, - UIMessageAnnotation, -} from '@/types/message'; -import { UIMessageType } from '@/types/message'; - -export function mergeMessages( - messages: UIMessageAnnotation[], -): UIMessageAnnotation[] { - if (!messages || messages.length === 0) { - return []; - } - - const merged: UIMessageAnnotation[] = []; - let textAccumulator = ''; - let reasoningAccumulator = ''; - - const isTextMessage = (msg: UIMessageAnnotation): msg is TextMessage => - msg.type === UIMessageType.Text; - - const isToolMessage = (msg: UIMessageAnnotation): msg is ToolMessage => - msg.type === UIMessageType.Tool; - - const isReasoningMessage = ( - msg: UIMessageAnnotation, - ): msg is ReasoningMessage => msg.type === UIMessageType.Reasoning; - - const findMatchingToolCall = ( - toolCallId: string, - fromIndex: number, - ): number => { - for (let i = fromIndex - 1; i >= 0; i--) { - const msg = merged[i]; - if ( - isToolMessage(msg) && - msg.toolCallId === toolCallId && - msg.state === 'call' - ) { - return i; - } - } - return -1; - }; - - const finalizeTextAccumulator = (): void => { - const lastMessage = merged[merged.length - 1]; - if ( - lastMessage && - isTextMessage(lastMessage) && - lastMessage.state === UIMessageType.TextDelta - ) { - lastMessage.state = UIMessageType.Text; - } - textAccumulator = ''; - }; - - const finalizeReasoningAccumulator = (): void => { - reasoningAccumulator = ''; - }; - - for (let i = 0; i < messages.length; i++) { - const message = messages[i]; - - if ( - message.type !== UIMessageType.TextDelta && - message.type !== UIMessageType.Text - ) { - finalizeTextAccumulator(); - } - - if (message.type !== UIMessageType.Reasoning) { - finalizeReasoningAccumulator(); - } - - switch (message.type) { - case UIMessageType.TextDelta: { - textAccumulator += message.text; - const lastMessage = merged[merged.length - 1]; - - if ( - lastMessage && - isTextMessage(lastMessage) && - lastMessage.state === UIMessageType.TextDelta - ) { - lastMessage.text = textAccumulator; - } else { - merged.push({ - type: UIMessageType.Text, - text: textAccumulator, - state: UIMessageType.TextDelta, - }); - } - break; - } - - case UIMessageType.Text: { - const lastMessage = merged[merged.length - 1]; - - if ( - lastMessage && - isTextMessage(lastMessage) && - lastMessage.state === UIMessageType.TextDelta - ) { - lastMessage.text = message.text; - lastMessage.state = UIMessageType.Text; - lastMessage.mode = message.mode; - } else { - merged.push({ - ...message, - state: UIMessageType.Text, - }); - } - textAccumulator = ''; - break; - } - - case UIMessageType.Reasoning: { - reasoningAccumulator += message.reasoning; - const lastMessage = merged[merged.length - 1]; - - if (lastMessage && isReasoningMessage(lastMessage)) { - lastMessage.reasoning = reasoningAccumulator; - } else { - merged.push({ - type: UIMessageType.Reasoning, - reasoning: reasoningAccumulator, - } as unknown as ReasoningMessage); - } - break; - } - - case UIMessageType.ToolCall: - merged.push({ - ...message, - type: UIMessageType.Tool, - state: 'call' as const, - step: 0, - }); - break; - - case UIMessageType.ToolCallResult: { - const matchingCallIndex = findMatchingToolCall( - message.toolCallId, - merged.length, - ); - - if (matchingCallIndex !== -1) { - const toolCallMessage = merged[matchingCallIndex] as ToolMessage; - toolCallMessage.state = 'result'; - toolCallMessage.step = 1; - toolCallMessage.result = message.result; - } else { - merged.push({ - type: UIMessageType.Tool, - toolCallId: message.toolCallId, - toolName: message.toolName, - args: message.args, - result: message.result, - state: 'result' as const, - step: 1, - }); - } - break; - } - - case UIMessageType.ToolApprovalRequest: - merged.push(message as ToolApprovalRequestMessage); - break; - - case UIMessageType.ToolApprovalResult: - merged.push(message as ToolApprovalResultMessage); - break; - - case UIMessageType.ToolApprovalError: - merged.push(message as ToolApprovalErrorMessage); - break; - - default: - merged.push(message); - console.warn(`Unhandled message type: ${message.type}`); - break; - } - } - - finalizeTextAccumulator(); - finalizeReasoningAccumulator(); - - return merged; -} diff --git a/browser/src/utils/message.ts b/browser/src/utils/message.ts new file mode 100644 index 000000000..cfa754896 --- /dev/null +++ b/browser/src/utils/message.ts @@ -0,0 +1,135 @@ +import { CANCELED_MESSAGE_TEXT } from '@/constants'; +import type { + Message, + ToolResultPart, + UIAssistantMessage, + UIMessage, +} from '@/types/chat'; +import { safeStringify } from './safeStringify'; + +export function isToolResultMessage(message: Message) { + return ( + Array.isArray(message.content) && + message.content.length === 1 && + message.content[0].type === 'tool_result' + ); +} + +export function jsonSafeParse(json: string) { + try { + return JSON.parse(json); + } catch (error) { + console.error(error); + return {}; + } +} + +export function formatParamsDescription(params: Record): string { + if (!params || typeof params !== 'object') { + return ''; + } + const entries = Object.entries(params); + if (entries.length === 0) { + return ''; + } + return entries + .filter(([_key, value]) => value !== null && value !== undefined) + .map(([key, value]) => { + return `${key}: ${safeStringify(value)}`; + }) + .join(', '); +} + +export function getMessageText(message: Message) { + if ( + 'uiContent' in message && + message.uiContent && + typeof message.uiContent === 'string' + ) { + return message.uiContent; + } + return typeof message.content === 'string' + ? message.content + : message.content + .filter((c) => c.type === 'text') + .map((c) => c.text) + .join(''); +} + +export function isCanceledMessage(message: Message) { + return ( + message.role === 'user' && + Array.isArray(message.content) && + message.content.length === 1 && + message.content[0].type === 'text' && + message.content[0].text === CANCELED_MESSAGE_TEXT + ); +} + +export function formatMessages(messages: Message[]): UIMessage[] { + const formattedMessages: UIMessage[] = []; + + for (const message of messages) { + if ( + message.role === 'assistant' && + Array.isArray(message.content) && + message.content.some((content) => content.type === 'tool_use') + ) { + const uiMessage = { + ...message, + content: message.content.map((part) => { + if (part.type === 'tool_use') { + return { + ...part, + type: 'tool', + state: 'tool_use', + }; + } + return part; + }), + } as UIMessage; + formattedMessages.push(uiMessage); + continue; + } + + if (isToolResultMessage(message)) { + const lastMessage = formattedMessages[ + formattedMessages.length - 1 + ] as UIAssistantMessage; + if (lastMessage) { + const toolResult = message.content[0] as ToolResultPart; + const matchToolUse = lastMessage.content.find( + (part) => + part.type === 'tool' && + part.state === 'tool_use' && + part.id === toolResult.id, + ); + if (!matchToolUse) { + throw new Error('Tool result message must be after tool use message'); + } + const uiMessage = { + ...lastMessage, + content: lastMessage.content.map((part) => { + if (part.type === 'tool' && part.id === toolResult.id) { + return { + ...part, + ...toolResult, + type: 'tool', + state: 'tool_result', + }; + } + return part; + }), + } as UIMessage; + formattedMessages[formattedMessages.length - 1] = uiMessage; + continue; + } else { + throw new Error('Tool result message must be after tool use message'); + } + } + + formattedMessages.push(message as UIMessage); + } + + return formattedMessages; +} diff --git a/browser/src/utils/quill.ts b/browser/src/utils/quill.ts new file mode 100644 index 000000000..c4fe276ac --- /dev/null +++ b/browser/src/utils/quill.ts @@ -0,0 +1,20 @@ +import type { Delta } from 'quill'; +import type { ContextBlotData } from '@/components/QuillEditor/ContextBlot'; +import { CONTEXT_BLOT_NAME } from '@/constants'; + +export function getPrompt(delta: Delta) { + return delta.ops + .map((op) => { + if (typeof op.insert === 'string') { + return op.insert; + } + + if (op.insert?.[CONTEXT_BLOT_NAME]) { + const blotData = op.insert?.[CONTEXT_BLOT_NAME] as ContextBlotData; + return `${blotData.prefix}${blotData.value}`; + } + + return op.insert; + }) + .join(''); +} diff --git a/browser/src/utils/request.ts b/browser/src/utils/request.ts index 744eb1528..f9c7954db 100644 --- a/browser/src/utils/request.ts +++ b/browser/src/utils/request.ts @@ -8,17 +8,26 @@ export const request = axios.create({ request.interceptors.response.use( (response) => { if (response.status === 200) { - // 如果返回值 success 为 false,则抛出错误 + // If the return value success is false, throw an error if (response.data.success === true) { return response.data; } - message.error(response.data.error); + message.error(response.data.message); return Promise.reject(response.data); } return Promise.reject(response); }, (error) => { + if (error?.response?.data?.message) { + message.error(error.response.data.message); + } else if (error?.code === 'ERR_BAD_RESPONSE') { + message.error(`Request failed, status code: ${error.response?.status}`); + } else if (error?.message) { + message.error(error.message); + } else { + message.error('Request error, please try again later'); + } return Promise.reject(error); }, ); diff --git a/browser/src/utils/safeStringify.ts b/browser/src/utils/safeStringify.ts new file mode 100644 index 000000000..aefe2788a --- /dev/null +++ b/browser/src/utils/safeStringify.ts @@ -0,0 +1,10 @@ +export function safeStringify( + obj: Record, + fallbackMessage = '[Unable to serialize object]', +): string { + try { + return JSON.stringify(obj, null, 2); + } catch (_error) { + return fallbackMessage; + } +} diff --git a/browser/src/utils/slashCommand.ts b/browser/src/utils/slashCommand.ts new file mode 100644 index 000000000..6c65e94a7 --- /dev/null +++ b/browser/src/utils/slashCommand.ts @@ -0,0 +1,17 @@ +export function parseSlashCommand(input: string): { + command: string; + args: string; +} { + const trimmed = input.trim(); + const spaceIndex = trimmed.indexOf(' '); + if (spaceIndex === -1) { + return { + command: trimmed.slice(1), + args: '', + }; + } + return { + command: trimmed.slice(1, spaceIndex), + args: trimmed.slice(spaceIndex + 1).trim(), + }; +} diff --git a/browser/src/utils/tokenCounter.ts b/browser/src/utils/tokenCounter.ts new file mode 100644 index 000000000..874cbc7a6 --- /dev/null +++ b/browser/src/utils/tokenCounter.ts @@ -0,0 +1,19 @@ +import { encode } from 'gpt-tokenizer'; + +/** + * Count tokens in a text string using gpt-tokenizer + * Falls back to simple estimation if encoding fails + * + * @param text - The text to count tokens for + * @returns The number of tokens + */ +export function countTokens(text: string): number { + if (!text) return 0; + try { + return encode(text).length; + } catch (error) { + console.warn('Failed to encode text with gpt-tokenizer:', error); + // Fallback to simple estimation if encoding fails + return Math.ceil(text.length / 4); + } +} diff --git a/browser/tailwind.config.js b/browser/tailwind.config.js index 32abe4db6..802b4c3f3 100644 --- a/browser/tailwind.config.js +++ b/browser/tailwind.config.js @@ -4,5 +4,5 @@ export default { theme: { extend: {}, }, - plugins: [require('@tailwindcss/typography')], + plugins: [require('@tailwindcss/typography'), require('tailwind-scrollbar')], }; diff --git a/browser/vite.config.mts b/browser/vite.config.mts index 1d4fc29c8..528c44031 100644 --- a/browser/vite.config.mts +++ b/browser/vite.config.mts @@ -41,6 +41,11 @@ export default defineConfig({ target: 'http://localhost:1024', changeOrigin: true, }, + '/ws': { + target: 'http://localhost:1024', + changeOrigin: true, + ws: true, + }, }, }, }); diff --git a/package.json b/package.json index 236c713b5..ac605c57e 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,6 @@ "@stagewise/agent-interface": "^0.2.3", "@types/bun": "^1.2.21", "@types/debug": "^4.1.12", - "@types/express": "^5.0.3", "@types/js-yaml": "^4.0.9", "@types/lodash-es": "^4.17.12", "@types/marked-terminal": "^6.1.1", @@ -106,7 +105,6 @@ "defu": "^6.1.4", "diff": "^8.0.2", "dotenv": "^17.2.1", - "express": "^5.1.0", "fastify": "^5.5.0", "fastmcp": "^3.17.0", "front-matter": "^4.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6954f8eaf..36111f845 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,13 +45,13 @@ importers: version: 8.2.0 '@fastify/type-provider-typebox': specifier: 5.2.0 - version: 5.2.0(@sinclair/typebox@0.34.38) + version: 5.2.0(@sinclair/typebox@0.34.41) '@microsoft/api-extractor': specifier: ^7.52.10 - version: 7.52.10(@types/node@24.2.1) + version: 7.52.11(@types/node@24.3.1) '@modelcontextprotocol/inspector': specifier: ^0.16.3 - version: 0.16.3(@swc/core@1.12.1)(@types/node@24.2.1)(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(typescript@5.9.2) + version: 0.16.6(@swc/core@1.12.1)(@types/node@24.3.1)(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(typescript@5.9.2) '@openai/agents': specifier: ^0.0.16 version: 0.0.16(ws@8.18.3)(zod@3.25.76) @@ -66,19 +66,16 @@ importers: version: 0.7.5(ai@4.3.19(react@19.1.1)(zod@3.25.76))(zod@3.25.76) '@sinclair/typebox': specifier: ^0.34.38 - version: 0.34.38 + version: 0.34.41 '@stagewise/agent-interface': specifier: ^0.2.3 version: 0.2.3 '@types/bun': specifier: ^1.2.21 - version: 1.2.21(@types/react@19.1.10) + version: 1.2.21(@types/react@19.1.12) '@types/debug': specifier: ^4.1.12 version: 4.1.12 - '@types/express': - specifier: ^5.0.3 - version: 5.0.3 '@types/js-yaml': specifier: ^4.0.9 version: 4.0.9 @@ -90,10 +87,10 @@ importers: version: 6.1.1 '@types/node': specifier: ^24.2.1 - version: 24.2.1 + version: 24.3.1 '@types/react': specifier: ^19.1.10 - version: 19.1.10 + version: 19.1.12 '@types/resolve': specifier: ^1.20.6 version: 1.20.6 @@ -117,7 +114,7 @@ importers: version: 4.3.19(react@19.1.1)(zod@3.25.76) chalk: specifier: ^5.5.0 - version: 5.5.0 + version: 5.6.2 chardet: specifier: ^2.1.0 version: 2.1.0 @@ -144,13 +141,10 @@ importers: version: 8.0.2 dotenv: specifier: ^17.2.1 - version: 17.2.1 - express: - specifier: ^5.1.0 - version: 5.1.0 + version: 17.2.2 fastify: specifier: ^5.5.0 - version: 5.5.0 + version: 5.6.0 fastmcp: specifier: ^3.17.0 version: 3.17.0 @@ -171,13 +165,13 @@ importers: version: 9.1.7 ink: specifier: ^6.1.0 - version: 6.1.0(@types/react@19.1.10)(react@19.1.1) + version: 6.3.0(@types/react@19.1.12)(react@19.1.1) ink-select-input: specifier: ^6.2.0 - version: 6.2.0(ink@6.1.0(@types/react@19.1.10)(react@19.1.1))(react@19.1.1) + version: 6.2.0(ink@6.3.0(@types/react@19.1.12)(react@19.1.1))(react@19.1.1) ink-spinner: specifier: ^5.0.0 - version: 5.0.0(ink@6.1.0(@types/react@19.1.10)(react@19.1.1))(react@19.1.1) + version: 5.0.0(ink@6.3.0(@types/react@19.1.12)(react@19.1.1))(react@19.1.1) jiti: specifier: ^2.5.1 version: 2.5.1 @@ -189,16 +183,16 @@ importers: version: 3.13.0 lint-staged: specifier: ^16.1.5 - version: 16.1.5 + version: 16.1.6 lodash-es: specifier: ^4.17.21 version: 4.17.21 marked: specifier: ^16.1.2 - version: 16.1.2 + version: 16.2.1 marked-terminal: specifier: ^7.3.0 - version: 7.3.0(patch_hash=536fe9685e91d559cf29a033191aa39da45729949e9d1c69989255091c8618fb)(marked@16.1.2) + version: 7.3.0(patch_hash=536fe9685e91d559cf29a033191aa39da45729949e9d1c69989255091c8618fb)(marked@16.2.1) minimatch: specifier: ^10.0.1 version: 10.0.3 @@ -225,13 +219,13 @@ importers: version: 5.1.2 strip-ansi: specifier: ^7.1.0 - version: 7.1.0 + version: 7.1.2 tar: specifier: ^7.4.3 version: 7.4.3 turndown: specifier: ^7.2.0 - version: 7.2.0 + version: 7.2.1 type-fest: specifier: ^4.41.0 version: 4.41.0 @@ -240,13 +234,13 @@ importers: version: 5.9.2 valtio: specifier: ^2.1.5 - version: 2.1.5(@types/react@19.1.10)(react@19.1.1) + version: 2.1.7(@types/react@19.1.12)(react@19.1.1) vitest: specifier: ^3.2.4 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.2.1)(jiti@2.5.1)(less@4.4.0)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) wrap-ansi: specifier: ^9.0.0 - version: 9.0.0 + version: 9.0.2 ws: specifier: ^8.18.3 version: 8.18.3 @@ -261,7 +255,149 @@ importers: version: 3.24.6(zod@3.25.76) zustand: specifier: ^5.0.8 - version: 5.0.8(@types/react@19.1.10)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)) + version: 5.0.8(@types/react@19.1.12)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)) + + browser: + dependencies: + '@ant-design/icons': + specifier: ^6.0.0 + version: 6.0.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@ant-design/x': + specifier: ^1.4.0 + version: 1.6.1(antd@5.27.4(date-fns@4.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@monaco-editor/react': + specifier: ^4.7.0 + version: 4.7.0(monaco-editor@0.53.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@shikijs/transformers': + specifier: ^3.12.0 + version: 3.13.0 + '@tanstack/react-router': + specifier: ^1.121.16 + version: 1.132.23(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@tanstack/react-router-devtools': + specifier: ^1.121.21 + version: 1.132.23(@tanstack/react-router@1.132.23(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.132.21)(@types/node@24.3.1)(csstype@3.1.3)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.9)(tiny-invariant@1.3.3)(tsx@4.20.5)(yaml@2.8.1) + ahooks: + specifier: ^3.7.0 + version: 3.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + antd: + specifier: ^5.15.0 + version: 5.27.4(date-fns@4.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + antd-style: + specifier: ^3.7.1 + version: 3.7.1(@types/react@19.1.12)(antd@5.27.4(date-fns@4.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + axios: + specifier: 1.10.0 + version: 1.10.0(debug@4.4.1) + classnames: + specifier: ^2.5.1 + version: 2.5.1 + dayjs: + specifier: ^1.11.13 + version: 1.11.18 + diff: + specifier: ^8.0.2 + version: 8.0.2 + i18next: + specifier: ^25.2.1 + version: 25.5.2(typescript@5.8.3) + i18next-browser-languagedetector: + specifier: ^8.2.0 + version: 8.2.0 + lodash-es: + specifier: ^4.17.21 + version: 4.17.21 + quill: + specifier: ^2.0.3 + version: 2.0.3 + rc-util: + specifier: ^5.44.4 + version: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: + specifier: ^19.1.1 + version: 19.1.1 + react-dom: + specifier: ^19.1.1 + version: 19.1.1(react@19.1.1) + react-i18next: + specifier: ^15.5.3 + version: 15.7.4(i18next@25.5.2(typescript@5.8.3))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.8.3) + react-icons: + specifier: ^5.5.0 + version: 5.5.0(react@19.1.1) + react-markdown: + specifier: ^9.0.0 + version: 9.1.0(@types/react@19.1.12)(react@19.1.1) + react-syntax-highlighter: + specifier: ^15.5.0 + version: 15.6.6(react@19.1.1) + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1 + shiki: + specifier: ^3.11.0 + version: 3.13.0 + streamdown: + specifier: ^1.1.6 + version: 1.3.0(@types/react@19.1.12)(react@19.1.1) + swr: + specifier: ^2.3.3 + version: 2.3.6(react@19.1.1) + valtio: + specifier: ^2.1.5 + version: 2.1.7(@types/react@19.1.12)(react@19.1.1) + devDependencies: + '@ant-design/v5-patch-for-react-19': + specifier: ^1.0.3 + version: 1.0.3(antd@5.27.4(date-fns@4.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@tailwindcss/typography': + specifier: ^0.5.16 + version: 0.5.19(tailwindcss@4.1.13) + '@tailwindcss/vite': + specifier: ^4.1.10 + version: 4.1.13(vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) + '@tanstack/router-plugin': + specifier: ^1.121.16 + version: 1.132.23(@tanstack/react-router@1.132.23(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) + '@types/diff': + specifier: ^5.2.3 + version: 5.2.3 + '@types/react': + specifier: ^19.1.2 + version: 19.1.12 + '@types/react-dom': + specifier: ^19.1.2 + version: 19.1.9(@types/react@19.1.12) + '@types/react-i18next': + specifier: ^8.1.0 + version: 8.1.0(i18next@25.5.2(typescript@5.8.3))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.8.3) + '@types/react-syntax-highlighter': + specifier: ^15.5.13 + version: 15.5.13 + '@vitejs/plugin-react': + specifier: ^4.5.2 + version: 4.7.0(vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) + globals: + specifier: ^16.0.0 + version: 16.4.0 + lucide-react: + specifier: ^0.545.0 + version: 0.545.0(react@19.1.1) + tailwind-scrollbar: + specifier: ^4.0.2 + version: 4.0.2(react@19.1.1)(tailwindcss@4.1.13) + tailwindcss: + specifier: ^4.1.10 + version: 4.1.13 + typescript: + specifier: ~5.8.3 + version: 5.8.3 + vite: + specifier: ^6.3.5 + version: 6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) + vite-plugin-svgr: + specifier: ^4.3.0 + version: 4.5.0(rollup@4.50.1)(typescript@5.8.3)(vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) examples/mcp: {} @@ -269,7 +405,7 @@ importers: devDependencies: '@types/vscode': specifier: ^1.99.0 - version: 1.102.0 + version: 1.103.0 '@vscode/test-cli': specifier: ^0.0.11 version: 0.0.11 @@ -281,7 +417,7 @@ importers: version: 3.6.0 esbuild: specifier: ^0.25.6 - version: 0.25.6 + version: 0.25.9 packages: @@ -347,9 +483,78 @@ packages: peerDependencies: zod: ^3.0.0 - '@alcalzone/ansi-tokenize@0.1.3': - resolution: {integrity: sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw==} - engines: {node: '>=14.13.1'} + '@alcalzone/ansi-tokenize@0.2.0': + resolution: {integrity: sha512-qI/5TaaaCZE4yeSZ83lu0+xi1r88JSxUjnH4OP/iZF7+KKZ75u3ee5isd0LxX+6N8U0npL61YrpbthILHB6BnA==} + engines: {node: '>=18'} + + '@ant-design/colors@7.2.1': + resolution: {integrity: sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==} + + '@ant-design/colors@8.0.0': + resolution: {integrity: sha512-6YzkKCw30EI/E9kHOIXsQDHmMvTllT8STzjMb4K2qzit33RW2pqCJP0sk+hidBntXxE+Vz4n1+RvCTfBw6OErw==} + + '@ant-design/cssinjs-utils@1.1.3': + resolution: {integrity: sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@ant-design/cssinjs@1.24.0': + resolution: {integrity: sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@ant-design/fast-color@2.0.6': + resolution: {integrity: sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==} + engines: {node: '>=8.x'} + + '@ant-design/fast-color@3.0.0': + resolution: {integrity: sha512-eqvpP7xEDm2S7dUzl5srEQCBTXZMmY3ekf97zI+M2DHOYyKdJGH0qua0JACHTqbkRnD/KHFQP9J1uMJ/XWVzzA==} + engines: {node: '>=8.x'} + + '@ant-design/icons-svg@4.4.2': + resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==} + + '@ant-design/icons@5.6.1': + resolution: {integrity: sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==} + engines: {node: '>=8'} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@ant-design/icons@6.0.2': + resolution: {integrity: sha512-1U1+6afDP+w+6jDkxrmn/kwoFJvB/aD4mQ/+Rhkp+BBRAfgK46gxKb6VxnoS/hYDiRdhIjzilkCmi6pD7zjxCw==} + engines: {node: '>=8'} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@ant-design/react-slick@1.1.2': + resolution: {integrity: sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==} + peerDependencies: + react: '>=16.9.0' + + '@ant-design/v5-patch-for-react-19@1.0.3': + resolution: {integrity: sha512-iWfZuSUl5kuhqLUw7jJXUQFMMkM7XpW7apmKzQBQHU0cpifYW4A79xIBt9YVO5IBajKpPG5UKP87Ft7Yrw1p/w==} + engines: {node: '>=12.x'} + peerDependencies: + antd: '>=5.22.6' + react: '>=19.0.0' + react-dom: '>=19.0.0' + + '@ant-design/x@1.6.1': + resolution: {integrity: sha512-6KO7iNGcnAMvIJzZPWJOdNHhx3kC4KUEMs43C0tSJORs5SosL+0FdCY6reozLZkUMSu3LqloPIJVba+OAlyA+w==} + peerDependencies: + antd: ^5.20.3 + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + + '@antfu/utils@9.2.1': + resolution: {integrity: sha512-TMilPqXyii1AsiEii6l6ubRzbo76p6oshUSYPaKsmXDavyMLqjzVDkcp3pHp5ELMUNJHATcEOGxKTTsX9yYhGg==} '@azu/format-text@1.0.2': resolution: {integrity: sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==} @@ -381,34 +586,171 @@ packages: resolution: {integrity: sha512-o0psW8QWQ58fq3i24Q1K2XfS/jYTxr7O1HRcyUE9bV9NttLU+kYOH82Ixj8DGlMTOWgxm1Sss2QAfKK5UkSPxw==} engines: {node: '>=20.0.0'} - '@azure/identity@4.10.2': - resolution: {integrity: sha512-Uth4vz0j+fkXCkbvutChUj03PDCokjbC6Wk9JT8hHEUtpy/EurNKAseb3+gO6Zi9VYBvwt61pgbzn1ovk942Qg==} + '@azure/identity@4.11.1': + resolution: {integrity: sha512-0ZdsLRaOyLxtCYgyuqyWqGU5XQ9gGnjxgfoNTt1pvELGkkUFrMATABZFIq8gusM7N1qbqpVtwLOhk0d/3kacLg==} engines: {node: '>=20.0.0'} '@azure/logger@1.3.0': resolution: {integrity: sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==} engines: {node: '>=20.0.0'} - '@azure/msal-browser@4.15.0': - resolution: {integrity: sha512-+AIGTvpVz+FIx5CsM1y+nW0r/qOb/ChRdM8/Cbp+jKWC0Wdw4ldnwPdYOBi5NaALUQnYITirD9XMZX7LdklEzQ==} + '@azure/msal-browser@4.22.0': + resolution: {integrity: sha512-JLWHzAW1aZ/L190Th56jN+2t3T1dMvXOs1obXYLEr3ZWi81vVmBCt0di3mPvTTOiWoE0Cf/4hVQ/LINilqjObA==} engines: {node: '>=0.8.0'} - '@azure/msal-common@15.8.1': - resolution: {integrity: sha512-ltIlFK5VxeJ5BurE25OsJIfcx1Q3H/IZg2LjV9d4vmH+5t4c1UCyRQ/HgKLgXuCZShs7qfc/TC95GYZfsUsJUQ==} + '@azure/msal-common@15.12.0': + resolution: {integrity: sha512-4ucXbjVw8KJ5QBgnGJUeA07c8iznwlk5ioHIhI4ASXcXgcf2yRFhWzYOyWg/cI49LC9ekpFJeQtO3zjDTbl6TQ==} engines: {node: '>=0.8.0'} - '@azure/msal-node@3.6.3': - resolution: {integrity: sha512-95wjsKGyUcAd5tFmQBo5Ug/kOj+hFh/8FsXuxluEvdfbgg6xCimhSP9qnyq6+xIg78/jREkBD1/BSqd7NIDDYQ==} + '@azure/msal-node@3.7.3': + resolution: {integrity: sha512-MoJxkKM/YpChfq4g2o36tElyzNUMG8mfD6u8NbuaPAsqfGpaw249khAcJYNoIOigUzRw45OjXCOrexE6ImdUxg==} engines: {node: '>=16'} '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.4': + resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.4': + resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.28.3': + resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.27.1': + resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-replace-supers@7.27.1': + resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.27.1': resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.27.1': + resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.0': + resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.27.1': + resolution: {integrity: sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -468,6 +810,24 @@ packages: '@borewit/text-codec@0.1.1': resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} + '@braintree/sanitize-url@7.1.1': + resolution: {integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==} + + '@chevrotain/cst-dts-gen@11.0.3': + resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==} + + '@chevrotain/gast@11.0.3': + resolution: {integrity: sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==} + + '@chevrotain/regexp-to-ast@11.0.3': + resolution: {integrity: sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==} + + '@chevrotain/types@11.0.3': + resolution: {integrity: sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==} + + '@chevrotain/utils@11.0.3': + resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==} + '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -476,314 +836,208 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@esbuild/aix-ppc64@0.25.6': - resolution: {integrity: sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} - '@esbuild/aix-ppc64@0.25.8': - resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} + '@emotion/css@11.13.5': + resolution: {integrity: sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==} + + '@emotion/hash@0.8.0': + resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.14.0': + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/unitless@0.7.5': + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + + '@esbuild/aix-ppc64@0.25.9': + resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.6': - resolution: {integrity: sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==} + '@esbuild/android-arm64@0.25.9': + resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.25.8': - resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.25.6': - resolution: {integrity: sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.25.8': - resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} + '@esbuild/android-arm@0.25.9': + resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.6': - resolution: {integrity: sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==} + '@esbuild/android-x64@0.25.9': + resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/android-x64@0.25.8': - resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.25.6': - resolution: {integrity: sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.25.8': - resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} + '@esbuild/darwin-arm64@0.25.9': + resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.6': - resolution: {integrity: sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.25.8': - resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} + '@esbuild/darwin-x64@0.25.9': + resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.6': - resolution: {integrity: sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==} + '@esbuild/freebsd-arm64@0.25.9': + resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.25.8': - resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.25.6': - resolution: {integrity: sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==} + '@esbuild/freebsd-x64@0.25.9': + resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.8': - resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.25.6': - resolution: {integrity: sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.25.8': - resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} + '@esbuild/linux-arm64@0.25.9': + resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.6': - resolution: {integrity: sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==} + '@esbuild/linux-arm@0.25.9': + resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.25.8': - resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.25.6': - resolution: {integrity: sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.25.8': - resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} + '@esbuild/linux-ia32@0.25.9': + resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.6': - resolution: {integrity: sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==} + '@esbuild/linux-loong64@0.25.9': + resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.25.8': - resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.25.6': - resolution: {integrity: sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.25.8': - resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} + '@esbuild/linux-mips64el@0.25.9': + resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.6': - resolution: {integrity: sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==} + '@esbuild/linux-ppc64@0.25.9': + resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.25.8': - resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.25.6': - resolution: {integrity: sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==} + '@esbuild/linux-riscv64@0.25.9': + resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.25.8': - resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.25.6': - resolution: {integrity: sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.25.8': - resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} + '@esbuild/linux-s390x@0.25.9': + resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.6': - resolution: {integrity: sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==} + '@esbuild/linux-x64@0.25.9': + resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.25.8': - resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.25.6': - resolution: {integrity: sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-arm64@0.25.8': - resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} + '@esbuild/netbsd-arm64@0.25.9': + resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.6': - resolution: {integrity: sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==} + '@esbuild/netbsd-x64@0.25.9': + resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.8': - resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.25.6': - resolution: {integrity: sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==} + '@esbuild/openbsd-arm64@0.25.9': + resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.25.8': - resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.6': - resolution: {integrity: sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.8': - resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} + '@esbuild/openbsd-x64@0.25.9': + resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.6': - resolution: {integrity: sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==} + '@esbuild/openharmony-arm64@0.25.9': + resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.25.8': - resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.25.6': - resolution: {integrity: sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==} + '@esbuild/sunos-x64@0.25.9': + resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.25.8': - resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.25.6': - resolution: {integrity: sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==} + '@esbuild/win32-arm64@0.25.9': + resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.25.8': - resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.25.6': - resolution: {integrity: sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==} + '@esbuild/win32-ia32@0.25.9': + resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.25.8': - resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.25.6': - resolution: {integrity: sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.25.8': - resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} + '@esbuild/win32-x64@0.25.9': + resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -829,11 +1083,11 @@ packages: '@floating-ui/core@1.7.3': resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} - '@floating-ui/dom@1.7.3': - resolution: {integrity: sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==} + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} - '@floating-ui/react-dom@2.1.5': - resolution: {integrity: sha512-HDO/1/1oH9fjj4eLgegrlH3dklZpHtUYYFiVwMUwfGvk9jWDRWqkklA2/NFScknrcNSspbV868WjXORvreDX+Q==} + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' @@ -841,6 +1095,12 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@3.0.2': + resolution: {integrity: sha512-EfJS0rLfVuRuJRn4psJHtK2A9TqVnkxPpHY6lYHiB9+8eSuudsxbwMiavocG45ujOo6FJ+CIRlRnlOGinzkaGQ==} + '@isaacs/balanced-match@4.0.1': resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} engines: {node: 20 || >=22} @@ -861,15 +1121,21 @@ packages: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.4': - resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.29': - resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} + '@jridgewell/trace-mapping@0.3.30': + resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -878,11 +1144,14 @@ packages: resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} engines: {node: '>=8'} + '@mermaid-js/parser@0.6.2': + resolution: {integrity: sha512-+PO02uGF6L6Cs0Bw8RpGhikVvMWEysfAyl27qTlroUB8jSWr1lL0Sf6zi78ZxlSnmgSY2AMMKVgghnN9jTtwkQ==} + '@microsoft/api-extractor-model@7.30.7': resolution: {integrity: sha512-TBbmSI2/BHpfR9YhQA7nH0nqVmGgJ0xH0Ex4D99/qBDAUpnhA2oikGmdXanbw9AWWY/ExBYIpkmY8dBHdla3YQ==} - '@microsoft/api-extractor@7.52.10': - resolution: {integrity: sha512-LhKytJM5ZJkbHQVfW/3o747rZUNs/MGg6j/wt/9qwwqEOfvUDTYXXxIBuMgrRXhJ528p41iyz4zjBVHZU74Odg==} + '@microsoft/api-extractor@7.52.11': + resolution: {integrity: sha512-IKQ7bHg6f/Io3dQds6r9QPYk4q0OlR9A4nFDtNhUt3UUIhyitbxAqRN1CLjUVtk6IBk3xzyCMOdwwtIXQ7AlGg==} hasBin: true '@microsoft/tsdoc-config@0.17.1': @@ -894,25 +1163,25 @@ packages: '@mixmark-io/domino@2.2.0': resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==} - '@modelcontextprotocol/inspector-cli@0.16.3': - resolution: {integrity: sha512-6Hbh+QVRsEDel7hA9qiRklwWEoW4dQXHw4Ltr8JdsU1ziqem4/ERgGxthg/d+qrmbVwW9shOqPIaGoRFaZ264g==} + '@modelcontextprotocol/inspector-cli@0.16.6': + resolution: {integrity: sha512-28RAaGoN9XgKYvl8kOo9wTHBrLp5Th+biTt5mNGUzowMdcoG/FpI8mHROIhcgDyp+kj0SYR5fmwcb6GIxBnjUw==} hasBin: true - '@modelcontextprotocol/inspector-client@0.16.3': - resolution: {integrity: sha512-BbFyNUesmZrl9FcM+TeogAo2DM6lA1+NJSSszd9+xtuumdX8WxEynv4quBgfxW/R+nHv6+D5h/ftQ3x9hwgskQ==} + '@modelcontextprotocol/inspector-client@0.16.6': + resolution: {integrity: sha512-2dwB0OXI02PTTsECCTIsB9DkERImIrsTAuZW6LlfUojtQMLI5NpuUID4Y4LaYPcdGnxkkkR1eddrPTsuzgabvg==} hasBin: true - '@modelcontextprotocol/inspector-server@0.16.3': - resolution: {integrity: sha512-7chp1jSSzM/7YswfQXA0CoBoWwEzUyKBe9s4H3e8DOBJPho6IxYgouHccHHmghWkd7e2W6ir3xwQBkLRnkt2nQ==} + '@modelcontextprotocol/inspector-server@0.16.6': + resolution: {integrity: sha512-BkE/4K2Y8ZcXK/cGBucG+rLTcTIUAaSyQabxqh0p+ErhkJDmepDvI+63OqQnauWUJydXPZYtBQyHppL4JN7RGw==} hasBin: true - '@modelcontextprotocol/inspector@0.16.3': - resolution: {integrity: sha512-9zOf8piPYNz/8vLEMHzmp8kBYQ/FZ1WP1Ao7fXHqkNcSbyw6d7CfTgZvI2Uj1Cb5VFxZ2jWvCD7J80q8G4okjg==} + '@modelcontextprotocol/inspector@0.16.6': + resolution: {integrity: sha512-6x6dzTf8MV6z/XIdzr/4EMK4elMn1XUzTJHxczsBePLg1G5VNAM/4g5abNFIB9bzuxJ/1VH8016Vv6S7sj/24Q==} engines: {node: '>=22.7.5'} hasBin: true - '@modelcontextprotocol/sdk@1.17.2': - resolution: {integrity: sha512-EFLRNXR/ixpXQWu6/3Cu30ndDFIFNaqUXcTqsGebujeMan9FzhAaFFswLRiFj61rgygDRr8WO1N+UijjgRxX9g==} + '@modelcontextprotocol/sdk@1.17.5': + resolution: {integrity: sha512-QakrKIGniGuRVfWBdMsDea/dx1PNE739QJ7gCM41s9q+qaCYTHCdsIBXQVVXry3mfWAiaM9kT22Hyz53Uw8mfg==} engines: {node: '>=18'} '@modelcontextprotocol/sdk@1.19.1': @@ -934,104 +1203,120 @@ packages: '@module-federation/webpack-bundler-runtime@0.8.12': resolution: {integrity: sha512-zd343RO7/R7Xjh5ym5KdnYQ70z4LBmMxWsa44FS0nyNv04sOq6V1eZSCGKbEhbfqqhbS5Wfj8OzJyedeVvV/OQ==} - '@napi-rs/nice-android-arm-eabi@1.0.4': - resolution: {integrity: sha512-OZFMYUkih4g6HCKTjqJHhMUlgvPiDuSLZPbPBWHLjKmFTv74COzRlq/gwHtmEVaR39mJQ6ZyttDl2HNMUbLVoA==} + '@monaco-editor/loader@1.5.0': + resolution: {integrity: sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw==} + + '@monaco-editor/react@4.7.0': + resolution: {integrity: sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==} + peerDependencies: + monaco-editor: '>= 0.25.0 < 1' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@napi-rs/nice-android-arm-eabi@1.1.1': + resolution: {integrity: sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==} engines: {node: '>= 10'} cpu: [arm] os: [android] - '@napi-rs/nice-android-arm64@1.0.4': - resolution: {integrity: sha512-k8u7cjeA64vQWXZcRrPbmwjH8K09CBnNaPnI9L1D5N6iMPL3XYQzLcN6WwQonfcqCDv5OCY3IqX89goPTV4KMw==} + '@napi-rs/nice-android-arm64@1.1.1': + resolution: {integrity: sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@napi-rs/nice-darwin-arm64@1.0.4': - resolution: {integrity: sha512-GsLdQvUcuVzoyzmtjsThnpaVEizAqH5yPHgnsBmq3JdVoVZHELFo7PuJEdfOH1DOHi2mPwB9sCJEstAYf3XCJA==} + '@napi-rs/nice-darwin-arm64@1.1.1': + resolution: {integrity: sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@napi-rs/nice-darwin-x64@1.0.4': - resolution: {integrity: sha512-1y3gyT3e5zUY5SxRl3QDtJiWVsbkmhtUHIYwdWWIQ3Ia+byd/IHIEpqAxOGW1nhhnIKfTCuxBadHQb+yZASVoA==} + '@napi-rs/nice-darwin-x64@1.1.1': + resolution: {integrity: sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@napi-rs/nice-freebsd-x64@1.0.4': - resolution: {integrity: sha512-06oXzESPRdXUuzS8n2hGwhM2HACnDfl3bfUaSqLGImM8TA33pzDXgGL0e3If8CcFWT98aHows5Lk7xnqYNGFeA==} + '@napi-rs/nice-freebsd-x64@1.1.1': + resolution: {integrity: sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] - '@napi-rs/nice-linux-arm-gnueabihf@1.0.4': - resolution: {integrity: sha512-CgklZ6g8WL4+EgVVkxkEvvsi2DSLf9QIloxWO0fvQyQBp6VguUSX3eHLeRpqwW8cRm2Hv/Q1+PduNk7VK37VZw==} + '@napi-rs/nice-linux-arm-gnueabihf@1.1.1': + resolution: {integrity: sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@napi-rs/nice-linux-arm64-gnu@1.0.4': - resolution: {integrity: sha512-wdAJ7lgjhAlsANUCv0zi6msRwq+D4KDgU+GCCHssSxWmAERZa2KZXO0H2xdmoJ/0i03i6YfK/sWaZgUAyuW2oQ==} + '@napi-rs/nice-linux-arm64-gnu@1.1.1': + resolution: {integrity: sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@napi-rs/nice-linux-arm64-musl@1.0.4': - resolution: {integrity: sha512-4b1KYG+sriufhFrpUS9uNOEYYJqSfcbnwGx6uGX7JjrH8tELG90cOpCawz5THNIwlS3DhLgnCOcn0+4p6z26QA==} + '@napi-rs/nice-linux-arm64-musl@1.1.1': + resolution: {integrity: sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@napi-rs/nice-linux-ppc64-gnu@1.0.4': - resolution: {integrity: sha512-iaf3vMRgr23oe1PUaKpxaH3DS0IMN0+N9iEiWVwYPm/U15vZFYdqVegGfN2PzrZLUl5lc8ZxbmEKDfuqslhAMA==} + '@napi-rs/nice-linux-ppc64-gnu@1.1.1': + resolution: {integrity: sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==} engines: {node: '>= 10'} cpu: [ppc64] os: [linux] - '@napi-rs/nice-linux-riscv64-gnu@1.0.4': - resolution: {integrity: sha512-UXoREY6Yw6rHrGuTwQgBxpfjK34t6mTjibE9/cXbefL9AuUCJ9gEgwNKZiONuR5QGswChqo9cnthjdKkYyAdDg==} + '@napi-rs/nice-linux-riscv64-gnu@1.1.1': + resolution: {integrity: sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] - '@napi-rs/nice-linux-s390x-gnu@1.0.4': - resolution: {integrity: sha512-eFbgYCRPmsqbYPAlLYU5hYTNbogmIDUvknilehHsFhCH1+0/kN87lP+XaLT0Yeq4V/rpwChSd9vlz4muzFArtw==} + '@napi-rs/nice-linux-s390x-gnu@1.1.1': + resolution: {integrity: sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==} engines: {node: '>= 10'} cpu: [s390x] os: [linux] - '@napi-rs/nice-linux-x64-gnu@1.0.4': - resolution: {integrity: sha512-4T3E6uTCwWT6IPnwuPcWVz3oHxvEp/qbrCxZhsgzwTUBEwu78EGNXGdHfKJQt3soth89MLqZJw+Zzvnhrsg1mQ==} + '@napi-rs/nice-linux-x64-gnu@1.1.1': + resolution: {integrity: sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@napi-rs/nice-linux-x64-musl@1.0.4': - resolution: {integrity: sha512-NtbBkAeyBPLvCBkWtwkKXkNSn677eaT0cX3tygq+2qVv71TmHgX4gkX6o9BXjlPzdgPGwrUudavCYPT9tzkEqQ==} + '@napi-rs/nice-linux-x64-musl@1.1.1': + resolution: {integrity: sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@napi-rs/nice-win32-arm64-msvc@1.0.4': - resolution: {integrity: sha512-vubOe3i+YtSJGEk/++73y+TIxbuVHi+W8ZzrRm2eETCjCRwNlgbfToQZ85dSA+4iBB/NJRGNp+O4hfdbbttZWA==} + '@napi-rs/nice-openharmony-arm64@1.1.1': + resolution: {integrity: sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [openharmony] + + '@napi-rs/nice-win32-arm64-msvc@1.1.1': + resolution: {integrity: sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@napi-rs/nice-win32-ia32-msvc@1.0.4': - resolution: {integrity: sha512-BMOVrUDZeg1RNRKVlh4eyLv5djAAVLiSddfpuuQ47EFjBcklg0NUeKMFKNrKQR4UnSn4HAiACLD7YK7koskwmg==} + '@napi-rs/nice-win32-ia32-msvc@1.1.1': + resolution: {integrity: sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@napi-rs/nice-win32-x64-msvc@1.0.4': - resolution: {integrity: sha512-kCNk6HcRZquhw/whwh4rHsdPyOSCQCgnVDVik+Y9cuSVTDy3frpiCJTScJqPPS872h4JgZKkr/+CwcwttNEo9Q==} + '@napi-rs/nice-win32-x64-msvc@1.1.1': + resolution: {integrity: sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@napi-rs/nice@1.0.4': - resolution: {integrity: sha512-Sqih1YARrmMoHlXGgI9JrrgkzxcaaEso0AH+Y7j8NHonUs+xe4iDsgC3IBIDNdzEewbNpccNN6hip+b5vmyRLw==} + '@napi-rs/nice@1.1.1': + resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==} engines: {node: '>= 10'} '@nodelib/fs.scandir@2.1.5': @@ -1092,8 +1377,8 @@ packages: '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} - '@radix-ui/primitive@1.1.2': - resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==} + '@radix-ui/primitive@1.1.3': + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} '@radix-ui/react-arrow@1.1.7': resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} @@ -1108,8 +1393,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-checkbox@1.3.2': - resolution: {integrity: sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==} + '@radix-ui/react-checkbox@1.3.3': + resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1152,8 +1437,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-dialog@1.1.14': - resolution: {integrity: sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==} + '@radix-ui/react-dialog@1.1.15': + resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1174,8 +1459,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-dismissable-layer@1.1.10': - resolution: {integrity: sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==} + '@radix-ui/react-dismissable-layer@1.1.11': + resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1187,8 +1472,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-focus-guards@1.1.2': - resolution: {integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==} + '@radix-ui/react-focus-guards@1.1.3': + resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -1236,8 +1521,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popover@1.1.14': - resolution: {integrity: sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==} + '@radix-ui/react-popover@1.1.15': + resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1249,8 +1534,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popper@1.2.7': - resolution: {integrity: sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==} + '@radix-ui/react-popper@1.2.8': + resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1275,8 +1560,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.4': - resolution: {integrity: sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==} + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1301,8 +1586,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-roving-focus@1.1.10': - resolution: {integrity: sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==} + '@radix-ui/react-roving-focus@1.1.11': + resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1314,8 +1599,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-select@2.2.5': - resolution: {integrity: sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==} + '@radix-ui/react-select@2.2.6': + resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1336,8 +1621,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-tabs@1.1.12': - resolution: {integrity: sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==} + '@radix-ui/react-tabs@1.1.13': + resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1349,8 +1634,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-toast@1.2.14': - resolution: {integrity: sha512-nAP5FBxBJGQ/YfUB+r+O6USFVkWq3gAInkxyEnmvEV5jtSbfDhfa4hwX8CraCnbjMLsE7XSf/K75l9xXY7joWg==} + '@radix-ui/react-toast@1.2.15': + resolution: {integrity: sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1362,8 +1647,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-tooltip@1.2.7': - resolution: {integrity: sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==} + '@radix-ui/react-tooltip@1.2.8': + resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1463,103 +1748,181 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - '@rollup/rollup-android-arm-eabi@4.45.1': - resolution: {integrity: sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==} + '@rc-component/async-validator@5.0.4': + resolution: {integrity: sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==} + engines: {node: '>=14.x'} + + '@rc-component/color-picker@2.0.1': + resolution: {integrity: sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/context@1.4.0': + resolution: {integrity: sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/mini-decimal@1.1.0': + resolution: {integrity: sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==} + engines: {node: '>=8.x'} + + '@rc-component/mutate-observer@1.1.0': + resolution: {integrity: sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/portal@1.1.2': + resolution: {integrity: sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/qrcode@1.0.1': + resolution: {integrity: sha512-g8eeeaMyFXVlq8cZUeaxCDhfIYjpao0l9cvm5gFwKXy/Vm1yDWV7h2sjH5jHYzdFedlVKBpATFB1VKMrHzwaWQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/tour@1.15.1': + resolution: {integrity: sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/trigger@2.3.0': + resolution: {integrity: sha512-iwaxZyzOuK0D7lS+0AQEtW52zUWxoGqTGkke3dRyb8pYiShmRpCjB/8TzPI4R6YySCH7Vm9BZj/31VPiiQTLBg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/util@1.3.0': + resolution: {integrity: sha512-hfXE04CVsxI/slmWKeSh6du7sSKpbvVdVEZCa8A+2QWDlL97EsCYme2c3ZWLn1uC9FR21JoewlrhUPWO4QgO8w==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.50.1': + resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.45.1': - resolution: {integrity: sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==} + '@rollup/rollup-android-arm64@4.50.1': + resolution: {integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.45.1': - resolution: {integrity: sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==} + '@rollup/rollup-darwin-arm64@4.50.1': + resolution: {integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.45.1': - resolution: {integrity: sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==} + '@rollup/rollup-darwin-x64@4.50.1': + resolution: {integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.45.1': - resolution: {integrity: sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==} + '@rollup/rollup-freebsd-arm64@4.50.1': + resolution: {integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.45.1': - resolution: {integrity: sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==} + '@rollup/rollup-freebsd-x64@4.50.1': + resolution: {integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.45.1': - resolution: {integrity: sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==} + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': + resolution: {integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.45.1': - resolution: {integrity: sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==} + '@rollup/rollup-linux-arm-musleabihf@4.50.1': + resolution: {integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.45.1': - resolution: {integrity: sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==} + '@rollup/rollup-linux-arm64-gnu@4.50.1': + resolution: {integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.45.1': - resolution: {integrity: sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==} + '@rollup/rollup-linux-arm64-musl@4.50.1': + resolution: {integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.45.1': - resolution: {integrity: sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==} + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': + resolution: {integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': - resolution: {integrity: sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==} + '@rollup/rollup-linux-ppc64-gnu@4.50.1': + resolution: {integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.45.1': - resolution: {integrity: sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==} + '@rollup/rollup-linux-riscv64-gnu@4.50.1': + resolution: {integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.45.1': - resolution: {integrity: sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==} + '@rollup/rollup-linux-riscv64-musl@4.50.1': + resolution: {integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.45.1': - resolution: {integrity: sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==} + '@rollup/rollup-linux-s390x-gnu@4.50.1': + resolution: {integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.45.1': - resolution: {integrity: sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==} + '@rollup/rollup-linux-x64-gnu@4.50.1': + resolution: {integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.45.1': - resolution: {integrity: sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==} + '@rollup/rollup-linux-x64-musl@4.50.1': + resolution: {integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.45.1': - resolution: {integrity: sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==} + '@rollup/rollup-openharmony-arm64@4.50.1': + resolution: {integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.50.1': + resolution: {integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.45.1': - resolution: {integrity: sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==} + '@rollup/rollup-win32-ia32-msvc@4.50.1': + resolution: {integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.45.1': - resolution: {integrity: sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==} + '@rollup/rollup-win32-x64-msvc@4.50.1': + resolution: {integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==} cpu: [x64] os: [win32] @@ -1588,53 +1951,77 @@ packages: '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - '@secretlint/config-creator@10.2.0': - resolution: {integrity: sha512-KW0aNs45F480TXy8NfqAHeB9vq0vHmU2lzGzXXul6vSqshWkZD0ArAyww/yj8Wq9Y3TEI1JinxNO4G+RWWvKdg==} + '@secretlint/config-creator@10.2.2': + resolution: {integrity: sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ==} engines: {node: '>=20.0.0'} - '@secretlint/config-loader@10.2.0': - resolution: {integrity: sha512-Mmi3/GVg2wIS4VuBiYdV7eOLD+bV7IbwHHka8fBh2N/ODeQmulPfeIgmbDzcpBWxHFQPYZBN0mLYEC5iSj9f7g==} + '@secretlint/config-loader@10.2.2': + resolution: {integrity: sha512-ndjjQNgLg4DIcMJp4iaRD6xb9ijWQZVbd9694Ol2IszBIbGPPkwZHzJYKICbTBmh6AH/pLr0CiCaWdGJU7RbpQ==} engines: {node: '>=20.0.0'} - '@secretlint/core@10.2.0': - resolution: {integrity: sha512-7yIk6wSP4AGsgqzGZm5v4hW3Tr/wXAth8Ax3D6ikPvv5oCNTj/3Dgq6JdaLOQa2sUJbyQrYcLCONtmwEdiQzxw==} + '@secretlint/core@10.2.2': + resolution: {integrity: sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==} engines: {node: '>=20.0.0'} - '@secretlint/formatter@10.2.0': - resolution: {integrity: sha512-0pu7QA+ebVzJS/sSf0JWMx0QwgiZnYRHxWjRaSsYkUCqY/MZeMn+TAs0jiSDCci23OcmRcNNrrpkjm6N/hIXcg==} + '@secretlint/formatter@10.2.2': + resolution: {integrity: sha512-10f/eKV+8YdGKNQmoDUD1QnYL7TzhI2kzyx95vsJKbEa8akzLAR5ZrWIZ3LbcMmBLzxlSQMMccRmi05yDQ5YDA==} engines: {node: '>=20.0.0'} - '@secretlint/node@10.2.0': - resolution: {integrity: sha512-B8acPnY5xNBfdOl5PrsG9Z+7vujhMHWx1pJChrCUIDo3HvRu3IM2SfFUt6TAmLzr7jz12BP55/xJa5ebzBXWHg==} + '@secretlint/node@10.2.2': + resolution: {integrity: sha512-eZGJQgcg/3WRBwX1bRnss7RmHHK/YlP/l7zOQsrjexYt6l+JJa5YhUmHbuGXS94yW0++3YkEJp0kQGYhiw1DMQ==} engines: {node: '>=20.0.0'} - '@secretlint/profiler@10.2.0': - resolution: {integrity: sha512-Om/0m84ApSTTPWdm/tUCL4rTQ1D+s5XFDz8Ew+kPMScHedBsrM+dZQNRHj67y7CW+YmrgE8n4zFCYtvjQHAf4Q==} + '@secretlint/profiler@10.2.2': + resolution: {integrity: sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==} - '@secretlint/resolver@10.2.0': - resolution: {integrity: sha512-0CQvCkMCtDo8sgASJHlE02YigCgWK7DYR2cSM1PW9rA01jnlV4zWb3skTfgUeZw0F6Ie3c/eQMriEYe0SiWxJw==} + '@secretlint/resolver@10.2.2': + resolution: {integrity: sha512-3md0cp12e+Ae5V+crPQYGd6aaO7ahw95s28OlULGyclyyUtf861UoRGS2prnUrKh7MZb23kdDOyGCYb9br5e4w==} - '@secretlint/secretlint-formatter-sarif@10.2.0': - resolution: {integrity: sha512-y1jIHG5VXHn8lywSUm9YhsuqIYHbQJNx6UZFWyAFAUUE9Isg1sto7NDSnlzY2JWsVG8B1xOzv2uEnDegZvL7qw==} + '@secretlint/secretlint-formatter-sarif@10.2.2': + resolution: {integrity: sha512-ojiF9TGRKJJw308DnYBucHxkpNovDNu1XvPh7IfUp0A12gzTtxuWDqdpuVezL7/IP8Ua7mp5/VkDMN9OLp1doQ==} - '@secretlint/secretlint-rule-no-dotenv@10.2.0': - resolution: {integrity: sha512-9hGk5e+Zxvo6SAIQglGk63tQ5Dn+IIfkEsuGLIh0gZDMu/PudKl/LeTC4fM3+lJLEA73QoVv4HJ057PRD1XSHw==} + '@secretlint/secretlint-rule-no-dotenv@10.2.2': + resolution: {integrity: sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==} engines: {node: '>=20.0.0'} - '@secretlint/secretlint-rule-preset-recommend@10.2.0': - resolution: {integrity: sha512-gRe3I7r5VQgwmG6HO8r3e0PVEl2cSmCqxzvThBLNGUehB0w1zMsav6emoYAIsfsZU29OukZ5hnJPzXH6sth1qQ==} + '@secretlint/secretlint-rule-preset-recommend@10.2.2': + resolution: {integrity: sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==} engines: {node: '>=20.0.0'} - '@secretlint/source-creator@10.2.0': - resolution: {integrity: sha512-BwHt5TiAx3aAfeLAd27LV9JbEIf33Wi1stke2x/V/1GpHPvyxcgCljTh2hm+Mib7oZQaU8Esj8Jkp4zlWPsgOA==} + '@secretlint/source-creator@10.2.2': + resolution: {integrity: sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==} engines: {node: '>=20.0.0'} - '@secretlint/types@10.2.0': - resolution: {integrity: sha512-8fHvsBMQtibVDxHKCyjaxDdWStE6E063xwBqrBz1zl/VArzEVUzXF+NLNc/LdIuyVrgQ41BG7Bmvo5bbZQ+XEg==} + '@secretlint/types@10.2.2': + resolution: {integrity: sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==} engines: {node: '>=20.0.0'} - '@sinclair/typebox@0.34.38': - resolution: {integrity: sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==} + '@shikijs/core@3.13.0': + resolution: {integrity: sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA==} + + '@shikijs/engine-javascript@3.13.0': + resolution: {integrity: sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg==} + + '@shikijs/engine-oniguruma@3.13.0': + resolution: {integrity: sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg==} + + '@shikijs/langs@3.13.0': + resolution: {integrity: sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ==} + + '@shikijs/themes@3.13.0': + resolution: {integrity: sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg==} + + '@shikijs/transformers@3.13.0': + resolution: {integrity: sha512-833lcuVzcRiG+fXvgslWsM2f4gHpjEgui1ipIknSizRuTgMkNZupiXE5/TVJ6eSYfhNBFhBZKkReKWO2GgYmqA==} + + '@shikijs/types@3.13.0': + resolution: {integrity: sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@sinclair/typebox@0.34.41': + resolution: {integrity: sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==} '@sindresorhus/is@4.6.0': resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} @@ -1654,6 +2041,74 @@ packages: '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': + resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': + resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0': + resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0': + resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0': + resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0': + resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0': + resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-svg-component@8.0.0': + resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} + engines: {node: '>=12'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-preset@8.1.0': + resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/core@8.1.0': + resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} + engines: {node: '>=14'} + + '@svgr/hast-util-to-babel-ast@8.0.0': + resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==} + engines: {node: '>=14'} + + '@svgr/plugin-jsx@8.1.0': + resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + '@swc/core-darwin-arm64@1.12.1': resolution: {integrity: sha512-nUjWVcJ3YS2N40ZbKwYO2RJ4+o2tWYRzNOcIQp05FqW0+aoUCVMdAUUzQinPDynfgwVshDAXCKemY8X7nN5MaA==} engines: {node: '>=10'} @@ -1708,44 +2163,216 @@ packages: cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.12.1': - resolution: {integrity: sha512-+Zh+JKDwiFqV5N9yAd2DhYVGPORGh9cfenu1ptr9yge+eHAf7vZJcC3rnj6QMR1QJh0Y5VC9+YBjRFjZVA7XDw==} - engines: {node: '>=10'} - cpu: [x64] - os: [win32] + '@swc/core-win32-x64-msvc@1.12.1': + resolution: {integrity: sha512-+Zh+JKDwiFqV5N9yAd2DhYVGPORGh9cfenu1ptr9yge+eHAf7vZJcC3rnj6QMR1QJh0Y5VC9+YBjRFjZVA7XDw==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.12.1': + resolution: {integrity: sha512-aKXdDTqxTVFl/bKQZ3EQUjEMBEoF6JBv29moMZq0kbVO43na6u/u+3Vcbhbrh+A2N0X5OL4RaveuWfAjEgOmeA==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '>=0.5.17' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/helpers@0.5.1': + resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==} + + '@swc/types@0.1.25': + resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} + + '@tailwindcss/node@4.1.13': + resolution: {integrity: sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==} + + '@tailwindcss/oxide-android-arm64@4.1.13': + resolution: {integrity: sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.13': + resolution: {integrity: sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.13': + resolution: {integrity: sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.13': + resolution: {integrity: sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': + resolution: {integrity: sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': + resolution: {integrity: sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.13': + resolution: {integrity: sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.13': + resolution: {integrity: sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.13': + resolution: {integrity: sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.13': + resolution: {integrity: sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': + resolution: {integrity: sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.13': + resolution: {integrity: sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.13': + resolution: {integrity: sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==} + engines: {node: '>= 10'} + + '@tailwindcss/typography@0.5.19': + resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + + '@tailwindcss/vite@4.1.13': + resolution: {integrity: sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + + '@tanstack/history@1.132.21': + resolution: {integrity: sha512-5ziPz3YarKU5cBJoEJ4muV8cy+5W4oWdJMqW7qosMrK5fb9Qfm+QWX+kO3emKJMu4YOUofVu3toEuuD3x1zXKw==} + engines: {node: '>=12'} + + '@tanstack/react-router-devtools@1.132.23': + resolution: {integrity: sha512-9+PvLsSfiKHaQSDXqzXDIIAkw1JKAhcb7dKHIxw3mNvFBhatC3ugGecygrLv7QZj0K0kKvo9rla06Ako5SrO5Q==} + engines: {node: '>=12'} + peerDependencies: + '@tanstack/react-router': ^1.132.23 + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-router@1.132.23': + resolution: {integrity: sha512-k/ffpPwWWUyPp4FS7EJR2zIWWdU7XvQwlI2eK+NQOCXNX8SA+Kyy73rAedOb0JUFAuGPF36ySP15IH7mzTB+Xg==} + engines: {node: '>=12'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-store@0.7.7': + resolution: {integrity: sha512-qqT0ufegFRDGSof9D/VqaZgjNgp4tRPHZIJq2+QIHkMUtHjaJ0lYrrXjeIUJvjnTbgPfSD1XgOMEt0lmANn6Zg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/router-core@1.132.21': + resolution: {integrity: sha512-C+Pe/p6EwMs9DIPBfAXoNtbp1POo/abfl4AEWsDXNAnjVr2OB6aHFMIENUJx1cjs6zUT/4t9FtA/+zptSWtv9A==} + engines: {node: '>=12'} + + '@tanstack/router-devtools-core@1.132.21': + resolution: {integrity: sha512-tcQxVHXME0gEcsmm2t7a+7EivIZsyN+IKOFmqfvEu+FAPSj0TB+iYc/K2QrXC6QKqJ2SM9YeHpRH2EgrD0E1Hg==} + engines: {node: '>=12'} + peerDependencies: + '@tanstack/router-core': ^1.132.21 + csstype: ^3.0.10 + solid-js: '>=1.9.5' + tiny-invariant: ^1.3.3 + peerDependenciesMeta: + csstype: + optional: true + + '@tanstack/router-generator@1.132.21': + resolution: {integrity: sha512-AyWjZdPxykhU5pbCRjMZELycVJ/pmuCndP48SmYUvB1m2wtUWl8Ssi7a4BlNFEcd+AXcciKq6P2EWjZ0zzm90g==} + engines: {node: '>=12'} - '@swc/core@1.12.1': - resolution: {integrity: sha512-aKXdDTqxTVFl/bKQZ3EQUjEMBEoF6JBv29moMZq0kbVO43na6u/u+3Vcbhbrh+A2N0X5OL4RaveuWfAjEgOmeA==} - engines: {node: '>=10'} + '@tanstack/router-plugin@1.132.23': + resolution: {integrity: sha512-XKyBi2PrLqs73O1udcTRQyjp6m/shYgRUK/ydGNspSkyGJ7TtRL6+6o6UKQMu95PLZOrZTRpVkv5W4yk6FRFVw==} + engines: {node: '>=12'} peerDependencies: - '@swc/helpers': '>=0.5.17' + '@rsbuild/core': '>=1.0.2' + '@tanstack/react-router': ^1.132.23 + vite: '>=5.0.0 || >=6.0.0 || >=7.0.0' + vite-plugin-solid: ^2.11.8 + webpack: '>=5.92.0' peerDependenciesMeta: - '@swc/helpers': + '@rsbuild/core': + optional: true + '@tanstack/react-router': + optional: true + vite: + optional: true + vite-plugin-solid: + optional: true + webpack: optional: true - '@swc/counter@0.1.3': - resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + '@tanstack/router-utils@1.132.21': + resolution: {integrity: sha512-d9MvyhdPaKN78hQOss89bayiv/HR/7FpVHuKTNGEzzYrBWDVIUBd0yMNaBxQBpTg6NuNxtZ01ZJS5VkbedbzJw==} + engines: {node: '>=12'} - '@swc/helpers@0.5.1': - resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==} + '@tanstack/store@0.7.7': + resolution: {integrity: sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ==} - '@swc/types@0.1.24': - resolution: {integrity: sha512-tjTMh3V4vAORHtdTprLlfoMptu1WfTZG9Rsca6yOKyNYsRr+MUXutKmliB17orgSZk5DpnDxs8GUdd/qwYxOng==} + '@tanstack/virtual-file-routes@1.132.21': + resolution: {integrity: sha512-+eT+vxZnf2/QPr9ci5aPn7i3MnVyWYNG2DUqiKJXGi7EvuFrXV9r8zDK40QfUTNGdCg9F880YOVe3cTwMCO0zw==} + engines: {node: '>=12'} - '@textlint/ast-node-types@15.2.0': - resolution: {integrity: sha512-nr9wEiZCNYafGZ++uWFZgPlDX3Bi7u4T2d5swpaoMvc1G2toXsBfe7UNVwXZq5dvYDbQN7vDeb3ltlKQ8JnPNQ==} + '@textlint/ast-node-types@15.2.2': + resolution: {integrity: sha512-9ByYNzWV8tpz6BFaRzeRzIov8dkbSZu9q7IWqEIfmRuLWb2qbI/5gTvKcoWT1HYs4XM7IZ8TKSXcuPvMb6eorA==} - '@textlint/linter-formatter@15.2.0': - resolution: {integrity: sha512-L+fM2OTs17hRxPCLKUdPjHce7cJp81gV9ku53FCL+cXnq5bZx0XYYkqKdtC0jnXujkQmrTYU3SYFrb4DgXqbtA==} + '@textlint/linter-formatter@15.2.2': + resolution: {integrity: sha512-oMVaMJ3exFvXhCj3AqmCbLaeYrTNLqaJnLJMIlmnRM3/kZdxvku4OYdaDzgtlI194cVxamOY5AbHBBVnY79kEg==} - '@textlint/module-interop@15.2.0': - resolution: {integrity: sha512-M3y1s2dZZH8PSHo4RUlnPOdK3qN90wmYGaEdy+il9/BQfrrift7S9R8lOfhHoPS0m9FEsnwyj3dQLkCUugPd9Q==} + '@textlint/module-interop@15.2.2': + resolution: {integrity: sha512-2rmNcWrcqhuR84Iio1WRzlc4tEoOMHd6T7urjtKNNefpTt1owrTJ9WuOe60yD3FrTW0J/R0ux5wxUbP/eaeFOA==} - '@textlint/resolver@15.2.0': - resolution: {integrity: sha512-1UC+5bEtuoht7uu0uGofb7sX7j17Mvyst9InrRtI4XgKhh1uMZz5YFiMYpNwry1GgCZvq7Wyq1fqtEIsvYWqFw==} + '@textlint/resolver@15.2.2': + resolution: {integrity: sha512-4hGWjmHt0y+5NAkoYZ8FvEkj8Mez9TqfbTm3BPjoV32cIfEixl2poTOgapn1rfm73905GSO3P1jiWjmgvii13Q==} - '@textlint/types@15.2.0': - resolution: {integrity: sha512-wpF+xjGJgJK2JiwUdYjuNZrbuas3KfC9VDnHKac6aBLFyrI1iXuXtuxKXQDFi5/hebACactSJOuVVbuQbdJZ1Q==} + '@textlint/types@15.2.2': + resolution: {integrity: sha512-X2BHGAR3yXJsCAjwYEDBIk9qUDWcH4pW61ISfmtejau+tVqKtnbbvEZnMTb6mWgKU1BvTmftd5DmB1XVDUtY3g==} '@tokenizer/inflate@0.2.7': resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} @@ -1772,8 +2399,17 @@ packages: '@types/argparse@1.0.38': resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} - '@types/body-parser@1.19.6': - resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} '@types/bun@1.2.21': resolution: {integrity: sha512-NiDnvEqmbfQ6dmZ3EeUO577s4P5bf4HCTXtI6trMc6f6RzirY5IrF3aIookuSpyslFzrnvv2lmEWv5HyC1X79A==} @@ -1784,8 +2420,98 @@ packages: '@types/chai@5.2.2': resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} - '@types/connect@3.4.38': - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-axis@3.0.6': + resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} + + '@types/d3-brush@3.0.6': + resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} + + '@types/d3-chord@3.0.6': + resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-contour@3.0.6': + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + + '@types/d3-delaunay@6.0.4': + resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} + + '@types/d3-dispatch@3.0.7': + resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-dsv@3.0.7': + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-fetch@3.0.7': + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-format@3.0.4': + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + + '@types/d3-geo@3.1.0': + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-polygon@3.0.2': + resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} + + '@types/d3-quadtree@3.0.6': + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + + '@types/d3-random@3.0.3': + resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} + + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@3.1.7': + resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} + + '@types/d3-time-format@4.0.3': + resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/d3@7.4.3': + resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -1796,24 +2522,36 @@ packages: '@types/diff-match-patch@1.0.36': resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} + '@types/diff@5.2.3': + resolution: {integrity: sha512-K0Oqlrq3kQMaO2RhfrNQX5trmt+XLyom88zS0u84nnIcLvFnRUMRRHmrGny5GSM+kNO9IZLARsdQHDzkhAgmrQ==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/express-serve-static-core@5.0.7': - resolution: {integrity: sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==} + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} - '@types/express@5.0.3': - resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==} + '@types/hast@2.3.10': + resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} - '@types/http-errors@2.0.5': - resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + '@types/js-cookie@3.0.6': + resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==} + '@types/js-yaml@4.0.9': resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + '@types/katex@0.16.7': + resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} + '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} @@ -1823,8 +2561,8 @@ packages: '@types/marked-terminal@6.1.1': resolution: {integrity: sha512-DfoUqkmFDCED7eBY9vFUhJ9fW8oZcMAK5EwRDQ9drjTbpQa+DnBTQQCwWhTFVf4WsZ6yYcJTI8D91wxTWXRZZQ==} - '@types/mime@1.3.5': - resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} '@types/mocha@10.0.10': resolution: {integrity: sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==} @@ -1832,25 +2570,32 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@24.2.1': - resolution: {integrity: sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==} + '@types/node@24.3.1': + resolution: {integrity: sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} - '@types/qs@6.14.0': - resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} - '@types/range-parser@1.2.7': - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/prismjs@1.26.5': + resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==} - '@types/react-dom@19.1.6': - resolution: {integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==} + '@types/react-dom@19.1.9': + resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==} peerDependencies: '@types/react': ^19.0.0 - '@types/react@19.1.10': - resolution: {integrity: sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==} + '@types/react-i18next@8.1.0': + resolution: {integrity: sha512-d4xhcjX5b3roNMObRNMfb1HinHQlQLPo8xlDj60dnHeeAw2bBymR2cy/l1giJpHzo/ZFgSvgVUvIWr4kCrenCg==} + deprecated: This is a stub types definition. react-i18next provides its own type definitions, so you do not need this installed. + + '@types/react-syntax-highlighter@15.5.13': + resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} + + '@types/react@19.1.12': + resolution: {integrity: sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==} '@types/resolve@1.20.6': resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==} @@ -1858,20 +2603,26 @@ packages: '@types/sarif@2.1.7': resolution: {integrity: sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==} - '@types/send@0.17.5': - resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} - - '@types/serve-static@1.15.8': - resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==} - '@types/tinycolor2@1.4.6': resolution: {integrity: sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==} + '@types/trusted-types@1.0.6': + resolution: {integrity: sha512-230RC8sFeHoT6sSUlRO6a8cAnclO06eeiq1QDfiv2FGCLWFvvERWgwIQD4FWqD9A69BN7Lzee4OXwoMVnnsWDw==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/turndown@5.0.5': resolution: {integrity: sha512-TL2IgGgc7B5j78rIccBtlYAnkuv8nUQqhQc+DSYV5j9Be9XOcm/SKOVRuA47xAVI3680Tk9B1d8flK2GWT2+4w==} - '@types/vscode@1.102.0': - resolution: {integrity: sha512-V9sFXmcXz03FtYTSUsYsu5K0Q9wH9w9V25slddcxrh5JgORD14LpnOA7ov0L9ALi+6HrTjskLJ/tY5zeRF3TFA==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/vscode@1.103.0': + resolution: {integrity: sha512-o4hanZAQdNfsKecexq9L3eHICd0AAvdbLk6hA60UzGXbGH/q8b/9xv2RgR7vV3ZcHuyKVq7b37IGd/+gM4Tu+Q==} '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} @@ -1946,6 +2697,15 @@ packages: resolution: {integrity: sha512-+0WOQ+N6NrKrT3WuxK4GWBmcCmGR7chITW19unxQeyzqXCoJ0pMTpmFF8g/fxjFth+5SYwS1lFWLEdjg4tS6lA==} hasBin: true + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} @@ -2065,6 +2825,13 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + ahooks@3.9.5: + resolution: {integrity: sha512-TrjXie49Q8HuHKTa84Fm9A+famMDAG1+7a9S9Gq6RQ0h90Jgqmiq3CkObuRjWT/C4d6nRZCw35Y2k2fmybb5eA==} + engines: {node: '>=18'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + ai@4.3.19: resolution: {integrity: sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q==} engines: {node: '>=18'} @@ -2111,18 +2878,34 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} + ansis@4.2.0: + resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} + engines: {node: '>=14'} + + antd-style@3.7.1: + resolution: {integrity: sha512-CQOfddVp4aOvBfCepa+Kj2e7ap+2XBINg1Kn2osdE3oQvrD7KJu/K0sfnLcFLkgCJygbxmuazYdWLKb+drPDYA==} + peerDependencies: + antd: '>=5.8.1' + react: '>=18' + + antd@5.27.4: + resolution: {integrity: sha512-rhArohoAUCxhkPjGI/BXthOrrjaElL4Fb7d4vEHnIR3DpxFXfegd4rN21IgGdiF+Iz4EFuUZu8MdS8NuJHLSVQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -2160,6 +2943,10 @@ packages: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} + ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} @@ -2185,6 +2972,9 @@ packages: avvio@9.1.0: resolution: {integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==} + axios@1.10.0: + resolution: {integrity: sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==} + azure-devops-node-api@12.5.0: resolution: {integrity: sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==} @@ -2196,6 +2986,16 @@ packages: react-native-b4a: optional: true + babel-dead-code-elimination@1.0.10: + resolution: {integrity: sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA==} + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -2235,6 +3035,10 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + baseline-browser-mapping@2.8.9: + resolution: {integrity: sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==} + hasBin: true + basic-ftp@5.0.5: resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} engines: {node: '>=10.0.0'} @@ -2306,6 +3110,11 @@ packages: browserify-zlib@0.2.0: resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + browserslist@4.26.2: + resolution: {integrity: sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} @@ -2376,22 +3185,49 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - chai@5.2.1: - resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==} + caniuse-lite@1.0.30001745: + resolution: {integrity: sha512-ywt6i8FzvdgrrrGbr1jZVObnVv6adj+0if2/omv9cmR2oiZs30zL4DIyaptKcbOrBdOIc74QTMoJvSE2QHh5UQ==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.5.0: - resolution: {integrity: sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + chardet@2.1.0: resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} @@ -2402,9 +3238,17 @@ packages: cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} - cheerio@1.1.0: - resolution: {integrity: sha512-+0hMx9eYhJvWbgpKV9hN7jg0JcwydpopZE4hgi+KvQtByZXPp04NiCWU0LzcAbP63abZckIHkTQaXVF52mX3xQ==} - engines: {node: '>=18.17'} + cheerio@1.1.2: + resolution: {integrity: sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==} + engines: {node: '>=20.18.1'} + + chevrotain-allstar@0.3.1: + resolution: {integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==} + peerDependencies: + chevrotain: ^11.0.0 + + chevrotain@11.0.3: + resolution: {integrity: sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==} chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} @@ -2438,6 +3282,9 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + cli-boxes@3.0.0: resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} engines: {node: '>=10'} @@ -2514,6 +3361,12 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + comma-separated-tokens@1.0.8: + resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@12.1.0: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} @@ -2526,14 +3379,31 @@ packages: resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} engines: {node: '>=20'} + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + compute-scroll-into-view@3.1.1: + resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - concurrently@9.2.0: - resolution: {integrity: sha512-IsB/fiXTupmagMW4MNp2lx2cdSN2FfZq78vF90LBB+zZHArbIQZjQtzXCiXnvTxCZSvXanTqFLWBjw2UkLx1SQ==} + concurrently@9.2.1: + resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==} engines: {node: '>=18'} hasBin: true + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + console-browserify@1.2.0: resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} @@ -2556,6 +3426,9 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -2563,6 +3436,9 @@ packages: resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + cookie-es@2.0.0: + resolution: {integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==} + cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} @@ -2585,6 +3461,9 @@ packages: copy-anything@2.0.6: resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + copy-to-clipboard@3.3.3: + resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + core-js@3.45.1: resolution: {integrity: sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==} @@ -2595,6 +3474,25 @@ packages: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} engines: {node: '>= 0.10'} + cose-base@1.0.3: + resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} + + cose-base@2.2.0: + resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + cosmiconfig@9.0.0: resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} engines: {node: '>=14'} @@ -2634,9 +3532,170 @@ packages: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + cytoscape-cose-bilkent@4.1.0: + resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape-fcose@2.2.0: + resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape@3.33.1: + resolution: {integrity: sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==} + engines: {node: '>=0.10'} + + d3-array@2.12.1: + resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-axis@3.0.0: + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + engines: {node: '>=12'} + + d3-brush@3.0.0: + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@1.0.9: + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-polygon@3.0.1: + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-sankey@0.12.3: + resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@1.3.7: + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d3@7.9.0: + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + engines: {node: '>=12'} + + dagre-d3-es@7.0.11: + resolution: {integrity: sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw==} + data-uri-to-buffer@6.0.2: resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} engines: {node: '>= 14'} @@ -2644,6 +3703,9 @@ packages: date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + dayjs@1.11.18: + resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==} + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -2674,6 +3736,9 @@ packages: resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} engines: {node: '>=10'} + decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} + decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -2709,6 +3774,9 @@ packages: resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} engines: {node: '>= 14'} + delaunator@5.0.1: + resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -2735,6 +3803,9 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + devtools-protocol@0.0.1508733: resolution: {integrity: sha512-QJ1R5gtck6nDcdM+nlsaJXcelPEI7ZxSMw1ujHpO1c4+9l+Nue5qlebi9xO1Z2MGr92bFOQTW7/rrheh5hHxDg==} @@ -2770,11 +3841,17 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} + dompurify@3.2.7: + resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==} + domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} - dotenv@17.2.1: - resolution: {integrity: sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==} + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dotenv@17.2.2: + resolution: {integrity: sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==} engines: {node: '>=12'} dunder-proto@1.0.1: @@ -2793,18 +3870,21 @@ packages: ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - editions@6.21.0: - resolution: {integrity: sha512-ofkXJtn7z0urokN62DI3SBo/5xAtF0rR7tn+S/bSYV79Ka8pTajIIl+fFQ1q88DQEImymmo97M4azY3WX/nUdg==} - engines: {node: '>=4'} + editions@6.22.0: + resolution: {integrity: sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==} + engines: {ecmascript: '>= es5', node: '>=4'} ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + electron-to-chromium@1.5.227: + resolution: {integrity: sha512-ITxuoPfJu3lsNWUi2lBM2PaBPYgH3uqmxut5vmBxgYvyI4AlJ6P3Cai1O76mOrkJCBzq0IxWg/NtqOrpu/0gKA==} + elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} - emoji-regex@10.4.0: - resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + emoji-regex@10.5.0: + resolution: {integrity: sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -2829,8 +3909,8 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - enhanced-resolve@5.18.2: - resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==} + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} entities@4.5.0: @@ -2875,16 +3955,11 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - es-toolkit@1.39.7: - resolution: {integrity: sha512-ek/wWryKouBrZIjkwW2BFf91CWOIMvoy2AE5YYgUrfWsJQM2Su1LoLtrw8uusEpN9RfqLlV/0FVNjT0WMv8Bxw==} - - esbuild@0.25.6: - resolution: {integrity: sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==} - engines: {node: '>=18'} - hasBin: true + es-toolkit@1.39.10: + resolution: {integrity: sha512-E0iGnTtbDhkeczB0T+mxmoVlT4YNweEKBLq7oaU4p11mecdsZpNWOglI4895Vh4usbQ+LsJiuLuI2L0Vdmfm2w==} - esbuild@0.25.8: - resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} + esbuild@0.25.9: + resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} engines: {node: '>=18'} hasBin: true @@ -2903,6 +3978,10 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + escodegen@2.1.0: resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} engines: {node: '>=6.0'} @@ -2917,6 +3996,12 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -2946,9 +4031,9 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - eventsource-parser@3.0.3: - resolution: {integrity: sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==} - engines: {node: '>=20.0.0'} + eventsource-parser@3.0.6: + resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} + engines: {node: '>=18.0.0'} eventsource@3.0.7: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} @@ -2987,6 +4072,12 @@ packages: resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} engines: {node: '>= 18'} + exsolve@1.0.7: + resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + extract-zip@2.0.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} @@ -2998,6 +4089,9 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + fast-fifo@1.3.2: resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} @@ -3018,14 +4112,14 @@ packages: resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} engines: {node: '>=6'} - fast-uri@3.0.6: - resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} fastify-plugin@5.0.1: resolution: {integrity: sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==} - fastify@5.5.0: - resolution: {integrity: sha512-ZWSWlzj3K/DcULCnCjEiC2zn2FBPdlZsSA/pnPa/dbUfLvxkD/Nqmb0XXMXLrWkeM4uQPUvjdJpwtXmTfriXqw==} + fastify@5.6.0: + resolution: {integrity: sha512-9j2r9TnwNsfGiCKGYT0Voqy244qwcoYM9qvNi/i+F8sNNWDnqUEVuGYNc9GyjldhXmMlJmVPS6gI1LdvjYGRJw==} fastmcp@3.17.0: resolution: {integrity: sha512-mNSPP/oG0qXFkPMA5TFXde8Zg5IjBvZE6QP/TMqcMpYYLTRJX1UJGDG7WyCKaPDF8JtgTWJigi1Osqo5rri+6A==} @@ -3034,11 +4128,15 @@ packages: fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fault@1.0.4: + resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} - fdir@6.4.6: - resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -3072,6 +4170,9 @@ packages: resolution: {integrity: sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==} engines: {node: '>=20'} + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -3080,6 +4181,15 @@ packages: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -3088,10 +4198,14 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - form-data@4.0.3: - resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} + format@0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -3110,10 +4224,6 @@ packages: fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - fs-extra@11.3.0: - resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} - engines: {node: '>=14.14'} - fs-extra@11.3.1: resolution: {integrity: sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==} engines: {node: '>=14.14'} @@ -3133,12 +4243,16 @@ packages: resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==} engines: {node: '>=10'} + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-east-asian-width@1.3.0: - resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + get-east-asian-width@1.3.1: + resolution: {integrity: sha512-R1QfovbPsKmosqTnPoRFiJ7CF9MLRgb53ChvMZm+r4p76/+8yKDy17qLL2PKInORy2RkZZekuK0efYgmzTkXyQ==} engines: {node: '>=18'} get-intrinsic@1.3.0: @@ -3192,10 +4306,23 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + + globals@16.4.0: + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} + engines: {node: '>=18'} + globby@14.1.0: resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} engines: {node: '>=18'} + goober@2.1.16: + resolution: {integrity: sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==} + peerDependencies: + csstype: ^3.0.10 + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -3210,6 +4337,15 @@ packages: resolution: {integrity: sha512-frdKI4Qi8Ihp4C6wZNB565de/THpIaw3DjP5ku87M+N9rNSGmPTjfkq61SdRXB7eCaL8O1hkKDvf6CDMtOzIAg==} engines: {node: '>=14'} + hachure-fill@0.5.2: + resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} + + harden-react-markdown@1.1.2: + resolution: {integrity: sha512-4VRzZUz/2oV07ugbEhQHG8elHhqqqiYHjaU4Y5Y3pf7+0kzVDJhcdAS/3SdAOKsAscT+YoPhiEMmTUnVktjDrw==} + peerDependencies: + react: '>=16.8.0' + react-markdown: '>=9.0.0' + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -3239,6 +4375,51 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-from-dom@5.0.1: + resolution: {integrity: sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==} + + hast-util-from-html-isomorphic@2.0.0: + resolution: {integrity: sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==} + + hast-util-from-html@2.0.3: + resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} + + hast-util-from-parse5@8.0.3: + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + + hast-util-parse-selector@2.2.5: + resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-to-parse5@8.0.0: + resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} + + hast-util-to-text@4.0.2: + resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@6.0.0: + resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} + + hastscript@9.0.1: + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true @@ -3246,9 +4427,15 @@ packages: highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + highlightjs-vue@1.0.0: + resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} + hmac-drbg@1.0.1: resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + hosted-git-info@4.1.0: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} engines: {node: '>=10'} @@ -3260,6 +4447,15 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-parse-stringify@3.0.1: + resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} + + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + htmlparser2@10.0.0: resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} @@ -3291,6 +4487,17 @@ packages: engines: {node: '>=18'} hasBin: true + i18next-browser-languagedetector@8.2.0: + resolution: {integrity: sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g==} + + i18next@25.5.2: + resolution: {integrity: sha512-lW8Zeh37i/o0zVr+NoCHfNnfvVw+M6FQbRp36ZZ/NyHDJ3NJVpp2HhAUyU9WafL5AssymNoOjMRB48mmx2P6Hw==} + peerDependencies: + typescript: ^5 + peerDependenciesMeta: + typescript: + optional: true + iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -3299,6 +4506,10 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + iconv-lite@0.7.0: + resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + engines: {node: '>=0.10.0'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -3360,8 +4571,8 @@ packages: ink: '>=4.0.0' react: '>=18.0.0' - ink@6.1.0: - resolution: {integrity: sha512-YQ+lbMD79y3FBAJXXZnuRajLEgaMFp102361eY5NrBIEVCi9oFo7gNZU4z2LBWlcjZFiTt7jetlkIbKCCH4KJA==} + ink@6.3.0: + resolution: {integrity: sha512-2CbJAa7XeziZYe6pDS5RVLirRY28iSGMQuEV8jRU5NQsONQNfcR/BZHHc9vkMg2lGYTHTM2pskxC1YmY28p6bQ==} engines: {node: '>=20'} peerDependencies: '@types/react': '>=19.0.0' @@ -3373,6 +4584,19 @@ packages: react-devtools-core: optional: true + inline-style-parser@0.2.4: + resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} + + internmap@1.0.1: + resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + intersection-observer@0.12.2: + resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} + ip-address@10.0.1: resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} engines: {node: '>= 12'} @@ -3385,6 +4609,18 @@ packages: resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} engines: {node: '>= 10'} + is-alphabetical@1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} @@ -3400,6 +4636,12 @@ packages: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} + is-decimal@1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3417,17 +4659,23 @@ packages: resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} engines: {node: '>=12'} - is-fullwidth-code-point@5.0.0: - resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} engines: {node: '>=18'} is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-in-ci@1.0.0: - resolution: {integrity: sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==} - engines: {node: '>=18'} + is-hexadecimal@1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-in-ci@2.0.0: + resolution: {integrity: sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w==} + engines: {node: '>=20'} hasBin: true is-inside-container@1.0.0: @@ -3439,6 +4687,9 @@ packages: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} + is-mobile@5.0.0: + resolution: {integrity: sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -3495,6 +4746,10 @@ packages: isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + isbot@5.1.31: + resolution: {integrity: sha512-DPgQshehErHAqSCKDb3rNW03pa2wS/v5evvUqtxt6TTnHRqAG8FdzcSSJs9656pK6Y+NT7K9R4acEYXLHYfpUQ==} + engines: {node: '>=18'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -3510,8 +4765,8 @@ packages: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} - istanbul-reports@3.1.7: - resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} istextorbinary@9.5.0: @@ -3525,10 +4780,6 @@ packages: resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} engines: {node: 20 || >=22} - jiti@1.21.7: - resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} - hasBin: true - jiti@2.5.1: resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} hasBin: true @@ -3536,6 +4787,10 @@ packages: jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3550,6 +4805,11 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} @@ -3565,6 +4825,9 @@ packages: json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + json2mq@0.2.0: + resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} + json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -3578,8 +4841,8 @@ packages: engines: {node: ^18.0.0 || >=20.0.0} hasBin: true - jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} jsonrepair@3.13.0: resolution: {integrity: sha512-5YRzlAQ7tuzV1nAJu3LvDlrKtBFIALHN2+a+I1MGJCt3ldRDBF/bZuvIPzae8Epot6KBXd0awRZZcuoeAsZ/mw==} @@ -3598,9 +4861,29 @@ packages: jws@3.2.2: resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + katex@0.16.22: + resolution: {integrity: sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==} + hasBin: true + keytar@7.9.0: resolution: {integrity: sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==} + khroma@2.1.0: + resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} + + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + + langium@3.3.1: + resolution: {integrity: sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==} + engines: {node: '>=16.0.0'} + + layout-base@1.0.2: + resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} + + layout-base@2.0.1: + resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + less-loader@12.3.0: resolution: {integrity: sha512-0M6+uYulvYIWs52y0LqN4+QM9TqWAohYSNTo4htE8Z7Cn3G/qQMEmktfHmyJT23k+20kU9zHH2wrfFXkxNLtVw==} engines: {node: '>= 18.12.0'} @@ -3614,8 +4897,8 @@ packages: webpack: optional: true - less@4.4.0: - resolution: {integrity: sha512-kdTwsyRuncDfjEs0DlRILWNvxhDG/Zij4YLO4TMJgDLW+8OzpfkdPnRgrsRuY1o+oaxJGWsps5f/RVBgGmmN0w==} + less@4.4.1: + resolution: {integrity: sha512-X9HKyiXPi0f/ed0XhgUlBeFfxrlDP3xR4M7768Zl+WXLUViuL9AOPPJP4nCV0tgRWvTYvpNmN0SFhZOQzy16PA==} engines: {node: '>=14'} hasBin: true @@ -3703,13 +4986,13 @@ packages: linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} - lint-staged@16.1.5: - resolution: {integrity: sha512-uAeQQwByI6dfV7wpt/gVqg+jAPaSp8WwOA8kKC/dv1qw14oGpnpAisY65ibGHUGDUv0rYaZ8CAJZ/1U8hUvC2A==} + lint-staged@16.1.6: + resolution: {integrity: sha512-U4kuulU3CKIytlkLlaHcGgKscNfJPNTiDF2avIUGFCv7K95/DCYQ7Ra62ydeRWmgQGg9zJYw2dzdbztwJlqrow==} engines: {node: '>=20.17'} hasBin: true - listr2@9.0.1: - resolution: {integrity: sha512-SL0JY3DaxylDuo/MecFeiC+7pedM0zia33zl0vcjgwcq1q1FWWF1To9EIauPbl8GbMCU0R2e0uJ8bZunhYKD2g==} + listr2@9.0.3: + resolution: {integrity: sha512-0aeh5HHHgmq1KRdMMDHfhMWQmIT/m7nRDTlxlFqni2Sp0had9baqsjJRvDGdlvgd6NmPE0nPloOipiQJGFtTHQ==} engines: {node: '>=20.0.0'} loader-runner@4.3.0: @@ -3720,6 +5003,10 @@ packages: resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} engines: {node: '>= 12.13.0'} + local-pkg@1.1.2: + resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} + engines: {node: '>=14'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -3727,12 +5014,19 @@ packages: lodash-es@4.17.21: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + lodash.includes@4.3.0: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} lodash.isboolean@3.0.3: resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + lodash.isinteger@4.0.4: resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} @@ -3766,20 +5060,32 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - loupe@3.1.4: - resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==} + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lowlight@1.20.0: + resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.1.0: - resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} + lru-cache@11.2.1: + resolution: {integrity: sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==} engines: {node: 20 || >=22} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -3793,8 +5099,18 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + lucide-react@0.542.0: + resolution: {integrity: sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + lucide-react@0.545.0: + resolution: {integrity: sha512-7r1/yUuflQDSt4f1bpn5ZAocyIxcTyVyBBChSVtBKn5M+392cPmI5YJMWOJKk/HUWGm5wg83chlAZtCcGbEZtw==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} make-dir@2.1.0: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} @@ -3811,6 +5127,9 @@ packages: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + marked-terminal@7.3.0: resolution: {integrity: sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==} engines: {node: '>=16.0.0'} @@ -3822,8 +5141,8 @@ packages: engines: {node: '>= 18'} hasBin: true - marked@16.1.2: - resolution: {integrity: sha512-rNQt5EvRinalby7zJZu/mB+BvaAY2oz3wCuCjt1RDrWNpS1Pdf9xqMOeC9Hm5adBdcV/3XZPJpG58eT+WBc0XQ==} + marked@16.2.1: + resolution: {integrity: sha512-r3UrXED9lMlHF97jJByry90cwrZBBvZmjG1L68oYfuPMW+uDTnuMbyJDymCWwbTE+f+3LhpNDKfpR3a3saFyjA==} engines: {node: '>= 20'} hasBin: true @@ -3838,6 +5157,54 @@ packages: md5.js@1.3.5: resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.2: + resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-math@3.0.0: + resolution: {integrity: sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} @@ -3863,10 +5230,100 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + mermaid@11.12.0: + resolution: {integrity: sha512-ZudVx73BwrMJfCFmSSJT84y6u5brEoV8DOItdHomNLz32uBjNrelm7mg95X7g+C6UoQH/W6mBLGDEDv73JdxBg==} + methods@1.1.2: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-extension-math@3.1.0: + resolution: {integrity: sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -3964,11 +5421,17 @@ packages: engines: {node: '>=10'} hasBin: true - mocha@11.7.1: - resolution: {integrity: sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==} + mlly@1.8.0: + resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + + mocha@11.7.2: + resolution: {integrity: sha512-lkqVJPmqqG/w5jmmFtiRvtA2jkDyNVUcefFJKb2uyX4dekk8Okgqop3cgbFiaIvj8uCRJVTP5x9dfxGyXm2jvQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true + monaco-editor@0.53.0: + resolution: {integrity: sha512-0WNThgC6CMWNXXBxTbaYYcunj08iB5rnx4/G56UOPeL9UVIUGGHA1GR0EWIh9Ebabj7NpCRawQ5b0hfN1jQmYQ==} + ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -3981,8 +5444,8 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nano-spawn@1.0.2: - resolution: {integrity: sha512-21t+ozMQDAL/UGgQVBbZ/xXvNO10++ZPuTmKRO8k9V3AClVRht49ahtDjfY8l1q6nSHOrE5ASfthzH3ol6R/hg==} + nano-spawn@1.0.3: + resolution: {integrity: sha512-jtpsQDetTnvS2Ts1fiRdci5rx0VYws5jGyC+4IYOTnIQ/wwdf6JdomlHBwqC3bJYOvaKu0C2GSZ1A60anrYpaA==} engines: {node: '>=20.17'} nanoid@3.3.11: @@ -4013,8 +5476,11 @@ packages: resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} engines: {node: '>= 0.4.0'} - node-abi@3.75.0: - resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==} + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-abi@3.77.0: + resolution: {integrity: sha512-DSmt0OEcLoK4i3NuscSbGjOf3bqiDEutejqENSplMSFA/gmB8mkED9G4pKWnPl7MDU4rSHebKPHeitpDfyH0cQ==} engines: {node: '>=10'} node-addon-api@4.3.0: @@ -4027,6 +5493,9 @@ packages: node-libs-browser-okam@2.2.5: resolution: {integrity: sha512-kD+WXACEThc6C5DA146KoCNbubjpXeYzXDrukvtXWr6MRzV3uvHCI0eb/GuugWVYnMoD4g3/uaIzvDYOpC4QWw==} + node-releases@2.0.21: + resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==} + node-sarif-builder@3.2.0: resolution: {integrity: sha512-kVIOdynrF2CRodHZeP/97Rh1syTUHBNiw17hUCIVhlhEsWlfJm19MuO56s4MdKbr22xWx6mzMnNAgXzVlIYM9Q==} engines: {node: '>=18'} @@ -4081,12 +5550,18 @@ packages: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} + oniguruma-parser@0.12.1: + resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + + oniguruma-to-es@4.3.3: + resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==} + open@10.2.0: resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} engines: {node: '>=18'} - openai@5.10.1: - resolution: {integrity: sha512-fq6xVfv1/gpLbsj8fArEt3b6B9jBxdhAK+VJ+bDvbUvNd+KTLlA3bnDeYZaBsGH9LUhJ1M1yXfp9sEyBLMx6eA==} + openai@5.20.0: + resolution: {integrity: sha512-Bmc2zLM/YWgFrDpXr9hwXqGGDdMmMpE9+qoZPsaHpn0Y/Qk1Vu26hNqXo7+nHdli+sLsXINvS1f8kR3NKhGKmA==} hasBin: true peerDependencies: ws: ^8.18.0 @@ -4127,9 +5602,15 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + package-manager-detector@1.3.0: + resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==} + pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parchment@3.0.0: + resolution: {integrity: sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -4138,6 +5619,12 @@ packages: resolution: {integrity: sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==} engines: {node: '>= 0.10'} + parse-entities@2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -4186,6 +5673,9 @@ packages: path-browserify@0.0.1: resolution: {integrity: sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==} + path-data-parser@0.1.0: + resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -4222,9 +5712,12 @@ packages: path-to-regexp@3.3.0: resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==} - path-to-regexp@8.2.0: - resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} - engines: {node: '>=16'} + path-to-regexp@8.3.0: + resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} path-type@6.0.0: resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} @@ -4254,10 +5747,6 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} - engines: {node: '>=12'} - picomatch@4.0.3: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} @@ -4277,8 +5766,8 @@ packages: pino-std-serializers@7.0.0: resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} - pino@9.7.0: - resolution: {integrity: sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg==} + pino@9.9.4: + resolution: {integrity: sha512-d1XorUQ7sSKqVcYdXuEYs2h1LKxejSorMEJ76XoZ0pPDf8VzJMe7GlPXpMBZeQ9gE4ZPIp5uGD+5Nw7scxiigg==} hasBin: true piscina@4.9.2: @@ -4292,6 +5781,12 @@ packages: resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} engines: {node: '>=16.20.0'} + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.3.0: + resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} + pluralize@2.0.0: resolution: {integrity: sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw==} @@ -4299,6 +5794,12 @@ packages: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} + points-on-curve@0.2.0: + resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} + + points-on-path@0.2.1: + resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} + portfinder@1.0.37: resolution: {integrity: sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==} engines: {node: '>= 10.12'} @@ -4307,8 +5808,8 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} - postcss-loader@8.1.1: - resolution: {integrity: sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==} + postcss-loader@8.2.0: + resolution: {integrity: sha512-tHX+RkpsXVcc7st4dSdDGliI+r4aAQDuv+v3vFYHixb6YgjreG5AG4SEB0kDK8u2s6htqEEpKlkhSBUTvWKYnA==} engines: {node: '>= 18.12.0'} peerDependencies: '@rspack/core': 0.x || 1.x @@ -4320,6 +5821,10 @@ packages: webpack: optional: true + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -4329,10 +5834,24 @@ packages: engines: {node: '>=10'} hasBin: true + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + pretty-ms@9.3.0: resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} engines: {node: '>=18'} + prism-react-renderer@2.4.1: + resolution: {integrity: sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==} + peerDependencies: + react: '>=16.0.0' + + prismjs@1.27.0: + resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} + engines: {node: '>=6'} + prismjs@1.30.0: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} @@ -4358,6 +5877,15 @@ packages: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} + property-information@5.6.0: + resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} + + property-information@6.5.0: + resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -4410,6 +5938,9 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + querystring-es3@0.2.1: resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} engines: {node: '>=0.4.x'} @@ -4420,6 +5951,14 @@ packages: quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + quill-delta@5.1.0: + resolution: {integrity: sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==} + engines: {node: '>= 12.0.0'} + + quill@2.0.3: + resolution: {integrity: sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==} + engines: {npm: '>=8.2.3'} + randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} @@ -4438,24 +5977,299 @@ packages: resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} - raw-body@3.0.0: - resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} - engines: {node: '>= 0.8'} + raw-body@3.0.1: + resolution: {integrity: sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==} + engines: {node: '>= 0.10'} + + rc-cascader@3.34.0: + resolution: {integrity: sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-checkbox@3.5.0: + resolution: {integrity: sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-collapse@3.9.0: + resolution: {integrity: sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' rc-config-loader@4.1.3: resolution: {integrity: sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==} + rc-dialog@9.6.0: + resolution: {integrity: sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-drawer@7.3.0: + resolution: {integrity: sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-dropdown@4.2.1: + resolution: {integrity: sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==} + peerDependencies: + react: '>=16.11.0' + react-dom: '>=16.11.0' + + rc-field-form@2.7.0: + resolution: {integrity: sha512-hgKsCay2taxzVnBPZl+1n4ZondsV78G++XVsMIJCAoioMjlMQR9YwAp7JZDIECzIu2Z66R+f4SFIRrO2DjDNAA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-image@7.12.0: + resolution: {integrity: sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-input-number@9.5.0: + resolution: {integrity: sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-input@1.8.0: + resolution: {integrity: sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + rc-mentions@2.20.0: + resolution: {integrity: sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-menu@9.16.1: + resolution: {integrity: sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-motion@2.9.5: + resolution: {integrity: sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-notification@5.6.4: + resolution: {integrity: sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-overflow@1.4.1: + resolution: {integrity: sha512-3MoPQQPV1uKyOMVNd6SZfONi+f3st0r8PksexIdBTeIYbMX0Jr+k7pHEDvsXtR4BpCv90/Pv2MovVNhktKrwvw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-pagination@5.1.0: + resolution: {integrity: sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-picker@4.11.3: + resolution: {integrity: sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg==} + engines: {node: '>=8.x'} + peerDependencies: + date-fns: '>= 2.x' + dayjs: '>= 1.x' + luxon: '>= 3.x' + moment: '>= 2.x' + react: '>=16.9.0' + react-dom: '>=16.9.0' + peerDependenciesMeta: + date-fns: + optional: true + dayjs: + optional: true + luxon: + optional: true + moment: + optional: true + + rc-progress@4.0.0: + resolution: {integrity: sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-rate@2.13.1: + resolution: {integrity: sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-resize-observer@1.4.3: + resolution: {integrity: sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-segmented@2.7.0: + resolution: {integrity: sha512-liijAjXz+KnTRVnxxXG2sYDGd6iLL7VpGGdR8gwoxAXy2KglviKCxLWZdjKYJzYzGSUwKDSTdYk8brj54Bn5BA==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + rc-select@14.16.8: + resolution: {integrity: sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '*' + react-dom: '*' + + rc-slider@11.1.9: + resolution: {integrity: sha512-h8IknhzSh3FEM9u8ivkskh+Ef4Yo4JRIY2nj7MrH6GQmrwV6mcpJf5/4KgH5JaVI1H3E52yCdpOlVyGZIeph5A==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-steps@6.0.1: + resolution: {integrity: sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-switch@4.1.0: + resolution: {integrity: sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-table@7.53.1: + resolution: {integrity: sha512-firAd7Z+liqIDS5TubJ1qqcoBd6YcANLKWQDZhFf3rfoOTt/UNPj4n3O+2vhl+z4QMqwPEUVAil661WHA8H8Aw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-tabs@15.7.0: + resolution: {integrity: sha512-ZepiE+6fmozYdWf/9gVp7k56PKHB1YYoDsKeQA1CBlJ/POIhjkcYiv0AGP0w2Jhzftd3AVvZP/K+V+Lpi2ankA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-textarea@1.10.2: + resolution: {integrity: sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-tooltip@6.4.0: + resolution: {integrity: sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-tree-select@5.27.0: + resolution: {integrity: sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww==} + peerDependencies: + react: '*' + react-dom: '*' + + rc-tree@5.13.1: + resolution: {integrity: sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==} + engines: {node: '>=10.x'} + peerDependencies: + react: '*' + react-dom: '*' + + rc-upload@4.9.2: + resolution: {integrity: sha512-nHx+9rbd1FKMiMRYsqQ3NkXUv7COHPBo3X1Obwq9SWS6/diF/A0aJ5OHubvwUAIDs+4RMleljV0pcrNUc823GQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-util@5.44.4: + resolution: {integrity: sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-virtual-list@3.19.2: + resolution: {integrity: sha512-Ys6NcjwGkuwkeaWBDqfI3xWuZ7rDiQXlH1o2zLfFzATfEgXcqpk8CkgMfbJD81McqjcJVez25a3kPxCR807evA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - react-dom@18.3.1: - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-dom@19.1.1: + resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} + peerDependencies: + react: ^19.1.1 + + react-error-overlay@6.0.9: + resolution: {integrity: sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==} + + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + + react-i18next@15.7.4: + resolution: {integrity: sha512-nyU8iKNrI5uDJch0z9+Y5XEr34b0wkyYj3Rp+tfbahxtlswxSCjcUL9H0nqXo9IR3/t5Y5PKIA3fx3MfUyR9Xw==} + peerDependencies: + i18next: '>= 23.4.0' + react: '>= 16.8.0' + react-dom: '*' + react-native: '*' + typescript: ^5 + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + typescript: + optional: true + + react-icons@5.5.0: + resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==} + peerDependencies: + react: '*' + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} peerDependencies: - react: ^18.3.1 + '@types/react': '>=18' + react: '>=18' - react-error-overlay@6.0.9: - resolution: {integrity: sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==} + react-markdown@9.1.0: + resolution: {integrity: sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' react-reconciler@0.32.0: resolution: {integrity: sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ==} @@ -4467,6 +6281,10 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} @@ -4503,6 +6321,11 @@ packages: '@types/react': optional: true + react-syntax-highlighter@15.6.6: + resolution: {integrity: sha512-DgXrc+AZF47+HvAPEmn7Ua/1p10jNoVZVI/LoPiYdtY+OM+/nG5yefLHKJwdKqY1adMuHFbeyBaG9j64ML7vTw==} + peerDependencies: + react: '>= 0.14.0' + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -4542,6 +6365,46 @@ packages: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} + recast@0.23.11: + resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} + engines: {node: '>= 4'} + + refractor@3.6.0: + resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.0.1: + resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} + + rehype-harden@1.1.2: + resolution: {integrity: sha512-58RSgd3BAYW/hULy6qvrLBIRe8qe5PElwEpRjrLilvhJ3N+Y6ptKAmy1CLIIyoMz7CMI30GqENhNXksJd5hGDg==} + + rehype-katex@7.0.1: + resolution: {integrity: sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==} + + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-math@6.0.0: + resolution: {integrity: sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -4550,6 +6413,9 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -4587,11 +6453,17 @@ packages: ripemd160@2.0.2: resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} - rollup@4.45.1: - resolution: {integrity: sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==} + robust-predicates@3.0.2: + resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} + + rollup@4.50.1: + resolution: {integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + roughjs@4.6.6: + resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} + router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} @@ -4603,6 +6475,9 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} @@ -4652,8 +6527,15 @@ packages: scheduler@0.26.0: resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} - secretlint@10.2.0: - resolution: {integrity: sha512-JxbGUpsa8OYeF9LsMKxyHbBMrojTIF+p6R7BHxbOSiMgD9Qct0Rlh3flkEZ3EeL/hQvANGSbL+EY7zyrxdY1EQ==} + screenfull@5.2.0: + resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} + engines: {node: '>=0.10.0'} + + scroll-into-view-if-needed@3.1.0: + resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} + + secretlint@10.2.2: + resolution: {integrity: sha512-xVpkeHV/aoWe4vP4TansF622nBEImzCY73y/0042DuJ29iKIaqgoJ8fGxre3rVSHHbxar4FdJobmTnLp9AU0eg==} engines: {node: '>=20.0.0'} hasBin: true @@ -4667,6 +6549,10 @@ packages: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + semver@7.5.4: resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} engines: {node: '>=10'} @@ -4688,6 +6574,16 @@ packages: serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + seroval-plugins@1.3.3: + resolution: {integrity: sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval@1.3.2: + resolution: {integrity: sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==} + engines: {node: '>=10'} + serve-handler@6.1.6: resolution: {integrity: sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==} @@ -4729,6 +6625,9 @@ packages: resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} engines: {node: '>= 0.4'} + shiki@3.13.0: + resolution: {integrity: sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g==} + side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -4780,14 +6679,17 @@ packages: resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} engines: {node: '>=12'} - slice-ansi@7.1.0: - resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} engines: {node: '>=18'} smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + socks-proxy-agent@8.0.5: resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} engines: {node: '>= 14'} @@ -4796,6 +6698,9 @@ packages: resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + solid-js@1.9.9: + resolution: {integrity: sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA==} + sonic-boom@4.2.0: resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} @@ -4803,10 +6708,24 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + space-separated-tokens@1.1.5: + resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + spawn-rx@5.1.2: resolution: {integrity: sha512-/y7tJKALVZ1lPzeZZB9jYnmtrL7d0N2zkorii5a7r7dhHkWIuLTzZpZzMJLK1dmYRgX/NCc4iarTO3F7BS2c/A==} @@ -4819,8 +6738,8 @@ packages: spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - spdx-license-ids@3.0.21: - resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} + spdx-license-ids@3.0.22: + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} @@ -4836,6 +6755,9 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + state-local@1.0.7: + resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} + statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -4860,6 +6782,11 @@ packages: stream-shift@1.0.3: resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + streamdown@1.3.0: + resolution: {integrity: sha512-vFZdoWKUeagzKwGGOcEqkV1fcgXOJOQqrNBor5/hbaAE/e/ULxZoIHHJJd5KEuaSddCM9KuYtIuZi3WSttXTEA==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + streamx@2.23.0: resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==} @@ -4870,6 +6797,9 @@ packages: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} + string-convert@0.2.1: + resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -4891,12 +6821,15 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} strip-final-newline@3.0.0: @@ -4925,6 +6858,18 @@ packages: structured-source@4.0.0: resolution: {integrity: sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==} + style-to-js@1.1.17: + resolution: {integrity: sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==} + + style-to-object@1.0.9: + resolution: {integrity: sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==} + + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + + stylis@4.3.6: + resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -4945,8 +6890,11 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - swr@2.3.3: - resolution: {integrity: sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==} + svg-parser@2.0.4: + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + + swr@2.3.6: + resolution: {integrity: sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw==} peerDependencies: react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -4961,13 +6909,20 @@ packages: tailwind-merge@2.6.0: resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} - tailwindcss-animate@1.0.7: - resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} + tailwind-merge@3.3.1: + resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} + + tailwind-scrollbar@4.0.2: + resolution: {integrity: sha512-wAQiIxAPqk0MNTPptVe/xoyWi27y+NRGnTwvn4PQnbvB9kp8QUBiGl/wsfoVBHnQxTmhXJSNt9NHTmcz9EivFA==} + engines: {node: '>=12.13.0'} peerDependencies: - tailwindcss: '>=3.0.0 || insiders' + tailwindcss: 4.x + + tailwindcss@4.1.13: + resolution: {integrity: sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==} - tapable@2.2.2: - resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} + tapable@2.2.3: + resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==} engines: {node: '>=6'} tar-fs@2.1.3: @@ -5015,6 +6970,10 @@ packages: thread-stream@3.1.0: resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + throttle-debounce@5.0.2: + resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} + engines: {node: '>=12.22'} + throttleit@2.1.0: resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} engines: {node: '>=18'} @@ -5026,6 +6985,12 @@ packages: resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} engines: {node: '>=0.6.0'} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -5035,8 +7000,11 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyglobby@0.2.14: - resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + tinyexec@1.0.1: + resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} tinygradient@1.1.5: @@ -5054,8 +7022,8 @@ packages: resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} engines: {node: '>=14.0.0'} - tmp@0.2.3: - resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} engines: {node: '>=14.14'} to-arraybuffer@1.0.1: @@ -5077,6 +7045,9 @@ packages: resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} engines: {node: '>=12'} + toggle-selection@1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} @@ -5089,6 +7060,16 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + ts-dedent@2.2.0: + resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} + engines: {node: '>=6.10'} + ts-node@10.9.2: resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true @@ -5106,8 +7087,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.20.3: - resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} + tsx@4.20.5: + resolution: {integrity: sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==} engines: {node: '>=18.0.0'} hasBin: true @@ -5121,8 +7102,8 @@ packages: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} - turndown@7.2.0: - resolution: {integrity: sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==} + turndown@7.2.1: + resolution: {integrity: sha512-7YiPJw6rLClQL3oUKN3KgMaXeJJ2lAyZItclgKDurqnH61so4k4IH/qwmMva0zpuJc/FhRExBBnk7EbeFANlgQ==} type-fest@4.41.0: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} @@ -5151,6 +7132,11 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + typescript@5.9.2: resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} engines: {node: '>=14.17'} @@ -5159,6 +7145,9 @@ packages: uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + uint8array-extras@1.5.0: resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} engines: {node: '>=18'} @@ -5169,8 +7158,8 @@ packages: undici-types@7.10.0: resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} - undici@7.11.0: - resolution: {integrity: sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==} + undici@7.15.0: + resolution: {integrity: sha512-7oZJCPvvMvTd0OlqWsIxTuItTpJBpU1tcbVl24FMn3xt3+VSunwUasmfPJRE57oNO1KsZ4PgA1xTdAX4hq8NyQ==} engines: {node: '>=20.18.1'} undici@7.16.0: @@ -5189,6 +7178,30 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-remove-position@5.0.0: + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -5197,6 +7210,16 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unplugin@2.3.10: + resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==} + engines: {node: '>=18.12.0'} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -5219,6 +7242,11 @@ packages: '@types/react': optional: true + use-merge-value@1.2.0: + resolution: {integrity: sha512-DXgG0kkgJN45TcyoXL49vJnn55LehnrmoHc7MbKi+QDBvr8dsesqws8UlyIWGHMR+JXgxc1nvY+jDGMlycsUcw==} + peerDependencies: + react: '>= 16.x' + use-sidecar@1.1.3: resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} engines: {node: '>=10'} @@ -5247,6 +7275,10 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + hasBin: true + uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -5261,8 +7293,8 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - valtio@2.1.5: - resolution: {integrity: sha512-vsh1Ixu5mT0pJFZm+Jspvhga5GzHUTYv0/+Th203pLfh3/wbHwxhu/Z2OkZDXIgHfjnjBns7SN9HNcbDvPmaGw==} + valtio@2.1.7: + resolution: {integrity: sha512-DwJhCDpujuQuKdJ2H84VbTjEJJteaSmqsuUltsfbfdbotVfNeTE4K/qc/Wi57I9x8/2ed4JNdjEna7O6PfavRg==} engines: {node: '>=12.20.0'} peerDependencies: '@types/react': '>=18.0.0' @@ -5277,17 +7309,71 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - version-range@4.14.0: - resolution: {integrity: sha512-gjb0ARm9qlcBAonU4zPwkl9ecKkas+tC2CGwFfptTCWWIVTWY1YUbT2zZKsOAF1jR/tNxxyLwwG0cb42XlYcTg==} + version-range@4.15.0: + resolution: {integrity: sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==} engines: {node: '>=4'} + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite-node@3.2.4: resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@7.0.5: - resolution: {integrity: sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==} + vite-plugin-svgr@4.5.0: + resolution: {integrity: sha512-W+uoSpmVkSmNOGPSsDCWVW/DDAyv+9fap9AZXBvWiQqrboJ08j2vh0tFxTD/LjwqwAd3yYSVJgm54S/1GhbdnA==} + peerDependencies: + vite: '>=2.6.0' + + vite@6.3.6: + resolution: {integrity: sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vite@7.1.7: + resolution: {integrity: sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -5357,9 +7443,39 @@ packages: vm-browserify@1.1.2: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + void-elements@3.1.0: + resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} + engines: {node: '>=0.10.0'} + + vscode-jsonrpc@8.2.0: + resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} + engines: {node: '>=14.0.0'} + + vscode-languageserver-protocol@3.17.5: + resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} + + vscode-languageserver-textdocument@1.0.12: + resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} + + vscode-languageserver-types@3.17.5: + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} + + vscode-languageserver@9.0.1: + resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} + hasBin: true + + vscode-uri@3.0.8: + resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} + + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + webdriver-bidi-protocol@0.3.6: resolution: {integrity: sha512-mlGndEOA9yK9YAbvtxaPTqdi/kaCWYYfwrZvGzcmkr/3lWM+tQj53BxtpVd6qbC6+E5OnHXgCcAhre6AkXzxjA==} + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} @@ -5397,8 +7513,8 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} - wrap-ansi@9.0.0: - resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} engines: {node: '>=18'} wrappy@1.0.2: @@ -5459,6 +7575,9 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} @@ -5466,6 +7585,10 @@ packages: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + yaml@2.8.1: resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} engines: {node: '>= 14.6'} @@ -5549,8 +7672,11 @@ packages: use-sync-external-store: optional: true - zx@8.7.1: - resolution: {integrity: sha512-28u1w2LlIfvyvJvYe6pmCipesk8oL5AFMVp+P/U445LcaPgzrU5lNDtAPd6nJvWmoCNyXZz37R/xKOGokccjsw==} + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + + zx@8.8.1: + resolution: {integrity: sha512-qvsKBnvWHstHKCluKPlEgI/D3+mdiQyMoSSeFR8IX/aXzWIas5A297KxKgPJhuPXdrR6ma0Jzx43+GQ/8sqbrw==} engines: {node: '>= 12.17.0'} hasBin: true @@ -5603,7 +7729,7 @@ snapshots: '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) react: 19.1.1 - swr: 2.3.3(react@19.1.1) + swr: 2.3.6(react@19.1.1) throttleit: 2.1.0 optionalDependencies: zod: 3.25.76 @@ -5622,10 +7748,102 @@ snapshots: '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) zod: 3.25.76 - '@alcalzone/ansi-tokenize@0.1.3': + '@alcalzone/ansi-tokenize@0.2.0': dependencies: - ansi-styles: 6.2.1 - is-fullwidth-code-point: 4.0.0 + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + '@ant-design/colors@7.2.1': + dependencies: + '@ant-design/fast-color': 2.0.6 + + '@ant-design/colors@8.0.0': + dependencies: + '@ant-design/fast-color': 3.0.0 + + '@ant-design/cssinjs-utils@1.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@ant-design/cssinjs': 1.24.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@babel/runtime': 7.28.4 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@ant-design/cssinjs@1.24.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@emotion/hash': 0.8.0 + '@emotion/unitless': 0.7.5 + classnames: 2.5.1 + csstype: 3.1.3 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + stylis: 4.3.6 + + '@ant-design/fast-color@2.0.6': + dependencies: + '@babel/runtime': 7.28.4 + + '@ant-design/fast-color@3.0.0': {} + + '@ant-design/icons-svg@4.4.2': {} + + '@ant-design/icons@5.6.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@ant-design/colors': 7.2.1 + '@ant-design/icons-svg': 4.4.2 + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@ant-design/icons@6.0.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@ant-design/colors': 8.0.0 + '@ant-design/icons-svg': 4.4.2 + '@rc-component/util': 1.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@ant-design/react-slick@1.1.2(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + json2mq: 0.2.0 + react: 19.1.1 + resize-observer-polyfill: 1.5.1 + throttle-debounce: 5.0.2 + + '@ant-design/v5-patch-for-react-19@1.0.3(antd@5.27.4(date-fns@4.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + antd: 5.27.4(date-fns@4.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@ant-design/x@1.6.1(antd@5.27.4(date-fns@4.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@ant-design/colors': 7.2.1 + '@ant-design/cssinjs': 1.24.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@ant-design/cssinjs-utils': 1.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@ant-design/fast-color': 2.0.6 + '@ant-design/icons': 5.6.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@babel/runtime': 7.28.4 + antd: 5.27.4(date-fns@4.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@antfu/install-pkg@1.1.0': + dependencies: + package-manager-detector: 1.3.0 + tinyexec: 1.0.1 + + '@antfu/utils@9.2.1': {} '@azu/format-text@1.0.2': {} @@ -5681,7 +7899,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@azure/identity@4.10.2': + '@azure/identity@4.11.1': dependencies: '@azure/abort-controller': 2.1.2 '@azure/core-auth': 1.10.0 @@ -5690,8 +7908,8 @@ snapshots: '@azure/core-tracing': 1.3.0 '@azure/core-util': 1.13.0 '@azure/logger': 1.3.0 - '@azure/msal-browser': 4.15.0 - '@azure/msal-node': 3.6.3 + '@azure/msal-browser': 4.22.0 + '@azure/msal-node': 3.7.3 open: 10.2.0 tslib: 2.8.1 transitivePeerDependencies: @@ -5704,15 +7922,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@azure/msal-browser@4.15.0': + '@azure/msal-browser@4.22.0': dependencies: - '@azure/msal-common': 15.8.1 + '@azure/msal-common': 15.12.0 - '@azure/msal-common@15.8.1': {} + '@azure/msal-common@15.12.0': {} - '@azure/msal-node@3.6.3': + '@azure/msal-node@3.7.3': dependencies: - '@azure/msal-common': 15.8.1 + '@azure/msal-common': 15.12.0 jsonwebtoken: 9.0.2 uuid: 8.3.2 @@ -5722,8 +7940,198 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/compat-data@7.28.4': {} + + '@babel/core@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.1(supports-color@8.1.1) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.3': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.4 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.26.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.28.4 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + + '@babel/parser@7.28.4': + dependencies: + '@babel/types': 7.28.4 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.28.4': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + + '@babel/traverse@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + debug: 4.4.1(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.4': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@bcoe/v8-coverage@0.2.3': {} '@biomejs/biome@2.2.4': @@ -5763,167 +8171,186 @@ snapshots: '@borewit/text-codec@0.1.1': {} - '@colors/colors@1.5.0': - optional: true + '@braintree/sanitize-url@7.1.1': {} - '@cspotcode/source-map-support@0.8.1': + '@chevrotain/cst-dts-gen@11.0.3': dependencies: - '@jridgewell/trace-mapping': 0.3.9 - - '@esbuild/aix-ppc64@0.25.6': - optional: true - - '@esbuild/aix-ppc64@0.25.8': - optional: true - - '@esbuild/android-arm64@0.25.6': - optional: true - - '@esbuild/android-arm64@0.25.8': - optional: true - - '@esbuild/android-arm@0.25.6': - optional: true + '@chevrotain/gast': 11.0.3 + '@chevrotain/types': 11.0.3 + lodash-es: 4.17.21 - '@esbuild/android-arm@0.25.8': - optional: true - - '@esbuild/android-x64@0.25.6': - optional: true + '@chevrotain/gast@11.0.3': + dependencies: + '@chevrotain/types': 11.0.3 + lodash-es: 4.17.21 - '@esbuild/android-x64@0.25.8': - optional: true + '@chevrotain/regexp-to-ast@11.0.3': {} - '@esbuild/darwin-arm64@0.25.6': - optional: true + '@chevrotain/types@11.0.3': {} - '@esbuild/darwin-arm64@0.25.8': - optional: true + '@chevrotain/utils@11.0.3': {} - '@esbuild/darwin-x64@0.25.6': + '@colors/colors@1.5.0': optional: true - '@esbuild/darwin-x64@0.25.8': - optional: true + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 - '@esbuild/freebsd-arm64@0.25.6': - optional: true + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.27.1 + '@babel/runtime': 7.28.4 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color - '@esbuild/freebsd-arm64@0.25.8': - optional: true + '@emotion/cache@11.14.0': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 - '@esbuild/freebsd-x64@0.25.6': - optional: true + '@emotion/css@11.13.5': + dependencies: + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + transitivePeerDependencies: + - supports-color - '@esbuild/freebsd-x64@0.25.8': - optional: true + '@emotion/hash@0.8.0': {} - '@esbuild/linux-arm64@0.25.6': - optional: true + '@emotion/hash@0.9.2': {} - '@esbuild/linux-arm64@0.25.8': - optional: true + '@emotion/memoize@0.9.0': {} - '@esbuild/linux-arm@0.25.6': - optional: true + '@emotion/react@11.14.0(@types/react@19.1.12)(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.1) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.12 + transitivePeerDependencies: + - supports-color - '@esbuild/linux-arm@0.25.8': - optional: true + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.1.3 - '@esbuild/linux-ia32@0.25.6': - optional: true + '@emotion/sheet@1.4.0': {} - '@esbuild/linux-ia32@0.25.8': - optional: true + '@emotion/unitless@0.10.0': {} - '@esbuild/linux-loong64@0.25.6': - optional: true + '@emotion/unitless@0.7.5': {} - '@esbuild/linux-loong64@0.25.8': - optional: true + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.1.1)': + dependencies: + react: 19.1.1 - '@esbuild/linux-mips64el@0.25.6': - optional: true + '@emotion/utils@1.4.2': {} - '@esbuild/linux-mips64el@0.25.8': - optional: true + '@emotion/weak-memoize@0.4.0': {} - '@esbuild/linux-ppc64@0.25.6': + '@esbuild/aix-ppc64@0.25.9': optional: true - '@esbuild/linux-ppc64@0.25.8': + '@esbuild/android-arm64@0.25.9': optional: true - '@esbuild/linux-riscv64@0.25.6': + '@esbuild/android-arm@0.25.9': optional: true - '@esbuild/linux-riscv64@0.25.8': + '@esbuild/android-x64@0.25.9': optional: true - '@esbuild/linux-s390x@0.25.6': + '@esbuild/darwin-arm64@0.25.9': optional: true - '@esbuild/linux-s390x@0.25.8': + '@esbuild/darwin-x64@0.25.9': optional: true - '@esbuild/linux-x64@0.25.6': + '@esbuild/freebsd-arm64@0.25.9': optional: true - '@esbuild/linux-x64@0.25.8': + '@esbuild/freebsd-x64@0.25.9': optional: true - '@esbuild/netbsd-arm64@0.25.6': + '@esbuild/linux-arm64@0.25.9': optional: true - '@esbuild/netbsd-arm64@0.25.8': + '@esbuild/linux-arm@0.25.9': optional: true - '@esbuild/netbsd-x64@0.25.6': + '@esbuild/linux-ia32@0.25.9': optional: true - '@esbuild/netbsd-x64@0.25.8': + '@esbuild/linux-loong64@0.25.9': optional: true - '@esbuild/openbsd-arm64@0.25.6': + '@esbuild/linux-mips64el@0.25.9': optional: true - '@esbuild/openbsd-arm64@0.25.8': + '@esbuild/linux-ppc64@0.25.9': optional: true - '@esbuild/openbsd-x64@0.25.6': + '@esbuild/linux-riscv64@0.25.9': optional: true - '@esbuild/openbsd-x64@0.25.8': + '@esbuild/linux-s390x@0.25.9': optional: true - '@esbuild/openharmony-arm64@0.25.6': + '@esbuild/linux-x64@0.25.9': optional: true - '@esbuild/openharmony-arm64@0.25.8': + '@esbuild/netbsd-arm64@0.25.9': optional: true - '@esbuild/sunos-x64@0.25.6': + '@esbuild/netbsd-x64@0.25.9': optional: true - '@esbuild/sunos-x64@0.25.8': + '@esbuild/openbsd-arm64@0.25.9': optional: true - '@esbuild/win32-arm64@0.25.6': + '@esbuild/openbsd-x64@0.25.9': optional: true - '@esbuild/win32-arm64@0.25.8': + '@esbuild/openharmony-arm64@0.25.9': optional: true - '@esbuild/win32-ia32@0.25.6': + '@esbuild/sunos-x64@0.25.9': optional: true - '@esbuild/win32-ia32@0.25.8': + '@esbuild/win32-arm64@0.25.9': optional: true - '@esbuild/win32-x64@0.25.6': + '@esbuild/win32-ia32@0.25.9': optional: true - '@esbuild/win32-x64@0.25.8': + '@esbuild/win32-x64@0.25.9': optional: true '@fastify/accept-negotiator@2.0.1': {} @@ -5932,7 +8359,7 @@ snapshots: dependencies: ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) - fast-uri: 3.0.6 + fast-uri: 3.1.0 '@fastify/compress@8.1.0': dependencies: @@ -5984,27 +8411,42 @@ snapshots: fastq: 1.19.1 glob: 11.0.3 - '@fastify/type-provider-typebox@5.2.0(@sinclair/typebox@0.34.38)': + '@fastify/type-provider-typebox@5.2.0(@sinclair/typebox@0.34.41)': dependencies: - '@sinclair/typebox': 0.34.38 + '@sinclair/typebox': 0.34.41 '@floating-ui/core@1.7.3': dependencies: '@floating-ui/utils': 0.2.10 - '@floating-ui/dom@1.7.3': + '@floating-ui/dom@1.7.4': dependencies: '@floating-ui/core': 1.7.3 '@floating-ui/utils': 0.2.10 - '@floating-ui/react-dom@2.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@floating-ui/react-dom@2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@floating-ui/dom': 1.7.3 + '@floating-ui/dom': 1.7.4 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) '@floating-ui/utils@0.2.10': {} + '@iconify/types@2.0.0': {} + + '@iconify/utils@3.0.2': + dependencies: + '@antfu/install-pkg': 1.1.0 + '@antfu/utils': 9.2.1 + '@iconify/types': 2.0.0 + debug: 4.4.1(supports-color@8.1.1) + globals: 15.15.0 + kolorist: 1.8.0 + local-pkg: 1.1.2 + mlly: 1.8.0 + transitivePeerDependencies: + - supports-color + '@isaacs/balanced-match@4.0.1': {} '@isaacs/brace-expansion@5.0.0': @@ -6015,7 +8457,7 @@ snapshots: dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 @@ -6026,39 +8468,53 @@ snapshots: '@istanbuljs/schema@0.1.3': {} + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.5.4': {} + '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/trace-mapping@0.3.29': + '@jridgewell/trace-mapping@0.3.30': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 '@lukeed/ms@2.0.2': {} - '@microsoft/api-extractor-model@7.30.7(@types/node@24.2.1)': + '@mermaid-js/parser@0.6.2': + dependencies: + langium: 3.3.1 + + '@microsoft/api-extractor-model@7.30.7(@types/node@24.3.1)': dependencies: '@microsoft/tsdoc': 0.15.1 '@microsoft/tsdoc-config': 0.17.1 - '@rushstack/node-core-library': 5.14.0(@types/node@24.2.1) + '@rushstack/node-core-library': 5.14.0(@types/node@24.3.1) transitivePeerDependencies: - '@types/node' - '@microsoft/api-extractor@7.52.10(@types/node@24.2.1)': + '@microsoft/api-extractor@7.52.11(@types/node@24.3.1)': dependencies: - '@microsoft/api-extractor-model': 7.30.7(@types/node@24.2.1) + '@microsoft/api-extractor-model': 7.30.7(@types/node@24.3.1) '@microsoft/tsdoc': 0.15.1 '@microsoft/tsdoc-config': 0.17.1 - '@rushstack/node-core-library': 5.14.0(@types/node@24.2.1) + '@rushstack/node-core-library': 5.14.0(@types/node@24.3.1) '@rushstack/rig-package': 0.5.3 - '@rushstack/terminal': 0.15.4(@types/node@24.2.1) - '@rushstack/ts-command-line': 5.0.2(@types/node@24.2.1) + '@rushstack/terminal': 0.15.4(@types/node@24.3.1) + '@rushstack/ts-command-line': 5.0.2(@types/node@24.3.1) lodash: 4.17.21 minimatch: 10.0.3 resolve: 1.22.10 @@ -6079,31 +8535,31 @@ snapshots: '@mixmark-io/domino@2.2.0': {} - '@modelcontextprotocol/inspector-cli@0.16.3': + '@modelcontextprotocol/inspector-cli@0.16.6': dependencies: - '@modelcontextprotocol/sdk': 1.17.2 + '@modelcontextprotocol/sdk': 1.17.5 commander: 13.1.0 spawn-rx: 5.1.2 transitivePeerDependencies: - supports-color - '@modelcontextprotocol/inspector-client@0.16.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)': + '@modelcontextprotocol/inspector-client@0.16.6(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)': dependencies: - '@modelcontextprotocol/sdk': 1.17.2 - '@radix-ui/react-checkbox': 1.3.2(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-dialog': 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modelcontextprotocol/sdk': 1.17.5 + '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-icons': 1.3.2(react@18.3.1) - '@radix-ui/react-label': 2.1.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-popover': 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-select': 2.2.5(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-tabs': 1.1.12(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-toast': 1.2.14(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-tooltip': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-label': 2.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-select': 2.2.6(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-toast': 1.2.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) ajv: 6.12.6 class-variance-authority: 0.7.1 clsx: 2.1.1 - cmdk: 1.1.1(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + cmdk: 1.1.1(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) lucide-react: 0.523.0(react@18.3.1) pkce-challenge: 4.1.0 prismjs: 1.30.0 @@ -6112,17 +8568,15 @@ snapshots: react-simple-code-editor: 0.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) serve-handler: 6.1.6 tailwind-merge: 2.6.0 - tailwindcss-animate: 1.0.7 zod: 3.25.76 transitivePeerDependencies: - '@types/react' - '@types/react-dom' - supports-color - - tailwindcss - '@modelcontextprotocol/inspector-server@0.16.3': + '@modelcontextprotocol/inspector-server@0.16.6': dependencies: - '@modelcontextprotocol/sdk': 1.17.2 + '@modelcontextprotocol/sdk': 1.17.5 cors: 2.8.5 express: 5.1.0 ws: 8.18.3 @@ -6132,17 +8586,17 @@ snapshots: - supports-color - utf-8-validate - '@modelcontextprotocol/inspector@0.16.3(@swc/core@1.12.1)(@types/node@24.2.1)(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(typescript@5.9.2)': + '@modelcontextprotocol/inspector@0.16.6(@swc/core@1.12.1)(@types/node@24.3.1)(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(typescript@5.9.2)': dependencies: - '@modelcontextprotocol/inspector-cli': 0.16.3 - '@modelcontextprotocol/inspector-client': 0.16.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10) - '@modelcontextprotocol/inspector-server': 0.16.3 - '@modelcontextprotocol/sdk': 1.17.2 - concurrently: 9.2.0 + '@modelcontextprotocol/inspector-cli': 0.16.6 + '@modelcontextprotocol/inspector-client': 0.16.6(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12) + '@modelcontextprotocol/inspector-server': 0.16.6 + '@modelcontextprotocol/sdk': 1.17.5 + concurrently: 9.2.1 open: 10.2.0 shell-quote: 1.8.3 spawn-rx: 5.1.2 - ts-node: 10.9.2(@swc/core@1.12.1)(@types/node@24.2.1)(typescript@5.9.2) + ts-node: 10.9.2(@swc/core@1.12.1)(@types/node@24.3.1)(typescript@5.9.2) zod: 3.25.76 transitivePeerDependencies: - '@swc/core' @@ -6152,22 +8606,21 @@ snapshots: - '@types/react-dom' - bufferutil - supports-color - - tailwindcss - typescript - utf-8-validate - '@modelcontextprotocol/sdk@1.17.2': + '@modelcontextprotocol/sdk@1.17.5': dependencies: ajv: 6.12.6 content-type: 1.0.5 cors: 2.8.5 cross-spawn: 7.0.6 eventsource: 3.0.7 - eventsource-parser: 3.0.3 + eventsource-parser: 3.0.6 express: 5.1.0 express-rate-limit: 7.5.1(express@5.1.0) pkce-challenge: 5.0.0 - raw-body: 3.0.0 + raw-body: 3.0.1 zod: 3.25.76 zod-to-json-schema: 3.24.6(zod@3.25.76) transitivePeerDependencies: @@ -6180,11 +8633,11 @@ snapshots: cors: 2.8.5 cross-spawn: 7.0.6 eventsource: 3.0.7 - eventsource-parser: 3.0.3 + eventsource-parser: 3.0.6 express: 5.1.0 express-rate-limit: 7.5.1(express@5.1.0) pkce-challenge: 5.0.0 - raw-body: 3.0.0 + raw-body: 3.0.1 zod: 3.25.76 zod-to-json-schema: 3.24.6(zod@3.25.76) transitivePeerDependencies: @@ -6212,72 +8665,87 @@ snapshots: '@module-federation/runtime': 0.8.12 '@module-federation/sdk': 0.8.12 - '@napi-rs/nice-android-arm-eabi@1.0.4': + '@monaco-editor/loader@1.5.0': + dependencies: + state-local: 1.0.7 + + '@monaco-editor/react@4.7.0(monaco-editor@0.53.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@monaco-editor/loader': 1.5.0 + monaco-editor: 0.53.0 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@napi-rs/nice-android-arm-eabi@1.1.1': optional: true - '@napi-rs/nice-android-arm64@1.0.4': + '@napi-rs/nice-android-arm64@1.1.1': optional: true - '@napi-rs/nice-darwin-arm64@1.0.4': + '@napi-rs/nice-darwin-arm64@1.1.1': optional: true - '@napi-rs/nice-darwin-x64@1.0.4': + '@napi-rs/nice-darwin-x64@1.1.1': optional: true - '@napi-rs/nice-freebsd-x64@1.0.4': + '@napi-rs/nice-freebsd-x64@1.1.1': optional: true - '@napi-rs/nice-linux-arm-gnueabihf@1.0.4': + '@napi-rs/nice-linux-arm-gnueabihf@1.1.1': optional: true - '@napi-rs/nice-linux-arm64-gnu@1.0.4': + '@napi-rs/nice-linux-arm64-gnu@1.1.1': optional: true - '@napi-rs/nice-linux-arm64-musl@1.0.4': + '@napi-rs/nice-linux-arm64-musl@1.1.1': optional: true - '@napi-rs/nice-linux-ppc64-gnu@1.0.4': + '@napi-rs/nice-linux-ppc64-gnu@1.1.1': optional: true - '@napi-rs/nice-linux-riscv64-gnu@1.0.4': + '@napi-rs/nice-linux-riscv64-gnu@1.1.1': optional: true - '@napi-rs/nice-linux-s390x-gnu@1.0.4': + '@napi-rs/nice-linux-s390x-gnu@1.1.1': optional: true - '@napi-rs/nice-linux-x64-gnu@1.0.4': + '@napi-rs/nice-linux-x64-gnu@1.1.1': optional: true - '@napi-rs/nice-linux-x64-musl@1.0.4': + '@napi-rs/nice-linux-x64-musl@1.1.1': optional: true - '@napi-rs/nice-win32-arm64-msvc@1.0.4': + '@napi-rs/nice-openharmony-arm64@1.1.1': optional: true - '@napi-rs/nice-win32-ia32-msvc@1.0.4': + '@napi-rs/nice-win32-arm64-msvc@1.1.1': optional: true - '@napi-rs/nice-win32-x64-msvc@1.0.4': + '@napi-rs/nice-win32-ia32-msvc@1.1.1': optional: true - '@napi-rs/nice@1.0.4': + '@napi-rs/nice-win32-x64-msvc@1.1.1': + optional: true + + '@napi-rs/nice@1.1.1': optionalDependencies: - '@napi-rs/nice-android-arm-eabi': 1.0.4 - '@napi-rs/nice-android-arm64': 1.0.4 - '@napi-rs/nice-darwin-arm64': 1.0.4 - '@napi-rs/nice-darwin-x64': 1.0.4 - '@napi-rs/nice-freebsd-x64': 1.0.4 - '@napi-rs/nice-linux-arm-gnueabihf': 1.0.4 - '@napi-rs/nice-linux-arm64-gnu': 1.0.4 - '@napi-rs/nice-linux-arm64-musl': 1.0.4 - '@napi-rs/nice-linux-ppc64-gnu': 1.0.4 - '@napi-rs/nice-linux-riscv64-gnu': 1.0.4 - '@napi-rs/nice-linux-s390x-gnu': 1.0.4 - '@napi-rs/nice-linux-x64-gnu': 1.0.4 - '@napi-rs/nice-linux-x64-musl': 1.0.4 - '@napi-rs/nice-win32-arm64-msvc': 1.0.4 - '@napi-rs/nice-win32-ia32-msvc': 1.0.4 - '@napi-rs/nice-win32-x64-msvc': 1.0.4 + '@napi-rs/nice-android-arm-eabi': 1.1.1 + '@napi-rs/nice-android-arm64': 1.1.1 + '@napi-rs/nice-darwin-arm64': 1.1.1 + '@napi-rs/nice-darwin-x64': 1.1.1 + '@napi-rs/nice-freebsd-x64': 1.1.1 + '@napi-rs/nice-linux-arm-gnueabihf': 1.1.1 + '@napi-rs/nice-linux-arm64-gnu': 1.1.1 + '@napi-rs/nice-linux-arm64-musl': 1.1.1 + '@napi-rs/nice-linux-ppc64-gnu': 1.1.1 + '@napi-rs/nice-linux-riscv64-gnu': 1.1.1 + '@napi-rs/nice-linux-s390x-gnu': 1.1.1 + '@napi-rs/nice-linux-x64-gnu': 1.1.1 + '@napi-rs/nice-linux-x64-musl': 1.1.1 + '@napi-rs/nice-openharmony-arm64': 1.1.1 + '@napi-rs/nice-win32-arm64-msvc': 1.1.1 + '@napi-rs/nice-win32-ia32-msvc': 1.1.1 + '@napi-rs/nice-win32-x64-msvc': 1.1.1 optional: true '@nodelib/fs.scandir@2.1.5': @@ -6296,9 +8764,9 @@ snapshots: dependencies: '@openai/zod': zod@3.25.67 debug: 4.4.1(supports-color@8.1.1) - openai: 5.10.1(ws@8.18.3)(zod@3.25.76) + openai: 5.20.0(ws@8.18.3)(zod@3.25.76) optionalDependencies: - '@modelcontextprotocol/sdk': 1.17.2 + '@modelcontextprotocol/sdk': 1.17.5 zod: 3.25.76 transitivePeerDependencies: - supports-color @@ -6320,7 +8788,7 @@ snapshots: '@openai/agents-core': 0.0.16(ws@8.18.3)(zod@3.25.76) '@openai/zod': zod@3.25.67 debug: 4.4.1(supports-color@8.1.1) - openai: 5.10.1(ws@8.18.3)(zod@3.25.76) + openai: 5.20.0(ws@8.18.3)(zod@3.25.76) transitivePeerDependencies: - supports-color - ws @@ -6345,7 +8813,7 @@ snapshots: '@openai/agents-openai': 0.0.16(ws@8.18.3)(zod@3.25.76) '@openai/agents-realtime': 0.0.16(zod@3.25.76) debug: 4.4.1(supports-color@8.1.1) - openai: 5.10.1(ws@8.18.3)(zod@3.25.76) + openai: 5.20.0(ws@8.18.3)(zod@3.25.76) transitivePeerDependencies: - bufferutil - supports-color @@ -6381,440 +8849,528 @@ snapshots: '@radix-ui/number@1.1.1': {} - '@radix-ui/primitive@1.1.2': {} + '@radix-ui/primitive@1.1.3': {} - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) - - '@radix-ui/react-checkbox@1.3.2(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.10)(react@18.3.1) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.12)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.10)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.10)(react@18.3.1)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.12)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 - '@radix-ui/react-context@1.1.2(@types/react@19.1.10)(react@18.3.1)': + '@radix-ui/react-context@1.1.2(@types/react@19.1.12)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 19.1.10 - - '@radix-ui/react-dialog@1.1.14(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.10)(react@18.3.1) + '@types/react': 19.1.12 + + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@18.3.1) aria-hidden: 1.2.6 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.7.1(@types/react@19.1.10)(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@19.1.12)(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) - '@radix-ui/react-direction@1.1.1(@types/react@19.1.10)(react@18.3.1)': + '@radix-ui/react-direction@1.1.1(@types/react@19.1.12)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 - '@radix-ui/react-dismissable-layer@1.1.10(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.10)(react@18.3.1) + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.12)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) - '@radix-ui/react-focus-guards@1.1.2(@types/react@19.1.10)(react@18.3.1)': + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.1.12)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.10)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) '@radix-ui/react-icons@1.3.2(react@18.3.1)': dependencies: react: 18.3.1 - '@radix-ui/react-id@1.1.1(@types/react@19.1.10)(react@18.3.1)': + '@radix-ui/react-id@1.1.1(@types/react@19.1.12)(react@18.3.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 - '@radix-ui/react-label@2.1.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-label@2.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) - - '@radix-ui/react-popover@1.1.14(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.10)(react@18.3.1) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@18.3.1) aria-hidden: 1.2.6 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.7.1(@types/react@19.1.10)(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@19.1.12)(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) - - '@radix-ui/react-popper@1.2.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@floating-ui/react-dom': 2.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.10)(react@18.3.1) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.12)(react@18.3.1) '@radix-ui/rect': 1.1.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) - '@radix-ui/react-presence@1.1.4(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.10)(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) - - '@radix-ui/react-roving-focus@1.1.10(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.10)(react@18.3.1) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) - '@radix-ui/react-select@2.2.5(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-select@2.2.6(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/number': 1.1.1 - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) aria-hidden: 1.2.6 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.7.1(@types/react@19.1.10)(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@19.1.12)(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) - '@radix-ui/react-slot@1.2.3(@types/react@19.1.10)(react@18.3.1)': + '@radix-ui/react-slot@1.2.3(@types/react@19.1.12)(react@18.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 19.1.10 - - '@radix-ui/react-tabs@1.1.12(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.10)(react@18.3.1) + '@types/react': 19.1.12 + + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) - - '@radix-ui/react-toast@1.2.14(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + + '@radix-ui/react-toast@1.2.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) - - '@radix-ui/react-tooltip@1.2.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.10)(react@18.3.1)': + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.12)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.10)(react@18.3.1)': + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.12)(react@18.3.1)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@18.3.1) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.10)(react@18.3.1)': + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.12)(react@18.3.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.10)(react@18.3.1)': + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.12)(react@18.3.1)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.10)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.10)(react@18.3.1)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.12)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 - '@radix-ui/react-use-previous@1.1.1(@types/react@19.1.10)(react@18.3.1)': + '@radix-ui/react-use-previous@1.1.1(@types/react@19.1.12)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.1.10)(react@18.3.1)': + '@radix-ui/react-use-rect@1.1.1(@types/react@19.1.12)(react@18.3.1)': dependencies: '@radix-ui/rect': 1.1.1 react: 18.3.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 - '@radix-ui/react-use-size@1.1.1(@types/react@19.1.10)(react@18.3.1)': + '@radix-ui/react-use-size@1.1.1(@types/react@19.1.12)(react@18.3.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 - '@types/react-dom': 19.1.6(@types/react@19.1.10) + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) '@radix-ui/rect@1.1.1': {} - '@rollup/rollup-android-arm-eabi@4.45.1': + '@rc-component/async-validator@5.0.4': + dependencies: + '@babel/runtime': 7.28.4 + + '@rc-component/color-picker@2.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@ant-design/fast-color': 2.0.6 + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/context@1.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.4 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/mini-decimal@1.1.0': + dependencies: + '@babel/runtime': 7.28.4 + + '@rc-component/mutate-observer@1.1.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/portal@1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/qrcode@1.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/tour@1.15.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/portal': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/trigger': 2.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/trigger@2.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/portal': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-resize-observer: 1.4.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@rc-component/util@1.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + is-mobile: 5.0.0 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-is: 18.3.1 + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/pluginutils@5.3.0(rollup@4.50.1)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.50.1 + + '@rollup/rollup-android-arm-eabi@4.50.1': optional: true - '@rollup/rollup-android-arm64@4.45.1': + '@rollup/rollup-android-arm64@4.50.1': optional: true - '@rollup/rollup-darwin-arm64@4.45.1': + '@rollup/rollup-darwin-arm64@4.50.1': optional: true - '@rollup/rollup-darwin-x64@4.45.1': + '@rollup/rollup-darwin-x64@4.50.1': optional: true - '@rollup/rollup-freebsd-arm64@4.45.1': + '@rollup/rollup-freebsd-arm64@4.50.1': optional: true - '@rollup/rollup-freebsd-x64@4.45.1': + '@rollup/rollup-freebsd-x64@4.50.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.45.1': + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.45.1': + '@rollup/rollup-linux-arm-musleabihf@4.50.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.45.1': + '@rollup/rollup-linux-arm64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.45.1': + '@rollup/rollup-linux-arm64-musl@4.50.1': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.45.1': + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': + '@rollup/rollup-linux-ppc64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.45.1': + '@rollup/rollup-linux-riscv64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.45.1': + '@rollup/rollup-linux-riscv64-musl@4.50.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.45.1': + '@rollup/rollup-linux-s390x-gnu@4.50.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.45.1': + '@rollup/rollup-linux-x64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-x64-musl@4.45.1': + '@rollup/rollup-linux-x64-musl@4.50.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.45.1': + '@rollup/rollup-openharmony-arm64@4.50.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.45.1': + '@rollup/rollup-win32-arm64-msvc@4.50.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.45.1': + '@rollup/rollup-win32-ia32-msvc@4.50.1': optional: true - '@rushstack/node-core-library@5.14.0(@types/node@24.2.1)': + '@rollup/rollup-win32-x64-msvc@4.50.1': + optional: true + + '@rushstack/node-core-library@5.14.0(@types/node@24.3.1)': dependencies: ajv: 8.13.0 ajv-draft-04: 1.0.0(ajv@8.13.0) @@ -6825,23 +9381,23 @@ snapshots: resolve: 1.22.10 semver: 7.5.4 optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.3.1 '@rushstack/rig-package@0.5.3': dependencies: resolve: 1.22.10 strip-json-comments: 3.1.1 - '@rushstack/terminal@0.15.4(@types/node@24.2.1)': + '@rushstack/terminal@0.15.4(@types/node@24.3.1)': dependencies: - '@rushstack/node-core-library': 5.14.0(@types/node@24.2.1) + '@rushstack/node-core-library': 5.14.0(@types/node@24.3.1) supports-color: 8.1.1 optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.3.1 - '@rushstack/ts-command-line@5.0.2(@types/node@24.2.1)': + '@rushstack/ts-command-line@5.0.2(@types/node@24.3.1)': dependencies: - '@rushstack/terminal': 0.15.4(@types/node@24.2.1) + '@rushstack/terminal': 0.15.4(@types/node@24.3.1) '@types/argparse': 1.0.38 argparse: 1.0.10 string-argv: 0.3.2 @@ -6850,81 +9406,119 @@ snapshots: '@sec-ant/readable-stream@0.4.1': {} - '@secretlint/config-creator@10.2.0': + '@secretlint/config-creator@10.2.2': dependencies: - '@secretlint/types': 10.2.0 + '@secretlint/types': 10.2.2 - '@secretlint/config-loader@10.2.0': + '@secretlint/config-loader@10.2.2': dependencies: - '@secretlint/profiler': 10.2.0 - '@secretlint/resolver': 10.2.0 - '@secretlint/types': 10.2.0 + '@secretlint/profiler': 10.2.2 + '@secretlint/resolver': 10.2.2 + '@secretlint/types': 10.2.2 ajv: 8.17.1 debug: 4.4.1(supports-color@8.1.1) rc-config-loader: 4.1.3 transitivePeerDependencies: - supports-color - '@secretlint/core@10.2.0': + '@secretlint/core@10.2.2': dependencies: - '@secretlint/profiler': 10.2.0 - '@secretlint/types': 10.2.0 + '@secretlint/profiler': 10.2.2 + '@secretlint/types': 10.2.2 debug: 4.4.1(supports-color@8.1.1) structured-source: 4.0.0 transitivePeerDependencies: - supports-color - '@secretlint/formatter@10.2.0': + '@secretlint/formatter@10.2.2': dependencies: - '@secretlint/resolver': 10.2.0 - '@secretlint/types': 10.2.0 - '@textlint/linter-formatter': 15.2.0 - '@textlint/module-interop': 15.2.0 - '@textlint/types': 15.2.0 - chalk: 5.5.0 + '@secretlint/resolver': 10.2.2 + '@secretlint/types': 10.2.2 + '@textlint/linter-formatter': 15.2.2 + '@textlint/module-interop': 15.2.2 + '@textlint/types': 15.2.2 + chalk: 5.6.2 debug: 4.4.1(supports-color@8.1.1) pluralize: 8.0.0 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 table: 6.9.0 terminal-link: 4.0.0 transitivePeerDependencies: - supports-color - '@secretlint/node@10.2.0': + '@secretlint/node@10.2.2': dependencies: - '@secretlint/config-loader': 10.2.0 - '@secretlint/core': 10.2.0 - '@secretlint/formatter': 10.2.0 - '@secretlint/profiler': 10.2.0 - '@secretlint/source-creator': 10.2.0 - '@secretlint/types': 10.2.0 + '@secretlint/config-loader': 10.2.2 + '@secretlint/core': 10.2.2 + '@secretlint/formatter': 10.2.2 + '@secretlint/profiler': 10.2.2 + '@secretlint/source-creator': 10.2.2 + '@secretlint/types': 10.2.2 debug: 4.4.1(supports-color@8.1.1) p-map: 7.0.3 transitivePeerDependencies: - supports-color - '@secretlint/profiler@10.2.0': {} + '@secretlint/profiler@10.2.2': {} - '@secretlint/resolver@10.2.0': {} + '@secretlint/resolver@10.2.2': {} - '@secretlint/secretlint-formatter-sarif@10.2.0': + '@secretlint/secretlint-formatter-sarif@10.2.2': dependencies: node-sarif-builder: 3.2.0 - '@secretlint/secretlint-rule-no-dotenv@10.2.0': + '@secretlint/secretlint-rule-no-dotenv@10.2.2': dependencies: - '@secretlint/types': 10.2.0 + '@secretlint/types': 10.2.2 - '@secretlint/secretlint-rule-preset-recommend@10.2.0': {} + '@secretlint/secretlint-rule-preset-recommend@10.2.2': {} - '@secretlint/source-creator@10.2.0': + '@secretlint/source-creator@10.2.2': dependencies: - '@secretlint/types': 10.2.0 + '@secretlint/types': 10.2.2 istextorbinary: 9.5.0 - '@secretlint/types@10.2.0': {} + '@secretlint/types@10.2.2': {} + + '@shikijs/core@3.13.0': + dependencies: + '@shikijs/types': 3.13.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.13.0': + dependencies: + '@shikijs/types': 3.13.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.3 + + '@shikijs/engine-oniguruma@3.13.0': + dependencies: + '@shikijs/types': 3.13.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.13.0': + dependencies: + '@shikijs/types': 3.13.0 + + '@shikijs/themes@3.13.0': + dependencies: + '@shikijs/types': 3.13.0 + + '@shikijs/transformers@3.13.0': + dependencies: + '@shikijs/core': 3.13.0 + '@shikijs/types': 3.13.0 + + '@shikijs/types@3.13.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} - '@sinclair/typebox@0.34.38': {} + '@sinclair/typebox@0.34.41': {} '@sindresorhus/is@4.6.0': {} @@ -6944,6 +9538,76 @@ snapshots: '@standard-schema/spec@1.0.0': {} + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-preset@8.1.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.28.4) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.28.4) + + '@svgr/core@8.1.0(typescript@5.8.3)': + dependencies: + '@babel/core': 7.28.4 + '@svgr/babel-preset': 8.1.0(@babel/core@7.28.4) + camelcase: 6.3.0 + cosmiconfig: 8.3.6(typescript@5.8.3) + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@svgr/hast-util-to-babel-ast@8.0.0': + dependencies: + '@babel/types': 7.28.4 + entities: 4.5.0 + + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.8.3))': + dependencies: + '@babel/core': 7.28.4 + '@svgr/babel-preset': 8.1.0(@babel/core@7.28.4) + '@svgr/core': 8.1.0(typescript@5.8.3) + '@svgr/hast-util-to-babel-ast': 8.0.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + '@swc/core-darwin-arm64@1.12.1': optional: true @@ -6977,7 +9641,7 @@ snapshots: '@swc/core@1.12.1': dependencies: '@swc/counter': 0.1.3 - '@swc/types': 0.1.24 + '@swc/types': 0.1.25 optionalDependencies: '@swc/core-darwin-arm64': 1.12.1 '@swc/core-darwin-x64': 1.12.1 @@ -6994,24 +9658,229 @@ snapshots: '@swc/counter@0.1.3': optional: true - '@swc/helpers@0.5.1': + '@swc/helpers@0.5.1': + dependencies: + tslib: 2.8.1 + + '@swc/types@0.1.25': + dependencies: + '@swc/counter': 0.1.3 + optional: true + + '@tailwindcss/node@4.1.13': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.3 + jiti: 2.5.1 + lightningcss: 1.30.1 + magic-string: 0.30.19 + source-map-js: 1.2.1 + tailwindcss: 4.1.13 + + '@tailwindcss/oxide-android-arm64@4.1.13': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.13': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.13': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.13': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.13': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.13': + optional: true + + '@tailwindcss/oxide@4.1.13': + dependencies: + detect-libc: 2.0.4 + tar: 7.4.3 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.13 + '@tailwindcss/oxide-darwin-arm64': 4.1.13 + '@tailwindcss/oxide-darwin-x64': 4.1.13 + '@tailwindcss/oxide-freebsd-x64': 4.1.13 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.13 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.13 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.13 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.13 + '@tailwindcss/oxide-linux-x64-musl': 4.1.13 + '@tailwindcss/oxide-wasm32-wasi': 4.1.13 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.13 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.13 + + '@tailwindcss/typography@0.5.19(tailwindcss@4.1.13)': + dependencies: + postcss-selector-parser: 6.0.10 + tailwindcss: 4.1.13 + + '@tailwindcss/vite@4.1.13(vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1))': + dependencies: + '@tailwindcss/node': 4.1.13 + '@tailwindcss/oxide': 4.1.13 + tailwindcss: 4.1.13 + vite: 6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) + + '@tanstack/history@1.132.21': {} + + '@tanstack/react-router-devtools@1.132.23(@tanstack/react-router@1.132.23(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.132.21)(@types/node@24.3.1)(csstype@3.1.3)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.9)(tiny-invariant@1.3.3)(tsx@4.20.5)(yaml@2.8.1)': + dependencies: + '@tanstack/react-router': 1.132.23(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@tanstack/router-devtools-core': 1.132.21(@tanstack/router-core@1.132.21)(@types/node@24.3.1)(csstype@3.1.3)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(solid-js@1.9.9)(tiny-invariant@1.3.3)(tsx@4.20.5)(yaml@2.8.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + vite: 7.1.7(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) + transitivePeerDependencies: + - '@tanstack/router-core' + - '@types/node' + - csstype + - jiti + - less + - lightningcss + - sass + - sass-embedded + - solid-js + - stylus + - sugarss + - terser + - tiny-invariant + - tsx + - yaml + + '@tanstack/react-router@1.132.23(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@tanstack/history': 1.132.21 + '@tanstack/react-store': 0.7.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@tanstack/router-core': 1.132.21 + isbot: 5.1.31 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + + '@tanstack/react-store@0.7.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@tanstack/store': 0.7.7 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + use-sync-external-store: 1.5.0(react@19.1.1) + + '@tanstack/router-core@1.132.21': + dependencies: + '@tanstack/history': 1.132.21 + '@tanstack/store': 0.7.7 + cookie-es: 2.0.0 + seroval: 1.3.2 + seroval-plugins: 1.3.3(seroval@1.3.2) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + + '@tanstack/router-devtools-core@1.132.21(@tanstack/router-core@1.132.21)(@types/node@24.3.1)(csstype@3.1.3)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(solid-js@1.9.9)(tiny-invariant@1.3.3)(tsx@4.20.5)(yaml@2.8.1)': + dependencies: + '@tanstack/router-core': 1.132.21 + clsx: 2.1.1 + goober: 2.1.16(csstype@3.1.3) + solid-js: 1.9.9 + tiny-invariant: 1.3.3 + vite: 7.1.7(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) + optionalDependencies: + csstype: 3.1.3 + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + + '@tanstack/router-generator@1.132.21': + dependencies: + '@tanstack/router-core': 1.132.21 + '@tanstack/router-utils': 1.132.21 + '@tanstack/virtual-file-routes': 1.132.21 + prettier: 3.6.2 + recast: 0.23.11 + source-map: 0.7.6 + tsx: 4.20.5 + zod: 3.25.76 + transitivePeerDependencies: + - supports-color + + '@tanstack/router-plugin@1.132.23(@tanstack/react-router@1.132.23(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1))': + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4) + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@tanstack/router-core': 1.132.21 + '@tanstack/router-generator': 1.132.21 + '@tanstack/router-utils': 1.132.21 + '@tanstack/virtual-file-routes': 1.132.21 + babel-dead-code-elimination: 1.0.10 + chokidar: 3.6.0 + unplugin: 2.3.10 + zod: 3.25.76 + optionalDependencies: + '@tanstack/react-router': 1.132.23(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + vite: 6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + + '@tanstack/router-utils@1.132.21': dependencies: - tslib: 2.8.1 + '@babel/core': 7.28.4 + '@babel/generator': 7.28.3 + '@babel/parser': 7.28.4 + '@babel/preset-typescript': 7.27.1(@babel/core@7.28.4) + ansis: 4.2.0 + diff: 8.0.2 + fast-glob: 3.3.3 + pathe: 2.0.3 + transitivePeerDependencies: + - supports-color - '@swc/types@0.1.24': - dependencies: - '@swc/counter': 0.1.3 - optional: true + '@tanstack/store@0.7.7': {} + + '@tanstack/virtual-file-routes@1.132.21': {} - '@textlint/ast-node-types@15.2.0': {} + '@textlint/ast-node-types@15.2.2': {} - '@textlint/linter-formatter@15.2.0': + '@textlint/linter-formatter@15.2.2': dependencies: '@azu/format-text': 1.0.2 '@azu/style-format': 1.0.1 - '@textlint/module-interop': 15.2.0 - '@textlint/resolver': 15.2.0 - '@textlint/types': 15.2.0 + '@textlint/module-interop': 15.2.2 + '@textlint/resolver': 15.2.2 + '@textlint/types': 15.2.2 chalk: 4.1.2 debug: 4.4.1(supports-color@8.1.1) js-yaml: 3.14.1 @@ -7024,13 +9893,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@textlint/module-interop@15.2.0': {} + '@textlint/module-interop@15.2.2': {} - '@textlint/resolver@15.2.0': {} + '@textlint/resolver@15.2.2': {} - '@textlint/types@15.2.0': + '@textlint/types@15.2.2': dependencies: - '@textlint/ast-node-types': 15.2.0 + '@textlint/ast-node-types': 15.2.2 '@tokenizer/inflate@0.2.7': dependencies: @@ -7054,14 +9923,30 @@ snapshots: '@types/argparse@1.0.38': {} - '@types/body-parser@1.19.6': + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.28.4 + + '@types/babel__template@7.4.4': dependencies: - '@types/connect': 3.4.38 - '@types/node': 24.2.1 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 - '@types/bun@1.2.21(@types/react@19.1.10)': + '@types/babel__traverse@7.28.0': dependencies: - bun-types: 1.2.21(@types/react@19.1.10) + '@babel/types': 7.28.4 + + '@types/bun@1.2.21(@types/react@19.1.12)': + dependencies: + bun-types: 1.2.21(@types/react@19.1.12) transitivePeerDependencies: - '@types/react' @@ -7071,9 +9956,122 @@ snapshots: dependencies: '@types/deep-eql': 4.0.2 - '@types/connect@3.4.38': + '@types/d3-array@3.2.2': {} + + '@types/d3-axis@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-brush@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-chord@3.0.6': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.2 + '@types/geojson': 7946.0.16 + + '@types/d3-delaunay@6.0.4': {} + + '@types/d3-dispatch@3.0.7': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-dsv@3.0.7': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + + '@types/d3-force@3.0.10': {} + + '@types/d3-format@3.0.4': {} + + '@types/d3-geo@3.1.0': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-polygon@3.0.2': {} + + '@types/d3-quadtree@3.0.6': {} + + '@types/d3-random@3.0.3': {} + + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@3.1.7': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time-format@4.0.3': {} + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/d3@7.4.3': dependencies: - '@types/node': 24.2.1 + '@types/d3-array': 3.2.2 + '@types/d3-axis': 3.0.6 + '@types/d3-brush': 3.0.6 + '@types/d3-chord': 3.0.6 + '@types/d3-color': 3.1.3 + '@types/d3-contour': 3.0.6 + '@types/d3-delaunay': 6.0.4 + '@types/d3-dispatch': 3.0.7 + '@types/d3-drag': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.0 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-polygon': 3.0.2 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.3 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-selection': 3.0.11 + '@types/d3-shape': 3.1.7 + '@types/d3-time': 3.0.4 + '@types/d3-time-format': 4.0.3 + '@types/d3-timer': 3.0.2 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 '@types/debug@4.1.12': dependencies: @@ -7083,27 +10081,32 @@ snapshots: '@types/diff-match-patch@1.0.36': {} - '@types/estree@1.0.8': {} + '@types/diff@5.2.3': {} - '@types/express-serve-static-core@5.0.7': + '@types/estree-jsx@1.0.5': dependencies: - '@types/node': 24.2.1 - '@types/qs': 6.14.0 - '@types/range-parser': 1.2.7 - '@types/send': 0.17.5 + '@types/estree': 1.0.8 + + '@types/estree@1.0.8': {} + + '@types/geojson@7946.0.16': {} - '@types/express@5.0.3': + '@types/hast@2.3.10': dependencies: - '@types/body-parser': 1.19.6 - '@types/express-serve-static-core': 5.0.7 - '@types/serve-static': 1.15.8 + '@types/unist': 2.0.11 - '@types/http-errors@2.0.5': {} + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 '@types/istanbul-lib-coverage@2.0.6': {} + '@types/js-cookie@3.0.6': {} + '@types/js-yaml@4.0.9': {} + '@types/katex@0.16.7': {} + '@types/lodash-es@4.17.12': dependencies: '@types/lodash': 4.17.20 @@ -7113,32 +10116,47 @@ snapshots: '@types/marked-terminal@6.1.1': dependencies: '@types/cardinal': 2.1.1 - '@types/node': 24.2.1 - chalk: 5.5.0 + '@types/node': 24.3.1 + chalk: 5.6.2 marked: 11.2.0 - '@types/mime@1.3.5': {} + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 '@types/mocha@10.0.10': {} '@types/ms@2.1.0': {} - '@types/node@24.2.1': + '@types/node@24.3.1': dependencies: undici-types: 7.10.0 '@types/normalize-package-data@2.4.4': {} - '@types/qs@6.14.0': {} + '@types/parse-json@4.0.2': {} - '@types/range-parser@1.2.7': {} + '@types/prismjs@1.26.5': {} - '@types/react-dom@19.1.6(@types/react@19.1.10)': + '@types/react-dom@19.1.9(@types/react@19.1.12)': dependencies: - '@types/react': 19.1.10 - optional: true + '@types/react': 19.1.12 + + '@types/react-i18next@8.1.0(i18next@25.5.2(typescript@5.8.3))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.8.3)': + dependencies: + react-i18next: 15.7.4(i18next@25.5.2(typescript@5.8.3))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.8.3) + transitivePeerDependencies: + - i18next + - react + - react-dom + - react-native + - typescript + + '@types/react-syntax-highlighter@15.5.13': + dependencies: + '@types/react': 19.1.12 - '@types/react@19.1.10': + '@types/react@19.1.12': dependencies: csstype: 3.1.3 @@ -7146,32 +10164,30 @@ snapshots: '@types/sarif@2.1.7': {} - '@types/send@0.17.5': - dependencies: - '@types/mime': 1.3.5 - '@types/node': 24.2.1 + '@types/tinycolor2@1.4.6': {} - '@types/serve-static@1.15.8': - dependencies: - '@types/http-errors': 2.0.5 - '@types/node': 24.2.1 - '@types/send': 0.17.5 + '@types/trusted-types@1.0.6': {} - '@types/tinycolor2@1.4.6': {} + '@types/trusted-types@2.0.7': + optional: true '@types/turndown@5.0.5': {} - '@types/vscode@1.102.0': {} + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@types/vscode@1.103.0': {} '@types/ws@8.18.1': dependencies: - '@types/node': 24.2.1 + '@types/node': 24.3.1 '@types/yargs-parser@21.0.3': {} '@types/yauzl@2.10.3': dependencies: - '@types/node': 24.2.1 + '@types/node': 24.3.1 optional: true '@typespec/ts-http-runtime@0.3.0': @@ -7219,15 +10235,15 @@ snapshots: '@swc/helpers': 0.5.1 '@types/resolve': 1.20.6 chalk: 4.1.2 - enhanced-resolve: 5.18.2 - less: 4.4.0 - less-loader: 12.3.0(less@4.4.0) + enhanced-resolve: 5.18.3 + less: 4.4.1 + less-loader: 12.3.0(less@4.4.1) loader-runner: 4.3.0 loader-utils: 3.3.1 lodash: 4.17.21 node-libs-browser-okam: 2.2.5 piscina: 4.9.2 - postcss-loader: 8.1.1(postcss@8.5.6)(typescript@5.9.2) + postcss-loader: 8.2.0(postcss@8.5.6)(typescript@5.9.2) react-error-overlay: 6.0.9 react-refresh: 0.14.2 resolve: 1.22.10 @@ -7255,7 +10271,7 @@ snapshots: '@umijs/tools@0.1.36(postcss@8.5.6)(typescript@5.9.2)': dependencies: '@umijs/mako': 0.11.13(postcss@8.5.6)(typescript@5.9.2) - zx: 8.7.1 + zx: 8.8.1 transitivePeerDependencies: - '@rspack/core' - node-sass @@ -7265,21 +10281,35 @@ snapshots: - typescript - webpack + '@ungap/structured-clone@1.3.0': {} + + '@vitejs/plugin-react@4.7.0(vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1))': + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + '@vitest/expect@3.2.4': dependencies: '@types/chai': 5.2.2 '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 - chai: 5.2.1 + chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.0.5(@types/node@24.2.1)(jiti@2.5.1)(less@4.4.0)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 - magic-string: 0.30.17 + magic-string: 0.30.19 optionalDependencies: - vite: 7.0.5(@types/node@24.2.1)(jiti@2.5.1)(less@4.4.0)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.1) + vite: 6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) '@vitest/pretty-format@3.2.4': dependencies: @@ -7294,7 +10324,7 @@ snapshots: '@vitest/snapshot@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 - magic-string: 0.30.17 + magic-string: 0.30.19 pathe: 2.0.3 '@vitest/spy@3.2.4': @@ -7304,7 +10334,7 @@ snapshots: '@vitest/utils@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 - loupe: 3.1.4 + loupe: 3.2.1 tinyrainbow: 2.0.0 '@vscode/test-cli@0.0.11': @@ -7312,10 +10342,10 @@ snapshots: '@types/mocha': 10.0.10 c8: 9.1.0 chokidar: 3.6.0 - enhanced-resolve: 5.18.2 + enhanced-resolve: 5.18.3 glob: 10.4.5 minimatch: 9.0.5 - mocha: 11.7.1 + mocha: 11.7.2 supports-color: 9.4.0 yargs: 17.7.2 @@ -7370,18 +10400,18 @@ snapshots: '@vscode/vsce@3.6.0': dependencies: - '@azure/identity': 4.10.2 - '@secretlint/node': 10.2.0 - '@secretlint/secretlint-formatter-sarif': 10.2.0 - '@secretlint/secretlint-rule-no-dotenv': 10.2.0 - '@secretlint/secretlint-rule-preset-recommend': 10.2.0 + '@azure/identity': 4.11.1 + '@secretlint/node': 10.2.2 + '@secretlint/secretlint-formatter-sarif': 10.2.2 + '@secretlint/secretlint-rule-no-dotenv': 10.2.2 + '@secretlint/secretlint-rule-preset-recommend': 10.2.2 '@vscode/vsce-sign': 2.0.6 azure-devops-node-api: 12.5.0 chalk: 4.1.2 - cheerio: 1.1.0 + cheerio: 1.1.2 cockatiel: 3.2.1 commander: 12.1.0 - form-data: 4.0.3 + form-data: 4.0.4 glob: 11.0.3 hosted-git-info: 4.1.0 jsonc-parser: 3.3.1 @@ -7391,9 +10421,9 @@ snapshots: minimatch: 3.1.2 parse-semver: 1.1.1 read: 1.0.7 - secretlint: 10.2.0 + secretlint: 10.2.2 semver: 7.7.2 - tmp: 0.2.3 + tmp: 0.2.5 typed-rest-client: 1.8.11 url-join: 4.0.1 xml2js: 0.5.0 @@ -7428,6 +10458,21 @@ snapshots: agent-base@7.1.4: {} + ahooks@3.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + '@types/js-cookie': 3.0.6 + dayjs: 1.11.18 + intersection-observer: 0.12.2 + js-cookie: 3.0.5 + lodash: 4.17.21 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-fast-compare: 3.2.2 + resize-observer-polyfill: 1.5.1 + screenfull: 5.2.0 + tslib: 2.8.1 + ai@4.3.19(react@19.1.1)(zod@3.25.76): dependencies: '@ai-sdk/provider': 1.1.3 @@ -7476,7 +10521,7 @@ snapshots: ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.0.6 + fast-uri: 3.1.0 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -7486,13 +10531,90 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.1.0: {} + ansi-regex@6.2.2: {} ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.1: {} + ansi-styles@6.2.3: {} + + ansis@4.2.0: {} + + antd-style@3.7.1(@types/react@19.1.12)(antd@5.27.4(date-fns@4.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@ant-design/cssinjs': 1.24.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@babel/runtime': 7.28.4 + '@emotion/cache': 11.14.0 + '@emotion/css': 11.13.5 + '@emotion/react': 11.14.0(@types/react@19.1.12)(react@19.1.1) + '@emotion/serialize': 1.3.3 + '@emotion/utils': 1.4.2 + antd: 5.27.4(date-fns@4.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + use-merge-value: 1.2.0(react@19.1.1) + transitivePeerDependencies: + - '@types/react' + - react-dom + - supports-color + + antd@5.27.4(date-fns@4.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@ant-design/colors': 7.2.1 + '@ant-design/cssinjs': 1.24.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@ant-design/cssinjs-utils': 1.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@ant-design/fast-color': 2.0.6 + '@ant-design/icons': 5.6.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@ant-design/react-slick': 1.1.2(react@19.1.1) + '@babel/runtime': 7.28.4 + '@rc-component/color-picker': 2.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/mutate-observer': 1.1.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/qrcode': 1.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/tour': 1.15.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@rc-component/trigger': 2.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + copy-to-clipboard: 3.3.3 + dayjs: 1.11.18 + rc-cascader: 3.34.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-checkbox: 3.5.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-collapse: 3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-dialog: 9.6.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-drawer: 7.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-dropdown: 4.2.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-field-form: 2.7.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-image: 7.12.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-input: 1.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-input-number: 9.5.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-mentions: 2.20.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-menu: 9.16.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-motion: 2.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-notification: 5.6.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-pagination: 5.1.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-picker: 4.11.3(date-fns@4.1.0)(dayjs@1.11.18)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-progress: 4.0.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-rate: 2.13.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-resize-observer: 1.4.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-segmented: 2.7.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-select: 14.16.8(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-slider: 11.1.9(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-steps: 6.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-switch: 4.1.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-table: 7.53.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-tabs: 15.7.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-textarea: 1.10.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-tooltip: 6.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-tree: 5.13.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-tree-select: 5.27.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-upload: 4.9.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + scroll-into-view-if-needed: 3.1.0 + throttle-debounce: 5.0.2 + transitivePeerDependencies: + - date-fns + - luxon + - moment any-promise@1.3.0: {} @@ -7532,6 +10654,10 @@ snapshots: dependencies: tslib: 2.8.1 + ast-types@0.16.1: + dependencies: + tslib: 2.8.1 + astral-regex@2.0.0: {} async@3.2.6: {} @@ -7551,6 +10677,14 @@ snapshots: '@fastify/error': 4.2.0 fastq: 1.19.1 + axios@1.10.0(debug@4.4.1): + dependencies: + follow-redirects: 1.15.11(debug@4.4.1) + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + azure-devops-node-api@12.5.0: dependencies: tunnel: 0.0.6 @@ -7558,6 +10692,23 @@ snapshots: b4a@1.7.3: {} + babel-dead-code-elimination@1.0.10: + dependencies: + '@babel/core': 7.28.4 + '@babel/parser': 7.28.4 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.28.4 + cosmiconfig: 7.1.0 + resolve: 1.22.10 + + bail@2.0.2: {} + balanced-match@1.0.2: {} bare-events@2.7.0: {} @@ -7597,13 +10748,15 @@ snapshots: base64-js@1.5.1: {} + baseline-browser-mapping@2.8.9: {} + basic-ftp@5.0.5: {} binary-extensions@2.3.0: {} binaryextensions@6.11.0: dependencies: - editions: 6.21.0 + editions: 6.22.0 bl@4.1.0: dependencies: @@ -7642,7 +10795,7 @@ snapshots: iconv-lite: 0.6.3 on-finished: 2.4.1 qs: 6.14.0 - raw-body: 3.0.0 + raw-body: 3.0.1 type-is: 2.0.1 transitivePeerDependencies: - supports-color @@ -7713,6 +10866,14 @@ snapshots: dependencies: pako: 1.0.11 + browserslist@4.26.2: + dependencies: + baseline-browser-mapping: 2.8.9 + caniuse-lite: 1.0.30001745 + electron-to-chromium: 1.5.227 + node-releases: 2.0.21 + update-browserslist-db: 1.1.3(browserslist@4.26.2) + buffer-crc32@0.2.13: {} buffer-equal-constant-time@1.0.1: {} @@ -7740,10 +10901,10 @@ snapshots: builtin-status-codes@3.0.0: {} - bun-types@1.2.21(@types/react@19.1.10): + bun-types@1.2.21(@types/react@19.1.12): dependencies: - '@types/node': 24.2.1 - '@types/react': 19.1.10 + '@types/node': 24.3.1 + '@types/react': 19.1.12 bundle-name@4.1.0: dependencies: @@ -7761,7 +10922,7 @@ snapshots: foreground-child: 3.3.1 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 - istanbul-reports: 3.1.7 + istanbul-reports: 3.2.0 test-exclude: 6.0.0 v8-to-istanbul: 9.3.0 yargs: 17.7.2 @@ -7790,12 +10951,16 @@ snapshots: camelcase@6.3.0: {} - chai@5.2.1: + caniuse-lite@1.0.30001745: {} + + ccount@2.0.1: {} + + chai@5.3.3: dependencies: assertion-error: 2.0.1 check-error: 2.1.1 deep-eql: 5.0.2 - loupe: 3.1.4 + loupe: 3.2.1 pathval: 2.0.1 chalk@4.1.2: @@ -7803,10 +10968,24 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.5.0: {} + chalk@5.6.2: {} char-regex@1.0.2: {} + character-entities-html4@2.1.0: {} + + character-entities-legacy@1.1.4: {} + + character-entities-legacy@3.0.0: {} + + character-entities@1.2.4: {} + + character-entities@2.0.2: {} + + character-reference-invalid@1.1.4: {} + + character-reference-invalid@2.0.1: {} + chardet@2.1.0: {} check-error@2.1.1: {} @@ -7820,7 +10999,7 @@ snapshots: domhandler: 5.0.3 domutils: 3.2.2 - cheerio@1.1.0: + cheerio@1.1.2: dependencies: cheerio-select: 2.1.0 dom-serializer: 2.0.0 @@ -7831,9 +11010,23 @@ snapshots: parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 parse5-parser-stream: 7.1.2 - undici: 7.11.0 + undici: 7.15.0 whatwg-mimetype: 4.0.0 + chevrotain-allstar@0.3.1(chevrotain@11.0.3): + dependencies: + chevrotain: 11.0.3 + lodash-es: 4.17.21 + + chevrotain@11.0.3: + dependencies: + '@chevrotain/cst-dts-gen': 11.0.3 + '@chevrotain/gast': 11.0.3 + '@chevrotain/regexp-to-ast': 11.0.3 + '@chevrotain/types': 11.0.3 + '@chevrotain/utils': 11.0.3 + lodash-es: 4.17.21 + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -7884,6 +11077,8 @@ snapshots: dependencies: clsx: 2.1.1 + classnames@2.5.1: {} + cli-boxes@3.0.0: {} cli-cursor@4.0.0: @@ -7937,17 +11132,17 @@ snapshots: cliui@9.0.1: dependencies: string-width: 7.2.0 - strip-ansi: 7.1.0 - wrap-ansi: 9.0.0 + strip-ansi: 7.1.2 + wrap-ansi: 9.0.2 clsx@2.1.1: {} - cmdk@1.1.1(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + cmdk@1.1.1(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-dialog': 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.10)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: @@ -7972,24 +11167,37 @@ snapshots: dependencies: delayed-stream: 1.0.0 + comma-separated-tokens@1.0.8: {} + + comma-separated-tokens@2.0.3: {} + commander@12.1.0: {} commander@13.1.0: {} commander@14.0.0: {} + commander@7.2.0: {} + + commander@8.3.0: {} + + compute-scroll-into-view@3.1.1: {} + concat-map@0.0.1: {} - concurrently@9.2.0: + concurrently@9.2.1: dependencies: chalk: 4.1.2 - lodash: 4.17.21 rxjs: 7.8.2 shell-quote: 1.8.3 supports-color: 8.1.1 tree-kill: 1.2.2 yargs: 17.7.2 + confbox@0.1.8: {} + + confbox@0.2.2: {} + console-browserify@1.2.0: {} constants-browserify@1.0.0: {} @@ -8006,10 +11214,14 @@ snapshots: content-type@1.0.5: {} + convert-source-map@1.9.0: {} + convert-source-map@2.0.0: {} convert-to-spaces@2.0.1: {} + cookie-es@2.0.0: {} + cookie-signature@1.0.6: {} cookie-signature@1.2.2: {} @@ -8024,6 +11236,10 @@ snapshots: dependencies: is-what: 3.14.1 + copy-to-clipboard@3.3.3: + dependencies: + toggle-selection: 1.0.6 + core-js@3.45.1: {} core-util-is@1.0.3: {} @@ -8033,6 +11249,31 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 + cose-base@1.0.3: + dependencies: + layout-base: 1.0.2 + + cose-base@2.2.0: + dependencies: + layout-base: 2.0.1 + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + cosmiconfig@8.3.6(typescript@5.8.3): + dependencies: + import-fresh: 3.3.1 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.8.3 + cosmiconfig@9.0.0(typescript@5.9.2): dependencies: env-paths: 2.2.1 @@ -8104,12 +11345,200 @@ snapshots: css-what@6.2.2: {} + cssesc@3.0.0: {} + csstype@3.1.3: {} + cytoscape-cose-bilkent@4.1.0(cytoscape@3.33.1): + dependencies: + cose-base: 1.0.3 + cytoscape: 3.33.1 + + cytoscape-fcose@2.2.0(cytoscape@3.33.1): + dependencies: + cose-base: 2.2.0 + cytoscape: 3.33.1 + + cytoscape@3.33.1: {} + + d3-array@2.12.1: + dependencies: + internmap: 1.0.1 + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.0.1 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.0: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@1.0.9: {} + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-sankey@0.12.3: + dependencies: + d3-array: 2.12.1 + d3-shape: 1.3.7 + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.0 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + + dagre-d3-es@7.0.11: + dependencies: + d3: 7.9.0 + lodash-es: 4.17.21 + data-uri-to-buffer@6.0.2: {} date-fns@4.1.0: {} + dayjs@1.11.18: {} + debug@2.6.9: dependencies: ms: 2.0.0 @@ -8126,6 +11555,10 @@ snapshots: decamelize@4.0.0: {} + decode-named-character-reference@1.2.0: + dependencies: + character-entities: 2.0.2 + decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 @@ -8159,6 +11592,10 @@ snapshots: escodegen: 2.1.0 esprima: 4.0.1 + delaunator@5.0.1: + dependencies: + robust-predicates: 3.0.2 + delayed-stream@1.0.0: {} depd@2.0.0: {} @@ -8172,11 +11609,14 @@ snapshots: destroy@1.2.0: {} - detect-libc@2.0.4: - optional: true + detect-libc@2.0.4: {} detect-node-es@1.1.0: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + devtools-protocol@0.0.1508733: {} diff-match-patch@1.0.5: {} @@ -8207,13 +11647,22 @@ snapshots: dependencies: domelementtype: 2.3.0 + dompurify@3.2.7: + optionalDependencies: + '@types/trusted-types': 2.0.7 + domutils@3.2.2: dependencies: dom-serializer: 2.0.0 domelementtype: 2.3.0 domhandler: 5.0.3 - dotenv@17.2.1: {} + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + dotenv@17.2.2: {} dunder-proto@1.0.1: dependencies: @@ -8241,12 +11690,14 @@ snapshots: dependencies: safe-buffer: 5.2.1 - editions@6.21.0: + editions@6.22.0: dependencies: - version-range: 4.14.0 + version-range: 4.15.0 ee-first@1.1.1: {} + electron-to-chromium@1.5.227: {} + elliptic@6.6.1: dependencies: bn.js: 4.12.2 @@ -8257,7 +11708,7 @@ snapshots: minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - emoji-regex@10.4.0: {} + emoji-regex@10.5.0: {} emoji-regex@8.0.0: {} @@ -8278,10 +11729,10 @@ snapshots: dependencies: once: 1.4.0 - enhanced-resolve@5.18.2: + enhanced-resolve@5.18.3: dependencies: graceful-fs: 4.2.11 - tapable: 2.2.2 + tapable: 2.2.3 entities@4.5.0: {} @@ -8317,65 +11768,36 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 - es-toolkit@1.39.7: {} + es-toolkit@1.39.10: {} - esbuild@0.25.6: + esbuild@0.25.9: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.6 - '@esbuild/android-arm': 0.25.6 - '@esbuild/android-arm64': 0.25.6 - '@esbuild/android-x64': 0.25.6 - '@esbuild/darwin-arm64': 0.25.6 - '@esbuild/darwin-x64': 0.25.6 - '@esbuild/freebsd-arm64': 0.25.6 - '@esbuild/freebsd-x64': 0.25.6 - '@esbuild/linux-arm': 0.25.6 - '@esbuild/linux-arm64': 0.25.6 - '@esbuild/linux-ia32': 0.25.6 - '@esbuild/linux-loong64': 0.25.6 - '@esbuild/linux-mips64el': 0.25.6 - '@esbuild/linux-ppc64': 0.25.6 - '@esbuild/linux-riscv64': 0.25.6 - '@esbuild/linux-s390x': 0.25.6 - '@esbuild/linux-x64': 0.25.6 - '@esbuild/netbsd-arm64': 0.25.6 - '@esbuild/netbsd-x64': 0.25.6 - '@esbuild/openbsd-arm64': 0.25.6 - '@esbuild/openbsd-x64': 0.25.6 - '@esbuild/openharmony-arm64': 0.25.6 - '@esbuild/sunos-x64': 0.25.6 - '@esbuild/win32-arm64': 0.25.6 - '@esbuild/win32-ia32': 0.25.6 - '@esbuild/win32-x64': 0.25.6 - - esbuild@0.25.8: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.8 - '@esbuild/android-arm': 0.25.8 - '@esbuild/android-arm64': 0.25.8 - '@esbuild/android-x64': 0.25.8 - '@esbuild/darwin-arm64': 0.25.8 - '@esbuild/darwin-x64': 0.25.8 - '@esbuild/freebsd-arm64': 0.25.8 - '@esbuild/freebsd-x64': 0.25.8 - '@esbuild/linux-arm': 0.25.8 - '@esbuild/linux-arm64': 0.25.8 - '@esbuild/linux-ia32': 0.25.8 - '@esbuild/linux-loong64': 0.25.8 - '@esbuild/linux-mips64el': 0.25.8 - '@esbuild/linux-ppc64': 0.25.8 - '@esbuild/linux-riscv64': 0.25.8 - '@esbuild/linux-s390x': 0.25.8 - '@esbuild/linux-x64': 0.25.8 - '@esbuild/netbsd-arm64': 0.25.8 - '@esbuild/netbsd-x64': 0.25.8 - '@esbuild/openbsd-arm64': 0.25.8 - '@esbuild/openbsd-x64': 0.25.8 - '@esbuild/openharmony-arm64': 0.25.8 - '@esbuild/sunos-x64': 0.25.8 - '@esbuild/win32-arm64': 0.25.8 - '@esbuild/win32-ia32': 0.25.8 - '@esbuild/win32-x64': 0.25.8 + '@esbuild/aix-ppc64': 0.25.9 + '@esbuild/android-arm': 0.25.9 + '@esbuild/android-arm64': 0.25.9 + '@esbuild/android-x64': 0.25.9 + '@esbuild/darwin-arm64': 0.25.9 + '@esbuild/darwin-x64': 0.25.9 + '@esbuild/freebsd-arm64': 0.25.9 + '@esbuild/freebsd-x64': 0.25.9 + '@esbuild/linux-arm': 0.25.9 + '@esbuild/linux-arm64': 0.25.9 + '@esbuild/linux-ia32': 0.25.9 + '@esbuild/linux-loong64': 0.25.9 + '@esbuild/linux-mips64el': 0.25.9 + '@esbuild/linux-ppc64': 0.25.9 + '@esbuild/linux-riscv64': 0.25.9 + '@esbuild/linux-s390x': 0.25.9 + '@esbuild/linux-x64': 0.25.9 + '@esbuild/netbsd-arm64': 0.25.9 + '@esbuild/netbsd-x64': 0.25.9 + '@esbuild/openbsd-arm64': 0.25.9 + '@esbuild/openbsd-x64': 0.25.9 + '@esbuild/openharmony-arm64': 0.25.9 + '@esbuild/sunos-x64': 0.25.9 + '@esbuild/win32-arm64': 0.25.9 + '@esbuild/win32-ia32': 0.25.9 + '@esbuild/win32-x64': 0.25.9 escalade@3.2.0: {} @@ -8385,6 +11807,8 @@ snapshots: escape-string-regexp@4.0.0: {} + escape-string-regexp@5.0.0: {} + escodegen@2.1.0: dependencies: esprima: 4.0.1 @@ -8397,6 +11821,10 @@ snapshots: estraverse@5.3.0: {} + estree-util-is-identifier-name@3.0.0: {} + + estree-walker@2.0.2: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 @@ -8417,11 +11845,11 @@ snapshots: events@3.3.0: {} - eventsource-parser@3.0.3: {} + eventsource-parser@3.0.6: {} eventsource@3.0.7: dependencies: - eventsource-parser: 3.0.3 + eventsource-parser: 3.0.6 evp_bytestokey@1.0.3: dependencies: @@ -8532,6 +11960,10 @@ snapshots: transitivePeerDependencies: - supports-color + exsolve@1.0.7: {} + + extend@3.0.2: {} + extract-zip@2.0.1: dependencies: debug: 4.4.3 @@ -8546,6 +11978,8 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-diff@1.3.0: {} + fast-fifo@1.3.2: {} fast-glob@3.3.3: @@ -8563,7 +11997,7 @@ snapshots: '@fastify/merge-json-schemas': 0.2.1 ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) - fast-uri: 3.0.6 + fast-uri: 3.1.0 json-schema-ref-resolver: 2.0.1 rfdc: 1.4.1 @@ -8573,11 +12007,11 @@ snapshots: fast-redact@3.5.0: {} - fast-uri@3.0.6: {} + fast-uri@3.1.0: {} fastify-plugin@5.0.1: {} - fastify@5.5.0: + fastify@5.6.0: dependencies: '@fastify/ajv-compiler': 4.0.2 '@fastify/error': 4.2.0 @@ -8588,7 +12022,7 @@ snapshots: fast-json-stringify: 6.0.1 find-my-way: 9.3.0 light-my-request: 6.6.0 - pino: 9.7.0 + pino: 9.9.4 process-warning: 5.0.0 rfdc: 1.4.1 secure-json-parse: 4.0.0 @@ -8597,7 +12031,7 @@ snapshots: fastmcp@3.17.0: dependencies: - '@modelcontextprotocol/sdk': 1.17.2 + '@modelcontextprotocol/sdk': 1.17.5 '@standard-schema/spec': 1.0.0 execa: 9.6.0 file-type: 21.0.0 @@ -8621,15 +12055,15 @@ snapshots: dependencies: reusify: 1.1.0 + fault@1.0.4: + dependencies: + format: 0.2.2 + fd-slicer@1.1.0: dependencies: pend: 1.2.0 - fdir@6.4.6(picomatch@4.0.2): - optionalDependencies: - picomatch: 4.0.2 - - fdir@6.4.6(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 @@ -8681,6 +12115,8 @@ snapshots: fast-querystring: 1.1.2 safe-regex2: 5.0.0 + find-root@1.1.0: {} + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -8688,6 +12124,10 @@ snapshots: flat@5.0.2: {} + follow-redirects@1.15.11(debug@4.4.1): + optionalDependencies: + debug: 4.4.1(supports-color@8.1.1) + for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -8697,7 +12137,7 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - form-data@4.0.3: + form-data@4.0.4: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 @@ -8705,6 +12145,8 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 + format@0.2.2: {} + forwarded@0.2.0: {} fresh@0.5.2: {} @@ -8718,16 +12160,10 @@ snapshots: fs-constants@1.0.0: optional: true - fs-extra@11.3.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - fs-extra@11.3.1: dependencies: graceful-fs: 4.2.11 - jsonfile: 6.1.0 + jsonfile: 6.2.0 universalify: 2.0.1 fs.realpath@1.0.0: {} @@ -8739,9 +12175,11 @@ snapshots: fuse.js@7.1.0: {} + gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} - get-east-asian-width@1.3.0: {} + get-east-asian-width@1.3.1: {} get-intrinsic@1.3.0: dependencies: @@ -8777,7 +12215,6 @@ snapshots: get-tsconfig@4.10.1: dependencies: resolve-pkg-maps: 1.0.0 - optional: true get-uri@6.0.5: dependencies: @@ -8821,6 +12258,10 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 + globals@15.15.0: {} + + globals@16.4.0: {} + globby@14.1.0: dependencies: '@sindresorhus/merge-streams': 2.3.0 @@ -8830,6 +12271,10 @@ snapshots: slash: 5.1.0 unicorn-magic: 0.3.0 + goober@2.1.16(csstype@3.1.3): + dependencies: + csstype: 3.1.3 + gopd@1.2.0: {} gpt-tokenizer@3.0.1: {} @@ -8838,9 +12283,17 @@ snapshots: gradient-string@3.0.0: dependencies: - chalk: 5.5.0 + chalk: 5.6.2 tinygradient: 1.1.5 + hachure-fill@0.5.2: {} + + harden-react-markdown@1.1.2(react-markdown@10.1.0(@types/react@19.1.12)(react@19.1.1))(react@19.1.1): + dependencies: + react: 19.1.1 + react-markdown: 10.1.0(@types/react@19.1.12)(react@19.1.1) + rehype-harden: 1.1.2 + has-flag@4.0.0: {} has-property-descriptors@1.0.2: @@ -8871,16 +12324,152 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-from-dom@5.0.1: + dependencies: + '@types/hast': 3.0.4 + hastscript: 9.0.1 + web-namespaces: 2.0.1 + + hast-util-from-html-isomorphic@2.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-dom: 5.0.1 + hast-util-from-html: 2.0.3 + unist-util-remove-position: 5.0.0 + + hast-util-from-html@2.0.3: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.3 + parse5: 7.3.0 + vfile: 6.0.3 + vfile-message: 4.0.3 + + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.1.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-parse-selector@2.2.5: {} + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.3.0 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.17 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-to-parse5@8.0.0: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-text@4.0.2: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hastscript@6.0.0: + dependencies: + '@types/hast': 2.3.10 + comma-separated-tokens: 1.0.8 + hast-util-parse-selector: 2.2.5 + property-information: 5.6.0 + space-separated-tokens: 1.1.5 + + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + he@1.2.0: {} highlight.js@10.7.3: {} + highlightjs-vue@1.0.0: {} + hmac-drbg@1.0.1: dependencies: hash.js: 1.1.7 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + hosted-git-info@4.1.0: dependencies: lru-cache: 6.0.0 @@ -8891,6 +12480,14 @@ snapshots: html-escaper@2.0.2: {} + html-parse-stringify@3.0.1: + dependencies: + void-elements: 3.1.0 + + html-url-attributes@3.0.1: {} + + html-void-elements@3.0.0: {} + htmlparser2@10.0.0: dependencies: domelementtype: 2.3.0 @@ -8928,6 +12525,16 @@ snapshots: husky@9.1.7: {} + i18next-browser-languagedetector@8.2.0: + dependencies: + '@babel/runtime': 7.28.4 + + i18next@25.5.2(typescript@5.8.3): + dependencies: + '@babel/runtime': 7.28.4 + optionalDependencies: + typescript: 5.8.3 + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -8936,6 +12543,10 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.7.0: + dependencies: + safer-buffer: 2.1.2 + ieee754@1.2.1: {} ignore@7.0.5: {} @@ -8970,58 +12581,79 @@ snapshots: ini@1.3.8: optional: true - ink-select-input@6.2.0(ink@6.1.0(@types/react@19.1.10)(react@19.1.1))(react@19.1.1): + ink-select-input@6.2.0(ink@6.3.0(@types/react@19.1.12)(react@19.1.1))(react@19.1.1): dependencies: figures: 6.1.0 - ink: 6.1.0(@types/react@19.1.10)(react@19.1.1) + ink: 6.3.0(@types/react@19.1.12)(react@19.1.1) react: 19.1.1 to-rotated: 1.0.0 - ink-spinner@5.0.0(ink@6.1.0(@types/react@19.1.10)(react@19.1.1))(react@19.1.1): + ink-spinner@5.0.0(ink@6.3.0(@types/react@19.1.12)(react@19.1.1))(react@19.1.1): dependencies: cli-spinners: 2.9.2 - ink: 6.1.0(@types/react@19.1.10)(react@19.1.1) + ink: 6.3.0(@types/react@19.1.12)(react@19.1.1) react: 19.1.1 - ink@6.1.0(@types/react@19.1.10)(react@19.1.1): + ink@6.3.0(@types/react@19.1.12)(react@19.1.1): dependencies: - '@alcalzone/ansi-tokenize': 0.1.3 + '@alcalzone/ansi-tokenize': 0.2.0 ansi-escapes: 7.0.0 - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 auto-bind: 5.0.1 - chalk: 5.5.0 + chalk: 5.6.2 cli-boxes: 3.0.0 cli-cursor: 4.0.0 cli-truncate: 4.0.0 code-excerpt: 4.0.0 - es-toolkit: 1.39.7 + es-toolkit: 1.39.10 indent-string: 5.0.0 - is-in-ci: 1.0.0 + is-in-ci: 2.0.0 patch-console: 2.0.0 react: 19.1.1 react-reconciler: 0.32.0(react@19.1.1) - scheduler: 0.23.2 signal-exit: 3.0.7 - slice-ansi: 7.1.0 + slice-ansi: 7.1.2 stack-utils: 2.0.6 string-width: 7.2.0 type-fest: 4.41.0 widest-line: 5.0.0 - wrap-ansi: 9.0.0 + wrap-ansi: 9.0.2 ws: 8.18.3 yoga-layout: 3.2.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 transitivePeerDependencies: - bufferutil - utf-8-validate + inline-style-parser@0.2.4: {} + + internmap@1.0.1: {} + + internmap@2.0.3: {} + + intersection-observer@0.12.2: {} + ip-address@10.0.1: {} ipaddr.js@1.9.1: {} ipaddr.js@2.2.0: {} + is-alphabetical@1.0.4: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@1.0.4: + dependencies: + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + is-arrayish@0.2.1: {} is-binary-path@2.1.0: @@ -9034,6 +12666,10 @@ snapshots: dependencies: hasown: 2.0.2 + is-decimal@1.0.4: {} + + is-decimal@2.0.1: {} + is-docker@3.0.0: {} is-extglob@2.1.1: {} @@ -9042,15 +12678,19 @@ snapshots: is-fullwidth-code-point@4.0.0: {} - is-fullwidth-code-point@5.0.0: + is-fullwidth-code-point@5.1.0: dependencies: - get-east-asian-width: 1.3.0 + get-east-asian-width: 1.3.1 is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - is-in-ci@1.0.0: {} + is-hexadecimal@1.0.4: {} + + is-hexadecimal@2.0.1: {} + + is-in-ci@2.0.0: {} is-inside-container@1.0.0: dependencies: @@ -9058,6 +12698,8 @@ snapshots: is-interactive@2.0.0: {} + is-mobile@5.0.0: {} + is-number@7.0.0: {} is-plain-obj@2.1.0: {} @@ -9094,6 +12736,8 @@ snapshots: isarray@2.0.5: {} + isbot@5.1.31: {} + isexe@2.0.0: {} isomorphic-rslog@0.0.7: {} @@ -9106,7 +12750,7 @@ snapshots: make-dir: 4.0.0 supports-color: 7.2.0 - istanbul-reports@3.1.7: + istanbul-reports@3.2.0: dependencies: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 @@ -9114,7 +12758,7 @@ snapshots: istextorbinary@9.5.0: dependencies: binaryextensions: 6.11.0 - editions: 6.21.0 + editions: 6.22.0 textextensions: 6.11.0 jackspeak@3.4.3: @@ -9127,12 +12771,12 @@ snapshots: dependencies: '@isaacs/cliui': 8.0.2 - jiti@1.21.7: {} - jiti@2.5.1: {} jju@1.4.0: {} + js-cookie@3.0.5: {} + js-tokens@4.0.0: {} js-tokens@9.0.1: {} @@ -9146,6 +12790,8 @@ snapshots: dependencies: argparse: 2.0.1 + jsesc@3.1.0: {} + json-parse-even-better-errors@2.3.1: {} json-schema-ref-resolver@2.0.1: @@ -9158,6 +12804,10 @@ snapshots: json-schema@0.4.0: {} + json2mq@0.2.0: + dependencies: + string-convert: 0.2.1 + json5@2.2.3: {} jsonc-parser@3.3.1: {} @@ -9165,10 +12815,10 @@ snapshots: jsondiffpatch@0.6.0: dependencies: '@types/diff-match-patch': 1.0.36 - chalk: 5.5.0 + chalk: 5.6.2 diff-match-patch: 1.0.5 - jsonfile@6.1.0: + jsonfile@6.2.0: dependencies: universalify: 2.0.1 optionalDependencies: @@ -9207,17 +12857,37 @@ snapshots: jwa: 1.4.2 safe-buffer: 5.2.1 + katex@0.16.22: + dependencies: + commander: 8.3.0 + keytar@7.9.0: dependencies: node-addon-api: 4.3.0 prebuild-install: 7.1.3 optional: true - less-loader@12.3.0(less@4.4.0): + khroma@2.1.0: {} + + kolorist@1.8.0: {} + + langium@3.3.1: + dependencies: + chevrotain: 11.0.3 + chevrotain-allstar: 0.3.1(chevrotain@11.0.3) + vscode-languageserver: 9.0.1 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.0.8 + + layout-base@1.0.2: {} + + layout-base@2.0.1: {} + + less-loader@12.3.0(less@4.4.1): dependencies: - less: 4.4.0 + less: 4.4.1 - less@4.4.0: + less@4.4.1: dependencies: copy-anything: 2.0.6 parse-node-version: 1.0.1 @@ -9287,7 +12957,6 @@ snapshots: lightningcss-linux-x64-musl: 1.30.1 lightningcss-win32-arm64-msvc: 1.30.1 lightningcss-win32-x64-msvc: 1.30.1 - optional: true lilconfig@3.1.3: {} @@ -9297,161 +12966,587 @@ snapshots: dependencies: uc.micro: 2.1.0 - lint-staged@16.1.5: + lint-staged@16.1.6: dependencies: - chalk: 5.5.0 + chalk: 5.6.2 commander: 14.0.0 debug: 4.4.1(supports-color@8.1.1) lilconfig: 3.1.3 - listr2: 9.0.1 + listr2: 9.0.3 micromatch: 4.0.8 - nano-spawn: 1.0.2 + nano-spawn: 1.0.3 pidtree: 0.6.0 string-argv: 0.3.2 yaml: 2.8.1 transitivePeerDependencies: - supports-color - listr2@9.0.1: + listr2@9.0.3: dependencies: cli-truncate: 4.0.0 colorette: 2.0.20 eventemitter3: 5.0.1 log-update: 6.1.0 rfdc: 1.4.1 - wrap-ansi: 9.0.0 + wrap-ansi: 9.0.2 loader-runner@4.3.0: {} loader-utils@3.3.1: {} + local-pkg@1.1.2: + dependencies: + mlly: 1.8.0 + pkg-types: 2.3.0 + quansync: 0.2.11 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 lodash-es@4.17.21: {} + lodash.clonedeep@4.5.0: {} + lodash.includes@4.3.0: {} lodash.isboolean@3.0.3: {} + lodash.isequal@4.5.0: {} + lodash.isinteger@4.0.4: {} lodash.isnumber@3.0.3: {} - lodash.isplainobject@4.0.6: {} + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + + lodash.once@4.1.1: {} + + lodash.truncate@4.4.2: {} + + lodash@4.17.21: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + log-symbols@6.0.0: + dependencies: + chalk: 5.6.2 + is-unicode-supported: 1.3.0 + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.0.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.1.2 + wrap-ansi: 9.0.2 + + longest-streak@3.1.0: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + loupe@3.2.1: {} + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lowlight@1.20.0: + dependencies: + fault: 1.0.4 + highlight.js: 10.7.3 + + lru-cache@10.4.3: {} + + lru-cache@11.2.1: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + lru-cache@7.18.3: {} + + lucide-react@0.523.0(react@18.3.1): + dependencies: + react: 18.3.1 + + lucide-react@0.542.0(react@19.1.1): + dependencies: + react: 19.1.1 + + lucide-react@0.545.0(react@19.1.1): + dependencies: + react: 19.1.1 + + magic-string@0.30.19: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + make-dir@2.1.0: + dependencies: + pify: 4.0.1 + semver: 5.7.2 + optional: true + + make-dir@4.0.0: + dependencies: + semver: 7.7.2 + + make-error@1.3.6: {} + + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + markdown-table@3.0.4: {} + + marked-terminal@7.3.0(patch_hash=536fe9685e91d559cf29a033191aa39da45729949e9d1c69989255091c8618fb)(marked@16.2.1): + dependencies: + ansi-escapes: 7.0.0 + ansi-regex: 6.2.2 + chalk: 5.6.2 + cli-highlight: 2.1.11 + cli-table3: 0.6.5 + marked: 16.2.1 + node-emoji: 2.2.0 + supports-hyperlinks: 3.2.0 + + marked@11.2.0: {} + + marked@16.2.1: {} + + math-intrinsics@1.1.0: {} + + mcp-proxy@5.6.0: {} + + md5.js@1.3.5: + dependencies: + hash-base: 3.0.5 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + mdast-util-from-markdown@2.0.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-math@3.0.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + longest-streak: 3.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + unist-util-remove-position: 5.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.0 + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdurl@2.0.0: {} + + media-typer@0.3.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@1.0.3: {} + + merge-descriptors@2.0.0: {} + + merge-stream@2.0.0: {} - lodash.isstring@4.0.1: {} + merge2@1.4.1: {} - lodash.once@4.1.1: {} + mermaid@11.12.0: + dependencies: + '@braintree/sanitize-url': 7.1.1 + '@iconify/utils': 3.0.2 + '@mermaid-js/parser': 0.6.2 + '@types/d3': 7.4.3 + cytoscape: 3.33.1 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.33.1) + cytoscape-fcose: 2.2.0(cytoscape@3.33.1) + d3: 7.9.0 + d3-sankey: 0.12.3 + dagre-d3-es: 7.0.11 + dayjs: 1.11.18 + dompurify: 3.2.7 + katex: 0.16.22 + khroma: 2.1.0 + lodash-es: 4.17.21 + marked: 16.2.1 + roughjs: 4.6.6 + stylis: 4.3.6 + ts-dedent: 2.2.0 + uuid: 11.1.0 + transitivePeerDependencies: + - supports-color - lodash.truncate@4.4.2: {} + methods@1.1.2: {} - lodash@4.17.21: {} + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - log-symbols@4.1.0: + micromark-extension-gfm-footnote@2.1.0: dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - log-symbols@6.0.0: + micromark-extension-gfm-strikethrough@2.1.0: dependencies: - chalk: 5.5.0 - is-unicode-supported: 1.3.0 + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - log-update@6.1.0: + micromark-extension-gfm-table@2.1.1: dependencies: - ansi-escapes: 7.0.0 - cli-cursor: 5.0.0 - slice-ansi: 7.1.0 - strip-ansi: 7.1.0 - wrap-ansi: 9.0.0 + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - loose-envify@1.4.0: + micromark-extension-gfm-tagfilter@2.0.0: dependencies: - js-tokens: 4.0.0 + micromark-util-types: 2.0.2 - loupe@3.1.4: {} + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - lru-cache@10.4.3: {} + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 - lru-cache@11.1.0: {} + micromark-extension-math@3.1.0: + dependencies: + '@types/katex': 0.16.7 + devlop: 1.1.0 + katex: 0.16.22 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - lru-cache@6.0.0: + micromark-factory-destination@2.0.1: dependencies: - yallist: 4.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - lru-cache@7.18.3: {} + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - lucide-react@0.523.0(react@18.3.1): + micromark-factory-space@2.0.1: dependencies: - react: 18.3.1 + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 - magic-string@0.30.17: + micromark-factory-title@2.0.1: dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - make-dir@2.1.0: + micromark-factory-whitespace@2.0.1: dependencies: - pify: 4.0.1 - semver: 5.7.2 - optional: true + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - make-dir@4.0.0: + micromark-util-character@2.1.1: dependencies: - semver: 7.7.2 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - make-error@1.3.6: {} + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 - markdown-it@14.1.0: + micromark-util-classify-character@2.0.1: dependencies: - argparse: 2.0.1 - entities: 4.5.0 - linkify-it: 5.0.0 - mdurl: 2.0.0 - punycode.js: 2.3.1 - uc.micro: 2.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - marked-terminal@7.3.0(patch_hash=536fe9685e91d559cf29a033191aa39da45729949e9d1c69989255091c8618fb)(marked@16.1.2): + micromark-util-combine-extensions@2.0.1: dependencies: - ansi-escapes: 7.0.0 - ansi-regex: 6.1.0 - chalk: 5.5.0 - cli-highlight: 2.1.11 - cli-table3: 0.6.5 - marked: 16.1.2 - node-emoji: 2.2.0 - supports-hyperlinks: 3.2.0 + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 - marked@11.2.0: {} + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 - marked@16.1.2: {} + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.2.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 - math-intrinsics@1.1.0: {} + micromark-util-encode@2.0.1: {} - mcp-proxy@5.6.0: {} + micromark-util-html-tag-name@2.0.1: {} - md5.js@1.3.5: + micromark-util-normalize-identifier@2.0.1: dependencies: - hash-base: 3.0.5 - inherits: 2.0.4 - safe-buffer: 5.2.1 - - mdurl@2.0.0: {} - - media-typer@0.3.0: {} + micromark-util-symbol: 2.0.1 - media-typer@1.1.0: {} + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 - merge-descriptors@1.0.3: {} + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 - merge-descriptors@2.0.0: {} + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - merge-stream@2.0.0: {} + micromark-util-symbol@2.0.1: {} - merge2@1.4.1: {} + micromark-util-types@2.0.2: {} - methods@1.1.2: {} + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.1(supports-color@8.1.1) + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color micromatch@4.0.8: dependencies: @@ -9526,7 +13621,14 @@ snapshots: mkdirp@3.0.1: {} - mocha@11.7.1: + mlly@1.8.0: + dependencies: + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 + + mocha@11.7.2: dependencies: browser-stdout: 1.3.1 chokidar: 4.0.3 @@ -9549,6 +13651,10 @@ snapshots: yargs-parser: 21.1.1 yargs-unparser: 2.0.0 + monaco-editor@0.53.0: + dependencies: + '@types/trusted-types': 1.0.6 + ms@2.0.0: {} ms@2.1.3: {} @@ -9561,7 +13667,7 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nano-spawn@1.0.2: {} + nano-spawn@1.0.3: {} nanoid@3.3.11: {} @@ -9582,7 +13688,12 @@ snapshots: netmask@2.0.2: {} - node-abi@3.75.0: + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-abi@3.77.0: dependencies: semver: 7.7.2 optional: true @@ -9623,10 +13734,12 @@ snapshots: util-okam: 0.11.1 vm-browserify: 1.1.2 + node-releases@2.0.21: {} + node-sarif-builder@3.2.0: dependencies: '@types/sarif': 2.1.7 - fs-extra: 11.3.0 + fs-extra: 11.3.1 normalize-package-data@6.0.2: dependencies: @@ -9675,6 +13788,14 @@ snapshots: dependencies: mimic-function: 5.0.1 + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.3: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.0.1 + regex-recursion: 6.0.2 + open@10.2.0: dependencies: default-browser: 5.2.1 @@ -9682,14 +13803,14 @@ snapshots: is-inside-container: 1.0.0 wsl-utils: 0.1.0 - openai@5.10.1(ws@8.18.3)(zod@3.25.76): + openai@5.20.0(ws@8.18.3)(zod@3.25.76): optionalDependencies: ws: 8.18.3 zod: 3.25.76 ora@8.2.0: dependencies: - chalk: 5.5.0 + chalk: 5.6.2 cli-cursor: 5.0.0 cli-spinners: 2.9.2 is-interactive: 2.0.0 @@ -9697,7 +13818,7 @@ snapshots: log-symbols: 6.0.0 stdin-discarder: 0.2.2 string-width: 7.2.0 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 os-browserify@0.3.0: {} @@ -9731,8 +13852,12 @@ snapshots: package-json-from-dist@1.0.1: {} + package-manager-detector@1.3.0: {} + pako@1.0.11: {} + parchment@3.0.0: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -9746,6 +13871,25 @@ snapshots: pbkdf2: 3.1.3 safe-buffer: 5.2.1 + parse-entities@2.0.0: + dependencies: + character-entities: 1.2.4 + character-entities-legacy: 1.1.4 + character-reference-invalid: 1.1.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + is-hexadecimal: 1.0.4 + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.2.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + parse-json@5.2.0: dependencies: '@babel/code-frame': 7.27.1 @@ -9794,6 +13938,8 @@ snapshots: path-browserify@0.0.1: {} + path-data-parser@0.1.0: {} + path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -9813,14 +13959,16 @@ snapshots: path-scurry@2.0.0: dependencies: - lru-cache: 11.1.0 + lru-cache: 11.2.1 minipass: 7.1.2 path-to-regexp@0.1.12: {} path-to-regexp@3.3.0: {} - path-to-regexp@8.2.0: {} + path-to-regexp@8.3.0: {} + + path-type@4.0.0: {} path-type@6.0.0: {} @@ -9849,8 +13997,6 @@ snapshots: picomatch@2.3.1: {} - picomatch@4.0.2: {} - picomatch@4.0.3: {} pidtree@0.6.0: {} @@ -9864,7 +14010,7 @@ snapshots: pino-std-serializers@7.0.0: {} - pino@9.7.0: + pino@9.9.4: dependencies: atomic-sleep: 1.0.0 fast-redact: 3.5.0 @@ -9880,16 +14026,35 @@ snapshots: piscina@4.9.2: optionalDependencies: - '@napi-rs/nice': 1.0.4 + '@napi-rs/nice': 1.1.1 pkce-challenge@4.1.0: {} pkce-challenge@5.0.0: {} + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.0 + pathe: 2.0.3 + + pkg-types@2.3.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.7 + pathe: 2.0.3 + pluralize@2.0.0: {} pluralize@8.0.0: {} + points-on-curve@0.2.0: {} + + points-on-path@0.2.1: + dependencies: + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + portfinder@1.0.37: dependencies: async: 3.2.6 @@ -9899,15 +14064,20 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-loader@8.1.1(postcss@8.5.6)(typescript@5.9.2): + postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.2): dependencies: cosmiconfig: 9.0.0(typescript@5.9.2) - jiti: 1.21.7 + jiti: 2.5.1 postcss: 8.5.6 semver: 7.7.2 transitivePeerDependencies: - typescript + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -9922,7 +14092,7 @@ snapshots: minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 2.0.0 - node-abi: 3.75.0 + node-abi: 3.77.0 pump: 3.0.3 rc: 1.2.8 simple-get: 4.0.1 @@ -9930,10 +14100,20 @@ snapshots: tunnel-agent: 0.6.0 optional: true + prettier@3.6.2: {} + pretty-ms@9.3.0: dependencies: parse-ms: 4.0.0 + prism-react-renderer@2.4.1(react@19.1.1): + dependencies: + '@types/prismjs': 1.26.5 + clsx: 2.1.1 + react: 19.1.1 + + prismjs@1.27.0: {} + prismjs@1.30.0: {} process-nextick-args@2.0.1: {} @@ -9948,6 +14128,14 @@ snapshots: progress@2.0.3: {} + property-information@5.6.0: + dependencies: + xtend: 4.0.2 + + property-information@6.5.0: {} + + property-information@7.1.0: {} + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -9997,75 +14185,410 @@ snapshots: punycode.js@2.3.1: {} - punycode@1.4.1: {} + punycode@1.4.1: {} + + punycode@2.3.1: {} + + puppeteer-core@24.23.0: + dependencies: + '@puppeteer/browsers': 2.10.10 + chromium-bidi: 9.1.0(devtools-protocol@0.0.1508733) + debug: 4.4.3 + devtools-protocol: 0.0.1508733 + typed-query-selector: 2.12.0 + webdriver-bidi-protocol: 0.3.6 + ws: 8.18.3 + transitivePeerDependencies: + - bare-buffer + - bufferutil + - react-native-b4a + - supports-color + - utf-8-validate + + qs@6.13.0: + dependencies: + side-channel: 1.1.0 + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + quansync@0.2.11: {} + + querystring-es3@0.2.1: {} + + queue-microtask@1.2.3: {} + + quick-format-unescaped@4.0.4: {} + + quill-delta@5.1.0: + dependencies: + fast-diff: 1.3.0 + lodash.clonedeep: 4.5.0 + lodash.isequal: 4.5.0 + + quill@2.0.3: + dependencies: + eventemitter3: 5.0.1 + lodash-es: 4.17.21 + parchment: 3.0.0 + quill-delta: 5.1.0 + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + randomfill@1.0.4: + dependencies: + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + range-parser@1.2.0: {} + + range-parser@1.2.1: {} + + raw-body@2.5.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + raw-body@3.0.1: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.7.0 + unpipe: 1.0.0 + + rc-cascader@3.34.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-select: 14.16.8(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-tree: 5.13.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-checkbox@3.5.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-collapse@3.9.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-config-loader@4.1.3: + dependencies: + debug: 4.4.1(supports-color@8.1.1) + js-yaml: 4.1.0 + json5: 2.2.3 + require-from-string: 2.0.2 + transitivePeerDependencies: + - supports-color + + rc-dialog@9.6.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/portal': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-drawer@7.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/portal': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-dropdown@4.2.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/trigger': 2.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-field-form@2.7.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/async-validator': 5.0.4 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-image@7.12.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/portal': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + rc-dialog: 9.6.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-motion: 2.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-input-number@9.5.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/mini-decimal': 1.1.0 + classnames: 2.5.1 + rc-input: 1.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-input@1.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-mentions@2.20.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/trigger': 2.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + rc-input: 1.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-menu: 9.16.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-textarea: 1.10.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-menu@9.16.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/trigger': 2.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-overflow: 1.4.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-motion@2.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-notification@5.6.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-overflow@1.4.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-resize-observer: 1.4.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-pagination@5.1.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-picker@4.11.3(date-fns@4.1.0)(dayjs@1.11.18)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/trigger': 2.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + rc-overflow: 1.4.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-resize-observer: 1.4.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + date-fns: 4.1.0 + dayjs: 1.11.18 + + rc-progress@4.0.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-rate@2.13.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + rc-resize-observer@1.4.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + resize-observer-polyfill: 1.5.1 - punycode@2.3.1: {} + rc-segmented@2.7.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - puppeteer-core@24.23.0: + rc-select@14.16.8(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - '@puppeteer/browsers': 2.10.10 - chromium-bidi: 9.1.0(devtools-protocol@0.0.1508733) - debug: 4.4.3 - devtools-protocol: 0.0.1508733 - typed-query-selector: 2.12.0 - webdriver-bidi-protocol: 0.3.6 - ws: 8.18.3 - transitivePeerDependencies: - - bare-buffer - - bufferutil - - react-native-b4a - - supports-color - - utf-8-validate + '@babel/runtime': 7.28.4 + '@rc-component/trigger': 2.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-overflow: 1.4.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-virtual-list: 3.19.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - qs@6.13.0: + rc-slider@11.1.9(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - side-channel: 1.1.0 + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - qs@6.14.0: + rc-steps@6.0.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - side-channel: 1.1.0 + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - querystring-es3@0.2.1: {} + rc-switch@4.1.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - queue-microtask@1.2.3: {} + rc-table@7.53.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/context': 1.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + rc-resize-observer: 1.4.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-virtual-list: 3.19.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - quick-format-unescaped@4.0.4: {} + rc-tabs@15.7.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-dropdown: 4.2.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-menu: 9.16.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-motion: 2.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-resize-observer: 1.4.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - randombytes@2.1.0: + rc-textarea@1.10.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - safe-buffer: 5.2.1 + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-input: 1.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-resize-observer: 1.4.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - randomfill@1.0.4: + rc-tooltip@6.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - randombytes: 2.1.0 - safe-buffer: 5.2.1 + '@babel/runtime': 7.28.4 + '@rc-component/trigger': 2.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - range-parser@1.2.0: {} + rc-tree-select@5.27.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-select: 14.16.8(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-tree: 5.13.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - range-parser@1.2.1: {} + rc-tree@5.13.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-virtual-list: 3.19.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - raw-body@2.5.2: + rc-upload@4.9.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) - raw-body@3.0.0: + rc-util@5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.6.3 - unpipe: 1.0.0 + '@babel/runtime': 7.28.4 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-is: 18.3.1 - rc-config-loader@4.1.3: + rc-virtual-list@3.19.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - debug: 4.4.1(supports-color@8.1.1) - js-yaml: 4.1.0 - json5: 2.2.3 - require-from-string: 2.0.2 - transitivePeerDependencies: - - supports-color + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-resize-observer: 1.4.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rc-util: 5.44.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) rc@1.2.8: dependencies: @@ -10081,8 +14604,69 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-dom@19.1.1(react@19.1.1): + dependencies: + react: 19.1.1 + scheduler: 0.26.0 + react-error-overlay@6.0.9: {} + react-fast-compare@3.2.2: {} + + react-i18next@15.7.4(i18next@25.5.2(typescript@5.8.3))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.8.3): + dependencies: + '@babel/runtime': 7.28.4 + html-parse-stringify: 3.0.1 + i18next: 25.5.2(typescript@5.8.3) + react: 19.1.1 + optionalDependencies: + react-dom: 19.1.1(react@19.1.1) + typescript: 5.8.3 + + react-icons@5.5.0(react@19.1.1): + dependencies: + react: 19.1.1 + + react-is@16.13.1: {} + + react-is@18.3.1: {} + + react-markdown@10.1.0(@types/react@19.1.12)(react@19.1.1): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 19.1.12 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.0 + react: 19.1.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + react-markdown@9.1.0(@types/react@19.1.12)(react@19.1.1): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 19.1.12 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.0 + react: 19.1.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + react-reconciler@0.32.0(react@19.1.1): dependencies: react: 19.1.1 @@ -10090,37 +14674,49 @@ snapshots: react-refresh@0.14.2: {} - react-remove-scroll-bar@2.3.8(@types/react@19.1.10)(react@18.3.1): + react-refresh@0.17.0: {} + + react-remove-scroll-bar@2.3.8(@types/react@19.1.12)(react@18.3.1): dependencies: react: 18.3.1 - react-style-singleton: 2.2.3(@types/react@19.1.10)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@19.1.12)(react@18.3.1) tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 - react-remove-scroll@2.7.1(@types/react@19.1.10)(react@18.3.1): + react-remove-scroll@2.7.1(@types/react@19.1.12)(react@18.3.1): dependencies: react: 18.3.1 - react-remove-scroll-bar: 2.3.8(@types/react@19.1.10)(react@18.3.1) - react-style-singleton: 2.2.3(@types/react@19.1.10)(react@18.3.1) + react-remove-scroll-bar: 2.3.8(@types/react@19.1.12)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@19.1.12)(react@18.3.1) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.1.10)(react@18.3.1) - use-sidecar: 1.1.3(@types/react@19.1.10)(react@18.3.1) + use-callback-ref: 1.3.3(@types/react@19.1.12)(react@18.3.1) + use-sidecar: 1.1.3(@types/react@19.1.12)(react@18.3.1) optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 react-simple-code-editor@0.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-style-singleton@2.2.3(@types/react@19.1.10)(react@18.3.1): + react-style-singleton@2.2.3(@types/react@19.1.12)(react@18.3.1): dependencies: get-nonce: 1.0.1 react: 18.3.1 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 + + react-syntax-highlighter@15.6.6(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.4 + highlight.js: 10.7.3 + highlightjs-vue: 1.0.0 + lowlight: 1.20.0 + prismjs: 1.30.0 + react: 19.1.1 + refractor: 3.6.0 react@18.3.1: dependencies: @@ -10172,14 +14768,100 @@ snapshots: real-require@0.2.0: {} + recast@0.23.11: + dependencies: + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 + + refractor@3.6.0: + dependencies: + hastscript: 6.0.0 + parse-entities: 2.0.0 + prismjs: 1.27.0 + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.0.1: + dependencies: + regex-utilities: 2.3.0 + + rehype-harden@1.1.2: {} + + rehype-katex@7.0.1: + dependencies: + '@types/hast': 3.0.4 + '@types/katex': 0.16.7 + hast-util-from-html-isomorphic: 2.0.0 + hast-util-to-text: 4.0.2 + katex: 0.16.22 + unist-util-visit-parents: 6.0.1 + vfile: 6.0.3 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-math@6.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-math: 3.0.0 + micromark-extension-math: 3.1.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.0 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + require-directory@2.1.1: {} require-from-string@2.0.2: {} + resize-observer-polyfill@1.5.1: {} + resolve-from@4.0.0: {} - resolve-pkg-maps@1.0.0: - optional: true + resolve-pkg-maps@1.0.0: {} resolve@1.22.10: dependencies: @@ -10213,39 +14895,49 @@ snapshots: hash-base: 3.0.5 inherits: 2.0.4 - rollup@4.45.1: + robust-predicates@3.0.2: {} + + rollup@4.50.1: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.45.1 - '@rollup/rollup-android-arm64': 4.45.1 - '@rollup/rollup-darwin-arm64': 4.45.1 - '@rollup/rollup-darwin-x64': 4.45.1 - '@rollup/rollup-freebsd-arm64': 4.45.1 - '@rollup/rollup-freebsd-x64': 4.45.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.45.1 - '@rollup/rollup-linux-arm-musleabihf': 4.45.1 - '@rollup/rollup-linux-arm64-gnu': 4.45.1 - '@rollup/rollup-linux-arm64-musl': 4.45.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.45.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.45.1 - '@rollup/rollup-linux-riscv64-gnu': 4.45.1 - '@rollup/rollup-linux-riscv64-musl': 4.45.1 - '@rollup/rollup-linux-s390x-gnu': 4.45.1 - '@rollup/rollup-linux-x64-gnu': 4.45.1 - '@rollup/rollup-linux-x64-musl': 4.45.1 - '@rollup/rollup-win32-arm64-msvc': 4.45.1 - '@rollup/rollup-win32-ia32-msvc': 4.45.1 - '@rollup/rollup-win32-x64-msvc': 4.45.1 + '@rollup/rollup-android-arm-eabi': 4.50.1 + '@rollup/rollup-android-arm64': 4.50.1 + '@rollup/rollup-darwin-arm64': 4.50.1 + '@rollup/rollup-darwin-x64': 4.50.1 + '@rollup/rollup-freebsd-arm64': 4.50.1 + '@rollup/rollup-freebsd-x64': 4.50.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.50.1 + '@rollup/rollup-linux-arm-musleabihf': 4.50.1 + '@rollup/rollup-linux-arm64-gnu': 4.50.1 + '@rollup/rollup-linux-arm64-musl': 4.50.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.50.1 + '@rollup/rollup-linux-ppc64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-musl': 4.50.1 + '@rollup/rollup-linux-s390x-gnu': 4.50.1 + '@rollup/rollup-linux-x64-gnu': 4.50.1 + '@rollup/rollup-linux-x64-musl': 4.50.1 + '@rollup/rollup-openharmony-arm64': 4.50.1 + '@rollup/rollup-win32-arm64-msvc': 4.50.1 + '@rollup/rollup-win32-ia32-msvc': 4.50.1 + '@rollup/rollup-win32-x64-msvc': 4.50.1 fsevents: 2.3.3 + roughjs@4.6.6: + dependencies: + hachure-fill: 0.5.2 + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + points-on-path: 0.2.1 + router@2.2.0: dependencies: debug: 4.4.1(supports-color@8.1.1) depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 - path-to-regexp: 8.2.0 + path-to-regexp: 8.3.0 transitivePeerDependencies: - supports-color @@ -10255,6 +14947,8 @@ snapshots: dependencies: queue-microtask: 1.2.3 + rw@1.3.3: {} + rxjs@7.8.2: dependencies: tslib: 2.8.1 @@ -10283,12 +14977,18 @@ snapshots: scheduler@0.26.0: {} - secretlint@10.2.0: + screenfull@5.2.0: {} + + scroll-into-view-if-needed@3.1.0: + dependencies: + compute-scroll-into-view: 3.1.1 + + secretlint@10.2.2: dependencies: - '@secretlint/config-creator': 10.2.0 - '@secretlint/formatter': 10.2.0 - '@secretlint/node': 10.2.0 - '@secretlint/profiler': 10.2.0 + '@secretlint/config-creator': 10.2.2 + '@secretlint/formatter': 10.2.2 + '@secretlint/node': 10.2.2 + '@secretlint/profiler': 10.2.2 debug: 4.4.1(supports-color@8.1.1) globby: 14.1.0 read-pkg: 9.0.1 @@ -10301,6 +15001,8 @@ snapshots: semver@5.7.2: {} + semver@6.3.1: {} + semver@7.5.4: dependencies: lru-cache: 6.0.0 @@ -10345,6 +15047,12 @@ snapshots: dependencies: randombytes: 2.1.0 + seroval-plugins@1.3.3(seroval@1.3.2): + dependencies: + seroval: 1.3.2 + + seroval@1.3.2: {} + serve-handler@6.1.6: dependencies: bytes: 3.0.0 @@ -10402,6 +15110,17 @@ snapshots: shell-quote@1.8.3: {} + shiki@3.13.0: + dependencies: + '@shikijs/core': 3.13.0 + '@shikijs/engine-javascript': 3.13.0 + '@shikijs/engine-oniguruma': 3.13.0 + '@shikijs/langs': 3.13.0 + '@shikijs/themes': 3.13.0 + '@shikijs/types': 3.13.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -10462,16 +15181,21 @@ snapshots: slice-ansi@5.0.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 is-fullwidth-code-point: 4.0.0 - slice-ansi@7.1.0: + slice-ansi@7.1.2: dependencies: - ansi-styles: 6.2.1 - is-fullwidth-code-point: 5.0.0 + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 smart-buffer@4.2.0: {} + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.4 @@ -10485,14 +15209,28 @@ snapshots: ip-address: 10.0.1 smart-buffer: 4.2.0 + solid-js@1.9.9: + dependencies: + csstype: 3.1.3 + seroval: 1.3.2 + seroval-plugins: 1.3.3(seroval@1.3.2) + sonic-boom@4.2.0: dependencies: atomic-sleep: 1.0.0 source-map-js@1.2.1: {} + source-map@0.5.7: {} + source-map@0.6.1: {} + source-map@0.7.6: {} + + space-separated-tokens@1.1.5: {} + + space-separated-tokens@2.0.2: {} + spawn-rx@5.1.2: dependencies: debug: 4.4.1(supports-color@8.1.1) @@ -10503,16 +15241,16 @@ snapshots: spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.21 + spdx-license-ids: 3.0.22 spdx-exceptions@2.5.0: {} spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.21 + spdx-license-ids: 3.0.22 - spdx-license-ids@3.0.21: {} + spdx-license-ids@3.0.22: {} split2@4.2.0: {} @@ -10524,6 +15262,8 @@ snapshots: stackback@0.0.2: {} + state-local@1.0.7: {} + statuses@2.0.1: {} statuses@2.0.2: {} @@ -10547,6 +15287,26 @@ snapshots: stream-shift@1.0.3: {} + streamdown@1.3.0(@types/react@19.1.12)(react@19.1.1): + dependencies: + clsx: 2.1.1 + harden-react-markdown: 1.1.2(react-markdown@10.1.0(@types/react@19.1.12)(react@19.1.1))(react@19.1.1) + katex: 0.16.22 + lucide-react: 0.542.0(react@19.1.1) + marked: 16.2.1 + mermaid: 11.12.0 + react: 19.1.1 + react-markdown: 10.1.0(@types/react@19.1.12)(react@19.1.1) + rehype-katex: 7.0.1 + rehype-raw: 7.0.0 + remark-gfm: 4.0.1 + remark-math: 6.0.0 + shiki: 3.13.0 + tailwind-merge: 3.3.1 + transitivePeerDependencies: + - '@types/react' + - supports-color + streamx@2.23.0: dependencies: events-universal: 1.0.1 @@ -10559,6 +15319,8 @@ snapshots: string-argv@0.3.2: {} + string-convert@0.2.1: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -10569,13 +15331,13 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 string-width@7.2.0: dependencies: - emoji-regex: 10.4.0 - get-east-asian-width: 1.3.0 - strip-ansi: 7.1.0 + emoji-regex: 10.5.0 + get-east-asian-width: 1.3.1 + strip-ansi: 7.1.2 string_decoder-okam@1.3.0: dependencies: @@ -10589,13 +15351,18 @@ snapshots: dependencies: safe-buffer: 5.2.1 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.0: + strip-ansi@7.1.2: dependencies: - ansi-regex: 6.1.0 + ansi-regex: 6.2.2 strip-final-newline@3.0.0: {} @@ -10618,6 +15385,18 @@ snapshots: dependencies: boundary: 2.0.0 + style-to-js@1.1.17: + dependencies: + style-to-object: 1.0.9 + + style-to-object@1.0.9: + dependencies: + inline-style-parser: 0.2.4 + + stylis@4.2.0: {} + + stylis@4.3.6: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -10635,7 +15414,9 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - swr@2.3.3(react@19.1.1): + svg-parser@2.0.4: {} + + swr@2.3.6(react@19.1.1): dependencies: dequal: 2.0.3 react: 19.1.1 @@ -10653,9 +15434,18 @@ snapshots: tailwind-merge@2.6.0: {} - tailwindcss-animate@1.0.7: {} + tailwind-merge@3.3.1: {} + + tailwind-scrollbar@4.0.2(react@19.1.1)(tailwindcss@4.1.13): + dependencies: + prism-react-renderer: 2.4.1(react@19.1.1) + tailwindcss: 4.1.13 + transitivePeerDependencies: + - react + + tailwindcss@4.1.13: {} - tapable@2.2.2: {} + tapable@2.2.3: {} tar-fs@2.1.3: dependencies: @@ -10723,7 +15513,7 @@ snapshots: textextensions@6.11.0: dependencies: - editions: 6.21.0 + editions: 6.22.0 thenify-all@1.6.0: dependencies: @@ -10737,6 +15527,8 @@ snapshots: dependencies: real-require: 0.2.0 + throttle-debounce@5.0.2: {} + throttleit@2.1.0: {} through2@2.0.5: @@ -10748,16 +15540,22 @@ snapshots: dependencies: setimmediate: 1.0.5 + tiny-invariant@1.3.3: {} + + tiny-warning@1.0.3: {} + tinybench@2.9.0: {} tinycolor2@1.6.0: {} tinyexec@0.3.2: {} - tinyglobby@0.2.14: + tinyexec@1.0.1: {} + + tinyglobby@0.2.15: dependencies: - fdir: 6.4.6(picomatch@4.0.2) - picomatch: 4.0.2 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 tinygradient@1.1.5: dependencies: @@ -10770,7 +15568,7 @@ snapshots: tinyspy@4.0.3: {} - tmp@0.2.3: {} + tmp@0.2.5: {} to-arraybuffer@1.0.1: {} @@ -10788,6 +15586,8 @@ snapshots: toad-cache@3.7.0: {} + toggle-selection@1.0.6: {} + toidentifier@1.0.1: {} token-types@6.1.1: @@ -10798,14 +15598,20 @@ snapshots: tree-kill@1.2.2: {} - ts-node@10.9.2(@swc/core@1.12.1)(@types/node@24.2.1)(typescript@5.9.2): + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + ts-dedent@2.2.0: {} + + ts-node@10.9.2(@swc/core@1.12.1)(@types/node@24.3.1)(typescript@5.9.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 24.2.1 + '@types/node': 24.3.1 acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -10820,13 +15626,12 @@ snapshots: tslib@2.8.1: {} - tsx@4.20.3: + tsx@4.20.5: dependencies: - esbuild: 0.25.8 + esbuild: 0.25.9 get-tsconfig: 4.10.1 optionalDependencies: fsevents: 2.3.3 - optional: true tty-browserify@0.0.0: {} @@ -10837,7 +15642,7 @@ snapshots: tunnel@0.0.6: {} - turndown@7.2.0: + turndown@7.2.1: dependencies: '@mixmark-io/domino': 2.2.0 @@ -10870,17 +15675,21 @@ snapshots: typescript@5.8.2: {} + typescript@5.8.3: {} + typescript@5.9.2: {} uc.micro@2.1.0: {} + ufo@1.6.1: {} + uint8array-extras@1.5.0: {} underscore@1.13.7: {} undici-types@7.10.0: {} - undici@7.11.0: {} + undici@7.15.0: {} undici@7.16.0: {} @@ -10890,10 +15699,66 @@ snapshots: unicorn-magic@0.3.0: {} + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-find-after@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-remove-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit: 5.0.0 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + universalify@2.0.1: {} unpipe@1.0.0: {} + unplugin@2.3.10: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.15.0 + picomatch: 4.0.3 + webpack-virtual-modules: 0.6.2 + + update-browserslist-db@1.1.3(browserslist@4.26.2): + dependencies: + browserslist: 4.26.2 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -10907,20 +15772,24 @@ snapshots: punycode: 1.4.1 qs: 6.14.0 - use-callback-ref@1.3.3(@types/react@19.1.10)(react@18.3.1): + use-callback-ref@1.3.3(@types/react@19.1.12)(react@18.3.1): dependencies: react: 18.3.1 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 + + use-merge-value@1.2.0(react@19.1.1): + dependencies: + react: 19.1.1 - use-sidecar@1.1.3(@types/react@19.1.10)(react@18.3.1): + use-sidecar@1.1.3(@types/react@19.1.12)(react@18.3.1): dependencies: detect-node-es: 1.1.0 react: 18.3.1 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 use-sync-external-store@1.5.0(react@19.1.1): dependencies: @@ -10938,13 +15807,15 @@ snapshots: utils-merge@1.0.1: {} + uuid@11.1.0: {} + uuid@8.3.2: {} v8-compile-cache-lib@3.0.1: {} v8-to-istanbul@9.3.0: dependencies: - '@jridgewell/trace-mapping': 0.3.29 + '@jridgewell/trace-mapping': 0.3.30 '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 @@ -10953,24 +15824,39 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - valtio@2.1.5(@types/react@19.1.10)(react@19.1.1): + valtio@2.1.7(@types/react@19.1.12)(react@19.1.1): dependencies: proxy-compare: 3.0.1 optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 react: 19.1.1 vary@1.1.2: {} - version-range@4.14.0: {} + version-range@4.15.0: {} + + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 - vite-node@3.2.4(@types/node@24.2.1)(jiti@2.5.1)(less@4.4.0)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.1): + vite-node@3.2.4(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1): dependencies: cac: 6.7.14 debug: 4.4.1(supports-color@8.1.1) es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.0.5(@types/node@24.2.1)(jiti@2.5.1)(less@4.4.0)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.1) + vite: 6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -10985,51 +15871,79 @@ snapshots: - tsx - yaml - vite@7.0.5(@types/node@24.2.1)(jiti@2.5.1)(less@4.4.0)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.1): + vite-plugin-svgr@4.5.0(rollup@4.50.1)(typescript@5.8.3)(vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)): + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.50.1) + '@svgr/core': 8.1.0(typescript@5.8.3) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.8.3)) + vite: 6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) + transitivePeerDependencies: + - rollup + - supports-color + - typescript + + vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1): + dependencies: + esbuild: 0.25.9 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.50.1 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.3.1 + fsevents: 2.3.3 + jiti: 2.5.1 + less: 4.4.1 + lightningcss: 1.30.1 + tsx: 4.20.5 + yaml: 2.8.1 + + vite@7.1.7(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1): dependencies: - esbuild: 0.25.8 - fdir: 6.4.6(picomatch@4.0.3) + esbuild: 0.25.9 + fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.45.1 - tinyglobby: 0.2.14 + rollup: 4.50.1 + tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.3.1 fsevents: 2.3.3 jiti: 2.5.1 - less: 4.4.0 + less: 4.4.1 lightningcss: 1.30.1 - tsx: 4.20.3 + tsx: 4.20.5 yaml: 2.8.1 - vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.2.1)(jiti@2.5.1)(less@4.4.0)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.1): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.0.5(@types/node@24.2.1)(jiti@2.5.1)(less@4.4.0)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 - chai: 5.2.1 + chai: 5.3.3 debug: 4.4.1(supports-color@8.1.1) expect-type: 1.2.2 - magic-string: 0.30.17 + magic-string: 0.30.19 pathe: 2.0.3 picomatch: 4.0.3 std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.0.5(@types/node@24.2.1)(jiti@2.5.1)(less@4.4.0)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.1) - vite-node: 3.2.4(@types/node@24.2.1)(jiti@2.5.1)(less@4.4.0)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.1) + vite: 6.3.6(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@24.3.1)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 24.2.1 + '@types/node': 24.3.1 transitivePeerDependencies: - jiti - less @@ -11046,8 +15960,31 @@ snapshots: vm-browserify@1.1.2: {} + void-elements@3.1.0: {} + + vscode-jsonrpc@8.2.0: {} + + vscode-languageserver-protocol@3.17.5: + dependencies: + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 + + vscode-languageserver-textdocument@1.0.12: {} + + vscode-languageserver-types@3.17.5: {} + + vscode-languageserver@9.0.1: + dependencies: + vscode-languageserver-protocol: 3.17.5 + + vscode-uri@3.0.8: {} + + web-namespaces@2.0.1: {} + webdriver-bidi-protocol@0.3.6: {} + webpack-virtual-modules@0.6.2: {} + whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 @@ -11087,15 +16024,15 @@ snapshots: wrap-ansi@8.1.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 5.1.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 - wrap-ansi@9.0.0: + wrap-ansi@9.0.2: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 7.2.0 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 wrappy@1.0.2: {} @@ -11121,10 +16058,14 @@ snapshots: y18n@5.0.8: {} + yallist@3.1.1: {} + yallist@4.0.0: {} yallist@5.0.0: {} + yaml@1.10.2: {} + yaml@2.8.1: {} yargs-parser@20.2.9: {} @@ -11194,10 +16135,12 @@ snapshots: zod@3.25.76: {} - zustand@5.0.8(@types/react@19.1.10)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)): + zustand@5.0.8(@types/react@19.1.12)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)): optionalDependencies: - '@types/react': 19.1.10 + '@types/react': 19.1.12 react: 19.1.1 use-sync-external-store: 1.5.0(react@19.1.1) - zx@8.7.1: {} + zwitch@2.0.4: {} + + zx@8.8.1: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7a196ec24..407e7d694 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,7 @@ packages: - vscode-extension - examples/* + - browser patchedDependencies: marked-terminal: patches/marked-terminal.patch diff --git a/src/commands/server.ts b/src/commands/server.ts deleted file mode 100644 index 8c7a89d77..000000000 --- a/src/commands/server.ts +++ /dev/null @@ -1,130 +0,0 @@ -// @ts-nocheck -import { withTrace } from '@openai/agents'; -import { format } from 'date-fns'; -import createDebug from 'debug'; -import { homedir } from 'os'; -import path from 'pathe'; -import type { RunCliOpts } from '..'; -import { Context } from '../context'; -import { PluginHookType } from '../plugin'; -import { contextPlugin } from '../server/plugins/context'; -import { runBrowserServer } from '../server/server'; -import { setupTracing } from '../tracing'; -import * as logger from '../utils/logger'; -import { randomUUID } from '../utils/randomUUID'; - -const debug = createDebug('neovate:commands:server'); - -function printHelp(p: string) { - console.log( - ` -Usage: - ${p} [options] [command] - -Run the code agent with a prompt, interactive by default, use -q/--quiet for non-interactive mode. - -Arguments: - prompt Prompt to run - -Options: - -h, --help Show help - -m, --model Specify model to use - --smallModel Specify a smaller model for some tasks - --logLevel Specify log level - --port Specify port to use - -Examples: - ${p} server "Refactor this file to use hooks." - ${p} server -m gpt-4o "Add tests for the following code." --port 3000 - `.trim(), - ); -} - -export async function runServer(opts: RunCliOpts) { - const { default: yargsParser } = await import('yargs-parser'); - const traceName = `${opts.productName}-server`; - return await withTrace(traceName, async () => { - const startTime = Date.now(); - - const argv = yargsParser(process.argv.slice(2), { - alias: { - model: 'm', - help: 'h', - }, - default: {}, - boolean: ['help', 'plan'], - string: ['model', 'smallModel', 'planModel', 'logLevel'], - number: ['port'], - }); - if (argv.help) { - printHelp(opts.productName.toLowerCase()); - return; - } - const uuid = randomUUID().slice(0, 4); - const traceFile = path.join( - homedir(), - `.${opts.productName.toLowerCase()}`, - 'sessions', - `${opts.productName}-${format(new Date(), 'yyyy-MM-dd-HHmmss')}-${uuid}.jsonl`, - ); - setupTracing(traceFile); - - debug('traceFile', traceFile); - - const cwd = opts.cwd || process.cwd(); - debug('cwd', cwd); - - const context = await Context.create({ - productName: opts.productName, - version: opts.version, - cwd, - argvConfig: { - model: argv.model, - smallModel: argv.smallModel, - planModel: argv.planModel, - quiet: argv.quiet, - plugins: argv.plugin, - }, - plugins: [contextPlugin, ...(opts.plugins || [])], - mcp: true, - }); - - logger.logIntro({ - productName: opts.productName, - version: opts.version, - }); - - logger.logGeneralInfo({ - infos: { - 'Log File': traceFile.replace(homedir(), '~'), - ...context.generalInfo, - }, - }); - - await context.apply({ - hook: 'cliStart', - args: [], - type: PluginHookType.Series, - }); - - await runBrowserServer({ - context, - prompt: argv._[0]! as string, - cwd, - logLevel: argv.logLevel, - port: argv.port, - }); - - await context.apply({ - hook: 'cliEnd', - args: [ - { - startTime, - endTime: Date.now(), - error: null, - }, - ], - type: PluginHookType.Series, - }); - }); -} diff --git a/src/commands/server/routes/files.ts b/src/commands/server/routes/files.ts new file mode 100644 index 000000000..47e3b9559 --- /dev/null +++ b/src/commands/server/routes/files.ts @@ -0,0 +1,116 @@ +import { Type } from '@sinclair/typebox'; +import createDebug from 'debug'; +import type { FastifyPluginAsync } from 'fastify'; +import * as fs from 'fs/promises'; +import path from 'pathe'; +import type { ContextCreateOpts } from '../../../context'; + +const debug = createDebug('neovate:server:files'); + +const FileEditRequestSchema = Type.Object({ + filePath: Type.String(), + content: Type.String(), +}); + +const FileReadRequestSchema = Type.Object({ + filePath: Type.String(), +}); + +interface FileEditRequest { + filePath: string; + content: string; +} + +const filesRoute: FastifyPluginAsync = async (app, opts) => { + app.get<{ Querystring: { filePath: string } }>( + '/files/read', + { + schema: { + querystring: FileReadRequestSchema, + }, + }, + async (request, reply) => { + try { + const { filePath } = request.query; + const cwd = opts.cwd; + const absolutePath = path.resolve(cwd, filePath); + + if (!absolutePath.startsWith(cwd)) { + return reply.code(400).send({ + success: false, + error: 'File path is outside of the project directory.', + }); + } + + const content = await fs.readFile(absolutePath, 'utf-8'); + + return reply.send({ + success: true, + data: { + content, + filePath, + }, + }); + } catch (error) { + debug(`File read API error:`, error); + if (error instanceof Error && error.message === 'ENOENT') { + return reply.code(404).send({ + success: false, + message: 'File not found.', + }); + } + return reply.code(500).send({ + success: false, + message: + error instanceof Error + ? error.message + : 'Unknown error occurred while reading file.', + }); + } + }, + ); + + app.post<{ Body: FileEditRequest }>( + '/files/edit', + { + schema: { + body: FileEditRequestSchema, + }, + }, + async (request, reply) => { + try { + const { filePath, content } = request.body; + const cwd = opts.cwd; + const absolutePath = path.resolve(cwd, filePath); + + if (!absolutePath.startsWith(cwd)) { + return reply.code(400).send({ + success: false, + error: 'File path is outside of the project directory.', + }); + } + + await fs.writeFile(absolutePath, content, 'utf-8'); + + return reply.send({ + success: true, + data: { + message: 'File updated successfully.', + filePath, + }, + }); + } catch (error) { + debug(`File edit API error:`, error); + return reply.code(500).send({ + success: false, + message: + error instanceof Error + ? error.message + : 'Unknown error occurred while editing file.', + }); + } + }, + ); +}; + +export default filesRoute; diff --git a/src/commands/server/routes/folders.ts b/src/commands/server/routes/folders.ts new file mode 100644 index 000000000..9716b1928 --- /dev/null +++ b/src/commands/server/routes/folders.ts @@ -0,0 +1,172 @@ +import { Type } from '@sinclair/typebox'; +import createDebug from 'debug'; +import type { FastifyPluginAsync } from 'fastify'; +import * as fsSync from 'fs'; +import * as fs from 'fs/promises'; +import path from 'pathe'; +import type { ContextCreateOpts } from '../../../context'; + +const debug = createDebug('neovate:server:folders'); + +const hiddenPrefix = '.'; +const isPlatformWindows = process.platform.indexOf('win') === 0; + +interface FolderItem { + name: string; + path: string; + isPackage: boolean; + hidden: boolean; + __typename: 'Folder'; +} + +interface FolderResponse { + name: string; + path: string; + isPackage: boolean; + children: FolderItem[]; + __typename: 'Folder'; +} + +const FolderListRequestSchema = Type.Object({ + path: Type.String(), +}); + +interface FolderListRequest { + path: string; +} + +async function isDirectory(filePath: string): Promise { + try { + const stats = await fs.stat(filePath); + return stats.isDirectory(); + } catch (e) { + if (process.env.DEBUG) debug('isDirectory error:', e); + return false; + } +} + +async function isHidden(filePath: string): Promise { + try { + const basename = path.basename(filePath); + const prefixed = basename.charAt(0) === hiddenPrefix; + + if (isPlatformWindows) { + // On Windows, check file attributes + try { + const stats = await fs.stat(filePath); + // For now, just use the prefix check + return prefixed; + } catch (e) { + return prefixed; + } + } + + return prefixed; + } catch (e) { + if (process.env.DEBUG) debug('isHidden error:', e); + return false; + } +} + +function isPackage(folderPath: string): boolean { + try { + return fsSync.existsSync(path.join(folderPath, 'package.json')); + } catch (e) { + debug('isPackage error:', e); + return false; + } +} + +async function listFolders(basePath: string): Promise { + try { + const files = await fs.readdir(basePath, 'utf8'); + + const folderPromises = files.map(async (file) => { + const fullPath = path.join(basePath, file); + + const [isDir, hidden] = await Promise.all([ + isDirectory(fullPath), + isHidden(fullPath), + ]); + + if (!isDir) { + return null; + } + + const isPackageFolder = isPackage(fullPath); + + return { + name: file, + path: fullPath, + isPackage: isPackageFolder, + hidden, + __typename: 'Folder' as const, + }; + }); + + const folders = await Promise.all(folderPromises); + return folders.filter((folder): folder is FolderItem => folder !== null); + } catch (e) { + debug('listFolders error:', e); + throw e; + } +} + +const foldersRoute: FastifyPluginAsync = async ( + app, + opts, +) => { + // List folders endpoint + app.get<{ Querystring: FolderListRequest }>( + '/folders/list', + { + schema: { + querystring: FolderListRequestSchema, + }, + }, + async (request, reply) => { + try { + const { path: folderPath } = request.query; + + // Security check: ensure the path is accessible + const absolutePath = path.resolve(folderPath); + + // Check if directory exists and is accessible + const dirExists = await isDirectory(absolutePath); + if (!dirExists) { + return reply.code(404).send({ + success: false, + message: 'Directory not found or not accessible.', + }); + } + + const children = await listFolders(absolutePath); + const isPackageFolder = isPackage(absolutePath); + + const response: FolderResponse = { + name: path.basename(absolutePath) || absolutePath, + path: absolutePath, + isPackage: isPackageFolder, + children, + __typename: 'Folder', + }; + + return reply.send({ + success: true, + data: response, + }); + } catch (error) { + debug('Folders list API error:', error); + return reply.code(500).send({ + success: false, + message: + error instanceof Error + ? error.message + : 'Unknown error occurred while listing folders.', + }); + } + }, + ); +}; + +export default foldersRoute; diff --git a/src/commands/server/routes/project.ts b/src/commands/server/routes/project.ts new file mode 100644 index 000000000..91b51104a --- /dev/null +++ b/src/commands/server/routes/project.ts @@ -0,0 +1,271 @@ +import { spawn } from 'node:child_process'; +import fs from 'node:fs'; +import os from 'node:os'; +import { Type } from '@sinclair/typebox'; +import createDebug from 'debug'; +import type { FastifyPluginAsync } from 'fastify'; +import path from 'pathe'; +import type { ContextCreateOpts } from '../../../context'; +import { Paths } from '../../../paths'; +import { getGitStatus } from '../../../utils/git'; + +const debug = createDebug('neovate:server:project'); + +interface SessionInfo { + sessionId: string; + modified: Date; + created: Date; + messageCount: number; + summary: string; +} + +interface ProjectInfo { + name: string; + path: string; + gitBranch?: string; + gitStatus?: 'clean' | 'modified' | 'staged' | 'conflicted'; + sessions: SessionInfo[]; +} + +type GitStatusType = ProjectInfo['gitStatus']; + +function parseGitStatus(gitStatusOutput: string): GitStatusType { + if (!gitStatusOutput || gitStatusOutput.trim() === '') { + return 'clean'; + } + + const lines = gitStatusOutput.split('\n').filter((line) => line.trim()); + + let hasModified = false; + let hasStaged = false; + let hasConflicted = false; + + for (const line of lines) { + const statusCode = line.substring(0, 2); + + // Check for conflicts (both sides modified) + if (statusCode.includes('U') || line.includes('<<<<<<< HEAD')) { + hasConflicted = true; + break; + } + + // Check for staged changes (first character) + if (statusCode[0] !== ' ' && statusCode[0] !== '?') { + hasStaged = true; + } + + // Check for modified changes (second character) + if (statusCode[1] !== ' ' && statusCode[1] !== '?') { + hasModified = true; + } + } + + if (hasConflicted) return 'conflicted'; + if (hasStaged) return 'staged'; + if (hasModified) return 'modified'; + + return 'clean'; +} + +interface ProjectRequest { + folder?: string; +} + +interface OpenEditorRequest { + projectPath: string; +} + +const EDITORS = { + vscode: { + command: 'code', + args: ['.'], + name: 'Visual Studio Code', + }, + webstorm: { + command: 'webstorm', + args: ['.'], + name: 'WebStorm', + }, + idea: { + command: 'idea', + args: ['.'], + name: 'IntelliJ IDEA', + }, + sublime: { + command: 'subl', + args: ['.'], + name: 'Sublime Text', + }, + atom: { + command: 'atom', + args: ['.'], + name: 'Atom', + }, + vim: { + command: 'vim', + args: ['.'], + name: 'Vim', + }, + emacs: { + command: 'emacs', + args: ['.'], + name: 'Emacs', + }, +}; + +function commandExists(command: string): Promise { + return new Promise((resolve) => { + const platform = os.platform(); + const cmd = platform === 'win32' ? 'where' : 'which'; + + const child = spawn(cmd, [command], { + stdio: 'ignore', + shell: true, + }); + + child.on('close', (code) => { + resolve(code === 0); + }); + + child.on('error', () => { + resolve(false); + }); + }); +} + +async function findAvailableEditor(): Promise< + (typeof EDITORS)[keyof typeof EDITORS] | null +> { + for (const editor of Object.values(EDITORS)) { + if (await commandExists(editor.command)) { + return editor; + } + } + return null; +} + +async function openProjectInEditor(projectPath: string): Promise { + if (!fs.existsSync(projectPath)) { + throw new Error(`Project path does not exist: ${projectPath}`); + } + + const stat = fs.statSync(projectPath); + if (!stat.isDirectory()) { + throw new Error(`Path is not a directory: ${projectPath}`); + } + + const editor = await findAvailableEditor(); + if (!editor) { + throw new Error( + 'No supported editor found. Please install VS Code, WebStorm, Sublime Text, or another supported editor.', + ); + } + + return new Promise((resolve, reject) => { + const child = spawn(editor.command, editor.args, { + cwd: projectPath, + detached: true, + stdio: 'ignore', + }); + + child.unref(); + + child.on('error', (error) => { + reject(new Error(`Failed to open ${editor.name}: ${error.message}`)); + }); + + // 给编辑器一点时间启动 + setTimeout(() => { + resolve(); + }, 1000); + }); +} + +const projectRoute: FastifyPluginAsync = async ( + app, + opts, +) => { + app.get<{ Querystring: ProjectRequest }>( + '/project/info', + { + schema: { + querystring: Type.Object({ + folder: Type.Optional(Type.String()), + }), + }, + }, + async (request, reply) => { + try { + const { folder } = request.query; + const cwd = folder || opts.cwd; + const projectPath = path.resolve(cwd); + const projectName = path.basename(projectPath); + const paths = new Paths({ + productName: opts.productName, + cwd, + }); + const sessions = paths.getAllSessions(); + + const gitStatus = await getGitStatus({ cwd }); + + const projectInfo: ProjectInfo = { + name: projectName, + path: projectPath, + gitBranch: gitStatus?.branch || undefined, + gitStatus: gitStatus ? parseGitStatus(gitStatus.status) : undefined, + sessions, + }; + + return reply.send({ + success: true, + data: projectInfo, + }); + } catch (error) { + debug('Project info API error:', error); + return reply.code(500).send({ + success: false, + message: + error instanceof Error + ? error.message + : 'Unknown error occurred while getting project info.', + }); + } + }, + ); + + app.post<{ Body: OpenEditorRequest }>( + '/project/open-in-editor', + { + schema: { + body: Type.Object({ + projectPath: Type.String(), + }), + }, + }, + async (request, reply) => { + try { + const { projectPath } = request.body; + + debug('Opening project in editor:', projectPath); + + await openProjectInEditor(projectPath); + + return reply.send({ + success: true, + message: 'Project opened in editor successfully', + }); + } catch (error) { + debug('Open in editor API error:', error); + return reply.code(500).send({ + success: false, + message: + error instanceof Error + ? error.message + : 'Unknown error occurred while opening project in editor.', + }); + } + }, + ); +}; + +export default projectRoute; diff --git a/src/commands/server/routes/session.ts b/src/commands/server/routes/session.ts new file mode 100644 index 000000000..7d6211cc5 --- /dev/null +++ b/src/commands/server/routes/session.ts @@ -0,0 +1,85 @@ +import { Type } from '@sinclair/typebox'; +import type { FastifyPluginAsync } from 'fastify'; +import type { ContextCreateOpts } from '../../../context'; +import { GlobalData } from '../../../globalData'; +import { Paths } from '../../../paths'; +import { loadSessionMessages, Session } from '../../../session'; + +const SessionInitializeRequestSchema = Type.Object({ + cwd: Type.Optional(Type.String()), + resume: Type.Optional(Type.String()), + continue: Type.Optional(Type.Boolean()), +}); + +interface SessionInitializeRequest { + cwd?: string; + resume?: string; + continue?: boolean; +} + +const sessionRoute: FastifyPluginAsync = async ( + app, + opts, +) => { + app.post<{ Body: SessionInitializeRequest }>( + '/session/initialize', + { + schema: { + body: SessionInitializeRequestSchema, + }, + }, + async (request, reply) => { + try { + const { body } = request; + + const cwd = body.cwd || opts.cwd; + + const paths = new Paths({ + productName: opts.productName, + cwd, + }); + + const sessionId = (() => { + if (body.resume) { + return body.resume; + } + if (body.continue) { + return paths.getLatestSessionId() || Session.createSessionId(); + } + return Session.createSessionId(); + })(); + + const [messages, history] = (() => { + const logPath = paths.getSessionLogPath(sessionId); + const messages = loadSessionMessages({ logPath }); + const globalData = new GlobalData({ + globalDataPath: paths.getGlobalDataPath(), + }); + const history = globalData.getProjectHistory({ cwd }); + return [messages, history]; + })(); + + return reply.send({ + success: true, + data: { + cwd, + sessionId, + messages, + history, + logFile: paths.getSessionLogPath(sessionId), + }, + }); + } catch (error) { + return reply.code(500).send({ + success: false, + message: + error instanceof Error + ? error.message + : 'Unknown error occurred while initializing session.', + }); + } + }, + ); +}; + +export default sessionRoute; diff --git a/src/commands/server/server.ts b/src/commands/server/server.ts new file mode 100644 index 000000000..9968b7a54 --- /dev/null +++ b/src/commands/server/server.ts @@ -0,0 +1,54 @@ +import portfinder from 'portfinder'; +import { WebServer } from './web-server'; + +const DEFAULT_PORT = 1024; +const DEFAULT_HOST = '127.0.0.1'; + +export async function runServer(opts: { cwd: string; contextCreateOpts: any }) { + const { default: yargsParser } = await import('yargs-parser'); + const argv = yargsParser(process.argv.slice(2), { + alias: { + port: 'p', + host: 'h', + }, + number: ['port'], + string: ['host'], + }); + + const port = await portfinder.getPortPromise({ + port: Number.parseInt(String(argv.port || DEFAULT_PORT), 10), + }); + + const server = new WebServer({ + port, + host: argv.host || DEFAULT_HOST, + contextCreateOpts: opts.contextCreateOpts, + cwd: opts.cwd, + }); + + let isShuttingDown = false; + + const shutdown = async () => { + if (isShuttingDown) return; + isShuttingDown = true; + + console.log('\n[WebServer] Shutting down...'); + try { + await server.stop(); + process.exit(0); + } catch (error) { + console.error('[WebServer] Error during shutdown:', error); + process.exit(1); + } + }; + + process.on('SIGINT', shutdown); + process.on('SIGTERM', shutdown); + + try { + await server.start(); + } catch (error) { + console.error('Failed to start server:', error); + process.exit(1); + } +} diff --git a/src/server/types/files.ts b/src/commands/server/types/files.ts similarity index 100% rename from src/server/types/files.ts rename to src/commands/server/types/files.ts diff --git a/src/commands/server/web-server.ts b/src/commands/server/web-server.ts new file mode 100644 index 000000000..3a3ea5ef7 --- /dev/null +++ b/src/commands/server/web-server.ts @@ -0,0 +1,264 @@ +import type { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'; +import fastify, { type FastifyInstance } from 'fastify'; +import fs from 'fs'; +import path from 'pathe'; +import { fileURLToPath } from 'url'; +import { WebSocketServer } from 'ws'; +import type { ContextCreateOpts } from '../../context'; +import { NodeBridge } from '../../nodeBridge'; +import { isLocal } from '../../utils/isLocal'; +import { WebSocketTransport } from './websocketTransport'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const BROWSER_DIST_PATH = isLocal() + ? path.resolve(__dirname, '../../../dist/browser') + : path.resolve(__dirname, './browser'); + +const BASE_API_PREFIX = '/api'; + +interface WebServerOptions { + port: number; + host?: string; + contextCreateOpts: ContextCreateOpts; + cwd: string; +} + +class WebServer { + private app: FastifyInstance; + private wss!: WebSocketServer; + private clients = new Map< + string, + { transport: WebSocketTransport; bridge: NodeBridge } + >(); + private port: number; + private host: string; + private contextCreateOpts: ContextCreateOpts; + private isWssRunning = false; + private cwd: string; + + constructor(options: WebServerOptions) { + this.port = options.port; + this.host = options.host || 'localhost'; + this.contextCreateOpts = options.contextCreateOpts || {}; + this.cwd = options.cwd; + // Initialize Fastify app + this.app = fastify({ + logger: false, + bodyLimit: 100 * 1024 * 1024, // 100MB limit for handling large images and files + }).withTypeProvider(); + } + + private async registerPlugins() { + await this.app.register(import('@fastify/cors'), { + origin: true, + methods: ['GET', 'HEAD', 'PUT', 'POST', 'PATCH', 'DELETE', 'OPTIONS'], + credentials: true, + allowedHeaders: [ + 'Content-Type', + 'Authorization', + 'X-Requested-With', + 'Accept', + 'Origin', + 'Sec-WebSocket-Protocol', + ], + }); + + await this.app.register(import('@fastify/compress'), { + global: true, + }); + + await this.app.register(import('@fastify/static'), { + root: BROWSER_DIST_PATH, + prefix: '/', + wildcard: false, + }); + + this.app.get('*', async (request, reply) => { + if (request.url.startsWith(BASE_API_PREFIX)) { + return reply.status(404).send('Not Found'); + } + + const htmlPath = path.join(BROWSER_DIST_PATH, 'index.html'); + if (fs.existsSync(htmlPath)) { + return reply.sendFile('index.html'); + } else { + return reply.status(404).send('Not Found'); + } + }); + } + + private async setupRoutes() { + // Health check endpoint + this.app.get('/health', async (_request, reply) => { + return reply.send({ + status: 'ok', + clients: this.clients.size, + timestamp: new Date().toISOString(), + }); + }); + + // Client info endpoint + this.app.get('/clients', async (_request, reply) => { + const clientInfo = Array.from(this.clients.entries()).map( + ([id, client]) => ({ + id, + connected: client.transport.isConnected(), + state: client.transport.getState(), + }), + ); + return reply.send(clientInfo); + }); + + // files + await this.app.register(import('./routes/files'), { + prefix: BASE_API_PREFIX, + ...this.contextCreateOpts, + }); + + // session + await this.app.register(import('./routes/session'), { + prefix: BASE_API_PREFIX, + ...this.contextCreateOpts, + cwd: this.cwd, + }); + + // folders + await this.app.register(import('./routes/folders'), { + prefix: BASE_API_PREFIX, + ...this.contextCreateOpts, + cwd: this.cwd, + }); + + // project + await this.app.register(import('./routes/project'), { + prefix: BASE_API_PREFIX, + ...this.contextCreateOpts, + cwd: this.cwd, + }); + } + + private setupWebSocket() { + // Initialize WebSocket server + this.wss = new WebSocketServer({ + server: this.app.server, + path: '/ws', + }); + this.isWssRunning = true; + + this.wss.on('connection', (ws, _req) => { + const clientId = this.generateClientId(); + console.log(`[WebServer] New client connected: ${clientId}`); + + // Create WebSocket transport + const transport = new WebSocketTransport(ws); + + // Create NodeBridge instance for this client + const bridge = new NodeBridge({ + contextCreateOpts: this.contextCreateOpts, + }); + + // Connect transport to bridge's message bus + bridge.messageBus.setTransport(transport); + + // Store client + this.clients.set(clientId, { transport, bridge }); + + // Handle transport events + transport.onError((error) => { + console.error(`[WebServer] Client ${clientId} error:`, error); + }); + + transport.onClose(() => { + console.log(`[WebServer] Client ${clientId} disconnected`); + this.clients.delete(clientId); + }); + + // Send welcome message + bridge.messageBus + .emitEvent('connected', { + clientId, + timestamp: new Date().toISOString(), + message: 'Welcome to Neovate WebSocket Server', + }) + .catch(console.error); + }); + + this.wss.on('error', (error) => { + console.error('[WebServer] WebSocket server error:', error); + }); + } + + private generateClientId(): string { + return `client-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; + } + + async start(): Promise { + try { + // Setup routes and WebSocket first + await this.registerPlugins(); + await this.setupRoutes(); + this.setupWebSocket(); + + // Start Fastify server + await this.app.listen({ + port: this.port, + host: this.host, + }); + + console.log( + `[WebServer] Server running at http://${this.host}:${this.port}`, + ); + console.log( + `[WebServer] WebSocket endpoint: ws://${this.host}:${this.port}/ws`, + ); + } catch (err) { + console.error('[WebServer] Failed to start server:', err); + throw err; + } + } + + async stop(): Promise { + try { + // Close all client connections + for (const [clientId, { transport }] of this.clients) { + console.log(`[WebServer] Closing client ${clientId}`); + try { + await transport.close(); + } catch (err) { + console.warn(`[WebServer] Failed to close client ${clientId}:`, err); + } + } + this.clients.clear(); + + // Close WebSocket server + if (this.wss && this.isWssRunning) { + await new Promise((resolve, reject) => { + this.wss.close((err) => { + this.isWssRunning = false; + if (err && err.message !== 'The server is not running') { + reject(err); + } else { + resolve(); + } + }); + }); + } + + // Close Fastify server + await this.app.close(); + console.log('[WebServer] Server stopped'); + } catch (err) { + console.error('[WebServer] Error stopping server:', err); + throw err; + } + } + + getClients() { + return this.clients; + } +} + +// Export the WebServer class +export { WebServer }; diff --git a/src/commands/servernext/websocketTransport.ts b/src/commands/server/websocketTransport.ts similarity index 100% rename from src/commands/servernext/websocketTransport.ts rename to src/commands/server/websocketTransport.ts diff --git a/src/commands/servernext/server.ts b/src/commands/servernext/server.ts deleted file mode 100644 index 0ee09afbf..000000000 --- a/src/commands/servernext/server.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { WebServer } from './web-server'; - -const DEFAULT_PORT = 7001; -const DEFAULT_HOST = '127.0.0.1'; - -export async function runServerNext(opts: { contextCreateOpts: any }) { - const { default: yargsParser } = await import('yargs-parser'); - const argv = yargsParser(process.argv.slice(2), { - alias: { - port: 'p', - host: 'h', - }, - number: ['port'], - string: ['host'], - }); - const server = new WebServer({ - port: argv.port || DEFAULT_PORT, - host: argv.host || DEFAULT_HOST, - contextCreateOpts: opts.contextCreateOpts, - }); - process.on('SIGINT', async () => { - console.log('\n[WebServer] Shutting down...'); - await server.stop(); - process.exit(0); - }); - - process.on('SIGTERM', async () => { - console.log('\n[WebServer] Shutting down...'); - await server.stop(); - process.exit(0); - }); - try { - await server.start(); - } catch (error) { - console.error('Failed to start server:', error); - process.exit(1); - } -} diff --git a/src/commands/servernext/web-client.html b/src/commands/servernext/web-client.html deleted file mode 100644 index a77018cad..000000000 --- a/src/commands/servernext/web-client.html +++ /dev/null @@ -1,965 +0,0 @@ - - - - - - Takumi Web Client Demo - - - -
    -

    🚀 Takumi Web Client Demo

    - -
    - Disconnected -
    - -
    -
    -

    Connection & Initialize

    -
    - - -
    -
    - - -
    - - - -
    - -
    -

    Send Message

    -
    - - -
    -
    - - -
    -
    - -
    - - -
    - -
    -

    Quick Actions

    -
    - - - - - - -
    -
    - -
    -

    Custom Request

    -
    - - -
    -
    - - -
    - -
    - -
    -

    Messages Log

    - -
    -
    -
    -
    - - - - diff --git a/src/commands/servernext/web-client.ts b/src/commands/servernext/web-client.ts deleted file mode 100644 index 62bc14ad0..000000000 --- a/src/commands/servernext/web-client.ts +++ /dev/null @@ -1,546 +0,0 @@ -#!/usr/bin/env node -import readline from 'readline'; -import type { MessageHandler } from '../../messageBus'; -import { MessageBus } from '../../messageBus'; -import { WebSocketTransport } from './websocketTransport'; - -// ANSI color codes for better terminal output -const colors = { - reset: '\x1b[0m', - bright: '\x1b[1m', - dim: '\x1b[2m', - red: '\x1b[31m', - green: '\x1b[32m', - yellow: '\x1b[33m', - blue: '\x1b[34m', - magenta: '\x1b[35m', - cyan: '\x1b[36m', - white: '\x1b[37m', -}; - -class WebClient { - private transport: WebSocketTransport | null = null; - private messageBus: MessageBus | null = null; - private rl: readline.Interface; - private wsUrl = 'ws://localhost:7001/ws'; - private cwd = '/tmp'; - private sessionId: string | undefined; - private currentSessionId: string | undefined; - private isConnected = false; - private disconnectMessageShown = false; - - constructor() { - this.rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - prompt: `${colors.cyan}takumi> ${colors.reset}`, - }); - - this.setupEventHandlers(); - this.printWelcome(); - this.showMenu(); - } - - private printWelcome() { - console.log(`${colors.bright}${colors.magenta} -╔════════════════════════════════════════╗ -║ 🚀 Takumi Web Client (Node.js) ║ -╚════════════════════════════════════════╝ -${colors.reset}`); - console.log( - `${colors.dim}Type 'help' for available commands${colors.reset}\n`, - ); - } - - private showMenu() { - this.rl.prompt(); - } - - private setupEventHandlers() { - this.rl.on('line', async (line) => { - const input = line.trim(); - if (!input) { - this.showMenu(); - return; - } - - const [command, ...args] = input.split(' '); - const arg = args.join(' '); - - try { - switch (command.toLowerCase()) { - case 'help': - case 'h': - this.showHelp(); - break; - case 'connect': - case 'c': - await this.connect(arg || this.wsUrl); - break; - case 'disconnect': - case 'd': - await this.disconnect(); - break; - case 'init': - case 'i': - await this.initialize(); - break; - case 'send': - case 's': - await this.sendMessage(arg || 'Hello, how can I help you today?'); - break; - case 'cancel': - await this.cancel(); - break; - case 'status': - await this.executeCommand('getStatus', { - cwd: this.cwd, - sessionId: this.currentSessionId || this.sessionId || 'default', - }); - break; - case 'sessions': - await this.executeCommand('getAllSessions', { cwd: this.cwd }); - break; - case 'models': - await this.executeCommand('getModels', { cwd: this.cwd }); - break; - case 'outputs': - await this.executeCommand('getOutputStyles', { cwd: this.cwd }); - break; - case 'commands': - await this.executeCommand('getSlashCommands', { cwd: this.cwd }); - break; - case 'mcp': - await this.executeCommand('getMcpStatus', { cwd: this.cwd }); - break; - case 'paths': - await this.executeCommand('getPaths', { cwd: this.cwd }); - break; - case 'seturl': - this.wsUrl = arg || 'ws://localhost:3000/ws'; - console.log( - `${colors.green}✓${colors.reset} WebSocket URL set to: ${this.wsUrl}`, - ); - break; - case 'setcwd': - this.cwd = arg || '/tmp'; - console.log( - `${colors.green}✓${colors.reset} Working directory set to: ${this.cwd}`, - ); - break; - case 'setsession': - this.sessionId = arg || undefined; - console.log( - `${colors.green}✓${colors.reset} Session ID ${arg ? `set to: ${this.sessionId}` : 'cleared'}`, - ); - break; - case 'request': - case 'r': - await this.customRequest(arg); - break; - case 'clear': - console.clear(); - this.printWelcome(); - break; - case 'info': - this.showInfo(); - break; - case 'exit': - case 'quit': - case 'q': - await this.exit(); - break; - default: - console.log( - `${colors.red}Unknown command: ${command}${colors.reset}`, - ); - console.log( - `${colors.dim}Type 'help' for available commands${colors.reset}`, - ); - } - } catch (error: any) { - console.error(`${colors.red}Error: ${error.message}${colors.reset}`); - } - - this.showMenu(); - }); - - this.rl.on('close', () => { - this.exit(); - }); - } - - private showHelp() { - console.log(` -${colors.bright}Available Commands:${colors.reset} - -${colors.cyan}Connection:${colors.reset} - ${colors.green}connect [url]${colors.reset} Connect to WebSocket server - ${colors.green}disconnect${colors.reset} Disconnect from server - ${colors.green}init${colors.reset} Initialize session - -${colors.cyan}Messaging:${colors.reset} - ${colors.green}send [message]${colors.reset} Send a message - ${colors.green}cancel${colors.reset} Cancel current operation - -${colors.cyan}Information:${colors.reset} - ${colors.green}status${colors.reset} Get status - ${colors.green}sessions${colors.reset} List all sessions - ${colors.green}models${colors.reset} Get available models - ${colors.green}outputs${colors.reset} Get output styles - ${colors.green}commands${colors.reset} Get slash commands - ${colors.green}mcp${colors.reset} Get MCP status - ${colors.green}paths${colors.reset} Get file paths - -${colors.cyan}Configuration:${colors.reset} - ${colors.green}seturl [url]${colors.reset} Set WebSocket URL - ${colors.green}setcwd [path]${colors.reset} Set working directory - ${colors.green}setsession [id]${colors.reset} Set session ID - -${colors.cyan}Advanced:${colors.reset} - ${colors.green}request ${colors.reset} Send custom request - Example: request {"method":"getStatus","params":{"cwd":"/tmp"}} - -${colors.cyan}Utility:${colors.reset} - ${colors.green}info${colors.reset} Show connection info - ${colors.green}clear${colors.reset} Clear screen - ${colors.green}help${colors.reset} Show this help - ${colors.green}exit${colors.reset} Exit the client - -${colors.dim}Shortcuts: c=connect, d=disconnect, i=init, s=send, r=request, q=quit${colors.reset} -`); - } - - private showInfo() { - console.log(` -${colors.bright}Connection Info:${colors.reset} - ${colors.cyan}Status:${colors.reset} ${this.isConnected ? colors.green + 'Connected' : colors.red + 'Disconnected'}${colors.reset} - ${colors.cyan}URL:${colors.reset} ${this.wsUrl} - ${colors.cyan}CWD:${colors.reset} ${this.cwd} - ${colors.cyan}Session ID:${colors.reset} ${this.sessionId || '(none)'} -`); - } - - private async connect(url?: string) { - if (this.isConnected) { - console.log(`${colors.yellow}Already connected${colors.reset}`); - return; - } - - const wsUrl = url || this.wsUrl; - console.log(`${colors.cyan}Connecting to ${wsUrl}...${colors.reset}`); - - try { - this.disconnectMessageShown = false; // Reset flag for new connection - this.transport = new WebSocketTransport(wsUrl); - this.messageBus = new MessageBus(); - - this.transport.onClose(() => { - if (!this.disconnectMessageShown) { - console.log(`\n${colors.red}Disconnected from server${colors.reset}`); - this.disconnectMessageShown = true; - } - this.isConnected = false; - }); - - this.transport.onError((error) => { - console.error( - `${colors.red}Transport error: ${error.message}${colors.reset}`, - ); - }); - - // Set up event handlers - this.messageBus.onEvent('connected', (data) => { - console.log( - `${colors.green}Server event:${colors.reset}`, - JSON.stringify(data, null, 2), - ); - }); - - this.messageBus.onEvent('message', (data) => { - console.log( - `${colors.blue}Message event:${colors.reset}`, - JSON.stringify(data, null, 2), - ); - }); - - this.messageBus.onEvent('textDelta', (data) => { - process.stdout.write(data.text); - }); - - this.messageBus.onEvent('chunk', (data) => { - // Silently handle chunks to avoid spam - }); - - // Handle tool approval requests - const toolApprovalHandler: MessageHandler = async (params) => { - console.log( - `\n${colors.yellow}Tool approval requested: ${params.toolUse.name}${colors.reset}`, - ); - - return new Promise((resolve) => { - this.rl.question('Approve? (y/n): ', (answer) => { - const approved = answer.toLowerCase() === 'y'; - console.log( - approved - ? `${colors.green}Approved${colors.reset}` - : `${colors.red}Denied${colors.reset}`, - ); - resolve({ approved }); - }); - }); - }; - this.messageBus.registerHandler('toolApproval', toolApprovalHandler); - - // Set transport first - this.messageBus!.setTransport(this.transport!); - - // Wait for connection to be established - await new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - reject(new Error('Connection timeout')); - }, 5000); - - const checkConnection = setInterval(() => { - if (this.transport!.isConnected()) { - clearInterval(checkConnection); - clearTimeout(timeout); - resolve(); - } - }, 100); - }); - - this.isConnected = true; - console.log(`${colors.green}✓ Connected successfully${colors.reset}`); - } catch (error: any) { - console.error( - `${colors.red}Failed to connect: ${error.message}${colors.reset}`, - ); - this.transport = null; - this.messageBus = null; - this.isConnected = false; - } - } - - private async disconnect() { - if (!this.isConnected) { - console.log(`${colors.yellow}Not connected${colors.reset}`); - return; - } - - try { - if (this.transport) { - await this.transport.close(); - } - this.transport = null; - this.messageBus = null; - this.isConnected = false; - console.log(`${colors.green}✓ Disconnected${colors.reset}`); - } catch (error: any) { - console.error( - `${colors.red}Failed to disconnect: ${error.message}${colors.reset}`, - ); - } - } - - private async initialize() { - if (!this.isConnected) { - console.log( - `${colors.red}Not connected. Use 'connect' first.${colors.reset}`, - ); - return; - } - - try { - console.log(`${colors.cyan}Initializing...${colors.reset}`); - const result = await this.messageBus!.request('session.initialize', { - cwd: this.cwd, - sessionId: this.sessionId, - }); - - if (result.success) { - console.log( - `${colors.green}✓ Initialized: ${result.data.productName} v${result.data.version}${colors.reset}`, - ); - console.log( - `${colors.cyan}Model: ${result.data.model || 'default'}${colors.reset}`, - ); - } else { - console.log(`${colors.red}Initialization failed${colors.reset}`); - } - console.log(JSON.stringify(result, null, 2)); - } catch (error: any) { - console.error( - `${colors.red}Failed to initialize: ${error.message}${colors.reset}`, - ); - } - } - - private async sendMessage(message: string) { - if (!this.isConnected) { - console.log( - `${colors.red}Not connected. Use 'connect' first.${colors.reset}`, - ); - return; - } - - try { - console.log(`${colors.cyan}Sending message...${colors.reset}`); - const result = await this.messageBus!.request('session.send', { - message, - cwd: this.cwd, - sessionId: this.sessionId, - planMode: false, - }); - - if (this.sessionId) { - this.currentSessionId = this.sessionId; - } - - console.log(`${colors.green}Response received:${colors.reset}`); - console.log(JSON.stringify(result, null, 2)); - } catch (error: any) { - console.error( - `${colors.red}Failed to send message: ${error.message}${colors.reset}`, - ); - } - } - - private async cancel() { - if (!this.isConnected) { - console.log( - `${colors.red}Not connected. Use 'connect' first.${colors.reset}`, - ); - return; - } - - const sessionId = this.currentSessionId || this.sessionId; - if (!sessionId) { - console.log( - `${colors.red}No session ID available to cancel${colors.reset}`, - ); - return; - } - - try { - const result = await this.messageBus!.request('session.cancel', { - cwd: this.cwd, - sessionId, - }); - console.log(`${colors.green}✓ Operation cancelled${colors.reset}`); - console.log(JSON.stringify(result, null, 2)); - } catch (error: any) { - console.error( - `${colors.red}Failed to cancel: ${error.message}${colors.reset}`, - ); - } - } - - private async executeCommand(method: string, params: any) { - if (!this.isConnected) { - console.log( - `${colors.red}Not connected. Use 'connect' first.${colors.reset}`, - ); - return; - } - - try { - console.log(`${colors.cyan}Executing ${method}...${colors.reset}`); - const result = await this.messageBus!.request(method, params); - console.log(`${colors.green}Result:${colors.reset}`); - console.log(JSON.stringify(result, null, 2)); - } catch (error: any) { - console.error( - `${colors.red}Failed to execute ${method}: ${error.message}${colors.reset}`, - ); - } - } - - private async customRequest(jsonStr: string) { - if (!this.isConnected) { - console.log( - `${colors.red}Not connected. Use 'connect' first.${colors.reset}`, - ); - return; - } - - try { - const { method, params } = JSON.parse(jsonStr); - - // Add cwd if not present and method likely requires it - if (!params.cwd && this.requiresCwd(method)) { - params.cwd = this.cwd; - } - - console.log( - `${colors.cyan}Sending custom request: ${method}${colors.reset}`, - ); - const result = await this.messageBus!.request(method, params); - console.log(`${colors.green}Result:${colors.reset}`); - console.log(JSON.stringify(result, null, 2)); - } catch (error: any) { - console.error( - `${colors.red}Failed to send custom request: ${error.message}${colors.reset}`, - ); - console.log( - `${colors.dim}Example: request {"method":"getStatus","params":{"cwd":"/tmp"}}${colors.reset}`, - ); - } - } - - private requiresCwd(method: string): boolean { - return [ - 'initialize', - 'send', - 'cancel', - 'getStatus', - 'setConfig', - 'getOutputStyles', - 'getAllSessions', - 'resumeSession', - 'getModels', - 'getSlashCommands', - 'getSlashCommand', - 'executeSlashCommand', - 'query', - 'getPaths', - 'compact', - 'getMcpStatus', - 'reconnectMcpServer', - ].includes(method); - } - - private async exit() { - console.log(`\n${colors.cyan}Shutting down...${colors.reset}`); - - if (this.isConnected && this.transport) { - await this.transport.close(); - } - - this.rl.close(); - process.exit(0); - } - - public start() { - this.rl.prompt(); - } -} - -// Handle process signals -process.on('SIGINT', () => { - console.log( - `\n${colors.cyan}Received SIGINT, shutting down gracefully...${colors.reset}`, - ); - process.exit(0); -}); - -process.on('SIGTERM', () => { - console.log( - `\n${colors.cyan}Received SIGTERM, shutting down gracefully...${colors.reset}`, - ); - process.exit(0); -}); - -// Start the client -const client = new WebClient(); -client.start(); diff --git a/src/commands/servernext/web-server.ts b/src/commands/servernext/web-server.ts deleted file mode 100644 index 73ecac16f..000000000 --- a/src/commands/servernext/web-server.ts +++ /dev/null @@ -1,177 +0,0 @@ -import express from 'express'; -import { createServer } from 'http'; -import path from 'pathe'; -import { fileURLToPath } from 'url'; -import { WebSocketServer } from 'ws'; -import { NodeBridge } from '../../nodeBridge'; -import { WebSocketTransport } from './websocketTransport'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -interface WebServerOptions { - port?: number; - host?: string; - contextCreateOpts?: any; -} - -class WebServer { - private app: express.Application; - private server: ReturnType; - private wss: WebSocketServer; - private clients = new Map< - string, - { transport: WebSocketTransport; bridge: NodeBridge } - >(); - private port: number; - private host: string; - private contextCreateOpts: any; - - constructor(options: WebServerOptions = {}) { - this.port = options.port || 3000; - this.host = options.host || 'localhost'; - this.contextCreateOpts = options.contextCreateOpts || {}; - - // Initialize Express app - this.app = express(); - this.server = createServer(this.app); - - // Initialize WebSocket server - this.wss = new WebSocketServer({ - server: this.server, - path: '/ws', - }); - - this.setupRoutes(); - this.setupWebSocket(); - } - - private setupRoutes() { - // Serve static files - this.app.use(express.static(__dirname)); - - // Serve the web client HTML - this.app.get('/', (req, res) => { - res.sendFile(path.join(__dirname, 'web-client.html')); - }); - - // Health check endpoint - this.app.get('/health', (req, res) => { - res.json({ - status: 'ok', - clients: this.clients.size, - timestamp: new Date().toISOString(), - }); - }); - - // Client info endpoint - this.app.get('/clients', (req, res) => { - const clientInfo = Array.from(this.clients.entries()).map( - ([id, client]) => ({ - id, - connected: client.transport.isConnected(), - state: client.transport.getState(), - }), - ); - res.json(clientInfo); - }); - } - - private setupWebSocket() { - this.wss.on('connection', (ws, req) => { - const clientId = this.generateClientId(); - console.log(`[WebServer] New client connected: ${clientId}`); - - // Create WebSocket transport - const transport = new WebSocketTransport(ws); - - // Create NodeBridge instance for this client - const bridge = new NodeBridge({ - contextCreateOpts: this.contextCreateOpts, - }); - - // Connect transport to bridge's message bus - bridge.messageBus.setTransport(transport); - - // Store client - this.clients.set(clientId, { transport, bridge }); - - // Handle transport events - transport.onError((error) => { - console.error(`[WebServer] Client ${clientId} error:`, error); - }); - - transport.onClose(() => { - console.log(`[WebServer] Client ${clientId} disconnected`); - this.clients.delete(clientId); - }); - - // Send welcome message - bridge.messageBus - .emitEvent('connected', { - clientId, - timestamp: new Date().toISOString(), - message: 'Welcome to Neovate WebSocket Server', - }) - .catch(console.error); - }); - - this.wss.on('error', (error) => { - console.error('[WebServer] WebSocket server error:', error); - }); - } - - private generateClientId(): string { - return `client-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; - } - - async start(): Promise { - return new Promise((resolve) => { - this.server.listen(this.port, this.host, () => { - console.log( - `[WebServer] Server running at http://${this.host}:${this.port}`, - ); - console.log( - `[WebServer] WebSocket endpoint: ws://${this.host}:${this.port}/ws`, - ); - resolve(); - }); - }); - } - - async stop(): Promise { - // Close all client connections - for (const [clientId, { transport }] of this.clients) { - console.log(`[WebServer] Closing client ${clientId}`); - await transport.close(); - } - this.clients.clear(); - - // Close WebSocket server - return new Promise((resolve, reject) => { - this.wss.close((err) => { - if (err) { - reject(err); - return; - } - - // Close HTTP server - this.server.close((err) => { - if (err) { - reject(err); - } else { - console.log('[WebServer] Server stopped'); - resolve(); - } - }); - }); - }); - } - - getClients() { - return this.clients; - } -} - -// Export the WebServer class -export { WebServer }; diff --git a/src/config.ts b/src/config.ts index 0b7625402..cfcf017ae 100644 --- a/src/config.ts +++ b/src/config.ts @@ -299,7 +299,14 @@ export class ConfigManager { let newValue: any = value; if (BOOLEAN_CONFIG_KEYS.includes(key)) { - newValue = value === 'true'; + if (typeof value === 'boolean') { + newValue = value; + } else { + newValue = value === 'true'; + } + } + if (ARRAY_CONFIG_KEYS.includes(key)) { + newValue = JSON.parse(value); } if (OBJECT_CONFIG_KEYS.includes(key)) { newValue = JSON.parse(value); diff --git a/src/index.ts b/src/index.ts index f842eb384..9cb1e1db3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ import { setTraceProcessors } from '@openai/agents'; import assert from 'assert'; import { render } from 'ink'; import React from 'react'; -import { runServerNext } from './commands/servernext/server'; +import { runServer } from './commands/server/server'; import { Context } from './context'; import { GlobalData } from './globalData'; import { parseMcpConfig } from './mcp'; @@ -309,8 +309,9 @@ export async function runNeovate(opts: { // sub commands const command = argv._[0]; - if (command === 'servernext') { - await runServerNext({ + if (command === 'server') { + await runServer({ + cwd, contextCreateOpts, }); return; diff --git a/src/nodeBridge.ts b/src/nodeBridge.ts index 0f4c3c7b9..c479fbd6f 100644 --- a/src/nodeBridge.ts +++ b/src/nodeBridge.ts @@ -22,6 +22,7 @@ import { Project } from './project'; import { query } from './query'; import { SessionConfigManager } from './session'; import { SlashCommandManager } from './slashCommand'; +import { getFiles } from './utils/files'; import { listDirectory } from './utils/list'; import { randomUUID } from './utils/randomUUID'; @@ -138,6 +139,22 @@ class NodeHandlerRegistry { }, ); + this.messageBus.registerHandler( + 'config.list', + async (data: { cwd: string }) => { + const { cwd } = data; + const context = await this.getContext(cwd); + return { + success: true, + data: { + globalConfigDir: context.paths.globalConfigDir, + projectConfigDir: context.paths.projectConfigDir, + config: context.config, + }, + }; + }, + ); + ////////////////////////////////////////////// // mcp this.messageBus.registerHandler( @@ -932,6 +949,25 @@ class NodeHandlerRegistry { }; }, ); + + this.messageBus.registerHandler( + 'utils.files.list', + async (data: { cwd: string; query?: string }) => { + const { cwd, query } = data; + const context = await this.getContext(cwd); + return { + success: true, + data: { + files: await getFiles({ + cwd, + maxSize: 50, + productName: context.productName, + query: query || '', + }), + }, + }; + }, + ); } } diff --git a/src/server/config.ts b/src/server/config.ts deleted file mode 100644 index 57acfc4a5..000000000 --- a/src/server/config.ts +++ /dev/null @@ -1,17 +0,0 @@ -interface Config { - port: number; - host: string; - logger: { - level: string; - }; -} - -const config: Config = { - port: parseInt(process.env.PORT || '1024'), - host: process.env.HOST || '0.0.0.0', - logger: { - level: process.env.LOG_LEVEL || 'info', - }, -}; - -export default config; diff --git a/src/server/context/context-files.ts b/src/server/context/context-files.ts deleted file mode 100644 index 28d318be5..000000000 --- a/src/server/context/context-files.ts +++ /dev/null @@ -1,486 +0,0 @@ -import createDebug from 'debug'; -import * as fsSync from 'fs'; -import fs from 'fs/promises'; -import path from 'pathe'; -import { logError } from '../../utils/logger'; - -const debug = createDebug('neovate:server:contextFiles'); - -interface FileContent { - path: string; - content: string; -} - -interface CacheEntry { - mtime: number; - size: number; - content: string; -} - -/** - * LRU cache implementation for storing file contents - * @param maxSize Maximum number of entries in the cache - */ -class LRUFileCache { - private maxSize: number; - private cache: Map; - - constructor(maxSize: number) { - this.maxSize = maxSize; - this.cache = new Map(); - } - - get(key: string): CacheEntry | undefined { - const entry = this.cache.get(key); - if (entry) { - // Move to the end of the cache to mark as recently used - this.cache.delete(key); - this.cache.set(key, entry); - } - return entry; - } - - set(key: string, entry: CacheEntry): void { - // Remove existing entry to update its position in the cache - if (this.cache.has(key)) { - this.cache.delete(key); - } - this.cache.set(key, entry); - - // Evict least recently used entry if cache exceeds capacity - if (this.cache.size > this.maxSize) { - const oldestKey = this.cache.keys().next().value; - if (oldestKey) { - this.cache.delete(oldestKey); - } - } - } - - delete(key: string): void { - this.cache.delete(key); - } - - keys(): IterableIterator { - return this.cache.keys(); - } -} - -// Cache for storing file contents to avoid repeated disk reads -const FILE_CONTENTS_CACHE = new LRUFileCache(1000); - -// Default list of glob patterns to ignore when scanning files -const DEFAULT_IGNORE_PATTERNS = ` -# Binaries and large media -*.woff -*.exe -*.dll -*.bin -*.dat -*.pdf -*.png -*.jpg -*.jpeg -*.gif -*.bmp -*.tiff -*.ico -*.zip -*.tar -*.gz -*.rar -*.7z -*.mp3 -*.mp4 -*.avi -*.mov -*.wmv - -# Build and distribution -build/* -dist/* - -# Logs and temporary files -*.log -*.tmp -*.swp -*.swo -*.bak -*.old - -# Python artifacts -*.egg-info/* -__pycache__/* -*.pyc -*.pyo -*.pyd -.pytest_cache/* -.ruff_cache/* -venv/* -.venv/* -env/* - -# Rust artifacts -target/* -Cargo.lock - -# Node.js artifacts -*.tsbuildinfo -node_modules -node_modules/* -package-lock.json - -# Environment files -.env/* - -# Git -.git/* - -# OS specific files -.DS_Store -Thumbs.db - -# Hidden files -.*/* -.* - -`; - -/** - * Loads gitignore patterns from .gitignore file - * @param cwd Current working directory (project root) - * @returns Array of gitignore patterns as strings - */ -export function loadGitignorePatterns(cwd: string): string[] { - try { - const gitignorePath = path.join(cwd, '.gitignore'); - if (!fsSync.existsSync(gitignorePath)) { - return []; - } - - const content = fsSync.readFileSync(gitignorePath, 'utf-8'); - const lines = content.split(/\r?\n/); - - return lines - .map((line: string) => line.trim()) - .filter((line: string) => { - // 过滤空行和注释行 - return line && !line.startsWith('#'); - }); - } catch (error) { - return []; - } -} - -/** - * Converts gitignore patterns to regular expressions - * @param patterns Array of gitignore pattern strings - * @param basePath Base path for relative pattern matching - * @returns Array of compiled RegExp patterns - */ -export function gitignorePatternsToRegex( - patterns: string[], - basePath: string, -): Array { - return patterns.map((pattern: string) => { - let regexPattern = pattern; - - // 处理以 / 开头的模式(绝对路径模式) - const isAbsolute = pattern.startsWith('/'); - if (isAbsolute) { - regexPattern = pattern.slice(1); // 移除开头的 / - } - - // 处理以 / 结尾的模式(目录模式) - const isDirectory = pattern.endsWith('/'); - if (isDirectory) { - regexPattern = regexPattern.slice(0, -1); // 移除结尾的 / - } - - // 转义特殊字符,但保留 gitignore 的通配符 - const escaped = regexPattern - .replace(/[.+^${}()|[\]\\]/g, '\\$&') - .replace(/\*\*/g, '.__DOUBLESTAR__.') - .replace(/\*/g, '[^/]*') - .replace(/\?/g, '[^/]') - .replace(/\.__DOUBLESTAR__\./g, '.*'); - - // 构建最终的正则表达式 - let finalPattern: string; - - if (isAbsolute) { - // 绝对路径模式:从项目根目录开始匹配 - finalPattern = `^${path.resolve(basePath, escaped).replace(/\\/g, '\\\\')}`; - } else { - // 相对路径模式:可以在任何目录层级匹配 - finalPattern = `(^|.*/)${escaped}`; - } - - if (isDirectory) { - // 目录模式:匹配目录及其内容 - finalPattern = `${finalPattern}(/.*)?$`; - } else { - finalPattern = `${finalPattern}$`; - } - - return new RegExp(finalPattern, 'i'); - }); -} - -/** - * Loads and compiles ignore patterns into regular expressions - * @param cwd Current working directory (optional, for gitignore support) - * @returns Array of compiled RegExp patterns - */ -export function loadIgnorePatterns(cwd?: string): Array { - try { - // 加载默认忽略模式 - const defaultLines = DEFAULT_IGNORE_PATTERNS.split(/\r?\n/) - .map((l: string) => l.trim()) - .filter((l: string) => l && !l.startsWith('#')); - - const defaultRegs = defaultLines.map((pattern: string) => { - const escaped = pattern - .replace(/[.+^${}()|[\]\\]/g, '\\$&') - .replace(/\*/g, '.*') - .replace(/\?/g, '.'); - const finalRe = `^(?:(?:(?:.*/)?)(?:${escaped}))$`; - return new RegExp(finalRe, 'i'); - }); - - // 如果提供了 cwd,则加载 .gitignore 模式 - if (cwd) { - const gitignorePatterns = loadGitignorePatterns(cwd); - const gitignoreRegs = gitignorePatternsToRegex(gitignorePatterns, cwd); - return [...defaultRegs, ...gitignoreRegs]; - } - - return defaultRegs; - } catch { - return []; - } -} - -/** - * Checks if a given path is ignored by any of the compiled patterns - * @param p Path to check - * @param compiledPatterns Array of RegExp patterns - * @returns Boolean indicating if path should be ignored - */ -export function shouldIgnorePath( - p: string, - compiledPatterns: Array, -): boolean { - const normalized = path.resolve(p); - for (const regex of compiledPatterns) { - if (regex.test(normalized)) { - return true; - } - } - return false; -} - -/** - * Retrieves file contents for specified paths, handling directories recursively - * @param opts Options containing ignore patterns and file paths - * @returns Array of file contents with their paths - */ -export async function getFileContents(opts: { - ignorePatterns: Array; - files: string[]; -}): Promise> { - const { ignorePatterns, files } = opts; - const candidateFiles: Array = []; - - // Process files and directories, respecting ignore patterns - const queue: Array = [...files]; - while (queue.length > 0) { - const currentPath = queue.pop()!; - try { - const stat = await fs.stat(currentPath); - if (stat.isDirectory()) { - const dirents = await fs.readdir(currentPath, { withFileTypes: true }); - for (const dirent of dirents) { - const resolved = path.resolve(currentPath, dirent.name); - // Skip symlinks for safety - const lstat = await fs.lstat(resolved); - if (lstat.isSymbolicLink()) { - continue; - } - if (dirent.isDirectory()) { - if (!shouldIgnorePath(resolved, ignorePatterns)) { - queue.push(resolved); - } - } else if (dirent.isFile()) { - if (!shouldIgnorePath(resolved, ignorePatterns)) { - candidateFiles.push(resolved); - } - } - } - } else if (stat.isFile()) { - if (!shouldIgnorePath(currentPath, ignorePatterns)) { - candidateFiles.push(currentPath); - } - } - } catch (error) {} - } - - const fileContents: Array = []; - const seenPaths = new Set(); - - // Read file contents, using cache when possible - for (const filePath of candidateFiles) { - seenPaths.add(filePath); - let st: fsSync.Stats | null = null; - try { - st = await fs.stat(filePath); - } catch { - continue; - } - if (!st) { - continue; - } - - const cEntry = FILE_CONTENTS_CACHE.get(filePath); - if ( - cEntry && - Math.abs(cEntry.mtime - st.mtime.getTime()) < 1 && - cEntry.size === st.size - ) { - // Use cached content if file hasn't changed - fileContents.push({ path: filePath, content: cEntry.content }); - } else { - // Read file and update cache - try { - const buf = await fs.readFile(filePath); - const content = buf.toString('utf-8'); - FILE_CONTENTS_CACHE.set(filePath, { - mtime: st.mtime.getTime(), - size: st.size, - content, - }); - fileContents.push({ path: filePath, content }); - } catch { - // Skip files that can't be read - } - } - } - - // Clean up cache entries for files that no longer exist - const currentKeys = [...FILE_CONTENTS_CACHE.keys()]; - for (const key of currentKeys) { - if (!seenPaths.has(key)) { - FILE_CONTENTS_CACHE.delete(key); - } - } - - // Sort files by path for consistent output - fileContents.sort((a, b) => a.path.localeCompare(b.path)); - return fileContents; -} - -/** - * Renders file contents as XML for context processing - * @param files Array of file contents - * @returns XML string representation of files - */ -export function renderFilesToXml(files: Array): string { - const fileContents = files - .map( - (fc) => ` - - ${fc.path} - - `, - ) - .join(''); - - return `This section contains the contents of the repository's files.\n${fileContents}\n`; -} - -// TODO: Need to support @src -const FILE_PATTERN = /@([^\s"]+(?:\.[a-zA-Z0-9]+|\/[^\s"]+))(?:\s|"|$)/g; - -const IGNORE_KEYWORDS = ['@codebase', '@bigfish']; - -/** - * Extracts file references from a prompt string - * @param opts Options containing prompt and current working directory - * @returns Array of file paths - */ -export async function getFilesByPrompt(opts: { - prompt?: string; - cwd: string; -}): Promise { - const { prompt, cwd } = opts; - if (!prompt) { - return []; - } - - // Check if the prompt contains file reference markers and doesn't contain ignore keywords - if ( - !prompt.includes('@') || - IGNORE_KEYWORDS.some((keyword) => prompt.includes(keyword)) - ) { - return []; - } - - const fileMatches = prompt.match(FILE_PATTERN); - if (!fileMatches) { - return []; - } - - // Process found file references - const promptFiles = ( - await Promise.all( - fileMatches.map(async (fileRef) => { - // Skip invalid references - if (!fileRef?.startsWith('@')) { - return null; - } - - // Extract and parse file path - const cleanPath = fileRef.replace('@', '').trim(); - const filePath = path.resolve(cwd, cleanPath); - - try { - const stat = await fs.stat(filePath); - return stat.isFile() || stat.isDirectory() ? filePath : null; - } catch (error: any) { - logError({ - error: `[context-files] File path does not exist: ${filePath}, error: ${error.message}`, - }); - return null; - } - }), - ) - ).filter((item) => item !== null); - - // Record results and return - if (promptFiles.length > 0) { - debug( - `[context-files] Detected file references: ${promptFiles.join(', ')}`, - ); - return promptFiles; - } - - debug(`[context-files] No valid file references detected`); - return []; -} - -/** - * Gets file context for specified files - * @param files Array of file paths to process - * @returns XML string containing file contents - */ -export async function getFileContext(files: string[]): Promise { - const ignorePatterns = loadIgnorePatterns(); - const fileContents = await getFileContents({ - files, - ignorePatterns, - }); - const xml = renderFilesToXml(fileContents); - debug( - `[context-files] Generated file context with ${fileContents.length} files`, - ); - return xml; -} diff --git a/src/server/plugins/context.ts b/src/server/plugins/context.ts deleted file mode 100644 index f643d0926..000000000 --- a/src/server/plugins/context.ts +++ /dev/null @@ -1,31 +0,0 @@ -import createDebug from 'debug'; -import type { Plugin } from '../../plugin'; -import { getFileContext } from '../context/context-files'; -import { ContextType } from '../types/completions'; - -const debug = createDebug('neovate:server:plugins:context'); - -let files: string[] = []; - -export const contextPlugin: Plugin = { - name: 'browser-context-plugin', - _serverRouteCompletions({ attachedContexts = [] }) { - debug('serverRouteCompletions', attachedContexts); - if (attachedContexts.length > 0) { - files = attachedContexts - .filter((c) => c.type === ContextType.FILE) - .map((c) => c.context.path); - } - }, - - async context() { - if (files.length === 0) { - return {}; - } - const browserFiles = await getFileContext(files); - - return { - files: browserFiles, - }; - }, -}; diff --git a/src/server/prompts.ts b/src/server/prompts.ts deleted file mode 100644 index e89a7d165..000000000 --- a/src/server/prompts.ts +++ /dev/null @@ -1,63 +0,0 @@ -export const PLAN_PROMPT_JSON = ` - You are an interactive CLI tool designed to assist users with software engineering tasks. Follow the instructions and use the available tools to help the user. - - Plan mode is active. The user has indicated that you should NOT execute any actions yet -- you MUST NOT make edits, run any non-readonly tools (including changing configs or making commits), or alter the system in any way. This overrides all other instructions (such as making edits). Instead, you should: - - 1. Answer the user's query. - 2. When your research is complete, return your plan. Do NOT make any file changes or run any tools that modify the system state until the user confirms the plan. - - - Always reply to the user in 中文. - - - Output your answer in pure JSON format according to the schema below. The JSON object must be directly parsable. DO NOT OUTPUT ANYTHING EXCEPT JSON, AND DO NOT DEVIATE FROM THIS SCHEMA: - - The JSON object should have the following structure: - - - { - "response": "A complete reply to the user's request.", - "task": "A full description of the user's task.", - "plan_summary": "A brief summary of the plan, or leave empty if not needed.", - "needs_plan": true or false, - "steps": [ - { - "title": "A concise step title", - "details": "Briefly restate the title.\\nAdd key details, no more than two sentences.", - } - // Multiple steps allowed - ] - } - - - - For each step's details field, the first sentence should briefly restate the title, followed by a line break and up to two additional sentences. - - Strictly follow the JSON structure above to ensure it is directly parsable. - - Do not output anything except JSON. - - Strictly follow the JSON structure above to ensure it is directly parsable. - - Only output pure JSON, do not include any code block markers or extra content. - - - { - "response": "This is a complete reply to the user's request.", - "task": "This is a full description of the user's task.", - "plan_summary": "This is a brief summary of the plan.", - "needs_plan": true, - "steps": [ - { - "title": "Analyze user requirements", - "details": "Analyze user requirements.\\nClarify task objectives and constraints.", - }, - { - "title": "Develop execution steps", - "details": "Develop execution steps.\\nBreak down the task into actionable sub-tasks.", - } - ] - } - - - ## 注意事项 - - 输出内容必须严格符合提供的JSON结构。 - - 除了JSON外不要输出任何其他内容。 - - 使用中文回复用户。 - - 不要执行任何可能改变系统状态的操作,除非用户明确确认了计划。 -`; diff --git a/src/server/routes/app-data.ts b/src/server/routes/app-data.ts deleted file mode 100644 index cb88fe997..000000000 --- a/src/server/routes/app-data.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { FastifyPluginAsync } from 'fastify'; -import type { ApiResponse, CreateServerOpts, ServerAppData } from '../types'; - -const appDataRoute: FastifyPluginAsync = async ( - app, - opts, -) => { - app.get('/app-data', async (request, reply) => { - const res: ApiResponse = { - success: true, - data: opts.appData, - }; - reply.send(res); - }); -}; - -export default appDataRoute; diff --git a/src/server/routes/completions.ts b/src/server/routes/completions.ts deleted file mode 100644 index d8fa7ea1a..000000000 --- a/src/server/routes/completions.ts +++ /dev/null @@ -1,119 +0,0 @@ -// @ts-nocheck -import { Type } from '@sinclair/typebox'; -import { pipeDataStreamToResponse } from 'ai'; -import createDebug from 'debug'; -import type { FastifyPluginAsync } from 'fastify'; -import { last } from 'lodash-es'; -import { PluginHookType } from '../../plugin'; -import { runCode } from '../services/completions'; -import type { RouteCompletionsOpts } from '../types'; -import { type CompletionRequest, ContextType } from '../types/completions'; - -const debug = createDebug('neovate:server:completions'); - -const CompletionRequestSchema = Type.Object({ - messages: Type.Array( - Type.Object({ - role: Type.Union([ - Type.Literal('user'), - Type.Literal('assistant'), - Type.Literal('system'), - ]), - content: Type.String(), - }), - { minItems: 1 }, - ), - mode: Type.String(), -}); - -const completionsRoute: FastifyPluginAsync = async ( - app, - opts, -) => { - app.post<{ Body: CompletionRequest }>( - '/completions', - { - schema: { - body: CompletionRequestSchema, - }, - }, - async (request, reply) => { - const messages = request.body.messages; - const mode = request.body.mode; - const lastMessage = last(messages); - debug('Received messages:', messages); - - if (!lastMessage) { - throw new Error('No messages provided'); - } - - await opts.context.apply({ - hook: 'serverRouteCompletions', - args: [ - { - message: lastMessage, - attachedContexts: lastMessage.attachedContexts || [], - }, - ], - type: PluginHookType.Series, - }); - - const prompt = lastMessage.planContent ?? lastMessage.content; - - opts.context.addHistory(prompt); - - reply.header('Content-Type', 'text/plain; charset=utf-8'); - reply.header('Cache-Control', 'no-cache'); - reply.header('Connection', 'keep-alive'); - - // Create an AbortController for cancelling requests - const abortController = new AbortController(); - debug('Created AbortController'); - - const handleAbort = (hadError: boolean) => { - if (reply.sent) { - return; - } - debug(`${hadError ? 'with error' : 'without error'}, aborting request`); - abortController.abort(); - }; - - // Register socket event listener (socket.close covers both user cancellation and network errors) - if (request.raw.socket) { - request.raw.socket.on('close', handleAbort); - } - - try { - await pipeDataStreamToResponse(reply.raw, { - async execute(dataStream) { - await runCode({ - ...opts, - prompt, - dataStream, - mode, - // Pass AbortSignal to runCode function - abortSignal: abortController.signal, - // files are processed through context in plugins, only handling other types here - attachedContexts: (lastMessage.attachedContexts || []).filter( - (context) => context.type !== ContextType.FILE, - ), - }); - }, - onError(error) { - debug('Error in completion:', error); - return error instanceof Error ? error.message : String(error); - }, - }); - } catch (error) { - debug('Unhandled error:', error); - console.log('error', error); - if (!reply.sent) { - reply.status(500).send({ error: 'Internal server error' }); - } - throw error; - } - }, - ); -}; - -export default completionsRoute; diff --git a/src/server/routes/files.ts b/src/server/routes/files.ts deleted file mode 100644 index 7855a019b..000000000 --- a/src/server/routes/files.ts +++ /dev/null @@ -1,440 +0,0 @@ -import { Type } from '@sinclair/typebox'; -import createDebug from 'debug'; -import type { FastifyPluginAsync } from 'fastify'; -import * as fs from 'fs/promises'; -import path from 'pathe'; -import { execFileNoThrow } from '../../utils/execFileNoThrow'; -import { loadIgnorePatterns } from '../context/context-files'; -import type { CreateServerOpts } from '../types'; -import type { FileItem, FileListRequest } from '../types/files'; - -const debug = createDebug('neovate:server:files'); - -const FileListRequestSchema = Type.Object({ - directory: Type.Optional(Type.String()), - pattern: Type.Optional(Type.String()), - maxDepth: Type.Optional(Type.Number()), - includeMetadata: Type.Optional(Type.Number()), - maxSize: Type.Optional(Type.Number()), - searchString: Type.Optional(Type.String()), -}); - -const FileEditRequestSchema = Type.Object({ - filePath: Type.String(), - content: Type.String(), -}); - -const FileReadRequestSchema = Type.Object({ - filePath: Type.String(), -}); - -interface FileEditRequest { - filePath: string; - content: string; -} - -const DEFAULT_DIRECTORY = '.'; -const DEFAULT_MAX_SIZE = 50; -const DEFAULT_INCLUDE_METADATA = 0; - -interface WalkContext { - cwd: string; - ignorePatterns: RegExp[]; - includeMetadata: boolean; - maxSize: number; - searchString?: string; -} - -function normalizeRequestParams(query: FileListRequest, cwd: string) { - const { - directory = DEFAULT_DIRECTORY, - includeMetadata = DEFAULT_INCLUDE_METADATA, - maxSize = DEFAULT_MAX_SIZE, - searchString, - } = query; - - return { - directory, - includeMetadata: includeMetadata === 1, - targetDir: path.resolve(cwd, directory), - maxSize, - searchString, - }; -} - -async function validateDirectory(targetDir: string) { - const fs = await import('fs/promises'); - - try { - const stat = await fs.stat(targetDir); - if (!stat.isDirectory()) { - return { isValid: false, error: 'The specified path is not a directory' }; - } - return { isValid: true }; - } catch { - return { isValid: false, error: 'The specified path does not exist' }; - } -} - -async function createFileItem( - fullPath: string, - relativePath: string, - name: string, - type: 'file' | 'directory', - includeMetadata: boolean, - isHidden: boolean, -): Promise { - const fileItem: FileItem = { - path: relativePath, - type, - name, - }; - - if (includeMetadata) { - try { - const fs = await import('fs/promises'); - const stat = await fs.stat(fullPath); - fileItem.metadata = { - size: type === 'file' ? stat.size : 0, - lastModified: stat.mtime.toISOString(), - isHidden, - }; - } catch (error) { - debug(`Failed to get ${type} metadata: ${fullPath}`, error); - } - } - - return fileItem; -} - -function shouldIncludeFileOrFolder( - path: string, - searchString?: string, -): boolean { - return ( - !searchString || path.toLowerCase().includes(searchString.toLowerCase()) - ); -} - -function shouldIgnorePath( - fullPath: string, - name: string, - ignorePatterns: RegExp[], -): boolean { - if (name.startsWith('.')) { - return true; - } - - return ignorePatterns.some((pattern) => pattern.test(fullPath)); -} - -async function recursiveWalk( - dir: string, - context: WalkContext, - currentItemCount: number = 0, -) { - if (currentItemCount > context.maxSize) { - return []; - } - - const items: FileItem[] = []; - const { cwd, ignorePatterns, includeMetadata, maxSize } = context; - try { - const fs = await import('fs/promises'); - const entries = await fs.readdir(dir, { withFileTypes: true }); - for (const entry of entries) { - if (items.length + currentItemCount > maxSize) { - break; - } - - const fullPath = path.join(dir, entry.name); - const relativePath = path.relative(cwd, fullPath); - const name = entry.name; - const isHidden = name.startsWith('.'); - if (shouldIgnorePath(fullPath, name, ignorePatterns)) { - continue; - } - - if (entry.isFile()) { - if (shouldIncludeFileOrFolder(relativePath, context.searchString)) { - const fileItem = await createFileItem( - fullPath, - relativePath, - name, - 'file', - includeMetadata, - isHidden, - ); - - items.push(fileItem); - } - } else if (entry.isDirectory()) { - if (shouldIncludeFileOrFolder(relativePath, context.searchString)) { - const folderItem = await createFileItem( - fullPath, - relativePath, - name, - 'directory', - includeMetadata, - isHidden, - ); - items.push(folderItem); - } - - const subItems = await recursiveWalk( - fullPath, - context, - currentItemCount + items.length, - ); - - items.push(...subItems); - } - } - - return items; - } catch (e) { - debug(`Error walking directory: ${dir}`, e); - return []; - } -} - -async function getGitStatusItems( - cwd: string, - includeMetadata: boolean, - searchString?: string, -) { - const gitStatus = await (async () => { - // won't throw error - const { stdout } = await execFileNoThrow( - cwd, - 'git', - ['status', '--short'], - undefined, - undefined, - false, - ); - // DO NOT USE TRIM HERE, it will make the result inconsistent - return stdout; - })(); - - const files = gitStatus - .split('\n') - .filter((line) => line.trim().length > 0) - .filter( - (line) => - !line.startsWith('D') && - !line.startsWith('??') && - !line.startsWith('R'), - ) - .map((line) => line.slice(3)) - .filter( - (path) => - !searchString || - path.toLowerCase().includes(searchString.toLowerCase()), - ); - - return Promise.all( - files.map(async (file) => { - const fullPath = path.join(cwd, file); - const relativePath = path.relative(cwd, fullPath); - const name = path.basename(file); - const isHidden = name.startsWith('.'); - const item = await createFileItem( - fullPath, - relativePath, - name, - 'file', - includeMetadata, - isHidden, - ); - - return item; - }), - ); -} - -function sortItems(items: FileItem[]): FileItem[] { - return items.sort((a, b) => { - if (a.type !== b.type) { - return a.type === 'directory' ? -1 : 1; - } - - return a.path.localeCompare(b.path); - }); -} - -function separateItemsByType(items: FileItem[]) { - const files = items.filter((item) => item.type === 'file'); - const directories = items.filter((item) => item.type === 'directory'); - - return { - files: files.map((item) => item.path), - directories: directories.map((item) => item.path), - }; -} - -const filesRoute: FastifyPluginAsync = async (app, opts) => { - app.get<{ Querystring: FileListRequest }>( - '/files/list', - { - schema: { - querystring: FileListRequestSchema, - }, - }, - async (request, reply) => { - try { - const cwd = opts.context.cwd; - const params = normalizeRequestParams(request.query, cwd); - - const validation = await validateDirectory(params.targetDir); - if (!validation.isValid) { - return reply - .code(validation.error?.includes('not exist') ? 404 : 400) - .send({ - success: false, - error: validation.error, - }); - } - - const gitStatusItems = await getGitStatusItems( - cwd, - params.includeMetadata, - params.searchString, - ); - - let targetItems: FileItem[] = gitStatusItems.slice(0, params.maxSize); - - if (targetItems.length < params.maxSize) { - const remainingSize = params.maxSize - gitStatusItems.length; - - const context: WalkContext = { - cwd, - ignorePatterns: loadIgnorePatterns(cwd), - maxSize: remainingSize, - searchString: params.searchString, - includeMetadata: params.includeMetadata, - }; - - const items = await recursiveWalk(params.targetDir, context); - - targetItems = [...targetItems, ...sortItems(items)]; - } - - const { files, directories } = separateItemsByType(targetItems); - - return reply.send({ - success: true, - data: { - cwd, - directory: params.targetDir, - items: targetItems, - files, - directories, - }, - }); - } catch (error) { - debug(`File list API error:`, error); - - return reply.code(500).send({ - success: false, - message: - error instanceof Error - ? error.message - : 'Unknown error occurred while getting file list', - }); - } - }, - ); - - app.get<{ Querystring: { filePath: string } }>( - '/files/read', - { - schema: { - querystring: FileReadRequestSchema, - }, - }, - async (request, reply) => { - try { - const { filePath } = request.query; - const cwd = opts.context.cwd; - const absolutePath = path.resolve(cwd, filePath); - - if (!absolutePath.startsWith(cwd)) { - return reply.code(400).send({ - success: false, - error: 'File path is outside of the project directory.', - }); - } - - const content = await fs.readFile(absolutePath, 'utf-8'); - - return reply.send({ - success: true, - data: { - content, - filePath, - }, - }); - } catch (error: any) { - debug(`File read API error:`, error); - if (error.code === 'ENOENT') { - return reply.code(404).send({ - success: false, - message: 'File not found.', - }); - } - return reply.code(500).send({ - success: false, - message: - error instanceof Error - ? error.message - : 'Unknown error occurred while reading file.', - }); - } - }, - ); - - app.post<{ Body: FileEditRequest }>( - '/files/edit', - { - schema: { - body: FileEditRequestSchema, - }, - }, - async (request, reply) => { - try { - const { filePath, content } = request.body; - const cwd = opts.context.cwd; - const absolutePath = path.resolve(cwd, filePath); - - if (!absolutePath.startsWith(cwd)) { - return reply.code(400).send({ - success: false, - error: 'File path is outside of the project directory.', - }); - } - - await fs.writeFile(absolutePath, content, 'utf-8'); - - return reply.send({ - success: true, - data: { - message: 'File updated successfully.', - filePath, - }, - }); - } catch (error) { - debug(`File edit API error:`, error); - return reply.code(500).send({ - success: false, - message: - error instanceof Error - ? error.message - : 'Unknown error occurred while editing file.', - }); - } - }, - ); -}; - -export default filesRoute; diff --git a/src/server/routes/mcp.ts b/src/server/routes/mcp.ts deleted file mode 100644 index 46dabe825..000000000 --- a/src/server/routes/mcp.ts +++ /dev/null @@ -1,284 +0,0 @@ -import { Type } from '@sinclair/typebox'; -import type { FastifyPluginAsync } from 'fastify'; -import { ConfigManager } from '../../config'; - -const mcpRoute: FastifyPluginAsync = async (app) => { - // Reuse existing ConfigManager logic - const createConfigManager = (cwd: string = process.cwd()) => { - return new ConfigManager(cwd, 'takumi', {}); - }; - - // Get MCP server list (corresponds to CLI: takumi mcp list) - app.get( - '/mcp/servers', - { - schema: { - querystring: Type.Object({ - global: Type.Optional(Type.Boolean()), - }), - }, - }, - async (request) => { - const { global = false } = request.query as { global?: boolean }; - const configManager = createConfigManager(); - - // Directly reuse logic from CLI - const mcpServers = global - ? configManager.globalConfig.mcpServers || {} - : configManager.projectConfig.mcpServers || {}; - - return { - success: true, - servers: mcpServers, - scope: global ? 'global' : 'project', - }; - }, - ); - - // Get single MCP server (corresponds to CLI: takumi mcp get ) - app.get( - '/mcp/servers/:name', - { - schema: { - params: Type.Object({ - name: Type.String(), - }), - querystring: Type.Object({ - global: Type.Optional(Type.Boolean()), - }), - }, - }, - async (request, reply) => { - const { name } = request.params as { name: string }; - const { global = false } = request.query as { global?: boolean }; - const configManager = createConfigManager(); - - // Reuse CLI get logic - const mcpServers = global - ? configManager.globalConfig.mcpServers || {} - : configManager.projectConfig.mcpServers || {}; - - const server = mcpServers[name as keyof typeof mcpServers]; - if (!server) { - return reply.code(404).send({ - error: `MCP server ${name} not found`, - }); - } - - return { success: true, server, name }; - }, - ); - - // Add/update MCP server (corresponds to CLI: takumi mcp add) - app.post( - '/mcp/servers', - { - schema: { - body: Type.Object({ - name: Type.String(), - command: Type.Optional(Type.String()), - args: Type.Optional(Type.Array(Type.String())), - url: Type.Optional(Type.String()), - transport: Type.Optional(Type.String()), - env: Type.Optional(Type.String()), // JSON string - global: Type.Optional(Type.Boolean()), - }), - }, - }, - async (request) => { - const { - name, - command, - args = [], - url, - transport = 'stdio', - env, - global = false, - } = request.body as { - name: string; - command?: string; - args?: string[]; - url?: string; - transport?: string; - env?: string; - global?: boolean; - }; - - const configManager = createConfigManager(); - - // Reuse CLI add logic - const mcpServers = global - ? configManager.globalConfig.mcpServers || {} - : configManager.projectConfig.mcpServers || {}; - - if (transport === 'sse') { - if (!url) { - throw new Error('URL is required for SSE transport'); - } - mcpServers[name] = { - type: 'sse', - url, - }; - } else { - if (!command) { - throw new Error('Command is required for stdio transport'); - } - mcpServers[name] = { - type: 'stdio', - command, - args, - env: env ? JSON.parse(env) : undefined, - }; - } - - // Directly use existing save logic - configManager.setConfig(global, 'mcpServers', JSON.stringify(mcpServers)); - - const configPath = global - ? configManager.globalConfigPath - : configManager.projectConfigPath; - - return { - success: true, - message: `Added ${name} to ${configPath}`, - server: mcpServers[name], - }; - }, - ); - - // Update MCP server - app.patch( - '/mcp/servers/:name', - { - schema: { - params: Type.Object({ - name: Type.String(), - }), - body: Type.Object({ - command: Type.Optional(Type.String()), - args: Type.Optional(Type.Array(Type.String())), - url: Type.Optional(Type.String()), - transport: Type.Optional(Type.String()), - env: Type.Optional(Type.String()), // JSON string - global: Type.Optional(Type.Boolean()), - }), - }, - }, - async (request) => { - const { name } = request.params as { name: string }; - const { - command, - args, - url, - transport, - env, - global = false, - } = request.body as { - command?: string; - args?: string[]; - url?: string; - transport?: string; - env?: string; - global?: boolean; - }; - - const configManager = createConfigManager(); - - // Get existing servers - const mcpServers = global - ? configManager.globalConfig.mcpServers || {} - : configManager.projectConfig.mcpServers || {}; - - if (!mcpServers[name]) { - throw new Error(`MCP server ${name} not found`); - } - - // Update existing server config - const existingServer = mcpServers[name]; - - if (transport === 'sse' || existingServer.type === 'sse') { - const sseServer = - existingServer.type === 'sse' ? existingServer : { url: '' }; - mcpServers[name] = { - type: 'sse', - url: url || sseServer.url, - }; - } else { - const stdioServer = - !existingServer.type || existingServer.type === 'stdio' - ? (existingServer as any) - : { command: '', args: [], env: undefined }; - mcpServers[name] = { - type: 'stdio', - command: command || stdioServer.command, - args: args || stdioServer.args, - env: env ? JSON.parse(env) : stdioServer.env, - }; - } - - configManager.setConfig(global, 'mcpServers', JSON.stringify(mcpServers)); - - const configPath = global - ? configManager.globalConfigPath - : configManager.projectConfigPath; - - return { - success: true, - message: `Updated ${name} in ${configPath}`, - server: mcpServers[name], - }; - }, - ); - - // Remove MCP server (corresponds to CLI: takumi mcp rm) - app.delete( - '/mcp/servers/:name', - { - schema: { - params: Type.Object({ - name: Type.String(), - }), - querystring: Type.Object({ - global: Type.Optional(Type.Boolean()), - }), - }, - }, - async (request) => { - const { name } = request.params as { name: string }; - const { global = false } = request.query as { global?: boolean }; - const configManager = createConfigManager(); - - // Reuse CLI remove logic - const mcpServers = global - ? { ...(configManager.globalConfig.mcpServers || {}) } - : { ...(configManager.projectConfig.mcpServers || {}) }; - - if (!mcpServers[name]) { - throw new Error(`MCP server ${name} not found`); - } - - delete mcpServers[name]; - - // Update the config and also update the in-memory config - configManager.setConfig(global, 'mcpServers', JSON.stringify(mcpServers)); - - // Ensure the in-memory config is updated - if (global) { - configManager.globalConfig.mcpServers = mcpServers; - } else { - configManager.projectConfig.mcpServers = mcpServers; - } - - const configPath = global - ? configManager.globalConfigPath - : configManager.projectConfigPath; - - return { - success: true, - message: `Removed ${name} from ${configPath}`, - }; - }, - ); -}; - -export default mcpRoute; diff --git a/src/server/routes/settings.ts b/src/server/routes/settings.ts deleted file mode 100644 index 7480ec154..000000000 --- a/src/server/routes/settings.ts +++ /dev/null @@ -1,314 +0,0 @@ -// @ts-nocheck -import type { FastifyPluginAsync } from 'fastify'; -import { ConfigManager } from '../../config'; -import { MODEL_ALIAS } from '../../provider'; -import type { CreateServerOpts } from '../types'; -import type { BatchUpdateRequest, SetSettingRequest } from '../types/settings'; - -const settingsRoute: FastifyPluginAsync = async ( - app, - opts, -) => { - const getCwd = () => opts.cwd || process.cwd(); - - // Get settings - app.get('/settings', async (request, reply) => { - try { - const { scope } = request.query as { scope?: string }; - if (!scope || (scope !== 'global' && scope !== 'project')) { - return reply.status(400).send({ - success: false, - error: 'Invalid scope parameter', - data: null, - }); - } - - const validScope = scope as 'global' | 'project'; - const configManager = new ConfigManager(getCwd(), 'takumi', {}); - - const settings = - validScope === 'global' - ? configManager.globalConfig - : configManager.projectConfig; - - return { - success: true, - data: settings, - }; - } catch (error) { - return reply.status(500).send({ - success: false, - error: error instanceof Error ? error.message : 'Unknown error', - data: null, - }); - } - }); - - // Get effective settings - app.get('/settings/effective', async (request, reply) => { - try { - const configManager = new ConfigManager(getCwd(), 'takumi', {}); - return { - success: true, - data: configManager.config, - }; - } catch (error) { - return reply.status(500).send({ - success: false, - error: error instanceof Error ? error.message : 'Unknown error', - data: null, - }); - } - }); - - // Get available models - app.get('/settings/models', async (request, reply) => { - try { - const models = Object.entries(MODEL_ALIAS).map(([key, value]) => ({ - key, - value, - })); - - return { - success: true, - data: models, - }; - } catch (error) { - return reply.status(500).send({ - success: false, - error: error instanceof Error ? error.message : 'Unknown error', - data: null, - }); - } - }); - - // Get available plugins - app.get('/settings/plugins', async (request, reply) => { - try { - // Return mock plugin data for now - const plugins = ['example-plugin', 'another-plugin']; - - return { - success: true, - data: plugins, - }; - } catch (error) { - return reply.status(500).send({ - success: false, - error: error instanceof Error ? error.message : 'Unknown error', - data: null, - }); - } - }); - - // Set configuration - app.post('/settings', async (request, reply) => { - try { - const { scope, key, value } = request.body as SetSettingRequest; - if (!scope || (scope !== 'global' && scope !== 'project') || !key) { - return reply.status(400).send({ - success: false, - error: 'Invalid parameters', - data: null, - }); - } - - const validScope = scope as 'global' | 'project'; - const configManager = new ConfigManager(getCwd(), 'takumi', {}); - - // Convert boolean values to string as ConfigManager.setConfig expects string parameters - let configValue: string = ''; - if (typeof value === 'boolean') { - configValue = value.toString(); - } else if (typeof value === 'string') { - configValue = value; - } else if (typeof value === 'number') { - configValue = value.toString(); - } else if (Array.isArray(value)) { - configValue = JSON.stringify(value); - } else if (value !== undefined) { - configValue = String(value); - } - - configManager.setConfig(validScope === 'global', key, configValue); - - return { - success: true, - data: { success: true }, - }; - } catch (error) { - return reply.status(500).send({ - success: false, - error: error instanceof Error ? error.message : 'Unknown error', - data: null, - }); - } - }); - - // Batch update settings - app.post('/settings/batch', async (request, reply) => { - try { - const { scope, settings } = request.body as BatchUpdateRequest; - if (!scope || (scope !== 'global' && scope !== 'project') || !settings) { - return reply.status(400).send({ - success: false, - error: 'Invalid parameters', - data: null, - }); - } - - const validScope = scope as 'global' | 'project'; - const configManager = new ConfigManager(getCwd(), 'takumi', {}); - - configManager.updateConfig(validScope === 'global', settings); - - return { - success: true, - data: { success: true }, - }; - } catch (error) { - return reply.status(500).send({ - success: false, - error: error instanceof Error ? error.message : 'Unknown error', - data: null, - }); - } - }); - - // Reset settings - app.post('/settings/reset', async (request, reply) => { - try { - const { scope } = request.body as { scope?: string }; - if (!scope || (scope !== 'global' && scope !== 'project')) { - return reply.status(400).send({ - success: false, - error: 'Invalid scope parameter', - data: null, - }); - } - - const validScope = scope as 'global' | 'project'; - const configManager = new ConfigManager(getCwd(), 'takumi', {}); - - // Clear all configurations for the specified scope - const config = - validScope === 'global' - ? configManager.globalConfig - : configManager.projectConfig; - Object.keys(config).forEach((key) => { - configManager.removeConfig(validScope === 'global', key); - }); - - return { - success: true, - data: { success: true }, - }; - } catch (error) { - return reply.status(500).send({ - success: false, - error: error instanceof Error ? error.message : 'Unknown error', - data: null, - }); - } - }); - - // Export settings - app.get('/settings/export', async (request, reply) => { - try { - const { scope } = request.query as { scope?: string }; - if (!scope || (scope !== 'global' && scope !== 'project')) { - return reply.status(400).send({ - success: false, - error: 'Invalid scope parameter', - data: null, - }); - } - - const validScope = scope as 'global' | 'project'; - const configManager = new ConfigManager(getCwd(), 'takumi', {}); - - const settings = - validScope === 'global' - ? configManager.globalConfig - : configManager.projectConfig; - - return { - success: true, - data: JSON.stringify(settings, null, 2), - }; - } catch (error) { - return reply.status(500).send({ - success: false, - error: error instanceof Error ? error.message : 'Unknown error', - data: null, - }); - } - }); - - // Import settings - app.post('/settings/import', async (request, reply) => { - try { - const { scope, settings } = request.body as { - scope?: string; - settings?: string; - }; - if (!scope || (scope !== 'global' && scope !== 'project') || !settings) { - return reply.status(400).send({ - success: false, - error: 'Invalid parameters', - data: null, - }); - } - - const validScope = scope as 'global' | 'project'; - const configManager = new ConfigManager(getCwd(), 'takumi', {}); - - const parsedSettings = - typeof settings === 'string' ? JSON.parse(settings) : settings; - configManager.updateConfig(validScope === 'global', parsedSettings); - - return { - success: true, - data: { success: true }, - }; - } catch (error) { - return reply.status(500).send({ - success: false, - error: error instanceof Error ? error.message : 'Unknown error', - data: null, - }); - } - }); - - // Delete configuration item - app.delete('/settings', async (request, reply) => { - try { - const { scope, key } = request.query as { scope?: string; key?: string }; - if (!scope || (scope !== 'global' && scope !== 'project') || !key) { - return reply.status(400).send({ - success: false, - error: 'Invalid parameters', - data: null, - }); - } - - const validScope = scope as 'global' | 'project'; - const configManager = new ConfigManager(getCwd(), 'takumi', {}); - - configManager.removeConfig(validScope === 'global', key); - - return { - success: true, - data: { success: true }, - }; - } catch (error) { - return reply.status(500).send({ - success: false, - error: error instanceof Error ? error.message : 'Unknown error', - data: null, - }); - } - }); -}; - -export default settingsRoute; diff --git a/src/server/routes/tool-approval.ts b/src/server/routes/tool-approval.ts deleted file mode 100644 index 311fbf1ea..000000000 --- a/src/server/routes/tool-approval.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { Type } from '@sinclair/typebox'; -import createDebug from 'debug'; -import type { FastifyPluginAsync } from 'fastify'; -import { getToolApprovalService } from '../services/tool-approval'; -import type { RouteCompletionsOpts } from '../types'; - -const debug = createDebug('takumi:server:routes:tool-approval'); - -const SubmitToolApprovalSchema = Type.Object({ - callId: Type.String(), - approved: Type.Boolean(), - option: Type.Optional( - Type.Union([ - Type.Literal('once'), - Type.Literal('always'), - Type.Literal('always_tool'), - ]), - ), -}); - -const ErrorResponseSchema = Type.Object({ - error: Type.String(), - code: Type.String(), -}); - -const SuccessResponseSchema = Type.Object({ - success: Type.Boolean(), -}); - -const toolApprovalRoute: FastifyPluginAsync = async ( - app, - opts, -) => { - const getService = () => getToolApprovalService(opts.context); - - const handleError = (error: unknown, reply: any) => { - debug('Error:', error); - const errorMessage = - error instanceof Error ? error.message : 'Unknown error'; - - if (errorMessage.includes('destroyed')) { - return reply.status(503).send({ - error: 'Service unavailable', - code: 'SERVICE_DESTROYED', - }); - } - - return reply.status(500).send({ - error: errorMessage, - code: 'INTERNAL_ERROR', - }); - }; - - app.post<{ - Body: { - callId: string; - approved: boolean; - option?: 'once' | 'always' | 'always_tool'; - }; - }>( - '/tool-approval/submit', - { - schema: { - body: SubmitToolApprovalSchema, - response: { - 200: SuccessResponseSchema, - 400: ErrorResponseSchema, - 404: ErrorResponseSchema, - 500: ErrorResponseSchema, - 503: ErrorResponseSchema, - }, - }, - }, - async (request, reply) => { - try { - const { callId, approved, option = 'once' } = request.body; - - const service = getService(); - const success = service.submitApproval(callId, approved, option); - - if (!success) { - return reply.status(404).send({ - error: 'Pending approval not found', - code: 'APPROVAL_NOT_FOUND', - }); - } - - debug(`Submitted approval: ${callId} -> ${approved} (${option})`); - return { success: true }; - } catch (error) { - return handleError(error, reply); - } - }, - ); -}; - -export default toolApprovalRoute; diff --git a/src/server/server.ts b/src/server/server.ts deleted file mode 100644 index 0d6e95064..000000000 --- a/src/server/server.ts +++ /dev/null @@ -1,193 +0,0 @@ -// @ts-nocheck -import type { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'; -import createDebug from 'debug'; -import fastify, { type FastifyInstance } from 'fastify'; -import fs from 'fs'; -import path from 'pathe'; -import portfinder from 'portfinder'; -import { PluginHookType } from '../plugin'; -import { Service } from '../service'; -import * as logger from '../utils/logger'; -import config from './config'; -import type { CreateServerOpts, RunBrowserServerOpts } from './types'; - -const debug = createDebug('neovate:server:completions'); - -const __dirname = path.dirname(new URL(import.meta.url).pathname); -const isLocal = - __dirname.endsWith('takumi/src/server') || - __dirname.endsWith('neovate/src/server') || - __dirname.endsWith('code/src/server'); -const BROWSER_DIST_PATH = isLocal - ? path.resolve(__dirname, '../../dist/browser') - : path.resolve(__dirname, '../dist/browser'); - -debug('BROWSER_DIST_PATH', BROWSER_DIST_PATH); - -const BASE_API_PREFIX = '/api'; - -const registerPlugins = async (app: FastifyInstance) => { - await app.register(import('@fastify/cors'), { - origin: true, - methods: ['GET', 'HEAD', 'PUT', 'POST', 'PATCH', 'DELETE', 'OPTIONS'], - credentials: true, - allowedHeaders: [ - 'Content-Type', - 'Authorization', - 'X-Requested-With', - 'Accept', - 'Origin', - 'Sec-WebSocket-Protocol', - ], - }); - await app.register(import('@fastify/compress'), { - global: true, - }); - - await app.register(import('@fastify/static'), { - root: BROWSER_DIST_PATH, - prefix: '/', - wildcard: false, - }); - - app.get('*', async (request, reply) => { - if (request.url.startsWith(BASE_API_PREFIX)) { - return reply.status(404).send('Not Found'); - } - - const htmlPath = path.join(BROWSER_DIST_PATH, 'index.html'); - if (fs.existsSync(htmlPath)) { - return reply.sendFile('index.html'); - } else { - debug('index.html not found'); - return reply.status(404).send('Not Found'); - } - }); -}; - -const registerRoutes = async ( - app: FastifyInstance, - opts: CreateServerOpts, - service: Service, - planService: Service, -) => { - const { logLevel: _, ...pluginOpts } = opts; - - await app.register(import('./routes/completions'), { - prefix: `${BASE_API_PREFIX}/chat`, - ...pluginOpts, - service, - planService, - }); - await app.register(import('./routes/tool-approval'), { - prefix: BASE_API_PREFIX, - ...pluginOpts, - service, - planService, - }); - await app.register(import('./routes/files'), { - prefix: BASE_API_PREFIX, - ...pluginOpts, - }); - await app.register(import('./routes/app-data'), { - prefix: BASE_API_PREFIX, - ...pluginOpts, - }); - await app.register(import('./routes/settings'), { - prefix: BASE_API_PREFIX, - ...pluginOpts, - }); - await app.register(import('./routes/mcp'), { - prefix: BASE_API_PREFIX, - ...pluginOpts, - }); - - await opts.context.apply({ - hook: 'serverRoutes', - args: [ - { - opts: { - app, - prefix: BASE_API_PREFIX, - opts, - }, - }, - ], - type: PluginHookType.Series, - }); -}; - -export async function runBrowserServer(opts: RunBrowserServerOpts) { - const appData = await opts.context.apply({ - hook: 'serverAppData', - args: [ - { - context: opts.context, - cwd: opts.cwd, - }, - ], - memo: { - productName: opts.context.productName, - version: opts.context.version, - cwd: opts.cwd, - config: opts.context.config, - }, - type: PluginHookType.SeriesMerge, - }); - - const exit = () => { - debug('exit'); - opts.context.destroy().then(() => { - process.exit(0); - }); - }; - - process.on('SIGINT', exit); - process.on('SIGQUIT', exit); - process.on('SIGTERM', exit); - - await createServer({ - ...opts, - appData, - }); -} - -export async function createServer(opts: CreateServerOpts) { - const app: FastifyInstance = fastify({ - logger: opts.logLevel ? { level: opts.logLevel } : false, - bodyLimit: 100 * 1024 * 1024, // 100MB limit for handling large images and files - }).withTypeProvider(); - - const port = await portfinder.getPortPromise({ - port: Number.parseInt(String(opts.port || config.port), 10), - }); - const host = config.host; - - const service = await Service.create({ - context: opts.context, - agentType: 'code', - }); - - const planService = await Service.create({ - context: opts.context, - agentType: 'plan', - }); - - try { - await registerPlugins(app); - await registerRoutes(app, opts, service, planService); - - await app.listen({ - port, - host, - }); - - const baseUrl = `http://${host === '0.0.0.0' ? 'localhost' : host}:${port}`; - logger.logInfo(`Browser is running on ${baseUrl}`); - } catch (err) { - app.log.error(err); - logger.logError({ error: err }); - process.exit(1); - } - return app; -} diff --git a/src/server/services/completions.ts b/src/server/services/completions.ts deleted file mode 100644 index 3bbfaccd9..000000000 --- a/src/server/services/completions.ts +++ /dev/null @@ -1,242 +0,0 @@ -// @ts-nocheck -import type { AgentInputItem } from '@openai/agents'; -import { type DataStreamWriter, formatDataStreamPart } from 'ai'; -import createDebug from 'debug'; -import { isReasoningModel } from '../../provider'; -import { query } from '../../query'; -import type { Service } from '../../service'; -import { delay } from '../../utils/delay'; -import { - type AttachmentItem, - type ContextItem, - ContextType, - type ImageItem, -} from '../types/completions'; -import type { CreateServerOpts } from '../types/server'; -import { getToolApprovalService } from './tool-approval'; - -const debug = createDebug('takumi:server:completions'); - -interface RunCompletionOpts extends CreateServerOpts { - dataStream: DataStreamWriter; - service: Service; - planService: Service; - mode: string; - attachedContexts: ContextItem[]; - abortSignal?: AbortSignal; -} - -function isImageContext(context: ContextItem): context is ContextItem & { - context: ImageItem; -} { - return context.type === ContextType.IMAGE && !!context.context; -} - -function isAttachmentContext(context: ContextItem): context is ContextItem & { - context: AttachmentItem; -} { - return ( - context.type === ContextType.ATTACHMENT && - !!context.context && - !!(context.context as AttachmentItem).url - ); -} - -type ContextConverter = ( - context: ContextItem, -) => - | { type: 'input_image'; image: string; providerData: { mime_type: string } } - | { type: 'input_file'; file: string; providerData: { name: string } } - | null; - -const contextConverters: Record = { - [ContextType.IMAGE]: (context: ContextItem) => { - if (!isImageContext(context)) return null; - - const { src, mime } = context.context; - return { - type: 'input_image' as const, - image: src, - providerData: { mime_type: mime }, - }; - }, - - [ContextType.ATTACHMENT]: (context: ContextItem) => { - if (!isAttachmentContext(context)) return null; - - const { url, name } = context.context; - if (!url) return null; - - return { - type: 'input_file' as const, - file: url, - providerData: { name }, - }; - }, - - [ContextType.FILE]: () => null, - [ContextType.UNKNOWN]: () => null, -}; - -function convertUserPromptToAgentInput( - prompt: string, - attachedContexts: ContextItem[], -): AgentInputItem[] { - if (attachedContexts.length === 0) { - return [ - { - role: 'user' as const, - content: prompt, - }, - ]; - } - - const contextMessages = attachedContexts - .map((context) => { - const converter = contextConverters[context.type]; - if (!converter) { - debug(`Unknown context type: ${context.type}`); - return null; - } - return converter(context); - }) - .filter((item): item is NonNullable => item !== null); - - return [ - { - role: 'user' as const, - content: [ - { - type: 'input_text', - text: prompt, - }, - ...contextMessages, - ], - }, - ]; -} - -export async function runCode(opts: RunCompletionOpts) { - const { dataStream, mode, attachedContexts, abortSignal } = opts; - try { - const input: AgentInputItem[] = convertUserPromptToAgentInput( - opts.prompt, - attachedContexts, - ); - - debug('input', JSON.stringify(input, null, 2)); - - const service = mode === 'plan' ? opts.planService : opts.service; - const toolApprovalService = getToolApprovalService(service.context); - - debug('mode', mode); - - const result = await query({ - input, - service, - thinking: isReasoningModel(service.context.config.model), - onCancelCheck: () => abortSignal?.aborted ?? false, - onTextDelta(text) { - debug(`Text delta: ${text}`); - dataStream.writeMessageAnnotation({ - type: 'text_delta', - text, - }); - }, - async onText(text) { - dataStream.writeMessageAnnotation({ - type: 'text', - text, - mode, - }); - await delay(10); - debug(`Text: ${text}`); - dataStream.write(formatDataStreamPart('text', text)); - }, - onReasoning(text) { - debug(`Reasoning: ${text}`); - dataStream.writeMessageAnnotation({ - type: 'reasoning', - reasoning: text, - }); - dataStream.write(formatDataStreamPart('reasoning', text)); - }, - onToolUse(callId, name, params) { - debug(`Tool use: ${name} with params ${JSON.stringify(params)}`); - dataStream.writeMessageAnnotation({ - type: 'tool_call', - toolCallId: callId, - toolName: name, - args: params, - }); - dataStream.write( - formatDataStreamPart('tool_call', { - toolCallId: callId, - toolName: name, - args: params, - }), - ); - }, - onToolUseResult(callId, name, result) { - debug(`Tool use result: ${name} with result ${JSON.stringify(result)}`); - dataStream.writeMessageAnnotation({ - type: 'tool_result', - toolCallId: callId, - toolName: name, - result: result, - }); - dataStream.write( - formatDataStreamPart('tool_result', { - toolCallId: callId, - result: JSON.stringify(result), - }), - ); - }, - async onToolApprove(callId, name, params) { - debug(`Tool approval request: ${name} with callId ${callId}`); - - try { - dataStream.writeMessageAnnotation({ - type: 'tool_approval_request', - toolCallId: callId, - toolName: name, - args: params, - }); - - const approved = await toolApprovalService.requestApproval( - callId, - name, - params, - ); - - dataStream.writeMessageAnnotation({ - type: 'tool_approval_result', - toolCallId: callId, - toolName: name, - approved, - }); - - debug( - `Tool approval result: ${name} with callId ${callId}, approved: ${approved}`, - ); - return approved; - } catch (error) { - debug(`Tool approval error: ${name} with callId ${callId}`, error); - - dataStream.writeMessageAnnotation({ - type: 'tool_approval_error', - toolCallId: callId, - toolName: name, - error: error instanceof Error ? error.message : 'Unknown error', - }); - - return false; - } - }, - }); - debug('result', result); - } finally { - // TODO: Cannot destroy here, otherwise it will cause mcp to be unavailable - // await opts.context.destroy(); - } -} diff --git a/src/server/services/tool-approval.ts b/src/server/services/tool-approval.ts deleted file mode 100644 index fac95ffb8..000000000 --- a/src/server/services/tool-approval.ts +++ /dev/null @@ -1,388 +0,0 @@ -// @ts-nocheck -import createDebug from 'debug'; -import type { Context } from '../../context'; -import { createStableToolKey } from '../../utils/formatToolUse'; - -const debug = createDebug('takumi:server:tool-approval'); - -export interface PendingApproval { - callId: string; - toolName: string; - params: Record; - resolve: (approved: boolean) => void; - timestamp: number; -} - -export interface ApprovalMemory { - proceedOnce: Set; - proceedAlways: Set; - proceedAlwaysTool: Set; -} - -export interface ToolApprovalConfig { - maxPendingApprovals?: number; - maxMemorySize?: number; -} - -export class ToolApprovalService { - private pendingApprovals = new Map(); - private approvalMemory: ApprovalMemory = { - proceedOnce: new Set(), - proceedAlways: new Set(), - proceedAlwaysTool: new Set(), - }; - private readonly config: ToolApprovalConfig; - private isDestroyed = false; - private cleanupTimer?: NodeJS.Timeout; - - constructor( - private context: Context, - config: ToolApprovalConfig = {}, - ) { - this.config = { - maxPendingApprovals: 100, - maxMemorySize: 1000, - ...config, - }; - - this.setupPeriodicCleanup(); - } - - async shouldApprove( - toolName: string, - params: Record, - ): Promise { - this.checkDestroyed(); - - const approvalMode = this.context.config.approvalMode || 'default'; - - const toolCategory = this.getToolCategory(toolName); - - switch (approvalMode) { - case 'yolo': - return false; - case 'autoEdit': - return toolCategory === 'command'; - case 'default': - default: - return toolCategory !== 'read'; - } - } - - async requestApproval( - callId: string, - toolName: string, - params: Record, - ): Promise { - this.checkDestroyed(); - debug(`Requesting approval for tool: ${toolName}, callId: ${callId}`); - - // 原子性检查并添加(防止竞态条件) - if (this.pendingApprovals.has(callId)) { - debug(`CallId ${callId} already exists, rejecting duplicate request`); - throw new Error(`Approval request with callId ${callId} already exists`); - } - - if (this.pendingApprovals.size >= this.config.maxPendingApprovals!) { - debug( - `Pending approvals queue is full (${this.config.maxPendingApprovals})`, - ); - throw new Error('Too many pending approval requests'); - } - - const toolKey = createStableToolKey(toolName, params); - const toolOnlyKey = toolName; - - if ( - this.approvalMemory.proceedAlways.has(toolKey) || - this.approvalMemory.proceedAlwaysTool.has(toolOnlyKey) - ) { - debug(`Auto-approved from memory: ${toolName}`); - return true; - } - - if (this.approvalMemory.proceedOnce.has(toolKey)) { - this.approvalMemory.proceedOnce.delete(toolKey); - debug(`Auto-approved once from memory: ${toolName}`); - return true; - } - - const needsApproval = await this.shouldApprove(toolName, params); - if (!needsApproval) { - debug(`Tool ${toolName} does not need approval`); - return true; - } - - return new Promise((resolve) => { - const pendingApproval: PendingApproval = { - callId, - toolName, - params, - resolve, - timestamp: Date.now(), - }; - - this.pendingApprovals.set(callId, pendingApproval); - debug( - `Added pending approval: ${callId} (queue size: ${this.pendingApprovals.size})`, - ); - }); - } - - // 提交审批结果 - submitApproval( - callId: string, - approved: boolean, - option: 'once' | 'always' | 'always_tool' = 'once', - ): boolean { - this.checkDestroyed(); - - const pendingApproval = this.pendingApprovals.get(callId); - if (!pendingApproval) { - debug(`No pending approval found for callId: ${callId}`); - return false; - } - - const { toolName, params, resolve } = pendingApproval; - const toolKey = createStableToolKey(toolName, params); - - if (approved) { - this.updateApprovalMemory(toolKey, toolName, option); - } - - this.pendingApprovals.delete(callId); - resolve(approved); - - debug( - `Submitted approval: ${callId}, approved: ${approved}, option: ${option}`, - ); - return true; - } - - // 取消待审批请求 - cancelApproval(callId: string): boolean { - this.checkDestroyed(); - - const pendingApproval = this.pendingApprovals.get(callId); - if (!pendingApproval) { - debug(`No pending approval found for callId: ${callId}`); - return false; - } - - this.pendingApprovals.delete(callId); - - // 安全地解析 Promise(防止异常) - try { - pendingApproval.resolve(false); - } catch (error) { - debug(`Error resolving cancelled approval ${callId}:`, error); - } - - debug(`Cancelled approval: ${callId}`); - return true; - } - - // 清理所有待审批请求 - clearAllPendingApprovals(): number { - this.checkDestroyed(); - - const count = this.pendingApprovals.size; - - // 安全地拒绝所有待审批请求 - for (const [callId, approval] of this.pendingApprovals) { - try { - approval.resolve(false); - debug(`Auto-rejected pending approval: ${callId}`); - } catch (error) { - debug(`Error rejecting approval ${callId}:`, error); - } - } - - this.pendingApprovals.clear(); - debug(`Cleared ${count} pending approvals`); - return count; - } - - // 清理过期的待审批请求 - cleanupStaleApprovals(maxAgeMs: number = 30 * 60 * 1000): number { - this.checkDestroyed(); - - const now = Date.now(); - const staleCallIds: string[] = []; - - for (const [callId, approval] of this.pendingApprovals) { - if (now - approval.timestamp > maxAgeMs) { - staleCallIds.push(callId); - } - } - - for (const callId of staleCallIds) { - this.cancelApproval(callId); - } - - debug(`Cleaned up ${staleCallIds.length} stale approvals`); - return staleCallIds.length; - } - - // 清除审批记忆 - clearApprovalMemory(): void { - this.checkDestroyed(); - - this.approvalMemory.proceedOnce.clear(); - this.approvalMemory.proceedAlways.clear(); - this.approvalMemory.proceedAlwaysTool.clear(); - debug('Cleared approval memory'); - } - - // 销毁服务 - destroy(): void { - if (this.isDestroyed) { - return; - } - - debug('Destroying ToolApprovalService'); - - if (this.cleanupTimer) { - clearInterval(this.cleanupTimer); - this.cleanupTimer = undefined; - } - - this.clearAllPendingApprovals(); - this.isDestroyed = true; - } - - // 设置定期清理过期请求 - private setupPeriodicCleanup(): void { - // 每5分钟清理一次过期请求 - this.cleanupTimer = setInterval( - () => { - if (!this.isDestroyed) { - try { - this.cleanupStaleApprovals(); - } catch (error) { - debug('Error during periodic cleanup:', error); - } - } - }, - 5 * 60 * 1000, - ); - } - - // 更新审批记忆(带限制检查) - private updateApprovalMemory( - toolKey: string, - toolName: string, - option: 'once' | 'always' | 'always_tool', - ): void { - const maxSize = this.config.maxMemorySize!; - - if (option === 'always') { - if (this.approvalMemory.proceedAlways.size >= maxSize) { - // 清理最旧的条目(简单的 FIFO) - const firstKey = this.approvalMemory.proceedAlways - .values() - .next().value; - if (firstKey) { - this.approvalMemory.proceedAlways.delete(firstKey); - } - } - this.approvalMemory.proceedAlways.add(toolKey); - debug(`Added to always approve: ${toolKey}`); - } else if (option === 'always_tool') { - if (this.approvalMemory.proceedAlwaysTool.size >= maxSize) { - const firstKey = this.approvalMemory.proceedAlwaysTool - .values() - .next().value; - if (firstKey) { - this.approvalMemory.proceedAlwaysTool.delete(firstKey); - } - } - this.approvalMemory.proceedAlwaysTool.add(toolName); - debug(`Added to always approve tool: ${toolName}`); - } - } - - private checkDestroyed(): void { - if (this.isDestroyed) { - throw new Error('ToolApprovalService has been destroyed'); - } - } - - private getToolCategory( - toolName: string, - ): 'read' | 'write' | 'command' | 'network' { - const readTools = ['read', 'ls', 'glob', 'grep']; - const writeTools = ['write', 'edit']; - const commandTools = ['bash']; - const networkTools = ['fetch']; - - if (readTools.includes(toolName)) return 'read'; - if (writeTools.includes(toolName)) return 'write'; - if (commandTools.includes(toolName)) return 'command'; - if (networkTools.includes(toolName)) return 'network'; - - return 'command'; - } -} - -// 使用 WeakMap 来自动处理 Context 生命周期(修复内存泄漏) -const toolApprovalServices = new WeakMap(); - -// 存储所有活跃的服务实例,用于全局清理 -const activeServices = new Set(); - -export function getToolApprovalService( - context: Context, - config?: ToolApprovalConfig, -): ToolApprovalService { - let service = toolApprovalServices.get(context); - if (!service) { - service = new ToolApprovalService(context, config); - toolApprovalServices.set(context, service); - activeServices.add(service); - debug(`Created new ToolApprovalService for context`); - } - return service; -} - -// 手动清理指定 context 的审批服务 -export function cleanupToolApprovalService(context: Context): boolean { - const service = toolApprovalServices.get(context); - if (service) { - service.destroy(); - activeServices.delete(service); - // WeakMap 会自动清理,但我们可以显式删除 - toolApprovalServices.delete(context); - debug(`Cleaned up ToolApprovalService for context`); - return true; - } - return false; -} - -// 全局清理函数(用于进程退出时) -export function cleanupAllToolApprovalServices(): void { - debug(`Cleaning up ${activeServices.size} active ToolApprovalServices`); - for (const service of activeServices) { - try { - service.destroy(); - } catch (error) { - debug('Error destroying service:', error); - } - } - activeServices.clear(); -} - -// 进程退出时自动清理 -process.on('exit', () => { - cleanupAllToolApprovalServices(); -}); - -process.on('SIGINT', () => { - cleanupAllToolApprovalServices(); - process.exit(0); -}); - -process.on('SIGTERM', () => { - cleanupAllToolApprovalServices(); - process.exit(0); -}); diff --git a/src/server/types/app-data.ts b/src/server/types/app-data.ts deleted file mode 100644 index 265188a01..000000000 --- a/src/server/types/app-data.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { Config } from '../../config'; - -export interface ModuleIframeView { - type: 'iframe'; - src: string; -} - -export type ModuleView = ModuleIframeView; - -export interface CustomTab { - name: string; - title: string; - icon: string; - view: ModuleView; -} - -export interface ContextMenuItem { - label: string; - value: string; - icon?: string; - children?: ContextMenuItem[]; - extra?: string; -} - -export interface UISchema { - logo?: string; - customTabs?: CustomTab[]; - contextMenu?: ContextMenuItem[]; -} - -export interface ServerAppData { - productName: string; - version: string; - cwd: string; - config: Config; - ui: UISchema; -} diff --git a/src/server/types/completions.ts b/src/server/types/completions.ts deleted file mode 100644 index fd2437c70..000000000 --- a/src/server/types/completions.ts +++ /dev/null @@ -1,63 +0,0 @@ -import type { Message } from 'ai'; -import type { FileItem } from './files'; - -export interface ImageItem { - /** URL or base64 string */ - src: string; - mime: string; -} - -export interface AttachmentItem { - uid: string; - size?: number; - name: string; - fileName?: string; - url?: string; -} - -export enum ContextType { - FILE = '__file', - ATTACHMENT = '__attachment', - IMAGE = '__image', - UNKNOWN = '__unknown', -} - -export interface ContextItem { - type: ContextType; - /** - * Only when it's used in context popup menu, it woule be like \@Abc:[xyz] - * - * it should be unique - */ - value: string; - displayText: string; - context?: FileItem | ImageItem | AttachmentItem; - [key: string]: any; -} - -export interface UserMessage extends Message { - role: 'user'; - attachedContexts: ContextItem[]; - /** - * Original content input by the user, including context markers - */ - contextContent: string; - planContent?: string; -} - -export interface CompletionRequest { - messages: Array; - /** - * The mode of the completion, can be 'agent', 'ask', 'plan' - */ - mode: string; -} - -export interface CompletionTextContent { - type: 'text'; - content: string; - /** - * The reason why the completion was finished. - */ - finishReason?: 'stop' | 'length' | 'content_filter'; -} diff --git a/src/server/types/index.ts b/src/server/types/index.ts deleted file mode 100644 index 720f299be..000000000 --- a/src/server/types/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { FastifyReply, FastifyRequest } from 'fastify'; - -export * from './app-data'; -export * from './server'; - -export interface TypedRequest - extends FastifyRequest { - body: Body; - query: Query; - params: Params; -} - -export interface ApiResponse { - success: boolean; - data?: T; - error?: string; - message?: string; -} - -export type TypedReply = FastifyReply; diff --git a/src/server/types/server.ts b/src/server/types/server.ts deleted file mode 100644 index 51602b767..000000000 --- a/src/server/types/server.ts +++ /dev/null @@ -1,23 +0,0 @@ -// @ts-nocheck -import type { ModelProvider } from '@openai/agents'; -import type { Context } from '../../context'; -import type { Service } from '../../service'; -import type { ServerAppData } from './app-data'; - -export interface RunBrowserServerOpts { - prompt: string; - cwd?: string; - modelProvider?: ModelProvider; - context: Context; - logLevel?: string; - port?: number; -} - -export interface CreateServerOpts extends RunBrowserServerOpts { - appData: ServerAppData; -} - -export interface RouteCompletionsOpts extends CreateServerOpts { - service: Service; - planService: Service; -} diff --git a/src/server/types/settings.ts b/src/server/types/settings.ts deleted file mode 100644 index eb5d9ccfc..000000000 --- a/src/server/types/settings.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface SetSettingRequest { - scope?: string; - key?: string; - value?: string | boolean | string[] | number; -} - -export interface BatchUpdateRequest { - scope?: string; - settings?: Record; -} diff --git a/src/utils/files.ts b/src/utils/files.ts new file mode 100644 index 000000000..861846e95 --- /dev/null +++ b/src/utils/files.ts @@ -0,0 +1,94 @@ +import path from 'pathe'; +import { execFileNoThrow } from './execFileNoThrow'; +import { listDirectory } from './list'; + +export interface FileItem { + path: string; + type: 'file' | 'directory'; + name: string; +} + +async function getGitStatusItems(cwd: string, query?: string) { + const gitStatus = await (async () => { + // won't throw error + const { stdout } = await execFileNoThrow( + cwd, + 'git', + ['status', '--short'], + undefined, + undefined, + false, + ); + // DO NOT USE TRIM HERE, it will make the result inconsistent + return stdout; + })(); + + const files = gitStatus + .split('\n') + .filter((line) => line.trim().length > 0) + .filter( + (line) => + !line.startsWith('D') && + !line.startsWith('??') && + !line.startsWith('R'), + ) + .map((line) => line.slice(3)) + .filter( + (path) => !query || path.toLowerCase().includes(query.toLowerCase()), + ); + + return Promise.all( + files.map(async (file) => { + const relativePath = path.join(cwd, file); + const name = path.basename(file); + const item = await createFileItem(relativePath, name, 'file'); + return item; + }), + ); +} + +export async function getFiles(opts: { + cwd: string; + maxSize: number; + productName: string; + query: string; +}) { + const { cwd, query, maxSize, productName } = opts; + let items = await getGitStatusItems(cwd, query); + if (items.length < maxSize) { + const remainingSize = maxSize - items.length; + const result = listDirectory(cwd, cwd, productName, 6000).filter( + (item) => !query || item.toLowerCase().includes(query.toLowerCase()), + ); + const remainingItems = result + .slice(0, remainingSize) + .map((item) => { + const isDir = item.endsWith(path.sep); + const name = path.basename(item) + (isDir ? path.sep : ''); + const type: 'file' | 'directory' = isDir ? 'directory' : 'file'; + return createFileItem(item, name, type); + }) + .sort((a, b) => { + if (a.type !== b.type) { + return a.type === 'directory' ? -1 : 1; + } + + return a.path.localeCompare(b.path); + }); + items = [...items, ...remainingItems]; + } + return items; +} + +function createFileItem( + relativePath: string, + name: string, + type: 'file' | 'directory', +): FileItem { + const fileItem: FileItem = { + path: relativePath, + type, + name, + }; + return fileItem; +}